Drizzled Public API Documentation

time.cc
00001 /* Copyright (C) 2004-2006 MySQL AB
00002 
00003  This program is free software; you can redistribute it and/or modify
00004  it under the terms of the GNU General Public License as published by
00005  the Free Software Foundation; version 2 of the License.
00006 
00007  This program is distributed in the hope that it will be useful,
00008  but WITHOUT ANY WARRANTY; without even the implied warranty of
00009  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010  GNU General Public License for more details.
00011 
00012  You should have received a copy of the GNU General Public License
00013  along with this program; if not, write to the Free Software
00014  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 #include <config.h>
00017 
00018 #include <drizzled/type/time.h>
00019 
00020 #include <drizzled/util/gmtime.h>
00021 
00022 #include <drizzled/internal/m_string.h>
00023 #include <drizzled/charset_info.h>
00024 #include <drizzled/util/test.h>
00025 #include <drizzled/definitions.h>
00026 #include <drizzled/sql_string.h>
00027 
00028 #include <cstdio>
00029 #include <algorithm>
00030 
00031 using namespace std;
00032 
00033 namespace drizzled
00034 {
00035 
00036 static int check_time_range(type::Time *my_time, int *warning);
00037 
00038 /* Windows version of localtime_r() is declared in my_ptrhead.h */
00039 
00040 uint64_t log_10_int[20]=
00041 {
00042   1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
00043   100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
00044   1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
00045   1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
00046   1000000000000000000ULL, 10000000000000000000ULL
00047 };
00048 
00049 
00050 /* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
00051 
00052 static unsigned char internal_format_positions[]=
00053 {0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
00054 
00055 static char time_separator=':';
00056 
00057 static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
00058 unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
00059 
00060 /*
00061   Offset of system time zone from UTC in seconds used to speed up
00062   work of my_system_gmt_sec() function.
00063 */
00064 static long my_time_zone=0;
00065 
00066 
00067 /* Calc days in one year. works with 0 <= year <= 99 */
00068 
00069 uint32_t calc_days_in_year(uint32_t year)
00070 {
00071   return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
00072           366 : 365);
00073 }
00074 
00075 
00076 namespace type {
00098 bool Time::check(bool not_zero_date, uint32_t flags, type::cut_t &was_cut) const
00099 {
00100   if (not_zero_date)
00101   {
00102     if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
00103          (month == 0 || day == 0)) ||
00104         (not (flags & TIME_INVALID_DATES) &&
00105          month && day > days_in_month[month-1] &&
00106          (month != 2 || calc_days_in_year(year) != 366 ||
00107           day != 29)))
00108     {
00109       was_cut= type::INVALID;
00110       return true;
00111     }
00112   }
00113   else if (flags & TIME_NO_ZERO_DATE)
00114   {
00115     /*
00116       We don't set &was_cut here to signal that the problem was a zero date
00117       and not an invalid date
00118     */
00119     return true;
00120   }
00121   return false;
00122 }
00123 
00124 /*
00125   Convert a timestamp string to a type::Time value.
00126 
00127   SYNOPSIS
00128     store()
00129     str                 String to parse
00130     length              Length of string
00131     l_time              Date is stored here
00132     flags               Bitmap of following items
00133                         TIME_FUZZY_DATE    Set if we should allow partial dates
00134                         TIME_DATETIME_ONLY Set if we only allow full datetimes.
00135                         TIME_NO_ZERO_IN_DATE  Don't allow partial dates
00136                         TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
00137                         TIME_INVALID_DATES  Allow 2000-02-31
00138     was_cut             0 Value OK
00139       1       If value was cut during conversion
00140       2 check(date,flags) considers date invalid
00141 
00142   DESCRIPTION
00143     At least the following formats are recogniced (based on number of digits)
00144     YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
00145     YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
00146     YYYYMMDDTHHMMSS  where T is a the character T (ISO8601)
00147     Also dates where all parts are zero are allowed
00148 
00149     The second part may have an optional .###### fraction part.
00150 
00151   NOTES
00152    This function should work with a format position vector as long as the
00153    following things holds:
00154    - All date are kept together and all time parts are kept together
00155    - Date and time parts must be separated by blank
00156    - Second fractions must come after second part and be separated
00157      by a '.'.  (The second fractions are optional)
00158    - AM/PM must come after second fractions (or after seconds if no fractions)
00159    - Year must always been specified.
00160    - If time is before date, then we will use datetime format only if
00161      the argument consist of two parts, separated by space.
00162      Otherwise we will assume the argument is a date.
00163    - The hour part must be specified in hour-minute-second order.
00164 
00165   RETURN VALUES
00166     DRIZZLE_TIMESTAMP_NONE        String wasn't a timestamp, like
00167                                 [DD [HH:[MM:[SS]]]].fraction.
00168                                 l_time is not changed.
00169     DRIZZLE_TIMESTAMP_DATE        DATE string (YY MM and DD parts ok)
00170     DRIZZLE_TIMESTAMP_DATETIME    Full timestamp
00171     DRIZZLE_TIMESTAMP_ERROR       Timestamp with wrong values.
00172                                 All elements in l_time is set to 0
00173 */
00174 
00175 #define MAX_DATE_PARTS 8
00176 
00177 type::timestamp_t Time::store(const char *str, uint32_t length, uint32_t flags, type::cut_t &was_cut)
00178 {
00179   uint32_t field_length, year_length=4, digits, i, number_of_fields;
00180   uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
00181   uint32_t add_hours= 0, start_loop;
00182   uint32_t not_zero_date, allow_space;
00183   bool is_internal_format;
00184   const char *pos, *last_field_pos=NULL;
00185   const char *end=str+length;
00186   const unsigned char *format_position;
00187   bool found_delimitier= 0, found_space= 0;
00188   uint32_t frac_pos, frac_len;
00189 
00190   was_cut= type::VALID;
00191 
00192   /* Skip space at start */
00193   for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
00194     ;
00195 
00196   if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
00197   {
00198     was_cut= type::CUT;
00199     return(type::DRIZZLE_TIMESTAMP_NONE);
00200   }
00201 
00202   is_internal_format= 0;
00203   /* This has to be changed if want to activate different timestamp formats */
00204   format_position= internal_format_positions;
00205 
00206   /*
00207     Calculate number of digits in first part.
00208     If length= 8 or >= 14 then year is of format YYYY.
00209     (YYYY-MM-DD,  YYYYMMDD, YYYYYMMDDHHMMSS)
00210   */
00211   for (pos=str;
00212        pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
00213        pos++)
00214     ;
00215 
00216   digits= (uint32_t) (pos-str);
00217   start_loop= 0;                                /* Start of scan loop */
00218   date_len[format_position[0]]= 0;              /* Length of year field */
00219   if (pos == end || *pos == '.')
00220   {
00221     /* Found date in internal format (only numbers like YYYYMMDD) */
00222     year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
00223     field_length= year_length;
00224     is_internal_format= 1;
00225     format_position= internal_format_positions;
00226   }
00227   else
00228   {
00229     if (format_position[0] >= 3)                /* If year is after HHMMDD */
00230     {
00231       /*
00232         If year is not in first part then we have to determinate if we got
00233         a date field or a datetime field.
00234         We do this by checking if there is two numbers separated by
00235         space in the input.
00236       */
00237       while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
00238         pos++;
00239       while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
00240         pos++;
00241       if (pos == end)
00242       {
00243         if (flags & TIME_DATETIME_ONLY)
00244         {
00245           was_cut= type::CUT;
00246           return(type::DRIZZLE_TIMESTAMP_NONE);   /* Can't be a full datetime */
00247         }
00248         /* Date field.  Set hour, minutes and seconds to 0 */
00249         date[0]= date[1]= date[2]= date[3]= date[4]= 0;
00250         start_loop= 5;                         /* Start with first date part */
00251       }
00252     }
00253 
00254     field_length= format_position[0] == 0 ? 4 : 2;
00255   }
00256 
00257   /*
00258     Only allow space in the first "part" of the datetime field and:
00259     - after days, part seconds
00260     - before and after AM/PM (handled by code later)
00261 
00262     2003-03-03 20:00:20 AM
00263     20:00:20.000000 AM 03-03-2000
00264   */
00265   i= max((uint32_t) format_position[0], (uint32_t) format_position[1]);
00266   set_if_bigger(i, (uint32_t) format_position[2]);
00267   allow_space= ((1 << i) | (1 << format_position[6]));
00268   allow_space&= (1 | 2 | 4 | 8);
00269 
00270   not_zero_date= 0;
00271   for (i = start_loop;
00272        i < MAX_DATE_PARTS-1 && str != end &&
00273          my_isdigit(&my_charset_utf8_general_ci,*str);
00274        i++)
00275   {
00276     const char *start= str;
00277     uint32_t tmp_value= (uint32_t) (unsigned char) (*str++ - '0');
00278     while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
00279            (!is_internal_format || --field_length))
00280     {
00281       tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
00282       str++;
00283     }
00284     date_len[i]= (uint32_t) (str - start);
00285     if (tmp_value > 999999)                     /* Impossible date part */
00286     {
00287       was_cut= type::CUT;
00288       return(type::DRIZZLE_TIMESTAMP_NONE);
00289     }
00290     date[i]=tmp_value;
00291     not_zero_date|= tmp_value;
00292 
00293     /* Length of next field */
00294     field_length= format_position[i+1] == 0 ? 4 : 2;
00295 
00296     if ((last_field_pos= str) == end)
00297     {
00298       i++;                                      /* Register last found part */
00299       break;
00300     }
00301     /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
00302     if (i == format_position[2] && *str == 'T')
00303     {
00304       str++;                                    /* ISO8601:  CCYYMMDDThhmmss */
00305       continue;
00306     }
00307     if (i == format_position[5])                /* Seconds */
00308     {
00309       if (*str == '.')                          /* Followed by part seconds */
00310       {
00311         str++;
00312         field_length= 6;                        /* 6 digits */
00313       }
00314       continue;
00315     }
00316     while (str != end &&
00317            (my_ispunct(&my_charset_utf8_general_ci,*str) ||
00318             my_isspace(&my_charset_utf8_general_ci,*str)))
00319     {
00320       if (my_isspace(&my_charset_utf8_general_ci,*str))
00321       {
00322         if (!(allow_space & (1 << i)))
00323         {
00324           was_cut= type::CUT;
00325           return(type::DRIZZLE_TIMESTAMP_NONE);
00326         }
00327         found_space= 1;
00328       }
00329       str++;
00330       found_delimitier= 1;                      /* Should be a 'normal' date */
00331     }
00332     /* Check if next position is AM/PM */
00333     if (i == format_position[6])                /* Seconds, time for AM/PM */
00334     {
00335       i++;                                      /* Skip AM/PM part */
00336       if (format_position[7] != 255)            /* If using AM/PM */
00337       {
00338         if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
00339         {
00340           if (str[0] == 'p' || str[0] == 'P')
00341             add_hours= 12;
00342           else if (str[0] != 'a' || str[0] != 'A')
00343             continue;                           /* Not AM/PM */
00344           str+= 2;                              /* Skip AM/PM */
00345           /* Skip space after AM/PM */
00346           while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
00347             str++;
00348         }
00349       }
00350     }
00351     last_field_pos= str;
00352   }
00353   if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
00354   {
00355     was_cut= type::CUT;
00356     return(type::DRIZZLE_TIMESTAMP_NONE);          /* Can't be a datetime */
00357   }
00358 
00359   str= last_field_pos;
00360 
00361   number_of_fields= i - start_loop;
00362   while (i < MAX_DATE_PARTS)
00363   {
00364     date_len[i]= 0;
00365     date[i++]= 0;
00366   }
00367 
00368   do 
00369   {
00370     if (not is_internal_format)
00371     {
00372       year_length= date_len[(uint32_t) format_position[0]];
00373       if (!year_length)                           /* Year must be specified */
00374       {
00375         was_cut= type::CUT;
00376         return(type::DRIZZLE_TIMESTAMP_NONE);
00377       }
00378 
00379       this->year=               date[(uint32_t) format_position[0]];
00380       this->month=              date[(uint32_t) format_position[1]];
00381       this->day=                date[(uint32_t) format_position[2]];
00382       this->hour=               date[(uint32_t) format_position[3]];
00383       this->minute=             date[(uint32_t) format_position[4]];
00384       this->second=             date[(uint32_t) format_position[5]];
00385 
00386       frac_pos= (uint32_t) format_position[6];
00387       frac_len= date_len[frac_pos];
00388       if (frac_len < 6)
00389         date[frac_pos]*= (uint32_t) log_10_int[6 - frac_len];
00390       this->second_part= date[frac_pos];
00391 
00392       if (format_position[7] != (unsigned char) 255)
00393       {
00394         if (this->hour > 12)
00395         {
00396           was_cut= type::CUT;
00397           break;
00398         }
00399         this->hour= this->hour%12 + add_hours;
00400       }
00401     }
00402     else
00403     {
00404       this->year=       date[0];
00405       this->month=      date[1];
00406       this->day=        date[2];
00407       this->hour=       date[3];
00408       this->minute=     date[4];
00409       this->second=     date[5];
00410       if (date_len[6] < 6)
00411         date[6]*= (uint32_t) log_10_int[6 - date_len[6]];
00412       this->second_part=date[6];
00413     }
00414     this->neg= 0;
00415 
00416     if (year_length == 2 && not_zero_date)
00417       this->year+= (this->year < YY_PART_YEAR ? 2000 : 1900);
00418 
00419     if (number_of_fields < 3 ||
00420         this->year > 9999 || this->month > 12 ||
00421         this->day > 31 || this->hour > 23 ||
00422         this->minute > 59 || this->second > 59)
00423     {
00424       /* Only give warning for a zero date if there is some garbage after */
00425       if (!not_zero_date)                         /* If zero date */
00426       {
00427         for (; str != end ; str++)
00428         {
00429           if (!my_isspace(&my_charset_utf8_general_ci, *str))
00430           {
00431             not_zero_date= 1;                     /* Give warning */
00432             break;
00433           }
00434         }
00435       }
00436       was_cut= test(not_zero_date) ? type::CUT : type::VALID;
00437       break;
00438     }
00439 
00440     if (check(not_zero_date != 0, flags, was_cut))
00441     {
00442       break;
00443     }
00444 
00445     this->time_type= (number_of_fields <= 3 ?
00446                       type::DRIZZLE_TIMESTAMP_DATE : type::DRIZZLE_TIMESTAMP_DATETIME);
00447 
00448     for (; str != end ; str++)
00449     {
00450       if (!my_isspace(&my_charset_utf8_general_ci,*str))
00451       {
00452         was_cut= type::CUT;
00453         break;
00454       }
00455     }
00456 
00457     return(time_type= (number_of_fields <= 3 ? type::DRIZZLE_TIMESTAMP_DATE : type::DRIZZLE_TIMESTAMP_DATETIME));
00458   } while (0);
00459 
00460   reset();
00461 
00462   return type::DRIZZLE_TIMESTAMP_ERROR;
00463 }
00464 
00465 type::timestamp_t Time::store(const char *str, uint32_t length, uint32_t flags)
00466 {
00467   type::cut_t was_cut;
00468   return store(str, length, flags, was_cut);
00469 }
00470 
00471 /*
00472  Convert a time string to a type::Time struct.
00473 
00474   SYNOPSIS
00475    str_to_time()
00476    str                  A string in full TIMESTAMP format or
00477                         [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
00478                         [M]MSS or [S]S
00479                         There may be an optional [.second_part] after seconds
00480    length               Length of str
00481    l_time               Store result here
00482    warning              Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
00483                         was cut during conversion, and/or
00484                         DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
00485                         out of range.
00486 
00487    NOTES
00488      Because of the extra days argument, this function can only
00489      work with times where the time arguments are in the above order.
00490 
00491    RETURN
00492      0  ok
00493      1  error
00494 */
00495 
00496 bool Time::store(const char *str, uint32_t length, int &warning, type::timestamp_t arg)
00497 {
00498   uint32_t date[5];
00499   uint64_t value;
00500   const char *end=str+length, *end_of_days;
00501   bool found_days,found_hours;
00502   uint32_t state;
00503 
00504   assert(arg == DRIZZLE_TIMESTAMP_TIME);
00505 
00506   this->neg=0;
00507   warning= 0;
00508   for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
00509     length--;
00510   if (str != end && *str == '-')
00511   {
00512     this->neg=1;
00513     str++;
00514     length--;
00515   }
00516   if (str == end)
00517     return true;
00518 
00519   /* Check first if this is a full TIMESTAMP */
00520   if (length >= 12)
00521   {                                             /* Probably full timestamp */
00522     type::cut_t was_cut;
00523     type::timestamp_t res= this->store(str, length, (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), was_cut);
00524     if ((int) res >= (int) type::DRIZZLE_TIMESTAMP_ERROR)
00525     {
00526       if (was_cut != type::VALID)
00527         warning|= DRIZZLE_TIME_WARN_TRUNCATED;
00528 
00529       return res == type::DRIZZLE_TIMESTAMP_ERROR;
00530     }
00531   }
00532 
00533   /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
00534   for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
00535     value=value*10L + (long) (*str - '0');
00536 
00537   /* Skip all space after 'days' */
00538   end_of_days= str;
00539   for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
00540     ;
00541 
00542   found_days=found_hours=0;
00543   if ((uint32_t) (end-str) > 1 && str != end_of_days &&
00544       my_isdigit(&my_charset_utf8_general_ci, *str))
00545   {                                             /* Found days part */
00546     date[0]= (uint32_t) value;
00547     state= 1;                                   /* Assume next is hours */
00548     found_days= 1;
00549   }
00550   else if ((end-str) > 1 &&  *str == time_separator &&
00551            my_isdigit(&my_charset_utf8_general_ci, str[1]))
00552   {
00553     date[0]= 0;                                 /* Assume we found hours */
00554     date[1]= (uint32_t) value;
00555     state=2;
00556     found_hours=1;
00557     str++;                                      /* skip ':' */
00558   }
00559   else
00560   {
00561     /* String given as one number; assume HHMMSS format */
00562     date[0]= 0;
00563     date[1]= (uint32_t) (value/10000);
00564     date[2]= (uint32_t) (value/100 % 100);
00565     date[3]= (uint32_t) (value % 100);
00566     state=4;
00567     goto fractional;
00568   }
00569 
00570   /* Read hours, minutes and seconds */
00571   for (;;)
00572   {
00573     for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
00574       value=value*10L + (long) (*str - '0');
00575     date[state++]= (uint32_t) value;
00576     if (state == 4 || (end-str) < 2 || *str != time_separator ||
00577         !my_isdigit(&my_charset_utf8_general_ci,str[1]))
00578       break;
00579     str++;                                      /* Skip time_separator (':') */
00580   }
00581 
00582   if (state != 4)
00583   {                                             /* Not HH:MM:SS */
00584     /* Fix the date to assume that seconds was given */
00585     if (!found_hours && !found_days)
00586     {
00587       internal::bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
00588                 sizeof(long)*(state-1));
00589       memset(date, 0, sizeof(long)*(4-state));
00590     }
00591     else
00592       memset(date+state, 0, sizeof(long)*(4-state));
00593   }
00594 
00595 fractional:
00596   /* Get fractional second part */
00597   if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
00598   {
00599     int field_length= 5;
00600     str++; value=(uint32_t) (unsigned char) (*str - '0');
00601     while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
00602     {
00603       if (field_length-- > 0)
00604         value= value*10 + (uint32_t) (unsigned char) (*str - '0');
00605     }
00606     if (field_length > 0)
00607     {
00608       value*= (long) log_10_int[field_length];
00609     }
00610     else if (field_length < 0)
00611     {
00612       warning|= DRIZZLE_TIME_WARN_TRUNCATED;
00613     }
00614 
00615     date[4]= (uint32_t) value;
00616   }
00617   else
00618   {
00619     date[4]=0;
00620   }
00621 
00622   /* Check for exponent part: E<gigit> | E<sign><digit> */
00623   /* (may occur as result of %g formatting of time value) */
00624   if ((end - str) > 1 &&
00625       (*str == 'e' || *str == 'E') &&
00626       (my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
00627        ((str[1] == '-' || str[1] == '+') &&
00628         (end - str) > 2 &&
00629         my_isdigit(&my_charset_utf8_general_ci, str[2]))))
00630     return 1;
00631 
00632   if (internal_format_positions[7] != 255)
00633   {
00634     /* Read a possible AM/PM */
00635     while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
00636       str++;
00637     if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
00638     {
00639       if (str[0] == 'p' || str[0] == 'P')
00640       {
00641         str+= 2;
00642         date[1]= date[1]%12 + 12;
00643       }
00644       else if (str[0] == 'a' || str[0] == 'A')
00645         str+=2;
00646     }
00647   }
00648 
00649   /* Integer overflow checks */
00650   if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
00651       date[2] > UINT_MAX || date[3] > UINT_MAX ||
00652       date[4] > UINT_MAX)
00653     return 1;
00654 
00655   this->year=         0;                      /* For protocol::store_time */
00656   this->month=        0;
00657   this->day=          date[0];
00658   this->hour=         date[1];
00659   this->minute=       date[2];
00660   this->second=       date[3];
00661   this->second_part=  date[4];
00662   this->time_type= type::DRIZZLE_TIMESTAMP_TIME;
00663 
00664   /* Check if the value is valid and fits into type::Time range */
00665   if (check_time_range(this, &warning))
00666   {
00667     return 1;
00668   }
00669 
00670   /* Check if there is garbage at end of the type::Time specification */
00671   if (str != end)
00672   {
00673     do
00674     {
00675       if (!my_isspace(&my_charset_utf8_general_ci,*str))
00676       {
00677         warning|= DRIZZLE_TIME_WARN_TRUNCATED;
00678         break;
00679       }
00680     } while (++str != end);
00681   }
00682   return 0;
00683 }
00684 
00685 } // namespace type
00686 
00687 
00688 
00689 /*
00690   Check 'time' value to lie in the type::Time range
00691 
00692   SYNOPSIS:
00693     check_time_range()
00694     time     pointer to type::Time value
00695     warning  set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
00696 
00697   DESCRIPTION
00698   If the time value lies outside of the range [-838:59:59, 838:59:59],
00699   set it to the closest endpoint of the range and set
00700   DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
00701 
00702   RETURN
00703     0        time value is valid, but was possibly truncated
00704     1        time value is invalid
00705 */
00706 
00707 static int check_time_range(type::Time *my_time, int *warning)
00708 {
00709   int64_t hour;
00710 
00711   if (my_time->minute >= 60 || my_time->second >= 60)
00712     return 1;
00713 
00714   hour= my_time->hour + (24*my_time->day);
00715   if (hour <= TIME_MAX_HOUR &&
00716       (hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
00717        my_time->second != TIME_MAX_SECOND || !my_time->second_part))
00718     return 0;
00719 
00720   my_time->day= 0;
00721   my_time->hour= TIME_MAX_HOUR;
00722   my_time->minute= TIME_MAX_MINUTE;
00723   my_time->second= TIME_MAX_SECOND;
00724   my_time->second_part= 0;
00725   *warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
00726   return 0;
00727 }
00728 
00729 
00730 /*
00731   Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
00732 
00733   SYNOPSIS
00734     init_time()
00735 */
00736 void init_time(void)
00737 {
00738   time_t seconds;
00739   struct tm *l_time,tm_tmp;
00740   type::Time my_time;
00741   type::Time::epoch_t epoch;
00742   bool not_used;
00743 
00744   seconds= (time_t) time((time_t*) 0);
00745   localtime_r(&seconds, &tm_tmp);
00746   l_time= &tm_tmp;
00747   my_time_zone=   3600;   /* Comp. for -3600 in my_gmt_sec */
00748   my_time.year=   (uint32_t) l_time->tm_year+1900;
00749   my_time.month=  (uint32_t) l_time->tm_mon+1;
00750   my_time.day=    (uint32_t) l_time->tm_mday;
00751   my_time.hour=   (uint32_t) l_time->tm_hour;
00752   my_time.minute= (uint32_t) l_time->tm_min;
00753   my_time.second= (uint32_t) l_time->tm_sec;
00754   my_time.time_type=  type::DRIZZLE_TIMESTAMP_NONE;
00755   my_time.second_part=  0;
00756   my_time.neg=          false;
00757   my_time.convert(epoch, &my_time_zone, &not_used); /* Init my_time_zone */
00758 }
00759 
00760 
00761 /*
00762   Handle 2 digit year conversions
00763 
00764   SYNOPSIS
00765   year_2000_handling()
00766   year     2 digit year
00767 
00768   RETURN
00769     Year between 1970-2069
00770 */
00771 
00772 uint32_t year_2000_handling(uint32_t year)
00773 {
00774   if ((year=year+1900) < 1900+YY_PART_YEAR)
00775     year+=100;
00776   return year;
00777 }
00778 
00779 
00780 /*
00781   Calculate nr of day since year 0 in new date-system (from 1615)
00782 
00783   SYNOPSIS
00784     calc_daynr()
00785     year     Year (exact 4 digit year, no year conversions)
00786     month    Month
00787     day      Day
00788 
00789   NOTES: 0000-00-00 is a valid date, and will return 0
00790 
00791   RETURN
00792     Days since 0000-00-00
00793 */
00794 
00795 long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
00796 {
00797   long delsum;
00798   int temp;
00799 
00800   if (year == 0 && month == 0 && day == 0)
00801     return(0);        /* Skip errors */
00802   delsum= (long) (365L * year+ 31*(month-1) +day);
00803   if (month <= 2)
00804       year--;
00805   else
00806     delsum-= (long) (month*4+23)/10;
00807   temp=(int) ((year/100+1)*3)/4;
00808   return(delsum+(int) year/4-temp);
00809 } /* calc_daynr */
00810 
00811 
00812 namespace type {
00813 /*
00814   Convert time in type::Time representation in system time zone to its
00815   time_t form (number of seconds in UTC since begginning of Unix Epoch).
00816 
00817   SYNOPSIS
00818     my_system_gmt_sec()
00819       t               - time value to be converted
00820       my_timezone     - pointer to long where offset of system time zone
00821                         from UTC will be stored for caching
00822       in_dst_time_gap - set to true if time falls into spring time-gap
00823 
00824   NOTES
00825     The idea is to cache the time zone offset from UTC (including daylight
00826     saving time) for the next call to make things faster. But currently we
00827     just calculate this offset during startup (by calling init_time()
00828     function) and use it all the time.
00829     Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
00830     is not allowed).
00831 
00832   RETURN VALUE
00833     Time in UTC seconds since Unix Epoch representation.
00834 */
00835 void Time::convert(epoch_t &epoch, long *my_timezone, bool *in_dst_time_gap, bool skip_timezone) const
00836 {
00837   uint32_t loop;
00838   int shift= 0;
00839   type::Time tmp_time;
00840   type::Time *t= &tmp_time;
00841   struct tm *l_time,tm_tmp;
00842   long diff, current_timezone;
00843 
00844   /*
00845     Use temp variable to avoid trashing input data, which could happen in
00846     case of shift required for boundary dates processing.
00847   */
00848   tmp_time= *this;
00849 
00850   if (not t->isValidEpoch())
00851   {
00852     epoch= 0;
00853     return;
00854   }
00855 
00856   /*
00857     Calculate the gmt time based on current time and timezone
00858     The -1 on the end is to ensure that if have a date that exists twice
00859     (like 2002-10-27 02:00:0 MET), we will find the initial date.
00860 
00861     By doing -3600 we will have to call localtime_r() several times, but
00862     I couldn't come up with a better way to get a repeatable result :(
00863 
00864     We can't use mktime() as it's buggy on many platforms and not thread safe.
00865 
00866     Note: this code assumes that our time_t estimation is not too far away
00867     from real value (we assume that localtime_r(epoch) will return something
00868     within 24 hrs from t) which is probably true for all current time zones.
00869 
00870     Note2: For the dates, which have time_t representation close to
00871     MAX_INT32 (efficient time_t limit for supported platforms), we should
00872     do a small trick to avoid overflow. That is, convert the date, which is
00873     two days earlier, and then add these days to the final value.
00874 
00875     The same trick is done for the values close to 0 in time_t
00876     representation for platfroms with unsigned time_t (QNX).
00877 
00878     To be more verbose, here is a sample (extracted from the code below):
00879     (calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
00880     would return -2147480896 because of the long type overflow. In result
00881     we would get 1901 year in localtime_r(), which is an obvious error.
00882 
00883     Alike problem raises with the dates close to Epoch. E.g.
00884     (calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
00885     will give -3600.
00886 
00887     On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
00888     wil give us a date around 2106 year. Which is no good.
00889 
00890     Theoreticaly, there could be problems with the latter conversion:
00891     there are at least two timezones, which had time switches near 1 Jan
00892     of 1970 (because of political reasons). These are America/Hermosillo and
00893     America/Mazatlan time zones. They changed their offset on
00894     1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
00895     the code below will give incorrect results for dates close to
00896     1970-01-01, in the case OS takes into account these historical switches.
00897     Luckily, it seems that we support only one platform with unsigned
00898     time_t. It's QNX. And QNX does not support historical timezone data at all.
00899     E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
00900     historical information for localtime_r() etc. That is, the problem is not
00901     relevant to QNX.
00902 
00903     We are safe with shifts close to MAX_INT32, as there are no known
00904     time switches on Jan 2038 yet :)
00905   */
00906 #ifdef TIME_T_UNSIGNED
00907   {
00908     /*
00909       We can get 0 in time_t representaion only on 1969, 31 of Dec or on
00910       1970, 1 of Jan. For both dates we use shift, which is added
00911       to t->day in order to step out a bit from the border.
00912       This is required for platforms, where time_t is unsigned.
00913       As far as I know, among the platforms we support it's only QNX.
00914       Note: the order of below if-statements is significant.
00915     */
00916 
00917     if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
00918         && (t->day <= 10))
00919     {
00920       t->day+= 2;
00921       shift= -2;
00922     }
00923 
00924     if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
00925         && (t->day == 31))
00926     {
00927       t->year++;
00928       t->month= 1;
00929       t->day= 2;
00930       shift= -2;
00931     }
00932   }
00933 #endif
00934 
00935   epoch= (type::Time::epoch_t) (((calc_daynr((uint32_t) t->year, (uint32_t) t->month, (uint32_t) t->day) -
00936                    (long) days_at_timestart)*86400L + (long) t->hour*3600L +
00937                   (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
00938                  3600);
00939 
00940   current_timezone= my_time_zone;
00941   if (skip_timezone)
00942   {
00943     util::gmtime(epoch, &tm_tmp);
00944   }
00945   else
00946   {
00947     util::localtime(epoch, &tm_tmp);
00948   }
00949 
00950   l_time= &tm_tmp;
00951   for (loop=0;
00952        loop < 2 &&
00953    (t->hour != (uint32_t) l_time->tm_hour ||
00954     t->minute != (uint32_t) l_time->tm_min ||
00955           t->second != (uint32_t) l_time->tm_sec);
00956        loop++)
00957   {         /* One check should be enough ? */
00958     /* Get difference in days */
00959     int days= t->day - l_time->tm_mday;
00960     if (days < -1)
00961       days= 1;          /* Month has wrapped */
00962     else if (days > 1)
00963       days= -1;
00964     diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
00965           (long) (60*((int) t->minute - (int) l_time->tm_min)) +
00966           (long) ((int) t->second - (int) l_time->tm_sec));
00967     current_timezone+= diff+3600;   /* Compensate for -3600 above */
00968     epoch+= (time_t) diff;
00969     if (skip_timezone)
00970     {
00971       util::gmtime(epoch, &tm_tmp);
00972     }
00973     else
00974     {
00975       util::localtime(epoch, &tm_tmp);
00976     }
00977     l_time=&tm_tmp;
00978   }
00979   /*
00980     Fix that if we are in the non existing daylight saving time hour
00981     we move the start of the next real hour.
00982 
00983     This code doesn't handle such exotical thing as time-gaps whose length
00984     is more than one hour or non-integer (latter can theoretically happen
00985     if one of seconds will be removed due leap correction, or because of
00986     general time correction like it happened for Africa/Monrovia time zone
00987     in year 1972).
00988   */
00989   if (loop == 2 && t->hour != (uint32_t) l_time->tm_hour)
00990   {
00991     int days= t->day - l_time->tm_mday;
00992     if (days < -1)
00993       days=1;         /* Month has wrapped */
00994     else if (days > 1)
00995       days= -1;
00996     diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
00997     (long) (60*((int) t->minute - (int) l_time->tm_min)) +
00998           (long) ((int) t->second - (int) l_time->tm_sec));
00999     if (diff == 3600)
01000       epoch+=3600 - t->minute*60 - t->second; /* Move to next hour */
01001     else if (diff == -3600)
01002       epoch-=t->minute*60 + t->second;    /* Move to previous hour */
01003 
01004     *in_dst_time_gap= true;
01005   }
01006   *my_timezone= current_timezone;
01007 
01008 
01009   /* shift back, if we were dealing with boundary dates */
01010   epoch+= shift*86400L;
01011 
01012   /*
01013     This is possible for dates, which slightly exceed boundaries.
01014     Conversion will pass ok for them, but we don't allow them.
01015     First check will pass for platforms with signed time_t.
01016     instruction above (epoch+= shift*86400L) could exceed
01017     MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
01018     So, epoch < TIMESTAMP_MIN_VALUE will be triggered.
01019   */
01020   if (epoch < TIMESTAMP_MIN_VALUE)
01021   {
01022     epoch= 0;
01023   }
01024 } /* my_system_gmt_sec */
01025 
01026 
01027 void Time::store(const struct tm &from)
01028 {
01029   _is_local_time= false;
01030   neg= 0;
01031   second_part= 0;
01032   year= (int32_t) ((from.tm_year+1900) % 10000);
01033   month= (int32_t) from.tm_mon+1;
01034   day= (int32_t) from.tm_mday;
01035   hour= (int32_t) from.tm_hour;
01036   minute= (int32_t) from.tm_min;
01037   second= (int32_t) from.tm_sec;
01038 
01039   time_type= DRIZZLE_TIMESTAMP_DATETIME;
01040 }
01041 
01042 void Time::store(const struct timeval &from)
01043 {
01044   store(from.tv_sec, (usec_t)from.tv_usec);
01045   time_type= type::DRIZZLE_TIMESTAMP_DATETIME;
01046 }
01047 
01048 
01049 void Time::store(const type::Time::epoch_t &from, bool use_localtime)
01050 {
01051   store(from, 0, use_localtime);
01052 }
01053 
01054 void Time::store(const type::Time::epoch_t &from_arg, const usec_t &from_fractional_seconds, bool use_localtime)
01055 {
01056   epoch_t from= from_arg;
01057 
01058   if (use_localtime)
01059   {
01060     util::localtime(from, *this);
01061     _is_local_time= true;
01062   }
01063   else
01064   {
01065     util::gmtime(from, *this);
01066   }
01067 
01068   // Since time_t/epoch_t doesn't have fractional seconds, we have to
01069   // collect them outside of the gmtime function.
01070   second_part= from_fractional_seconds;
01071   time_type= DRIZZLE_TIMESTAMP_DATETIME;
01072 }
01073 
01074 // Only implemented for one case, extend as needed.
01075 void Time::truncate(const timestamp_t arg)
01076 {
01077   assert(arg == type::DRIZZLE_TIMESTAMP_TIME);
01078   year= month= day= 0;
01079 
01080   time_type= arg;
01081 }
01082 
01083 void Time::convert(String &str, timestamp_t arg)
01084 {
01085   str.alloc(MAX_STRING_LENGTH);
01086   size_t length= MAX_STRING_LENGTH;
01087 
01088   convert(str.c_ptr(), length, arg);
01089 
01090   str.length(length);
01091   str.set_charset(&my_charset_bin);
01092 }
01093 
01094 void Time::convert(char *str, size_t &to_length, timestamp_t arg)
01095 {
01096   int32_t length= 0;
01097   switch (arg) {
01098   case DRIZZLE_TIMESTAMP_DATETIME:
01099     length= snprintf(str, to_length,
01100                      "%04" PRIu32 "-%02" PRIu32 "-%02" PRIu32
01101                      " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ".%06" PRIu32,
01102                      year,
01103                      month,
01104                      day,
01105                      hour,
01106                      minute,
01107                      second,
01108                      second_part);
01109     break;
01110 
01111   case DRIZZLE_TIMESTAMP_DATE:
01112     length= snprintf(str, to_length, "%04u-%02u-%02u",
01113                      year,
01114                      month,
01115                      day);
01116     break;
01117 
01118   case DRIZZLE_TIMESTAMP_TIME:
01119     {
01120       uint32_t extra_hours= 0;
01121 
01122       length= snprintf(str, to_length,
01123                        "%s%02u:%02u:%02u",
01124                        (neg ? "-" : ""),
01125                        extra_hours+ hour,
01126                        minute,
01127                        second);
01128     }
01129     break;
01130 
01131   case DRIZZLE_TIMESTAMP_NONE:
01132   case DRIZZLE_TIMESTAMP_ERROR:
01133     assert(0);
01134     break;
01135   }
01136 
01137   if (length < 0)
01138   {
01139     to_length= 0;
01140     return;
01141   }
01142 
01143   to_length= length;
01144 }
01145 
01146 }
01147 
01148 /*
01149   Convert datetime value specified as number to broken-down TIME
01150   representation and form value of DATETIME type as side-effect.
01151 
01152   SYNOPSIS
01153     number_to_datetime()
01154       nr         - datetime value as number
01155       time_res   - pointer for structure for broken-down representation
01156       flags      - flags to use in validating date, as in store()
01157       was_cut    0      Value ok
01158                  1      If value was cut during conversion
01159                  2      check(date,flags) considers date invalid
01160 
01161   DESCRIPTION
01162     Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
01163     YYYYMMDDHHMMSS to broken-down type::Time representation. Return value in
01164     YYYYMMDDHHMMSS format as side-effect.
01165 
01166     This function also checks if datetime value fits in DATETIME range.
01167 
01168   RETURN VALUE
01169     -1              Timestamp with wrong values
01170     anything else   DATETIME as integer in YYYYMMDDHHMMSS format
01171     Datetime value in YYYYMMDDHHMMSS format.
01172 */
01173 
01174 static int64_t number_to_datetime(int64_t nr, type::Time *time_res,
01175                                   uint32_t flags, type::cut_t &was_cut)
01176 {
01177   long part1,part2;
01178 
01179   was_cut= type::VALID;
01180   time_res->reset();
01181   time_res->time_type=type::DRIZZLE_TIMESTAMP_DATE;
01182 
01183   if (nr == 0LL || nr >= 10000101000000LL)
01184   {
01185     time_res->time_type= type::DRIZZLE_TIMESTAMP_DATETIME;
01186     goto ok;
01187   }
01188   if (nr < 101)
01189     goto err;
01190   if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
01191   {
01192     nr= (nr+20000000L)*1000000L;                 /* YYMMDD, year: 2000-2069 */
01193     goto ok;
01194   }
01195   if (nr < (YY_PART_YEAR)*10000L+101L)
01196     goto err;
01197   if (nr <= 991231L)
01198   {
01199     nr= (nr+19000000L)*1000000L;                 /* YYMMDD, year: 1970-1999 */
01200     goto ok;
01201   }
01202   if (nr < 10000101L)
01203     goto err;
01204   if (nr <= 99991231L)
01205   {
01206     nr= nr*1000000L;
01207     goto ok;
01208   }
01209   if (nr < 101000000L)
01210     goto err;
01211 
01212   time_res->time_type= type::DRIZZLE_TIMESTAMP_DATETIME;
01213 
01214   if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
01215   {
01216     nr= nr + 20000000000000LL;                   /* YYMMDDHHMMSS, 2000-2069 */
01217     goto ok;
01218   }
01219   if (nr <  YY_PART_YEAR * 10000000000LL + 101000000LL)
01220     goto err;
01221   if (nr <= 991231235959LL)
01222     nr= nr + 19000000000000LL;    /* YYMMDDHHMMSS, 1970-1999 */
01223 
01224  ok:
01225   part1=(long) (nr / 1000000LL);
01226   part2=(long) (nr - (int64_t) part1 * 1000000LL);
01227   time_res->year=  (int) (part1/10000L);  part1%=10000L;
01228   time_res->month= (int) part1 / 100;
01229   time_res->day=   (int) part1 % 100;
01230   time_res->hour=  (int) (part2/10000L);  part2%=10000L;
01231   time_res->minute=(int) part2 / 100;
01232   time_res->second=(int) part2 % 100;
01233 
01234   if (time_res->year <= 9999 && time_res->month <= 12 &&
01235       time_res->day <= 31 && time_res->hour <= 23 &&
01236       time_res->minute <= 59 && time_res->second <= 59 &&
01237       not time_res->check((nr != 0), flags, was_cut))
01238   {
01239     return nr;
01240   }
01241 
01242   /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
01243   if (!nr && (flags & TIME_NO_ZERO_DATE))
01244     return -1LL;
01245 
01246  err:
01247   was_cut= type::CUT;
01248   return -1LL;
01249 }
01250 
01251 
01252 namespace type {
01253 
01254 void Time::convert(datetime_t &ret, int64_t nr, uint32_t flags)
01255 {
01256   type::cut_t was_cut;
01257   ret= number_to_datetime(nr, this, flags, was_cut);
01258 }
01259 
01260 void Time::convert(datetime_t &ret, int64_t nr, uint32_t flags, type::cut_t &was_cut)
01261 {
01262   ret= number_to_datetime(nr, this, flags, was_cut);
01263 }
01264 
01265 /*
01266   Convert struct type::Time (date and time split into year/month/day/hour/...
01267   to a number in format YYYYMMDDHHMMSS (DATETIME),
01268   YYYYMMDD (DATE)  or HHMMSS (TIME).
01269 */
01270 
01271 
01272 void Time::convert(datetime_t &datetime, timestamp_t arg)
01273 {
01274   switch (arg)
01275   {
01276     // Convert to YYYYMMDDHHMMSS format
01277   case type::DRIZZLE_TIMESTAMP_DATETIME:
01278     datetime= ((int64_t) (year * 10000UL + month * 100UL + day) * 1000000ULL +
01279                (int64_t) (hour * 10000UL + minute * 100UL + second));
01280     break;
01281 
01282     // Convert to YYYYMMDD
01283   case type::DRIZZLE_TIMESTAMP_DATE:
01284     datetime= (year * 10000UL + month * 100UL + day);
01285     break;
01286 
01287     // Convert to HHMMSS
01288   case type::DRIZZLE_TIMESTAMP_TIME:
01289     datetime= (hour * 10000UL + minute * 100UL + second);
01290     break;
01291 
01292   case type::DRIZZLE_TIMESTAMP_NONE:
01293   case type::DRIZZLE_TIMESTAMP_ERROR:
01294     datetime= 0;
01295   }
01296 }
01297 
01298 } // namespace type
01299 
01300 } /* namespace drizzled */