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 
00027 #include <cstdio>
00028 #include <algorithm>
00029 
00030 using namespace std;
00031 
00032 namespace drizzled
00033 {
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],
00067                     strlen(locale->month_names->type_names[l_time->month-1]),
00068                     system_charset_info);
00069         break;
00070       case 'b':
00071         if (!l_time->month)
00072           return 1;
00073         str->append(locale->ab_month_names->type_names[l_time->month-1],
00074                     strlen(locale->ab_month_names->type_names[l_time->month-1]),
00075                     system_charset_info);
00076         break;
00077       case 'W':
00078         if (type == type::DRIZZLE_TIMESTAMP_TIME)
00079           return 1;
00080         weekday= calc_weekday(calc_daynr(l_time->year,l_time->month,
00081                               l_time->day),0);
00082         str->append(locale->day_names->type_names[weekday],
00083                     strlen(locale->day_names->type_names[weekday]),
00084                     system_charset_info);
00085         break;
00086       case 'a':
00087         if (type == type::DRIZZLE_TIMESTAMP_TIME)
00088           return 1;
00089         weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
00090                              l_time->day),0);
00091         str->append(locale->ab_day_names->type_names[weekday],
00092                     strlen(locale->ab_day_names->type_names[weekday]),
00093                     system_charset_info);
00094         break;
00095       case 'D':
00096   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00097     return 1;
00098   length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
00099   str->append_with_prefill(intbuff, length, 1, '0');
00100   if (l_time->day >= 10 &&  l_time->day <= 19)
00101     str->append(STRING_WITH_LEN("th"));
00102   else
00103   {
00104     switch (l_time->day %10) {
00105     case 1:
00106       str->append(STRING_WITH_LEN("st"));
00107       break;
00108     case 2:
00109       str->append(STRING_WITH_LEN("nd"));
00110       break;
00111     case 3:
00112       str->append(STRING_WITH_LEN("rd"));
00113       break;
00114     default:
00115       str->append(STRING_WITH_LEN("th"));
00116       break;
00117     }
00118   }
00119   break;
00120       case 'Y':
00121   length= internal::int10_to_str(l_time->year, intbuff, 10) - intbuff;
00122   str->append_with_prefill(intbuff, length, 4, '0');
00123   break;
00124       case 'y':
00125   length= internal::int10_to_str(l_time->year%100, intbuff, 10) - intbuff;
00126   str->append_with_prefill(intbuff, length, 2, '0');
00127   break;
00128       case 'm':
00129   length= internal::int10_to_str(l_time->month, intbuff, 10) - intbuff;
00130   str->append_with_prefill(intbuff, length, 2, '0');
00131   break;
00132       case 'c':
00133   length= internal::int10_to_str(l_time->month, intbuff, 10) - intbuff;
00134   str->append_with_prefill(intbuff, length, 1, '0');
00135   break;
00136       case 'd':
00137   length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
00138   str->append_with_prefill(intbuff, length, 2, '0');
00139   break;
00140       case 'e':
00141   length= internal::int10_to_str(l_time->day, intbuff, 10) - intbuff;
00142   str->append_with_prefill(intbuff, length, 1, '0');
00143   break;
00144       case 'f':
00145   length= internal::int10_to_str(l_time->second_part, intbuff, 10) - intbuff;
00146   str->append_with_prefill(intbuff, length, 6, '0');
00147   break;
00148       case 'H':
00149   length= internal::int10_to_str(l_time->hour, intbuff, 10) - intbuff;
00150   str->append_with_prefill(intbuff, length, 2, '0');
00151   break;
00152       case 'h':
00153       case 'I':
00154   hours_i= (l_time->hour%24 + 11)%12+1;
00155   length= internal::int10_to_str(hours_i, intbuff, 10) - intbuff;
00156   str->append_with_prefill(intbuff, length, 2, '0');
00157   break;
00158       case 'i':         /* minutes */
00159   length= internal::int10_to_str(l_time->minute, intbuff, 10) - intbuff;
00160   str->append_with_prefill(intbuff, length, 2, '0');
00161   break;
00162       case 'j':
00163   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00164     return 1;
00165   length= internal::int10_to_str(calc_daynr(l_time->year,l_time->month,
00166           l_time->day) -
00167          calc_daynr(l_time->year,1,1) + 1, intbuff, 10) - intbuff;
00168   str->append_with_prefill(intbuff, length, 3, '0');
00169   break;
00170       case 'k':
00171   length= internal::int10_to_str(l_time->hour, intbuff, 10) - intbuff;
00172   str->append_with_prefill(intbuff, length, 1, '0');
00173   break;
00174       case 'l':
00175   hours_i= (l_time->hour%24 + 11)%12+1;
00176   length= internal::int10_to_str(hours_i, intbuff, 10) - intbuff;
00177   str->append_with_prefill(intbuff, length, 1, '0');
00178   break;
00179       case 'p':
00180   hours_i= l_time->hour%24;
00181   str->append(hours_i < 12 ? "AM" : "PM",2);
00182   break;
00183       case 'r':
00184   length= snprintf(intbuff, sizeof(intbuff), 
00185         ((l_time->hour % 24) < 12) ?
00186                     "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM",
00187         (l_time->hour+11)%12+1,
00188         l_time->minute,
00189         l_time->second);
00190   str->append(intbuff, length);
00191   break;
00192       case 'S':
00193       case 's':
00194   length= internal::int10_to_str(l_time->second, intbuff, 10) - intbuff;
00195   str->append_with_prefill(intbuff, length, 2, '0');
00196   break;
00197       case 'T':
00198   length= snprintf(intbuff, sizeof(intbuff), 
00199         "%02d:%02d:%02d",
00200         l_time->hour,
00201         l_time->minute,
00202         l_time->second);
00203   str->append(intbuff, length);
00204   break;
00205       case 'U':
00206       case 'u':
00207       {
00208   uint32_t year;
00209   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00210     return 1;
00211   length= internal::int10_to_str(calc_week(l_time,
00212                (*ptr) == 'U' ?
00213                WEEK_FIRST_WEEKDAY : WEEK_MONDAY_FIRST,
00214                &year),
00215            intbuff, 10) - intbuff;
00216   str->append_with_prefill(intbuff, length, 2, '0');
00217       }
00218       break;
00219       case 'v':
00220       case 'V':
00221       {
00222   uint32_t year;
00223   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00224     return 1;
00225   length= internal::int10_to_str(calc_week(l_time,
00226                ((*ptr) == 'V' ?
00227           (WEEK_YEAR | WEEK_FIRST_WEEKDAY) :
00228           (WEEK_YEAR | WEEK_MONDAY_FIRST)),
00229                &year),
00230            intbuff, 10) - intbuff;
00231   str->append_with_prefill(intbuff, length, 2, '0');
00232       }
00233       break;
00234       case 'x':
00235       case 'X':
00236       {
00237   uint32_t year;
00238   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00239     return 1;
00240   (void) calc_week(l_time,
00241        ((*ptr) == 'X' ?
00242         WEEK_YEAR | WEEK_FIRST_WEEKDAY :
00243         WEEK_YEAR | WEEK_MONDAY_FIRST),
00244        &year);
00245   length= internal::int10_to_str(year, intbuff, 10) - intbuff;
00246   str->append_with_prefill(intbuff, length, 4, '0');
00247       }
00248       break;
00249       case 'w':
00250   if (type == type::DRIZZLE_TIMESTAMP_TIME)
00251     return 1;
00252   weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,
00253           l_time->day),1);
00254   length= internal::int10_to_str(weekday, intbuff, 10) - intbuff;
00255   str->append_with_prefill(intbuff, length, 1, '0');
00256   break;
00257 
00258       default:
00259   str->append(*ptr);
00260   break;
00261       }
00262     }
00263   }
00264   return 0;
00265 }
00266 
00267 void Item_func_date_format::fix_length_and_dec()
00268 {
00269   Item *arg1= args[1];
00270 
00271   decimals=0;
00272   const CHARSET_INFO * const cs= getSession().variables.getCollation();
00273   collation.set(cs, arg1->collation.derivation);
00274   if (arg1->type() == STRING_ITEM)
00275   {                                             // Optimize the normal case
00276     fixed_length= 1;
00277     max_length= format_length(&arg1->str_value) *
00278                 collation.collation->mbmaxlen;
00279   }
00280   else
00281   {
00282     fixed_length= 0;
00283     max_length= min(arg1->max_length,(uint32_t) MAX_BLOB_WIDTH) * 10 *
00284                    collation.collation->mbmaxlen;
00285     set_if_smaller(max_length,MAX_BLOB_WIDTH);
00286   }
00287   maybe_null= 1;                                 // If wrong date
00288 }
00289 
00290 bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const
00291 {
00292   Item_func_date_format *item_func;
00293 
00294   if (item->type() != FUNC_ITEM)
00295     return 0;
00296   if (func_name() != ((Item_func*) item)->func_name())
00297     return 0;
00298   if (this == item)
00299     return 1;
00300   item_func= (Item_func_date_format*) item;
00301   if (!args[0]->eq(item_func->args[0], binary_cmp))
00302     return 0;
00303   /*
00304     We must compare format string case sensitive.
00305     This needed because format modifiers with different case,
00306     for example %m and %M, have different meaning.
00307   */
00308   if (!args[1]->eq(item_func->args[1], 1))
00309     return 0;
00310   return 1;
00311 }
00312 
00313 uint32_t Item_func_date_format::format_length(const String *format)
00314 {
00315   uint32_t size=0;
00316   const char *ptr=format->ptr();
00317   const char *end=ptr+format->length();
00318 
00319   for (; ptr != end ; ptr++)
00320   {
00321     if (*ptr != '%' || ptr == end-1)
00322       size++;
00323     else
00324     {
00325       switch(*++ptr) {
00326       case 'M': /* month, textual */
00327       case 'W': /* day (of the week), textual */
00328   size += 64; /* large for UTF8 locale data */
00329   break;
00330       case 'D': /* day (of the month), numeric plus english suffix */
00331       case 'Y': /* year, numeric, 4 digits */
00332       case 'x': /* Year, used with 'v' */
00333       case 'X': /* Year, used with 'v, where week starts with Monday' */
00334   size += 4;
00335   break;
00336       case 'a': /* locale's abbreviated weekday name (Sun..Sat) */
00337       case 'b': /* locale's abbreviated month name (Jan.Dec) */
00338   size += 32; /* large for UTF8 locale data */
00339   break;
00340       case 'j': /* day of year (001..366) */
00341   size += 3;
00342   break;
00343       case 'U': /* week (00..52) */
00344       case 'u': /* week (00..52), where week starts with Monday */
00345       case 'V': /* week 1..53 used with 'x' */
00346       case 'v': /* week 1..53 used with 'x', where week starts with Monday */
00347       case 'y': /* year, numeric, 2 digits */
00348       case 'm': /* month, numeric */
00349       case 'd': /* day (of the month), numeric */
00350       case 'h': /* hour (01..12) */
00351       case 'I': /* --||-- */
00352       case 'i': /* minutes, numeric */
00353       case 'l': /* hour ( 1..12) */
00354       case 'p': /* locale's AM or PM */
00355       case 'S': /* second (00..61) */
00356       case 's': /* seconds, numeric */
00357       case 'c': /* month (0..12) */
00358       case 'e': /* day (0..31) */
00359   size += 2;
00360   break;
00361       case 'k': /* hour ( 0..23) */
00362       case 'H': /* hour (00..23; value > 23 OK, padding always 2-digit) */
00363   size += 7; /* docs allow > 23, range depends on sizeof(unsigned int) */
00364   break;
00365       case 'r': /* time, 12-hour (hh:mm:ss [AP]M) */
00366   size += 11;
00367   break;
00368       case 'T': /* time, 24-hour (hh:mm:ss) */
00369   size += 8;
00370   break;
00371       case 'f': /* microseconds */
00372   size += 6;
00373   break;
00374       case 'w': /* day (of the week), numeric */
00375       case '%':
00376       default:
00377   size++;
00378   break;
00379       }
00380     }
00381   }
00382   return size;
00383 }
00384 
00385 
00386 String *Item_func_date_format::val_str(String *str)
00387 {
00388   String *format;
00389   type::Time l_time;
00390   uint32_t size;
00391   assert(fixed == 1);
00392 
00393   if (!is_time_format)
00394   {
00395     if (get_arg0_date(l_time, TIME_FUZZY_DATE))
00396       return 0;
00397   }
00398   else
00399   {
00400     String *res;
00401     if (!(res=args[0]->val_str(str)) ||
00402         (str_to_time_with_warn(&getSession(), res->ptr(), res->length(), &l_time)))
00403       goto null_date;
00404 
00405     l_time.year=l_time.month=l_time.day=0;
00406     null_value=0;
00407   }
00408 
00409   if (!(format = args[1]->val_str(str)) || !format->length())
00410     goto null_date;
00411 
00412   if (fixed_length)
00413     size= max_length;
00414   else
00415     size= format_length(format);
00416 
00417   if (size < type::Time::MAX_STRING_LENGTH)
00418     size= type::Time::MAX_STRING_LENGTH;
00419 
00420   if (format == str)
00421     str= &value;        // Save result here
00422 
00423   if (str->alloc(size))
00424     goto null_date;
00425 
00426   /* Create the result string */
00427   str->set_charset(collation.collation);
00428   if (not make_date_time(getSession(),
00429                          format, &l_time,
00430                          is_time_format ? type::DRIZZLE_TIMESTAMP_TIME :
00431                          type::DRIZZLE_TIMESTAMP_DATE,
00432                          str))
00433     return str;
00434 
00435 null_date:
00436   null_value=1;
00437 
00438   return 0;
00439 }
00440 
00441 } /* namespace drizzled */