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