Drizzled Public API Documentation

date_format.cc
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include <config.h>
00021 #include <drizzled/function/time/date_format.h>
00022 #include <drizzled/session.h>
00023 #include <drizzled/time_functions.h>
00024 #include <drizzled/internal/m_string.h>
00025 #include <drizzled/typelib.h>
00026 #include <drizzled/system_variables.h>
00027 
00028 #include <cstdio>
00029 #include <algorithm>
00030 
00031 using namespace std;
00032 
00033 namespace drizzled {
00034 
00039 static bool make_date_time(Session &session,
00040                            String *format, type::Time *l_time,
00041                            type::timestamp_t type, String *str)
00042 {
00043   char intbuff[15];
00044   uint32_t hours_i;
00045   uint32_t weekday;
00046   ulong length;
00047   const char *ptr, *end;
00048   MY_LOCALE *locale= session.variables.lc_time_names;
00049 
00050   str->length(0);
00051 
00052   if (l_time->neg)
00053     str->append('-');
00054 
00055   end= (ptr= format->c_ptr()) + format->length();
00056   for (; ptr != end ; ptr++)
00057   {
00058     if (*ptr != '%' || ptr+1 == end)
00059       str->append(*ptr);
00060     else
00061     {
00062       switch (*++ptr) {
00063       case 'M':
00064         if (!l_time->month)
00065           return 1;
00066         str->append(locale->month_names->type_names[l_time->month-1], strlen(locale->month_names->type_names[l_time->month-1]));
00067         break;
00068       case 'b':
00069         if (!l_time->month)
00070           return 1;
00071         str->append(locale->ab_month_names->type_names[l_time->month-1], strlen(locale->ab_month_names->type_names[l_time->month-1]));
00072         break;
00073       case 'W':
00074         if (type == type::DRIZZLE_TIMESTAMP_TIME)
00075           return 1;
00076         weekday= calc_weekday(calc_daynr(l_time->year,l_time->month,
00077                               l_time->day),0);
00078         str->append(locale->day_names->type_names[weekday], strlen(locale->day_names->type_names[weekday]));
00079         break;
00080       case 'a':
00081         if (type == type::DRIZZLE_TIMESTAMP_TIME)
00082           return 1;
00083         weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),0);
00084         str->append(locale->ab_day_names->type_names[weekday], strlen(locale->ab_day_names->type_names[weekday]));
00085         break;
00086       case 'D':
00087   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00088     return 1;
00089   length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
00090   str->append_with_prefill(intbuff, length, 1, '0');
00091   if (l_time->day >= 10 &&  l_time->day <= 19)
00092     str->append(STRING_WITH_LEN("th"));
00093   else
00094   {
00095     switch (l_time->day %10) {
00096     case 1:
00097       str->append(STRING_WITH_LEN("st"));
00098       break;
00099     case 2:
00100       str->append(STRING_WITH_LEN("nd"));
00101       break;
00102     case 3:
00103       str->append(STRING_WITH_LEN("rd"));
00104       break;
00105     default:
00106       str->append(STRING_WITH_LEN("th"));
00107       break;
00108     }
00109   }
00110   break;
00111       case 'Y':
00112   length= internal::int10_to_str(l_time->year, intbuff, 10) - intbuff;
00113   str->append_with_prefill(intbuff, length, 4, '0');
00114   break;
00115       case 'y':
00116   length= internal::int10_to_str(l_time->year%100, intbuff, 10) - intbuff;
00117   str->append_with_prefill(intbuff, length, 2, '0');
00118   break;
00119       case 'm':
00120   length= internal::int10_to_str(l_time->month, intbuff, 10) - intbuff;
00121   str->append_with_prefill(intbuff, length, 2, '0');
00122   break;
00123       case 'c':
00124   length= internal::int10_to_str(l_time->month, intbuff, 10) - intbuff;
00125   str->append_with_prefill(intbuff, length, 1, '0');
00126   break;
00127       case 'd':
00128   length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
00129   str->append_with_prefill(intbuff, length, 2, '0');
00130   break;
00131       case 'e':
00132   length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
00133   str->append_with_prefill(intbuff, length, 1, '0');
00134   break;
00135       case 'f':
00136   length= internal::int10_to_str(l_time->second_part, intbuff, 10) - intbuff;
00137   str->append_with_prefill(intbuff, length, 6, '0');
00138   break;
00139       case 'H':
00140   length= internal::int10_to_str(l_time->hour, intbuff, 10) - intbuff;
00141   str->append_with_prefill(intbuff, length, 2, '0');
00142   break;
00143       case 'h':
00144       case 'I':
00145   hours_i= (l_time->hour%24 + 11)%12+1;
00146   length= internal::int10_to_str(hours_i, intbuff, 10) - intbuff;
00147   str->append_with_prefill(intbuff, length, 2, '0');
00148   break;
00149       case 'i':         /* minutes */
00150   length= internal::int10_to_str(l_time->minute, intbuff, 10) - intbuff;
00151   str->append_with_prefill(intbuff, length, 2, '0');
00152   break;
00153       case 'j':
00154   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00155     return 1;
00156   length= internal::int10_to_str(calc_daynr(l_time->year,l_time->month,
00157           l_time->day) -
00158          calc_daynr(l_time->year,1,1) + 1, intbuff, 10) - intbuff;
00159   str->append_with_prefill(intbuff, length, 3, '0');
00160   break;
00161       case 'k':
00162   length= internal::int10_to_str(l_time->hour, intbuff, 10) - intbuff;
00163   str->append_with_prefill(intbuff, length, 1, '0');
00164   break;
00165       case 'l':
00166   hours_i= (l_time->hour%24 + 11)%12+1;
00167   length= internal::int10_to_str(hours_i, intbuff, 10) - intbuff;
00168   str->append_with_prefill(intbuff, length, 1, '0');
00169   break;
00170       case 'p':
00171   hours_i= l_time->hour%24;
00172   str->append(hours_i < 12 ? "AM" : "PM",2);
00173   break;
00174       case 'r':
00175   length= snprintf(intbuff, sizeof(intbuff), 
00176         ((l_time->hour % 24) < 12) ?
00177                     "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM",
00178         (l_time->hour+11)%12+1,
00179         l_time->minute,
00180         l_time->second);
00181   str->append(intbuff, length);
00182   break;
00183       case 'S':
00184       case 's':
00185   length= internal::int10_to_str(l_time->second, intbuff, 10) - intbuff;
00186   str->append_with_prefill(intbuff, length, 2, '0');
00187   break;
00188       case 'T':
00189   length= snprintf(intbuff, sizeof(intbuff), 
00190         "%02d:%02d:%02d",
00191         l_time->hour,
00192         l_time->minute,
00193         l_time->second);
00194   str->append(intbuff, length);
00195   break;
00196       case 'U':
00197       case 'u':
00198       {
00199   uint32_t year;
00200   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00201     return 1;
00202   length= internal::int10_to_str(calc_week(l_time,
00203                (*ptr) == 'U' ?
00204                WEEK_FIRST_WEEKDAY : WEEK_MONDAY_FIRST,
00205                &year),
00206            intbuff, 10) - intbuff;
00207   str->append_with_prefill(intbuff, length, 2, '0');
00208       }
00209       break;
00210       case 'v':
00211       case 'V':
00212       {
00213   uint32_t year;
00214   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00215     return 1;
00216   length= internal::int10_to_str(calc_week(l_time,
00217                ((*ptr) == 'V' ?
00218           (WEEK_YEAR | WEEK_FIRST_WEEKDAY) :
00219           (WEEK_YEAR | WEEK_MONDAY_FIRST)),
00220                &year),
00221            intbuff, 10) - intbuff;
00222   str->append_with_prefill(intbuff, length, 2, '0');
00223       }
00224       break;
00225       case 'x':
00226       case 'X':
00227       {
00228   uint32_t year;
00229   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00230     return 1;
00231   (void) calc_week(l_time,
00232        ((*ptr) == 'X' ?
00233         WEEK_YEAR | WEEK_FIRST_WEEKDAY :
00234         WEEK_YEAR | WEEK_MONDAY_FIRST),
00235        &year);
00236   length= internal::int10_to_str(year, intbuff, 10) - intbuff;
00237   str->append_with_prefill(intbuff, length, 4, '0');
00238       }
00239       break;
00240       case 'w':
00241   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00242     return 1;
00243   weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
00244           l_time->day),1);
00245   length= internal::int10_to_str(weekday, intbuff, 10) - intbuff;
00246   str->append_with_prefill(intbuff, length, 1, '0');
00247   break;
00248 
00249       default:
00250   str->append(*ptr);
00251   break;
00252       }
00253     }
00254   }
00255   return 0;
00256 }
00257 
00258 void Item_func_date_format::fix_length_and_dec()
00259 {
00260   Item *arg1= args[1];
00261 
00262   decimals=0;
00263   const charset_info_st * const cs= getSession().variables.getCollation();
00264   collation.set(cs, arg1->collation.derivation);
00265   if (arg1->type() == STRING_ITEM)
00266   {                                             // Optimize the normal case
00267     fixed_length= 1;
00268     max_length= format_length(&arg1->str_value) *
00269                 collation.collation->mbmaxlen;
00270   }
00271   else
00272   {
00273     fixed_length= 0;
00274     max_length= min(arg1->max_length,(uint32_t) MAX_BLOB_WIDTH) * 10 *
00275                    collation.collation->mbmaxlen;
00276     set_if_smaller(max_length,MAX_BLOB_WIDTH);
00277   }
00278   maybe_null= 1;                                 // If wrong date
00279 }
00280 
00281 bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const
00282 {
00283   Item_func_date_format *item_func;
00284 
00285   if (item->type() != FUNC_ITEM)
00286     return 0;
00287   if (func_name() != ((Item_func*) item)->func_name())
00288     return 0;
00289   if (this == item)
00290     return 1;
00291   item_func= (Item_func_date_format*) item;
00292   if (!args[0]->eq(item_func->args[0], binary_cmp))
00293     return 0;
00294   /*
00295     We must compare format string case sensitive.
00296     This needed because format modifiers with different case,
00297     for example %m and %M, have different meaning.
00298   */
00299   if (!args[1]->eq(item_func->args[1], 1))
00300     return 0;
00301   return 1;
00302 }
00303 
00304 uint32_t Item_func_date_format::format_length(const String *format)
00305 {
00306   uint32_t size=0;
00307   const char *ptr=format->ptr();
00308   const char *end=ptr+format->length();
00309 
00310   for (; ptr != end ; ptr++)
00311   {
00312     if (*ptr != '%' || ptr == end-1)
00313       size++;
00314     else
00315     {
00316       switch(*++ptr) {
00317       case 'M': /* month, textual */
00318       case 'W': /* day (of the week), textual */
00319   size += 64; /* large for UTF8 locale data */
00320   break;
00321       case 'D': /* day (of the month), numeric plus english suffix */
00322       case 'Y': /* year, numeric, 4 digits */
00323       case 'x': /* Year, used with 'v' */
00324       case 'X': /* Year, used with 'v, where week starts with Monday' */
00325   size += 4;
00326   break;
00327       case 'a': /* locale's abbreviated weekday name (Sun..Sat) */
00328       case 'b': /* locale's abbreviated month name (Jan.Dec) */
00329   size += 32; /* large for UTF8 locale data */
00330   break;
00331       case 'j': /* day of year (001..366) */
00332   size += 3;
00333   break;
00334       case 'U': /* week (00..52) */
00335       case 'u': /* week (00..52), where week starts with Monday */
00336       case 'V': /* week 1..53 used with 'x' */
00337       case 'v': /* week 1..53 used with 'x', where week starts with Monday */
00338       case 'y': /* year, numeric, 2 digits */
00339       case 'm': /* month, numeric */
00340       case 'd': /* day (of the month), numeric */
00341       case 'h': /* hour (01..12) */
00342       case 'I': /* --||-- */
00343       case 'i': /* minutes, numeric */
00344       case 'l': /* hour ( 1..12) */
00345       case 'p': /* locale's AM or PM */
00346       case 'S': /* second (00..61) */
00347       case 's': /* seconds, numeric */
00348       case 'c': /* month (0..12) */
00349       case 'e': /* day (0..31) */
00350   size += 2;
00351   break;
00352       case 'k': /* hour ( 0..23) */
00353       case 'H': /* hour (00..23; value > 23 OK, padding always 2-digit) */
00354   size += 7; /* docs allow > 23, range depends on sizeof(unsigned int) */
00355   break;
00356       case 'r': /* time, 12-hour (hh:mm:ss [AP]M) */
00357   size += 11;
00358   break;
00359       case 'T': /* time, 24-hour (hh:mm:ss) */
00360   size += 8;
00361   break;
00362       case 'f': /* microseconds */
00363   size += 6;
00364   break;
00365       case 'w': /* day (of the week), numeric */
00366       case '%':
00367       default:
00368   size++;
00369   break;
00370       }
00371     }
00372   }
00373   return size;
00374 }
00375 
00376 
00377 String *Item_func_date_format::val_str(String *str)
00378 {
00379   String *format;
00380   type::Time l_time;
00381   uint32_t size;
00382   assert(fixed == 1);
00383 
00384   if (!is_time_format)
00385   {
00386     if (get_arg0_date(l_time, TIME_FUZZY_DATE))
00387       return 0;
00388   }
00389   else
00390   {
00391     String *res=args[0]->val_str(str);
00392     if (not res || str_to_time_with_warn(getSession(), *res, l_time))
00393       goto null_date;
00394 
00395     l_time.year=l_time.month=l_time.day=0;
00396     null_value=0;
00397   }
00398 
00399   if (!(format = args[1]->val_str(str)) || !format->length())
00400     goto null_date;
00401 
00402   if (fixed_length)
00403     size= max_length;
00404   else
00405     size= format_length(format);
00406 
00407   if (size < type::Time::MAX_STRING_LENGTH)
00408     size= type::Time::MAX_STRING_LENGTH;
00409 
00410   if (format == str)
00411     str= &value;        // Save result here
00412 
00413   str->alloc(size);
00414 
00415   /* Create the result string */
00416   str->set_charset(collation.collation);
00417   if (not make_date_time(getSession(),
00418                          format, &l_time,
00419                          is_time_format ? type::DRIZZLE_TIMESTAMP_TIME :
00420                          type::DRIZZLE_TIMESTAMP_DATE,
00421                          str))
00422     return str;
00423 
00424 null_date:
00425   null_value=1;
00426 
00427   return 0;
00428 }
00429 
00430 } /* namespace drizzled */