Drizzled Public API Documentation

date_format.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2008 Sun Microsystems, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <config.h>
21 #include <drizzled/function/time/date_format.h>
22 #include <drizzled/session.h>
23 #include <drizzled/time_functions.h>
24 #include <drizzled/internal/m_string.h>
25 #include <drizzled/typelib.h>
26 #include <drizzled/system_variables.h>
27 
28 #include <cstdio>
29 #include <algorithm>
30 
31 using namespace std;
32 
33 namespace drizzled {
34 
39 static bool make_date_time(Session &session,
40  String *format, type::Time *l_time,
41  type::timestamp_t type, String *str)
42 {
43  char intbuff[15];
44  uint32_t hours_i;
45  uint32_t weekday;
46  ulong length;
47  const char *ptr, *end;
48  MY_LOCALE *locale= session.variables.lc_time_names;
49 
50  str->length(0);
51 
52  if (l_time->neg)
53  str->append('-');
54 
55  end= (ptr= format->c_ptr()) + format->length();
56  for (; ptr != end ; ptr++)
57  {
58  if (*ptr != '%' || ptr+1 == end)
59  str->append(*ptr);
60  else
61  {
62  switch (*++ptr) {
63  case 'M':
64  if (!l_time->month)
65  return 1;
66  str->append(locale->month_names->type_names[l_time->month-1], strlen(locale->month_names->type_names[l_time->month-1]));
67  break;
68  case 'b':
69  if (!l_time->month)
70  return 1;
71  str->append(locale->ab_month_names->type_names[l_time->month-1], strlen(locale->ab_month_names->type_names[l_time->month-1]));
72  break;
73  case 'W':
74  if (type == type::DRIZZLE_TIMESTAMP_TIME)
75  return 1;
76  weekday= calc_weekday(calc_daynr(l_time->year,l_time->month,
77  l_time->day),0);
78  str->append(locale->day_names->type_names[weekday], strlen(locale->day_names->type_names[weekday]));
79  break;
80  case 'a':
81  if (type == type::DRIZZLE_TIMESTAMP_TIME)
82  return 1;
83  weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),0);
84  str->append(locale->ab_day_names->type_names[weekday], strlen(locale->ab_day_names->type_names[weekday]));
85  break;
86  case 'D':
87  if (type == type::DRIZZLE_TIMESTAMP_TIME)
88  return 1;
89  length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
90  str->append_with_prefill(intbuff, length, 1, '0');
91  if (l_time->day >= 10 && l_time->day <= 19)
92  str->append(STRING_WITH_LEN("th"));
93  else
94  {
95  switch (l_time->day %10) {
96  case 1:
97  str->append(STRING_WITH_LEN("st"));
98  break;
99  case 2:
100  str->append(STRING_WITH_LEN("nd"));
101  break;
102  case 3:
103  str->append(STRING_WITH_LEN("rd"));
104  break;
105  default:
106  str->append(STRING_WITH_LEN("th"));
107  break;
108  }
109  }
110  break;
111  case 'Y':
112  length= internal::int10_to_str(l_time->year, intbuff, 10) - intbuff;
113  str->append_with_prefill(intbuff, length, 4, '0');
114  break;
115  case 'y':
116  length= internal::int10_to_str(l_time->year%100, intbuff, 10) - intbuff;
117  str->append_with_prefill(intbuff, length, 2, '0');
118  break;
119  case 'm':
120  length= internal::int10_to_str(l_time->month, intbuff, 10) - intbuff;
121  str->append_with_prefill(intbuff, length, 2, '0');
122  break;
123  case 'c':
124  length= internal::int10_to_str(l_time->month, intbuff, 10) - intbuff;
125  str->append_with_prefill(intbuff, length, 1, '0');
126  break;
127  case 'd':
128  length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
129  str->append_with_prefill(intbuff, length, 2, '0');
130  break;
131  case 'e':
132  length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
133  str->append_with_prefill(intbuff, length, 1, '0');
134  break;
135  case 'f':
136  length= internal::int10_to_str(l_time->second_part, intbuff, 10) - intbuff;
137  str->append_with_prefill(intbuff, length, 6, '0');
138  break;
139  case 'H':
140  length= internal::int10_to_str(l_time->hour, intbuff, 10) - intbuff;
141  str->append_with_prefill(intbuff, length, 2, '0');
142  break;
143  case 'h':
144  case 'I':
145  hours_i= (l_time->hour%24 + 11)%12+1;
146  length= internal::int10_to_str(hours_i, intbuff, 10) - intbuff;
147  str->append_with_prefill(intbuff, length, 2, '0');
148  break;
149  case 'i': /* minutes */
150  length= internal::int10_to_str(l_time->minute, intbuff, 10) - intbuff;
151  str->append_with_prefill(intbuff, length, 2, '0');
152  break;
153  case 'j':
154  if (type == type::DRIZZLE_TIMESTAMP_TIME)
155  return 1;
156  length= internal::int10_to_str(calc_daynr(l_time->year,l_time->month,
157  l_time->day) -
158  calc_daynr(l_time->year,1,1) + 1, intbuff, 10) - intbuff;
159  str->append_with_prefill(intbuff, length, 3, '0');
160  break;
161  case 'k':
162  length= internal::int10_to_str(l_time->hour, intbuff, 10) - intbuff;
163  str->append_with_prefill(intbuff, length, 1, '0');
164  break;
165  case 'l':
166  hours_i= (l_time->hour%24 + 11)%12+1;
167  length= internal::int10_to_str(hours_i, intbuff, 10) - intbuff;
168  str->append_with_prefill(intbuff, length, 1, '0');
169  break;
170  case 'p':
171  hours_i= l_time->hour%24;
172  str->append(hours_i < 12 ? "AM" : "PM",2);
173  break;
174  case 'r':
175  length= snprintf(intbuff, sizeof(intbuff),
176  ((l_time->hour % 24) < 12) ?
177  "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM",
178  (l_time->hour+11)%12+1,
179  l_time->minute,
180  l_time->second);
181  str->append(intbuff, length);
182  break;
183  case 'S':
184  case 's':
185  length= internal::int10_to_str(l_time->second, intbuff, 10) - intbuff;
186  str->append_with_prefill(intbuff, length, 2, '0');
187  break;
188  case 'T':
189  length= snprintf(intbuff, sizeof(intbuff),
190  "%02d:%02d:%02d",
191  l_time->hour,
192  l_time->minute,
193  l_time->second);
194  str->append(intbuff, length);
195  break;
196  case 'U':
197  case 'u':
198  {
199  uint32_t year;
200  if (type == type::DRIZZLE_TIMESTAMP_TIME)
201  return 1;
202  length= internal::int10_to_str(calc_week(l_time,
203  (*ptr) == 'U' ?
204  WEEK_FIRST_WEEKDAY : WEEK_MONDAY_FIRST,
205  &year),
206  intbuff, 10) - intbuff;
207  str->append_with_prefill(intbuff, length, 2, '0');
208  }
209  break;
210  case 'v':
211  case 'V':
212  {
213  uint32_t year;
214  if (type == type::DRIZZLE_TIMESTAMP_TIME)
215  return 1;
216  length= internal::int10_to_str(calc_week(l_time,
217  ((*ptr) == 'V' ?
218  (WEEK_YEAR | WEEK_FIRST_WEEKDAY) :
219  (WEEK_YEAR | WEEK_MONDAY_FIRST)),
220  &year),
221  intbuff, 10) - intbuff;
222  str->append_with_prefill(intbuff, length, 2, '0');
223  }
224  break;
225  case 'x':
226  case 'X':
227  {
228  uint32_t year;
229  if (type == type::DRIZZLE_TIMESTAMP_TIME)
230  return 1;
231  (void) calc_week(l_time,
232  ((*ptr) == 'X' ?
233  WEEK_YEAR | WEEK_FIRST_WEEKDAY :
234  WEEK_YEAR | WEEK_MONDAY_FIRST),
235  &year);
236  length= internal::int10_to_str(year, intbuff, 10) - intbuff;
237  str->append_with_prefill(intbuff, length, 4, '0');
238  }
239  break;
240  case 'w':
241  if (type == type::DRIZZLE_TIMESTAMP_TIME)
242  return 1;
243  weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
244  l_time->day),1);
245  length= internal::int10_to_str(weekday, intbuff, 10) - intbuff;
246  str->append_with_prefill(intbuff, length, 1, '0');
247  break;
248 
249  default:
250  str->append(*ptr);
251  break;
252  }
253  }
254  }
255  return 0;
256 }
257 
258 void Item_func_date_format::fix_length_and_dec()
259 {
260  Item *arg1= args[1];
261 
262  decimals=0;
263  const charset_info_st * const cs= getSession().variables.getCollation();
264  collation.set(cs, arg1->collation.derivation);
265  if (arg1->type() == STRING_ITEM)
266  { // Optimize the normal case
267  fixed_length= 1;
268  max_length= format_length(&arg1->str_value) *
269  collation.collation->mbmaxlen;
270  }
271  else
272  {
273  fixed_length= 0;
274  max_length= min(arg1->max_length,(uint32_t) MAX_BLOB_WIDTH) * 10 *
275  collation.collation->mbmaxlen;
276  set_if_smaller(max_length,MAX_BLOB_WIDTH);
277  }
278  maybe_null= 1; // If wrong date
279 }
280 
281 bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const
282 {
283  Item_func_date_format *item_func;
284 
285  if (item->type() != FUNC_ITEM)
286  return 0;
287  if (func_name() != ((Item_func*) item)->func_name())
288  return 0;
289  if (this == item)
290  return 1;
291  item_func= (Item_func_date_format*) item;
292  if (!args[0]->eq(item_func->args[0], binary_cmp))
293  return 0;
294  /*
295  We must compare format string case sensitive.
296  This needed because format modifiers with different case,
297  for example %m and %M, have different meaning.
298  */
299  if (!args[1]->eq(item_func->args[1], 1))
300  return 0;
301  return 1;
302 }
303 
304 uint32_t Item_func_date_format::format_length(const String *format)
305 {
306  uint32_t size=0;
307  const char *ptr=format->ptr();
308  const char *end=ptr+format->length();
309 
310  for (; ptr != end ; ptr++)
311  {
312  if (*ptr != '%' || ptr == end-1)
313  size++;
314  else
315  {
316  switch(*++ptr) {
317  case 'M': /* month, textual */
318  case 'W': /* day (of the week), textual */
319  size += 64; /* large for UTF8 locale data */
320  break;
321  case 'D': /* day (of the month), numeric plus english suffix */
322  case 'Y': /* year, numeric, 4 digits */
323  case 'x': /* Year, used with 'v' */
324  case 'X': /* Year, used with 'v, where week starts with Monday' */
325  size += 4;
326  break;
327  case 'a': /* locale's abbreviated weekday name (Sun..Sat) */
328  case 'b': /* locale's abbreviated month name (Jan.Dec) */
329  size += 32; /* large for UTF8 locale data */
330  break;
331  case 'j': /* day of year (001..366) */
332  size += 3;
333  break;
334  case 'U': /* week (00..52) */
335  case 'u': /* week (00..52), where week starts with Monday */
336  case 'V': /* week 1..53 used with 'x' */
337  case 'v': /* week 1..53 used with 'x', where week starts with Monday */
338  case 'y': /* year, numeric, 2 digits */
339  case 'm': /* month, numeric */
340  case 'd': /* day (of the month), numeric */
341  case 'h': /* hour (01..12) */
342  case 'I': /* --||-- */
343  case 'i': /* minutes, numeric */
344  case 'l': /* hour ( 1..12) */
345  case 'p': /* locale's AM or PM */
346  case 'S': /* second (00..61) */
347  case 's': /* seconds, numeric */
348  case 'c': /* month (0..12) */
349  case 'e': /* day (0..31) */
350  size += 2;
351  break;
352  case 'k': /* hour ( 0..23) */
353  case 'H': /* hour (00..23; value > 23 OK, padding always 2-digit) */
354  size += 7; /* docs allow > 23, range depends on sizeof(unsigned int) */
355  break;
356  case 'r': /* time, 12-hour (hh:mm:ss [AP]M) */
357  size += 11;
358  break;
359  case 'T': /* time, 24-hour (hh:mm:ss) */
360  size += 8;
361  break;
362  case 'f': /* microseconds */
363  size += 6;
364  break;
365  case 'w': /* day (of the week), numeric */
366  case '%':
367  default:
368  size++;
369  break;
370  }
371  }
372  }
373  return size;
374 }
375 
376 
377 String *Item_func_date_format::val_str(String *str)
378 {
379  String *format;
380  type::Time l_time;
381  uint32_t size;
382  assert(fixed == 1);
383 
384  if (!is_time_format)
385  {
386  if (get_arg0_date(l_time, TIME_FUZZY_DATE))
387  return 0;
388  }
389  else
390  {
391  String *res=args[0]->val_str(str);
392  if (not res || str_to_time_with_warn(getSession(), *res, l_time))
393  goto null_date;
394 
395  l_time.year=l_time.month=l_time.day=0;
396  null_value=0;
397  }
398 
399  if (!(format = args[1]->val_str(str)) || !format->length())
400  goto null_date;
401 
402  if (fixed_length)
403  size= max_length;
404  else
405  size= format_length(format);
406 
407  if (size < type::Time::MAX_STRING_LENGTH)
408  size= type::Time::MAX_STRING_LENGTH;
409 
410  if (format == str)
411  str= &value; // Save result here
412 
413  str->alloc(size);
414 
415  /* Create the result string */
416  str->set_charset(collation.collation);
417  if (not make_date_time(getSession(),
418  format, &l_time,
419  is_time_format ? type::DRIZZLE_TIMESTAMP_TIME :
420  type::DRIZZLE_TIMESTAMP_DATE,
421  str))
422  return str;
423 
424 null_date:
425  null_value=1;
426 
427  return 0;
428 }
429 
430 } /* namespace drizzled */