1 /* Copyright (c) 2004, 2021, Oracle and/or its affiliates.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License, version 2.0,
5  as published by the Free Software Foundation.
6 
7  This program is also distributed with certain software (including
8  but not limited to OpenSSL) that is licensed under separate terms,
9  as designated in a particular file or component or in included license
10  documentation.  The authors of MySQL hereby grant you an additional
11  permission to link the program and your derivative works with the
12  separately licensed software that they have included with MySQL.
13 
14  Without limiting anything contained in the foregoing, this file,
15  which is part of C Driver for MySQL (Connector/C), is also subject to the
16  Universal FOSS Exception, version 1.0, a copy of which can be found at
17  http://oss.oracle.com/licenses/universal-foss-exception.
18 
19  This program is distributed in the hope that it will be useful,
20  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  GNU General Public License, version 2.0, for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with this program; if not, write to the Free Software
26  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
27 
28 #include <my_time.h>
29 #include <m_string.h>
30 #include <m_ctype.h>
31 #include <myisampack.h>
32 /* Windows version of localtime_r() is declared in my_global.h */
33 #include <my_global.h>
34 #include "binary_log_types.h"
35 
36 ulonglong log_10_int[20]=
37 {
38   1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
39   100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
40   1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
41   1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
42   1000000000000000000ULL, 10000000000000000000ULL
43 };
44 
45 
46 const char my_zero_datetime6[]= "0000-00-00 00:00:00.000000";
47 
48 
49 /* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
50 
51 static uchar internal_format_positions[]=
52 {0, 1, 2, 3, 4, 5, 6, (uchar) 255};
53 
54 static char time_separator=':';
55 
56 static ulong const days_at_timestart=719528;	/* daynr at 1970.01.01 */
57 uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
58 
59 /*
60   Offset of system time zone from UTC in seconds used to speed up
61   work of my_system_gmt_sec() function.
62 */
63 static long my_time_zone=0;
64 
65 
66 /* Calc days in one year. works with 0 <= year <= 99 */
67 
calc_days_in_year(uint year)68 uint calc_days_in_year(uint year)
69 {
70   return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
71           366 : 365);
72 }
73 
74 
75 /**
76    Set MYSQL_TIME structure to 0000-00-00 00:00:00.000000
77    @param tm[OUT]    The value to set.
78    @param time_type  Timestasmp type
79 */
set_zero_time(MYSQL_TIME * tm,enum enum_mysql_timestamp_type time_type)80 void set_zero_time(MYSQL_TIME *tm,
81                    enum enum_mysql_timestamp_type time_type)
82 {
83   memset(tm, 0, sizeof(*tm));
84   tm->time_type= time_type;
85 }
86 
87 
88 /**
89   Set hour, minute and second of a MYSQL_TIME variable to maximum time value.
90   Unlike set_max_time(), does not touch the other structure members.
91 */
set_max_hhmmss(MYSQL_TIME * tm)92 void set_max_hhmmss(MYSQL_TIME *tm)
93 {
94   tm->hour= TIME_MAX_HOUR;
95   tm->minute= TIME_MAX_MINUTE;
96   tm->second= TIME_MAX_SECOND;
97 }
98 
99 
100 /**
101   Set MYSQL_TIME variable to maximum time value
102   @param tm    OUT  The variable to set.
103   @param neg        Sign: 1 if negative, 0 if positive.
104 */
set_max_time(MYSQL_TIME * tm,my_bool neg)105 void set_max_time(MYSQL_TIME *tm, my_bool neg)
106 {
107   set_zero_time(tm, MYSQL_TIMESTAMP_TIME);
108   set_max_hhmmss(tm);
109   tm->neg= neg;
110 }
111 
112 
113 /**
114   @brief Check datetime value for validity according to flags.
115 
116   @param[in]  ltime          Date to check.
117   @param[in]  not_zero_date  ltime is not the zero date
118   @param[in]  flags          flags to check
119                              (see str_to_datetime() flags in my_time.h)
120   @param[out] was_cut        set to 2 if value was invalid according to flags.
121                              (Feb 29 in non-leap etc.)  This remains unchanged
122                              if value is not invalid.
123   @details Here we assume that year and month is ok!
124     If month is 0 we allow any date. (This only happens if we allow zero
125     date parts in str_to_datetime())
126     Disallow dates with zero year and non-zero month and/or day.
127 
128   @return
129     0  OK
130     1  error
131 */
132 
check_date(const MYSQL_TIME * ltime,my_bool not_zero_date,my_time_flags_t flags,int * was_cut)133 my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
134                    my_time_flags_t flags, int *was_cut)
135 {
136   if (not_zero_date)
137   {
138     if (((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
139         (ltime->month == 0 || ltime->day == 0))
140     {
141       *was_cut= MYSQL_TIME_WARN_ZERO_IN_DATE;
142       return TRUE;
143     }
144     else if ((!(flags & TIME_INVALID_DATES) &&
145               ltime->month && ltime->day > days_in_month[ltime->month-1] &&
146               (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
147                ltime->day != 29)))
148     {
149       *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
150       return TRUE;
151     }
152   }
153   else if (flags & TIME_NO_ZERO_DATE)
154   {
155     *was_cut= MYSQL_TIME_WARN_ZERO_DATE;
156     return TRUE;
157   }
158   return FALSE;
159 }
160 
161 
162 /**
163   Check if TIME fields are fatally bad and cannot be further adjusted.
164   @param ltime  Time value.
165   @retval  TRUE   if the value is fatally bad.
166   @retval  FALSE  if the value is Ok.
167 */
check_time_mmssff_range(const MYSQL_TIME * ltime)168 my_bool check_time_mmssff_range(const MYSQL_TIME *ltime)
169 {
170   return ltime->minute >= 60 || ltime->second >= 60 ||
171          ltime->second_part > 999999;
172 }
173 
174 
175 /**
176   Check TIME range. The value can include day part,
177   for example:  '1 10:20:30.123456'.
178 
179   minute, second and second_part values are not checked
180   unless hour is equal TIME_MAX_HOUR.
181 
182   @param ltime   Rime value.
183   @returns       Test result.
184   @retval        FALSE if value is Ok.
185   @retval        TRUE if value is out of range.
186 */
check_time_range_quick(const MYSQL_TIME * ltime)187 my_bool check_time_range_quick(const MYSQL_TIME *ltime)
188 {
189   longlong hour= (longlong) ltime->hour + 24LL * ltime->day;
190   /* The input value should not be fatally bad */
191   assert(!check_time_mmssff_range(ltime));
192   if (hour <= TIME_MAX_HOUR &&
193       (hour != TIME_MAX_HOUR || ltime->minute != TIME_MAX_MINUTE ||
194        ltime->second != TIME_MAX_SECOND || !ltime->second_part))
195     return FALSE;
196   return TRUE;
197 }
198 
199 
200 /**
201   Check datetime, date, or normalized time (i.e. time without days) range.
202   @param ltime   Datetime value.
203   @returns
204   @retval   FALSE on success
205   @retval   TRUE  on error
206 */
check_datetime_range(const MYSQL_TIME * ltime)207 my_bool check_datetime_range(const MYSQL_TIME *ltime)
208 {
209   /*
210     In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
211     In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
212   */
213   return
214     ltime->year > 9999U || ltime->month > 12U  || ltime->day > 31U ||
215     ltime->minute > 59U || ltime->second > 59U || ltime->second_part > 999999U ||
216     (ltime->hour >
217      (ltime->time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23U));
218 }
219 
220 
221 /*
222   Convert a timestamp string to a MYSQL_TIME value.
223 
224   SYNOPSIS
225     str_to_datetime()
226     str                 String to parse
227     length              Length of string
228     l_time              Date is stored here
229     flags               Bitmap of following items
230                         TIME_FUZZY_DATE    Set if we should allow partial dates
231                         TIME_DATETIME_ONLY Set if we only allow full datetimes.
232                         TIME_NO_ZERO_IN_DATE	Don't allow partial dates
233                         TIME_NO_ZERO_DATE	Don't allow 0000-00-00 date
234                         TIME_INVALID_DATES	Allow 2000-02-31
235     status              Conversion status
236 
237 
238   DESCRIPTION
239     At least the following formats are recogniced (based on number of digits)
240     YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
241     YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
242     YYYYMMDDTHHMMSS  where T is a the character T (ISO8601)
243     Also dates where all parts are zero are allowed
244 
245     The second part may have an optional .###### fraction part.
246 
247   NOTES
248    This function should work with a format position vector as long as the
249    following things holds:
250    - All date are kept together and all time parts are kept together
251    - Date and time parts must be separated by blank
252    - Second fractions must come after second part and be separated
253      by a '.'.  (The second fractions are optional)
254    - AM/PM must come after second fractions (or after seconds if no fractions)
255    - Year must always been specified.
256    - If time is before date, then we will use datetime format only if
257      the argument consist of two parts, separated by space.
258      Otherwise we will assume the argument is a date.
259    - The hour part must be specified in hour-minute-second order.
260 
261     status->warnings is set to:
262     0                            Value OK
263     MYSQL_TIME_WARN_TRUNCATED    If value was cut during conversion
264     MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
265 
266     l_time->time_type is set as follows:
267     MYSQL_TIMESTAMP_NONE        String wasn't a timestamp, like
268                                 [DD [HH:[MM:[SS]]]].fraction.
269                                 l_time is not changed.
270     MYSQL_TIMESTAMP_DATE        DATE string (YY MM and DD parts ok)
271     MYSQL_TIMESTAMP_DATETIME    Full timestamp
272     MYSQL_TIMESTAMP_ERROR       Timestamp with wrong values.
273                                 All elements in l_time is set to 0
274   RETURN VALUES
275     0 - Ok
276     1 - Error
277 */
278 
279 #define MAX_DATE_PARTS 8
280 
281 my_bool
str_to_datetime(const char * str,size_t length,MYSQL_TIME * l_time,my_time_flags_t flags,MYSQL_TIME_STATUS * status)282 str_to_datetime(const char *str, size_t length, MYSQL_TIME *l_time,
283                 my_time_flags_t flags, MYSQL_TIME_STATUS *status)
284 {
285   uint field_length= 0, year_length= 0, digits, i, number_of_fields;
286   uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
287   uint add_hours= 0, start_loop;
288   ulong not_zero_date, allow_space;
289   my_bool is_internal_format;
290   const char *pos, *last_field_pos= NULL;
291   const char *end=str+length;
292   const uchar *format_position;
293   my_bool found_delimitier= 0, found_space= 0;
294   uint frac_pos, frac_len;
295   DBUG_ENTER("str_to_datetime");
296   DBUG_PRINT("ENTER", ("str: %.*s", (int)length, str));
297 
298   my_time_status_init(status);
299 
300   /* Skip space at start */
301   for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
302     ;
303   if (str == end || ! my_isdigit(&my_charset_latin1, *str))
304   {
305     status->warnings= MYSQL_TIME_WARN_TRUNCATED;
306     l_time->time_type= MYSQL_TIMESTAMP_NONE;
307     DBUG_RETURN(1);
308   }
309 
310   is_internal_format= 0;
311   /* This has to be changed if want to activate different timestamp formats */
312   format_position= internal_format_positions;
313 
314   /*
315     Calculate number of digits in first part.
316     If length= 8 or >= 14 then year is of format YYYY.
317     (YYYY-MM-DD,  YYYYMMDD, YYYYYMMDDHHMMSS)
318   */
319   for (pos=str;
320        pos != end && (my_isdigit(&my_charset_latin1,*pos) || *pos == 'T');
321        pos++)
322     ;
323 
324   digits= (uint) (pos-str);
325   start_loop= 0;                                /* Start of scan loop */
326   date_len[format_position[0]]= 0;              /* Length of year field */
327   if (pos == end || *pos == '.')
328   {
329     /* Found date in internal format (only numbers like YYYYMMDD) */
330     year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
331     field_length= year_length;
332     is_internal_format= 1;
333     format_position= internal_format_positions;
334   }
335   else
336   {
337     if (format_position[0] >= 3)                /* If year is after HHMMDD */
338     {
339       /*
340         If year is not in first part then we have to determinate if we got
341         a date field or a datetime field.
342         We do this by checking if there is two numbers separated by
343         space in the input.
344       */
345       while (pos < end && !my_isspace(&my_charset_latin1, *pos))
346         pos++;
347       while (pos < end && !my_isdigit(&my_charset_latin1, *pos))
348         pos++;
349       if (pos == end)
350       {
351         if (flags & TIME_DATETIME_ONLY)
352         {
353           status->warnings= MYSQL_TIME_WARN_TRUNCATED;
354           l_time->time_type= MYSQL_TIMESTAMP_NONE;
355           DBUG_RETURN(1); /* Can't be a full datetime */
356         }
357         /* Date field.  Set hour, minutes and seconds to 0 */
358         date[0]= date[1]= date[2]= date[3]= date[4]= 0;
359         start_loop= 5;                         /* Start with first date part */
360       }
361     }
362 
363     field_length= format_position[0] == 0 ? 4 : 2;
364   }
365 
366   /*
367     Only allow space in the first "part" of the datetime field and:
368     - after days, part seconds
369     - before and after AM/PM (handled by code later)
370 
371     2003-03-03 20:00:20 AM
372     20:00:20.000000 AM 03-03-2000
373   */
374   i= MY_MAX((uint) format_position[0], (uint) format_position[1]);
375   set_if_bigger(i, (uint) format_position[2]);
376   allow_space= ((1 << i) | (1 << format_position[6]));
377   allow_space&= (1 | 2 | 4 | 8 | 64);
378 
379   not_zero_date= 0;
380   for (i = start_loop;
381        i < MAX_DATE_PARTS-1 && str != end &&
382          my_isdigit(&my_charset_latin1,*str);
383        i++)
384   {
385     const char *start= str;
386     ulong tmp_value= (uint) (uchar) (*str++ - '0');
387 
388     /*
389       Internal format means no delimiters; every field has a fixed
390       width. Otherwise, we scan until we find a delimiter and discard
391       leading zeroes -- except for the microsecond part, where leading
392       zeroes are significant, and where we never process more than six
393       digits.
394     */
395     my_bool     scan_until_delim= !is_internal_format &&
396                                   ((i != format_position[6]));
397 
398     while (str != end && my_isdigit(&my_charset_latin1,str[0]) &&
399            (scan_until_delim || --field_length))
400     {
401       tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0');
402       str++;
403     }
404     date_len[i]= (uint) (str - start);
405     if (tmp_value > 999999)                     /* Impossible date part */
406     {
407       status->warnings= MYSQL_TIME_WARN_TRUNCATED;
408       l_time->time_type= MYSQL_TIMESTAMP_NONE;
409       DBUG_RETURN(1);
410     }
411     date[i]=tmp_value;
412     not_zero_date|= tmp_value;
413 
414     /* Length of next field */
415     field_length= format_position[i+1] == 0 ? 4 : 2;
416 
417     if ((last_field_pos= str) == end)
418     {
419       i++;                                      /* Register last found part */
420       break;
421     }
422     /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
423     if (i == format_position[2] && *str == 'T')
424     {
425       str++;                                    /* ISO8601:  CCYYMMDDThhmmss */
426       continue;
427     }
428     if (i == format_position[5])                /* Seconds */
429     {
430       if (*str == '.')                          /* Followed by part seconds */
431       {
432         str++;
433         /*
434           Shift last_field_pos, so '2001-01-01 00:00:00.'
435           is treated as a valid value
436         */
437         last_field_pos= str;
438         field_length= 6;                        /* 6 digits */
439       }
440       else if (my_isdigit(&my_charset_latin1,str[0]))
441       {
442         /*
443           We do not see a decimal point which would have indicated a
444           fractional second part in further read. So we skip the further
445           processing of digits.
446         */
447         i++;
448         break;
449       }
450       continue;
451     }
452     while (str != end &&
453            (my_ispunct(&my_charset_latin1,*str) ||
454             my_isspace(&my_charset_latin1,*str)))
455     {
456       if (my_isspace(&my_charset_latin1,*str))
457       {
458         if (!(allow_space & (1 << i)))
459         {
460           status->warnings= MYSQL_TIME_WARN_TRUNCATED;
461           l_time->time_type= MYSQL_TIMESTAMP_NONE;
462           DBUG_RETURN(1);
463         }
464         found_space= 1;
465       }
466       str++;
467       found_delimitier= 1;                      /* Should be a 'normal' date */
468     }
469     /* Check if next position is AM/PM */
470     if (i == format_position[6])                /* Seconds, time for AM/PM */
471     {
472       i++;                                      /* Skip AM/PM part */
473       if (format_position[7] != 255)            /* If using AM/PM */
474       {
475         if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
476         {
477           if (str[0] == 'p' || str[0] == 'P')
478             add_hours= 12;
479           else if (str[0] != 'a' || str[0] != 'A')
480             continue;                           /* Not AM/PM */
481           str+= 2;                              /* Skip AM/PM */
482           /* Skip space after AM/PM */
483           while (str != end && my_isspace(&my_charset_latin1,*str))
484             str++;
485         }
486       }
487     }
488     last_field_pos= str;
489   }
490   if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
491   {
492     status->warnings= MYSQL_TIME_WARN_TRUNCATED;
493     l_time->time_type= MYSQL_TIMESTAMP_NONE;
494     DBUG_RETURN(1);  /* Can't be a datetime */
495   }
496 
497   str= last_field_pos;
498 
499   number_of_fields= i - start_loop;
500   while (i < MAX_DATE_PARTS)
501   {
502     date_len[i]= 0;
503     date[i++]= 0;
504   }
505 
506   if (!is_internal_format)
507   {
508     year_length= date_len[(uint) format_position[0]];
509     if (!year_length)                           /* Year must be specified */
510     {
511       status->warnings= MYSQL_TIME_WARN_TRUNCATED;
512       l_time->time_type= MYSQL_TIMESTAMP_NONE;
513       DBUG_RETURN(1);
514     }
515 
516     l_time->year=               date[(uint) format_position[0]];
517     l_time->month=              date[(uint) format_position[1]];
518     l_time->day=                date[(uint) format_position[2]];
519     l_time->hour=               date[(uint) format_position[3]];
520     l_time->minute=             date[(uint) format_position[4]];
521     l_time->second=             date[(uint) format_position[5]];
522 
523     frac_pos= (uint) format_position[6];
524     frac_len= date_len[frac_pos];
525     status->fractional_digits= frac_len;
526     if (frac_len < 6)
527       date[frac_pos]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - frac_len];
528     l_time->second_part= date[frac_pos];
529 
530     if (format_position[7] != (uchar) 255)
531     {
532       if (l_time->hour > 12)
533       {
534         status->warnings= MYSQL_TIME_WARN_TRUNCATED;
535         goto err;
536       }
537       l_time->hour= l_time->hour%12 + add_hours;
538     }
539   }
540   else
541   {
542     l_time->year=       date[0];
543     l_time->month=      date[1];
544     l_time->day=        date[2];
545     l_time->hour=       date[3];
546     l_time->minute=     date[4];
547     l_time->second=     date[5];
548     if (date_len[6] < 6)
549       date[6]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - date_len[6]];
550     l_time->second_part=date[6];
551     status->fractional_digits= date_len[6];
552   }
553   l_time->neg= 0;
554 
555   if (year_length == 2 && not_zero_date)
556     l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
557 
558   /*
559     Set time_type before check_datetime_range(),
560     as the latter relies on initialized time_type value.
561   */
562   l_time->time_type= (number_of_fields <= 3 ?
563                       MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
564 
565   if (number_of_fields < 3 || check_datetime_range(l_time))
566   {
567     /* Only give warning for a zero date if there is some garbage after */
568     if (!not_zero_date)                         /* If zero date */
569     {
570       for (; str != end ; str++)
571       {
572         if (!my_isspace(&my_charset_latin1, *str))
573         {
574           not_zero_date= 1;                     /* Give warning */
575           break;
576         }
577       }
578     }
579     status->warnings|= not_zero_date ? MYSQL_TIME_WARN_TRUNCATED :
580                                        MYSQL_TIME_WARN_ZERO_DATE;
581     goto err;
582   }
583 
584   if (check_date(l_time, not_zero_date != 0, flags, &status->warnings))
585     goto err;
586 
587   /* Scan all digits left after microseconds */
588   if (status->fractional_digits == 6 && str != end)
589   {
590     if (my_isdigit(&my_charset_latin1, *str))
591     {
592       /*
593         We don't need the exact nanoseconds value.
594         Knowing the first digit is enough for rounding.
595       */
596       status->nanoseconds= 100 * (int) (*str++ - '0');
597       for (; str != end && my_isdigit(&my_charset_latin1, *str); str++)
598       { }
599     }
600   }
601 
602   for (; str != end ; str++)
603   {
604     if (!my_isspace(&my_charset_latin1,*str))
605     {
606       status->warnings= MYSQL_TIME_WARN_TRUNCATED;
607       break;
608     }
609   }
610 
611   DBUG_RETURN(0);
612 
613 err:
614   set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
615   DBUG_RETURN(1);
616 }
617 
618 
619 /*
620  Convert a time string to a MYSQL_TIME struct.
621 
622   SYNOPSIS
623    str_to_time()
624    str                  A string in full TIMESTAMP format or
625                         [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
626                         [M]MSS or [S]S
627                         There may be an optional [.second_part] after seconds
628    length               Length of str
629    l_time               Store result here
630    status               Conversion status
631 
632    status.warning is set to:
633      MYSQL_TIME_WARN_TRUNCATED flag if the input string
634                         was cut during conversion, and/or
635      MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is out of range.
636 
637    NOTES
638      Because of the extra days argument, this function can only
639      work with times where the time arguments are in the above order.
640 
641    RETURN
642      0  ok
643      1  error
644 */
645 
str_to_time(const char * str,size_t length,MYSQL_TIME * l_time,MYSQL_TIME_STATUS * status)646 my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
647                     MYSQL_TIME_STATUS *status)
648 {
649   ulong date[5];
650   ulonglong value;
651   const char *end=str+length, *end_of_days;
652   my_bool found_days,found_hours;
653   uint state;
654 
655   my_time_status_init(status);
656   l_time->neg=0;
657   for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
658     length--;
659   if (str != end && *str == '-')
660   {
661     l_time->neg=1;
662     str++;
663     length--;
664   }
665   if (str == end)
666     return 1;
667 
668   /* Check first if this is a full TIMESTAMP */
669   if (length >= 12)
670   {                                             /* Probably full timestamp */
671     (void) str_to_datetime(str, length, l_time,
672                            (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), status);
673     if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
674       return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
675     my_time_status_init(status);
676   }
677 
678   /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
679   for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
680     value=value*10L + (long) (*str - '0');
681 
682   if (value > UINT_MAX)
683     return 1;
684 
685   /* Skip all space after 'days' */
686   end_of_days= str;
687   for (; str != end && my_isspace(&my_charset_latin1, str[0]) ; str++)
688     ;
689 
690   state= 0;
691   found_days=found_hours=0;
692   if ((uint) (end-str) > 1 && str != end_of_days &&
693       my_isdigit(&my_charset_latin1, *str))
694   {                                             /* Found days part */
695     date[0]= (ulong) value;
696     state= 1;                                   /* Assume next is hours */
697     found_days= 1;
698   }
699   else if ((end-str) > 1 &&  *str == time_separator &&
700            my_isdigit(&my_charset_latin1, str[1]))
701   {
702     date[0]= 0;                                 /* Assume we found hours */
703     date[1]= (ulong) value;
704     state=2;
705     found_hours=1;
706     str++;                                      /* skip ':' */
707   }
708   else
709   {
710     /* String given as one number; assume HHMMSS format */
711     date[0]= 0;
712     date[1]= (ulong) (value/10000);
713     date[2]= (ulong) (value/100 % 100);
714     date[3]= (ulong) (value % 100);
715     state=4;
716     goto fractional;
717   }
718 
719   /* Read hours, minutes and seconds */
720   for (;;)
721   {
722     for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
723       value=value*10L + (long) (*str - '0');
724     date[state++]= (ulong) value;
725     if (state == 4 || (end-str) < 2 || *str != time_separator ||
726         !my_isdigit(&my_charset_latin1,str[1]))
727       break;
728     str++;                                      /* Skip time_separator (':') */
729   }
730 
731   if (state != 4)
732   {                                             /* Not HH:MM:SS */
733     /* Fix the date to assume that seconds was given */
734     if (!found_hours && !found_days)
735     {
736       size_t len= sizeof(long) * (state - 1);
737       memmove((uchar*) (date+4) - len, (uchar*) (date+state) - len, len);
738       memset(date, 0, sizeof(long)*(4-state));
739     }
740     else
741       memset((date+state), 0, sizeof(long)*(4-state));
742   }
743 
744 fractional:
745   /* Get fractional second part */
746   if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1]))
747   {
748     int field_length= 5;
749     str++; value=(uint) (uchar) (*str - '0');
750     while (++str != end && my_isdigit(&my_charset_latin1, *str))
751     {
752       if (field_length-- > 0)
753         value= value*10 + (uint) (uchar) (*str - '0');
754     }
755     if (field_length >= 0)
756     {
757       status->fractional_digits= DATETIME_MAX_DECIMALS - field_length;
758       if (field_length > 0)
759         value*= (long) log_10_int[field_length];
760     }
761     else
762     {
763       /* Scan digits left after microseconds */
764       status->fractional_digits= 6;
765       status->nanoseconds= 100 * (int) (str[-1] - '0');
766       for ( ; str != end && my_isdigit(&my_charset_latin1, *str); str++)
767       { }
768     }
769     date[4]= (ulong) value;
770   }
771   else if ((end - str) == 1 && *str == '.')
772   {
773     str++;
774     date[4]= 0;
775   }
776   else
777     date[4]=0;
778 
779   /* Check for exponent part: E<gigit> | E<sign><digit> */
780   /* (may occur as result of %g formatting of time value) */
781   if ((end - str) > 1 &&
782       (*str == 'e' || *str == 'E') &&
783       (my_isdigit(&my_charset_latin1, str[1]) ||
784        ((str[1] == '-' || str[1] == '+') &&
785         (end - str) > 2 &&
786         my_isdigit(&my_charset_latin1, str[2]))))
787     return 1;
788 
789   if (internal_format_positions[7] != 255)
790   {
791     /* Read a possible AM/PM */
792     while (str != end && my_isspace(&my_charset_latin1, *str))
793       str++;
794     if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
795     {
796       if (str[0] == 'p' || str[0] == 'P')
797       {
798         str+= 2;
799         date[1]= date[1]%12 + 12;
800       }
801       else if (str[0] == 'a' || str[0] == 'A')
802         str+=2;
803     }
804   }
805 
806   /* Integer overflow checks */
807   if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
808       date[2] > UINT_MAX || date[3] > UINT_MAX ||
809       date[4] > UINT_MAX)
810     return 1;
811 
812   l_time->year=         0;                      /* For protocol::store_time */
813   l_time->month=        0;
814 
815   l_time->day=  0;
816   l_time->hour= date[1] + date[0] * 24; /* Mix days and hours */
817 
818   l_time->minute=       date[2];
819   l_time->second=       date[3];
820   l_time->second_part=  date[4];
821   l_time->time_type= MYSQL_TIMESTAMP_TIME;
822 
823   if (check_time_mmssff_range(l_time))
824   {
825     status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
826     return TRUE;
827   }
828 
829   /* Adjust the value into supported MYSQL_TIME range */
830   adjust_time_range(l_time, &status->warnings);
831 
832   /* Check if there is garbage at end of the MYSQL_TIME specification */
833   if (str != end)
834   {
835     do
836     {
837       if (!my_isspace(&my_charset_latin1,*str))
838       {
839         status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
840         break;
841       }
842     } while (++str != end);
843   }
844   return 0;
845 }
846 
847 
848 /**
849   Convert number to TIME
850   @param nr            Number to convert.
851   @param OUT ltime     Variable to convert to.
852   @param OUT warnings  Warning vector.
853 
854   @retval false OK
855   @retval true No. is out of range
856 */
857 my_bool
number_to_time(longlong nr,MYSQL_TIME * ltime,int * warnings)858 number_to_time(longlong nr, MYSQL_TIME *ltime, int *warnings)
859 {
860   if (nr > TIME_MAX_VALUE)
861   {
862     /* For huge numbers try full DATETIME, like str_to_time does. */
863     if (nr >= 10000000000LL) /* '0001-00-00 00-00-00' */
864     {
865       int warnings_backup= *warnings;
866       if (number_to_datetime(nr, ltime, 0, warnings) != -1LL)
867         return FALSE;
868       *warnings= warnings_backup;
869     }
870     set_max_time(ltime, 0);
871     *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
872     return TRUE;
873   }
874   else if (nr < -TIME_MAX_VALUE)
875   {
876     set_max_time(ltime, 1);
877     *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
878     return TRUE;
879   }
880   if ((ltime->neg= (nr < 0)))
881     nr= -nr;
882   if (nr % 100 >= 60 || nr / 100 % 100 >= 60) /* Check hours and minutes */
883   {
884     set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
885     *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
886     return TRUE;
887   }
888   ltime->time_type= MYSQL_TIMESTAMP_TIME;
889   ltime->year= ltime->month= ltime->day= 0;
890   TIME_set_hhmmss(ltime, (uint)nr);
891   ltime->second_part= 0;
892   return FALSE;
893 }
894 
895 
896 /**
897   Adjust 'time' value to lie in the MYSQL_TIME range.
898   If the time value lies outside of the range [-838:59:59, 838:59:59],
899   set it to the closest endpoint of the range and set
900   MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
901 
902   @param  time     pointer to MYSQL_TIME value
903   @param  warning  set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
904 */
adjust_time_range(struct st_mysql_time * my_time,int * warning)905 void adjust_time_range(struct st_mysql_time *my_time, int *warning)
906 {
907   assert(!check_time_mmssff_range(my_time));
908   if (check_time_range_quick(my_time))
909   {
910     my_time->day= my_time->second_part= 0;
911     set_max_hhmmss(my_time);
912     *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
913   }
914 }
915 
916 
917 /*
918   Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
919 
920   SYNOPSIS
921     my_init_time()
922 */
my_init_time(void)923 void my_init_time(void)
924 {
925   time_t seconds;
926   struct tm *l_time,tm_tmp;
927   MYSQL_TIME my_time;
928   my_bool not_used;
929 
930   seconds= (time_t) time((time_t*) 0);
931   localtime_r(&seconds,&tm_tmp);
932   l_time= &tm_tmp;
933   my_time_zone=		3600;		/* Comp. for -3600 in my_gmt_sec */
934   my_time.year=		(uint) l_time->tm_year+1900;
935   my_time.month=	(uint) l_time->tm_mon+1;
936   my_time.day=		(uint) l_time->tm_mday;
937   my_time.hour=		(uint) l_time->tm_hour;
938   my_time.minute=	(uint) l_time->tm_min;
939   my_time.second=	(uint) l_time->tm_sec;
940   my_time.time_type= MYSQL_TIMESTAMP_DATETIME;
941   my_time.neg= 0;
942   my_time.second_part= 0;
943   my_system_gmt_sec(&my_time, &my_time_zone, &not_used); /* Init my_time_zone */
944 }
945 
946 
947 /*
948   Handle 2 digit year conversions
949 
950   SYNOPSIS
951   year_2000_handling()
952   year     2 digit year
953 
954   RETURN
955     Year between 1970-2069
956 */
957 
year_2000_handling(uint year)958 uint year_2000_handling(uint year)
959 {
960   if ((year=year+1900) < 1900+YY_PART_YEAR)
961     year+=100;
962   return year;
963 }
964 
965 
966 /*
967   Calculate nr of day since year 0 in new date-system (from 1615)
968 
969   SYNOPSIS
970     calc_daynr()
971     year		 Year (exact 4 digit year, no year conversions)
972     month		 Month
973     day			 Day
974 
975   NOTES: 0000-00-00 is a valid date, and will return 0
976 
977   RETURN
978     Days since 0000-00-00
979 */
980 
calc_daynr(uint year,uint month,uint day)981 long calc_daynr(uint year,uint month,uint day)
982 {
983   long delsum;
984   int temp;
985   int y= year;                                  /* may be < 0 temporarily */
986   DBUG_ENTER("calc_daynr");
987 
988   if (y == 0 && month == 0)
989     DBUG_RETURN(0);				/* Skip errors */
990   /* Cast to int to be able to handle month == 0 */
991   delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day);
992   if (month <= 2)
993       y--;
994   else
995     delsum-= (long) ((int) month * 4 + 23) / 10;
996   temp=(int) ((y/100+1)*3)/4;
997   DBUG_PRINT("exit",("year: %d  month: %d  day: %d -> daynr: %ld",
998 		     y+(month <= 2),month,day,delsum+y/4-temp));
999   assert(delsum+(int) y/4-temp >= 0);
1000   DBUG_RETURN(delsum+(int) y/4-temp);
1001 } /* calc_daynr */
1002 
1003 
1004 /*
1005   Convert time in MYSQL_TIME representation in system time zone to its
1006   my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
1007 
1008   SYNOPSIS
1009     my_system_gmt_sec()
1010       t               - time value to be converted
1011       my_timezone     - pointer to long where offset of system time zone
1012                         from UTC will be stored for caching
1013       in_dst_time_gap - set to true if time falls into spring time-gap
1014 
1015   NOTES
1016     The idea is to cache the time zone offset from UTC (including daylight
1017     saving time) for the next call to make things faster. But currently we
1018     just calculate this offset during startup (by calling my_init_time()
1019     function) and use it all the time.
1020     Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
1021     is not allowed).
1022 
1023   RETURN VALUE
1024     Time in UTC seconds since Unix Epoch representation.
1025 */
1026 my_time_t
my_system_gmt_sec(const MYSQL_TIME * t_src,long * my_timezone,my_bool * in_dst_time_gap)1027 my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
1028                   my_bool *in_dst_time_gap)
1029 {
1030   uint loop;
1031   time_t tmp= 0;
1032   int shift= 0;
1033   MYSQL_TIME tmp_time;
1034   MYSQL_TIME *t= &tmp_time;
1035   struct tm *l_time,tm_tmp;
1036   long diff, current_timezone;
1037 
1038   /*
1039     Use temp variable to avoid trashing input data, which could happen in
1040     case of shift required for boundary dates processing.
1041   */
1042   memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME));
1043 
1044   if (!validate_timestamp_range(t))
1045     return 0;
1046 
1047   /*
1048     Calculate the gmt time based on current time and timezone
1049     The -1 on the end is to ensure that if have a date that exists twice
1050     (like 2002-10-27 02:00:0 MET), we will find the initial date.
1051 
1052     By doing -3600 we will have to call localtime_r() several times, but
1053     I couldn't come up with a better way to get a repeatable result :(
1054 
1055     We can't use mktime() as it's buggy on many platforms and not thread safe.
1056 
1057     Note: this code assumes that our time_t estimation is not too far away
1058     from real value (we assume that localtime_r(tmp) will return something
1059     within 24 hrs from t) which is probably true for all current time zones.
1060 
1061     Note2: For the dates, which have time_t representation close to
1062     MAX_INT32 (efficient time_t limit for supported platforms), we should
1063     do a small trick to avoid overflow. That is, convert the date, which is
1064     two days earlier, and then add these days to the final value.
1065 
1066     The same trick is done for the values close to 0 in time_t
1067     representation for platfroms with unsigned time_t (QNX).
1068 
1069     To be more verbose, here is a sample (extracted from the code below):
1070     (calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
1071     would return -2147480896 because of the long type overflow. In result
1072     we would get 1901 year in localtime_r(), which is an obvious error.
1073 
1074     Alike problem raises with the dates close to Epoch. E.g.
1075     (calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
1076     will give -3600.
1077 
1078     On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
1079     wil give us a date around 2106 year. Which is no good.
1080 
1081     Theoreticaly, there could be problems with the latter conversion:
1082     there are at least two timezones, which had time switches near 1 Jan
1083     of 1970 (because of political reasons). These are America/Hermosillo and
1084     America/Mazatlan time zones. They changed their offset on
1085     1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
1086     the code below will give incorrect results for dates close to
1087     1970-01-01, in the case OS takes into account these historical switches.
1088     Luckily, it seems that we support only one platform with unsigned
1089     time_t. It's QNX. And QNX does not support historical timezone data at all.
1090     E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
1091     historical information for localtime_r() etc. That is, the problem is not
1092     relevant to QNX.
1093 
1094     We are safe with shifts close to MAX_INT32, as there are no known
1095     time switches on Jan 2038 yet :)
1096   */
1097   if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
1098   {
1099     /*
1100       Below we will pass (uint) (t->day - shift) to calc_daynr.
1101       As we don't want to get an overflow here, we will shift
1102       only safe dates. That's why we have (t->day > 4) above.
1103     */
1104     t->day-= 2;
1105     shift= 2;
1106   }
1107 
1108   tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
1109                    (long) days_at_timestart) * SECONDS_IN_24H +
1110                    (long) t->hour*3600L +
1111                   (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
1112                  3600);
1113 
1114   current_timezone= my_time_zone;
1115   localtime_r(&tmp,&tm_tmp);
1116   l_time=&tm_tmp;
1117   for (loop=0;
1118        loop < 2 &&
1119 	 (t->hour != (uint) l_time->tm_hour ||
1120 	  t->minute != (uint) l_time->tm_min ||
1121           t->second != (uint) l_time->tm_sec);
1122        loop++)
1123   {					/* One check should be enough ? */
1124     /* Get difference in days */
1125     int days= t->day - l_time->tm_mday;
1126     if (days < -1)
1127       days= 1;					/* Month has wrapped */
1128     else if (days > 1)
1129       days= -1;
1130     diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
1131           (long) (60*((int) t->minute - (int) l_time->tm_min)) +
1132           (long) ((int) t->second - (int) l_time->tm_sec));
1133     current_timezone+= diff+3600;		/* Compensate for -3600 above */
1134     tmp+= (time_t) diff;
1135     localtime_r(&tmp,&tm_tmp);
1136     l_time=&tm_tmp;
1137   }
1138   /*
1139     Fix that if we are in the non existing daylight saving time hour
1140     we move the start of the next real hour.
1141 
1142     This code doesn't handle such exotical thing as time-gaps whose length
1143     is more than one hour or non-integer (latter can theoretically happen
1144     if one of seconds will be removed due leap correction, or because of
1145     general time correction like it happened for Africa/Monrovia time zone
1146     in year 1972).
1147   */
1148   if (loop == 2 && t->hour != (uint) l_time->tm_hour)
1149   {
1150     int days= t->day - l_time->tm_mday;
1151     if (days < -1)
1152       days=1;					/* Month has wrapped */
1153     else if (days > 1)
1154       days= -1;
1155     diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
1156 	  (long) (60*((int) t->minute - (int) l_time->tm_min)) +
1157           (long) ((int) t->second - (int) l_time->tm_sec));
1158     if (diff == 3600)
1159       tmp+=3600 - t->minute*60 - t->second;	/* Move to next hour */
1160     else if (diff == -3600)
1161       tmp-=t->minute*60 + t->second;		/* Move to previous hour */
1162 
1163     *in_dst_time_gap= 1;
1164   }
1165   *my_timezone= current_timezone;
1166 
1167 
1168   /* shift back, if we were dealing with boundary dates */
1169   tmp+= shift * SECONDS_IN_24H;
1170 
1171   /*
1172     This is possible for dates, which slightly exceed boundaries.
1173     Conversion will pass ok for them, but we don't allow them.
1174     First check will pass for platforms with signed time_t.
1175     instruction above (tmp+= shift*86400L) could exceed
1176     MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
1177     So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
1178     with unsigned time_t tmp+= shift*86400L might result in a number,
1179     larger then TIMESTAMP_MAX_VALUE, so another check will work.
1180   */
1181   if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp))
1182     tmp= 0;
1183 
1184   return (my_time_t) tmp;
1185 } /* my_system_gmt_sec */
1186 
1187 
1188 /**
1189   Print the microsecond part: ".NNN"
1190   @param to        OUT The string pointer to print at
1191   @param useconds      The microseconds value.
1192   @param dec           Precision, between 1 and 6.
1193   @return              The length of the result string.
1194 */
1195 static inline int
my_useconds_to_str(char * to,ulong useconds,uint dec)1196 my_useconds_to_str(char *to, ulong useconds, uint dec)
1197 {
1198   assert(dec <= DATETIME_MAX_DECIMALS);
1199   return sprintf(to, ".%0*lu", (int) dec,
1200                  useconds / (ulong) log_10_int[DATETIME_MAX_DECIMALS - dec]);
1201 }
1202 
1203 
1204 /*
1205   Functions to convert time/date/datetime value to a string,
1206   using default format.
1207   This functions don't check that given MYSQL_TIME structure members are
1208   in valid range. If they are not, return value won't reflect any
1209   valid date either. Additionally, make_time doesn't take into
1210   account time->day member: it's assumed that days have been converted
1211   to hours already.
1212 
1213   RETURN
1214     number of characters written to 'to'
1215 */
1216 
my_time_to_str(const MYSQL_TIME * l_time,char * to,uint dec)1217 int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1218 {
1219   uint extra_hours= 0;
1220   int len= sprintf(to, "%s%02u:%02u:%02u", (l_time->neg ? "-" : ""),
1221                    extra_hours + l_time->hour, l_time->minute, l_time->second);
1222   if (dec)
1223     len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1224   return len;
1225 }
1226 
my_date_to_str(const MYSQL_TIME * l_time,char * to)1227 int my_date_to_str(const MYSQL_TIME *l_time, char *to)
1228 {
1229   return sprintf(to, "%04u-%02u-%02u",
1230                  l_time->year, l_time->month, l_time->day);
1231 }
1232 
1233 
1234 /*
1235   Convert datetime to a string 'YYYY-MM-DD hh:mm:ss'.
1236   Open coded for better performance.
1237   This code previously resided in field.cc, in Field_timestamp::val_str().
1238 
1239   @param  to     OUT  The string pointer to print at.
1240   @param  ltime       The MYSQL_TIME value.
1241   @return             The length of the result string.
1242 */
1243 static inline int
TIME_to_datetime_str(char * to,const MYSQL_TIME * ltime)1244 TIME_to_datetime_str(char *to, const MYSQL_TIME *ltime)
1245 {
1246   uint32 temp, temp2;
1247   /* Year */
1248   temp= ltime->year / 100;
1249   *to++= (char) ('0' + temp / 10);
1250   *to++= (char) ('0' + temp % 10);
1251   temp= ltime->year % 100;
1252   *to++= (char) ('0' + temp / 10);
1253   *to++= (char) ('0' + temp % 10);
1254   *to++= '-';
1255   /* Month */
1256   temp= ltime->month;
1257   temp2= temp / 10;
1258   temp= temp-temp2 * 10;
1259   *to++= (char) ('0' + (char) (temp2));
1260   *to++= (char) ('0' + (char) (temp));
1261   *to++= '-';
1262   /* Day */
1263   temp= ltime->day;
1264   temp2= temp / 10;
1265   temp= temp - temp2 * 10;
1266   *to++= (char) ('0' + (char) (temp2));
1267   *to++= (char) ('0' + (char) (temp));
1268   *to++= ' ';
1269   /* Hour */
1270   temp= ltime->hour;
1271   temp2= temp / 10;
1272   temp= temp - temp2 * 10;
1273   *to++= (char) ('0' + (char) (temp2));
1274   *to++= (char) ('0' + (char) (temp));
1275   *to++= ':';
1276   /* Minute */
1277   temp= ltime->minute;
1278   temp2= temp / 10;
1279   temp= temp - temp2 * 10;
1280   *to++= (char) ('0' + (char) (temp2));
1281   *to++= (char) ('0' + (char) (temp));
1282   *to++= ':';
1283   /* Second */
1284   temp= ltime->second;
1285   temp2=temp / 10;
1286   temp= temp - temp2 * 10;
1287   *to++= (char) ('0' + (char) (temp2));
1288   *to++= (char) ('0' + (char) (temp));
1289   return 19;
1290 }
1291 
1292 
1293 /**
1294   Print a datetime value with an optional fractional part.
1295 
1296   @l_time       The MYSQL_TIME value to print.
1297   @to      OUT  The string pointer to print at.
1298   @return       The length of the result string.
1299 */
my_datetime_to_str(const MYSQL_TIME * l_time,char * to,uint dec)1300 int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1301 {
1302   int len= TIME_to_datetime_str(to, l_time);
1303   if (dec)
1304     len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1305   else
1306     to[len]= '\0';
1307   return len;
1308 }
1309 
1310 
1311 /*
1312   Convert struct DATE/TIME/DATETIME value to string using built-in
1313   MySQL time conversion formats.
1314 
1315   SYNOPSIS
1316     my_TIME_to_string()
1317 
1318   NOTE
1319     The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1320 */
1321 
my_TIME_to_str(const MYSQL_TIME * l_time,char * to,uint dec)1322 int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1323 {
1324   switch (l_time->time_type) {
1325   case MYSQL_TIMESTAMP_DATETIME:
1326     return my_datetime_to_str(l_time, to, dec);
1327   case MYSQL_TIMESTAMP_DATE:
1328     return my_date_to_str(l_time, to);
1329   case MYSQL_TIMESTAMP_TIME:
1330     return my_time_to_str(l_time, to, dec);
1331   case MYSQL_TIMESTAMP_NONE:
1332   case MYSQL_TIMESTAMP_ERROR:
1333     to[0]='\0';
1334     return 0;
1335   default:
1336     assert(0);
1337     return 0;
1338   }
1339 }
1340 
1341 
1342 /**
1343   Print a timestamp with an oprional fractional part: XXXXX[.YYYYY]
1344 
1345   @param      tm  The timestamp value to print.
1346   @param  OUT to  The string pointer to print at.
1347   @param      dec Precision, in the range 0..6.
1348   @return         The length of the result string.
1349 */
my_timeval_to_str(const struct timeval * tm,char * to,uint dec)1350 int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
1351 {
1352   int len= sprintf(to, "%d", (int) tm->tv_sec);
1353   if (dec)
1354     len+= my_useconds_to_str(to + len, tm->tv_usec, dec);
1355   return len;
1356 }
1357 
1358 
1359 /*
1360   Convert datetime value specified as number to broken-down TIME
1361   representation and form value of DATETIME type as side-effect.
1362 
1363   SYNOPSIS
1364     number_to_datetime()
1365       nr         - datetime value as number
1366       time_res   - pointer for structure for broken-down representation
1367       flags      - flags to use in validating date, as in str_to_datetime()
1368       was_cut    0      Value ok
1369                  1      If value was cut during conversion
1370                  2      check_date(date,flags) considers date invalid
1371 
1372   DESCRIPTION
1373     Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1374     YYYYMMDDHHMMSS to broken-down MYSQL_TIME representation. Return value in
1375     YYYYMMDDHHMMSS format as side-effect.
1376 
1377     This function also checks if datetime value fits in DATETIME range.
1378 
1379   RETURN VALUE
1380     -1              Timestamp with wrong values
1381     anything else   DATETIME as integer in YYYYMMDDHHMMSS format
1382     Datetime value in YYYYMMDDHHMMSS format.
1383 
1384     was_cut         if return value -1: one of
1385                       - MYSQL_TIME_WARN_OUT_OF_RANGE
1386                       - MYSQL_TIME_WARN_ZERO_DATE
1387                       - MYSQL_TIME_WARN_TRUNCATED
1388                     otherwise 0.
1389 */
1390 
number_to_datetime(longlong nr,MYSQL_TIME * time_res,my_time_flags_t flags,int * was_cut)1391 longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
1392                             my_time_flags_t flags, int *was_cut)
1393 {
1394   long part1,part2;
1395 
1396   *was_cut= 0;
1397   memset(time_res, 0, sizeof(*time_res));
1398   time_res->time_type=MYSQL_TIMESTAMP_DATE;
1399 
1400   if (nr == 0LL || nr >= 10000101000000LL)
1401   {
1402     time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1403     if (nr > 99999999999999LL) /* 9999-99-99 99:99:99 */
1404     {
1405       *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
1406       return -1LL;
1407     }
1408     goto ok;
1409   }
1410   if (nr < 101)
1411     goto err;
1412   if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1413   {
1414     nr= (nr+20000000L)*1000000L;                 /* YYMMDD, year: 2000-2069 */
1415     goto ok;
1416   }
1417   if (nr < (YY_PART_YEAR)*10000L+101L)
1418     goto err;
1419   if (nr <= 991231L)
1420   {
1421     nr= (nr+19000000L)*1000000L;                 /* YYMMDD, year: 1970-1999 */
1422     goto ok;
1423   }
1424   /*
1425     Though officially we support DATE values from 1000-01-01 only, one can
1426     easily insert a value like 1-1-1. So, for consistency reasons such dates
1427     are allowed when TIME_FUZZY_DATE is set.
1428   */
1429   if (nr < 10000101L && !(flags & TIME_FUZZY_DATE))
1430     goto err;
1431   if (nr <= 99991231L)
1432   {
1433     nr= nr*1000000L;
1434     goto ok;
1435   }
1436   if (nr < 101000000L)
1437     goto err;
1438 
1439   time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1440 
1441   if (nr <= (YY_PART_YEAR-1)*10000000000LL+1231235959LL)
1442   {
1443     nr= nr+20000000000000LL;                   /* YYMMDDHHMMSS, 2000-2069 */
1444     goto ok;
1445   }
1446   if (nr <  YY_PART_YEAR*10000000000LL+ 101000000LL)
1447     goto err;
1448   if (nr <= 991231235959LL)
1449     nr= nr+19000000000000LL;		/* YYMMDDHHMMSS, 1970-1999 */
1450 
1451  ok:
1452   part1=(long) (nr/1000000LL);
1453   part2=(long) (nr - (longlong) part1*1000000LL);
1454   time_res->year=  (int) (part1/10000L);  part1%=10000L;
1455   time_res->month= (int) part1 / 100;
1456   time_res->day=   (int) part1 % 100;
1457   time_res->hour=  (int) (part2/10000L);  part2%=10000L;
1458   time_res->minute=(int) part2 / 100;
1459   time_res->second=(int) part2 % 100;
1460 
1461   if (!check_datetime_range(time_res) &&
1462       !check_date(time_res, (nr != 0), flags, was_cut))
1463     return nr;
1464 
1465   /* Don't want to have was_cut get set if TIME_NO_ZERO_DATE was violated. */
1466   if (!nr && (flags & TIME_NO_ZERO_DATE))
1467     return -1LL;
1468 
1469  err:
1470   *was_cut= MYSQL_TIME_WARN_TRUNCATED;
1471   return -1LL;
1472 }
1473 
1474 
1475 /**
1476   Convert time value to integer in YYYYMMDDHHMMSS.
1477   @param  my_time  The MYSQL_TIME value to convert.
1478   @return          A number in format YYYYMMDDHHMMSS.
1479 */
TIME_to_ulonglong_datetime(const MYSQL_TIME * my_time)1480 ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)
1481 {
1482   return ((ulonglong) (my_time->year * 10000UL +
1483                        my_time->month * 100UL +
1484                        my_time->day) * 1000000ULL +
1485           (ulonglong) (my_time->hour * 10000UL +
1486                        my_time->minute * 100UL +
1487                        my_time->second));
1488 }
1489 
1490 
1491 
1492 /**
1493   Convert MYSQL_TIME value to integer in YYYYMMDD format
1494   @param my_time  The MYSQL_TIME value to convert.
1495   @return         A number in format YYYYMMDD.
1496 */
TIME_to_ulonglong_date(const MYSQL_TIME * my_time)1497 ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *my_time)
1498 {
1499   return (ulonglong) (my_time->year * 10000UL + my_time->month * 100UL +
1500                       my_time->day);
1501 }
1502 
1503 
1504 /**
1505   Convert MYSQL_TIME value to integer in HHMMSS format.
1506   This function doesn't take into account time->day member:
1507   it's assumed that days have been converted to hours already.
1508   @param my_time  The TIME value to convert.
1509   @return         The number in HHMMSS format.
1510 */
TIME_to_ulonglong_time(const MYSQL_TIME * my_time)1511 ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time)
1512 {
1513   return (ulonglong) (my_time->hour * 10000UL +
1514                       my_time->minute * 100UL +
1515                       my_time->second);
1516 }
1517 
1518 
1519 /**
1520   Set day, month and year from a number
1521   @param ltime    MYSQL_TIME variable
1522   @param yymmdd   Number in YYYYMMDD format
1523 */
TIME_set_yymmdd(MYSQL_TIME * ltime,uint yymmdd)1524 void TIME_set_yymmdd(MYSQL_TIME *ltime, uint yymmdd)
1525 {
1526   ltime->day=   (int) (yymmdd % 100);
1527   ltime->month= (int) (yymmdd / 100) % 100;
1528   ltime->year=  (int) (yymmdd / 10000);
1529 }
1530 
1531 
1532 /**
1533   Set hour, minute and secondr from a number
1534   @param ltime    MYSQL_TIME variable
1535   @param hhmmss   Number in HHMMSS format
1536 */
TIME_set_hhmmss(MYSQL_TIME * ltime,uint hhmmss)1537 void TIME_set_hhmmss(MYSQL_TIME *ltime, uint hhmmss)
1538 {
1539   ltime->second=  (int) (hhmmss % 100);
1540   ltime->minute=  (int) (hhmmss / 100) % 100;
1541   ltime->hour=    (int) (hhmmss / 10000);
1542 }
1543 
1544 
1545 /*
1546   Convert struct MYSQL_TIME (date and time split into year/month/day/hour/...
1547   to a number in format YYYYMMDDHHMMSS (DATETIME),
1548   YYYYMMDD (DATE)  or HHMMSS (TIME).
1549 
1550   SYNOPSIS
1551     TIME_to_ulonglong()
1552 
1553   DESCRIPTION
1554     The function is used when we need to convert value of time item
1555     to a number if it's used in numeric context, i. e.:
1556     SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1557     SELECT ?+1;
1558 
1559   NOTE
1560     This function doesn't check that given MYSQL_TIME structure members are
1561     in valid range. If they are not, return value won't reflect any
1562     valid date either.
1563 */
1564 
TIME_to_ulonglong(const MYSQL_TIME * my_time)1565 ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time)
1566 {
1567   switch (my_time->time_type) {
1568   case MYSQL_TIMESTAMP_DATETIME:
1569     return TIME_to_ulonglong_datetime(my_time);
1570   case MYSQL_TIMESTAMP_DATE:
1571     return TIME_to_ulonglong_date(my_time);
1572   case MYSQL_TIMESTAMP_TIME:
1573     return TIME_to_ulonglong_time(my_time);
1574   case MYSQL_TIMESTAMP_NONE:
1575   case MYSQL_TIMESTAMP_ERROR:
1576     return 0ULL;
1577   default:
1578     assert(0);
1579   }
1580   return 0;
1581 }
1582 
1583 
1584 /*** TIME low-level memory and disk representation routines ***/
1585 
1586 /*
1587   In-memory format:
1588 
1589    1  bit sign          (Used for sign, when on disk)
1590    1  bit unused        (Reserved for wider hour range, e.g. for intervals)
1591    10 bit hour          (0-836)
1592    6  bit minute        (0-59)
1593    6  bit second        (0-59)
1594   24  bits microseconds (0-999999)
1595 
1596  Total: 48 bits = 6 bytes
1597    Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1598 */
1599 
1600 
1601 /**
1602   Convert time value to numeric packed representation.
1603 
1604   @param    ltime   The value to convert.
1605   @return           Numeric packed representation.
1606 */
TIME_to_longlong_time_packed(const MYSQL_TIME * ltime)1607 longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
1608 {
1609   /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
1610   long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
1611             (ltime->minute << 6) | ltime->second;
1612   longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
1613   return ltime->neg ? -tmp : tmp;
1614 }
1615 
1616 
1617 /**
1618   Convert time packed numeric representation to time.
1619 
1620   @param  OUT ltime  The MYSQL_TIME variable to set.
1621   @param      tmp    The packed numeric representation.
1622 */
TIME_from_longlong_time_packed(MYSQL_TIME * ltime,longlong tmp)1623 void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
1624 {
1625   longlong hms;
1626   if ((ltime->neg= (tmp < 0)))
1627     tmp= -tmp;
1628   hms= MY_PACKED_TIME_GET_INT_PART(tmp);
1629   ltime->year=   (uint) 0;
1630   ltime->month=  (uint) 0;
1631   ltime->day=    (uint) 0;
1632   ltime->hour=   (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
1633   ltime->minute= (uint) (hms >> 6)  % (1 << 6);  /* 6 bits starting at 6th   */
1634   ltime->second= (uint)  hms        % (1 << 6);  /* 6 bits starting at 0th   */
1635   ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1636   ltime->time_type= MYSQL_TIMESTAMP_TIME;
1637 }
1638 
1639 /*
1640   On disk we convert from signed representation to unsigned
1641   representation using TIMEF_OFS, so all values become binary comparable.
1642 */
1643 #define TIMEF_OFS 0x800000000000LL
1644 #define TIMEF_INT_OFS 0x800000LL
1645 
1646 
1647 /**
1648   Convert in-memory numeric time representation to on-disk representation
1649 
1650   @param       nr   Value in packed numeric time format.
1651   @param   OUT ptr  The buffer to put value at.
1652   @param       dec  Precision.
1653 */
my_time_packed_to_binary(longlong nr,uchar * ptr,uint dec)1654 void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1655 {
1656   assert(dec <= DATETIME_MAX_DECIMALS);
1657   /* Make sure the stored value was previously properly rounded or truncated */
1658   assert((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1659           (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1660 
1661   switch (dec)
1662   {
1663   case 0:
1664   default:
1665     mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1666     break;
1667 
1668   case 1:
1669   case 2:
1670     mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1671     ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1672     break;
1673 
1674   case 4:
1675   case 3:
1676     mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1677     mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1678     break;
1679 
1680   case 5:
1681   case 6:
1682     mi_int6store(ptr, nr + TIMEF_OFS);
1683     break;
1684   }
1685 }
1686 
1687 
1688 /**
1689   Convert on-disk time representation to in-memory packed numeric
1690   representation.
1691 
1692   @param   ptr  The pointer to read the value at.
1693   @param   dec  Precision.
1694   @return       Packed numeric time representation.
1695 */
my_time_packed_from_binary(const uchar * ptr,uint dec)1696 longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
1697 {
1698   assert(dec <= DATETIME_MAX_DECIMALS);
1699 
1700   switch (dec)
1701   {
1702   case 0:
1703   default:
1704     {
1705       longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1706       return MY_PACKED_TIME_MAKE_INT(intpart);
1707     }
1708   case 1:
1709   case 2:
1710     {
1711       longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1712       int frac= (uint) ptr[3];
1713       if (intpart < 0 && frac)
1714       {
1715         /*
1716           Negative values are stored with reverse fractional part order,
1717           for binary sort compatibility.
1718 
1719             Disk value  intpart frac   Time value   Memory value
1720             800000.00    0      0      00:00:00.00  0000000000.000000
1721             7FFFFF.FF   -1      255   -00:00:00.01  FFFFFFFFFF.FFD8F0
1722             7FFFFF.9D   -1      99    -00:00:00.99  FFFFFFFFFF.F0E4D0
1723             7FFFFF.00   -1      0     -00:00:01.00  FFFFFFFFFF.000000
1724             7FFFFE.FF   -1      255   -00:00:01.01  FFFFFFFFFE.FFD8F0
1725             7FFFFE.F6   -2      246   -00:00:01.10  FFFFFFFFFE.FE7960
1726 
1727             Formula to convert fractional part from disk format
1728             (now stored in "frac" variable) to absolute value: "0x100 - frac".
1729             To reconstruct in-memory value, we shift
1730             to the next integer value and then substruct fractional part.
1731         */
1732         intpart++;    /* Shift to the next integer value */
1733         frac-= 0x100; /* -(0x100 - frac) */
1734       }
1735       return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
1736     }
1737 
1738   case 3:
1739   case 4:
1740     {
1741       longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1742       int frac= mi_uint2korr(ptr + 3);
1743       if (intpart < 0 && frac)
1744       {
1745         /*
1746           Fix reverse fractional part order: "0x10000 - frac".
1747           See comments for FSP=1 and FSP=2 above.
1748         */
1749         intpart++;      /* Shift to the next integer value */
1750         frac-= 0x10000; /* -(0x10000-frac) */
1751       }
1752       return MY_PACKED_TIME_MAKE(intpart, frac * 100);
1753     }
1754 
1755   case 5:
1756   case 6:
1757     return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
1758   }
1759 }
1760 
1761 
1762 /*** DATETIME and DATE low-level memory and disk representation routines ***/
1763 
1764 /*
1765     1 bit  sign            (used when on disk)
1766    17 bits year*13+month   (year 0-9999, month 0-12)
1767     5 bits day             (0-31)
1768     5 bits hour            (0-23)
1769     6 bits minute          (0-59)
1770     6 bits second          (0-59)
1771    24 bits microseconds    (0-999999)
1772 
1773    Total: 64 bits = 8 bytes
1774 
1775    SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1776 */
1777 
1778 /**
1779   Convert datetime to packed numeric datetime representation.
1780   @param ltime  The value to convert.
1781   @return       Packed numeric representation of ltime.
1782 */
TIME_to_longlong_datetime_packed(const MYSQL_TIME * ltime)1783 longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
1784 {
1785   longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1786   longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
1787   longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
1788   assert(!check_datetime_range(ltime)); /* Make sure no overflow */
1789   return ltime->neg ? -tmp : tmp;
1790 }
1791 
1792 
1793 /**
1794   Convert date to packed numeric date representation.
1795   Numeric packed date format is similar to numeric packed datetime
1796   representation, with zero hhmmss part.
1797 
1798   @param ltime The value to convert.
1799   @return      Packed numeric representation of ltime.
1800 */
TIME_to_longlong_date_packed(const MYSQL_TIME * ltime)1801 longlong TIME_to_longlong_date_packed(const MYSQL_TIME *ltime)
1802 {
1803   longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1804   return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1805 }
1806 
1807 
1808 /**
1809   Convert year to packed numeric date representation.
1810   Packed value for YYYY is the same to packed value for date YYYY-00-00.
1811 */
year_to_longlong_datetime_packed(long year)1812 longlong year_to_longlong_datetime_packed(long year)
1813 {
1814   longlong ymd= ((year * 13) << 5);
1815   return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1816 }
1817 
1818 
1819 /**
1820   Convert packed numeric datetime representation to MYSQL_TIME.
1821   @param OUT  ltime The datetime variable to convert to.
1822   @param      tmp   The packed numeric datetime value.
1823 */
TIME_from_longlong_datetime_packed(MYSQL_TIME * ltime,longlong tmp)1824 void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
1825 {
1826   longlong ymd, hms;
1827   longlong ymdhms, ym;
1828   if ((ltime->neg= (tmp < 0)))
1829     tmp= -tmp;
1830 
1831   ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1832   ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
1833 
1834   ymd= ymdhms >> 17;
1835   ym= ymd >> 5;
1836   hms= ymdhms % (1 << 17);
1837 
1838   ltime->day= ymd % (1 << 5);
1839   ltime->month= ym % 13;
1840   ltime->year= (uint)(ym / 13);
1841 
1842   ltime->second= hms % (1 << 6);
1843   ltime->minute= (hms >> 6) % (1 << 6);
1844   ltime->hour= (uint)(hms >> 12);
1845 
1846   ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
1847 }
1848 
1849 
1850 /**
1851   Convert packed numeric date representation to MYSQL_TIME.
1852   @param OUT  ltime The date variable to convert to.
1853   @param      tmp   The packed numeric date value.
1854 */
TIME_from_longlong_date_packed(MYSQL_TIME * ltime,longlong tmp)1855 void TIME_from_longlong_date_packed(MYSQL_TIME *ltime, longlong tmp)
1856 {
1857   TIME_from_longlong_datetime_packed(ltime, tmp);
1858   ltime->time_type= MYSQL_TIMESTAMP_DATE;
1859 }
1860 
1861 
1862 /*
1863   On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
1864   for HA_KETYPE_BINARY compatibilty purposes.
1865 */
1866 #define DATETIMEF_INT_OFS 0x8000000000LL
1867 
1868 
1869 /**
1870   Convert on-disk datetime representation
1871   to in-memory packed numeric representation.
1872 
1873   @param ptr   The pointer to read value at.
1874   @param dec   Precision.
1875   @return      In-memory packed numeric datetime representation.
1876 */
my_datetime_packed_from_binary(const uchar * ptr,uint dec)1877 longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
1878 {
1879   longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
1880   int frac;
1881   assert(dec <= DATETIME_MAX_DECIMALS);
1882   switch (dec)
1883   {
1884   case 0:
1885   default:
1886     return MY_PACKED_TIME_MAKE_INT(intpart);
1887   case 1:
1888   case 2:
1889     frac= ((int) (signed char) ptr[5]) * 10000;
1890     break;
1891   case 3:
1892   case 4:
1893     frac= mi_sint2korr(ptr + 5) * 100;
1894     break;
1895   case 5:
1896   case 6:
1897     frac= mi_sint3korr(ptr + 5);
1898     break;
1899   }
1900   return MY_PACKED_TIME_MAKE(intpart, frac);
1901 }
1902 
1903 
1904 /**
1905   Store in-memory numeric packed datetime representation to disk.
1906 
1907   @param      nr  In-memory numeric packed datetime representation.
1908   @param OUT  ptr The pointer to store at.
1909   @param      dec Precision, 1-6.
1910 */
my_datetime_packed_to_binary(longlong nr,uchar * ptr,uint dec)1911 void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1912 {
1913   assert(dec <= DATETIME_MAX_DECIMALS);
1914   /* The value being stored must have been properly rounded or truncated */
1915   assert((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1916           (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1917 
1918   mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
1919   switch (dec)
1920   {
1921   case 0:
1922   default:
1923     break;
1924   case 1:
1925   case 2:
1926     ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1927     break;
1928   case 3:
1929   case 4:
1930     mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1931     break;
1932   case 5:
1933   case 6:
1934     mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
1935   }
1936 }
1937 
1938 
1939 /*** TIMESTAMP low-level memory and disk representation routines ***/
1940 
1941 /**
1942   Convert binary timestamp representation to in-memory representation.
1943 
1944   @param  OUT tm  The variable to convert to.
1945   @param      ptr The pointer to read the value from.
1946   @param      dec Precision.
1947 */
my_timestamp_from_binary(struct timeval * tm,const uchar * ptr,uint dec)1948 void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
1949 {
1950   assert(dec <= DATETIME_MAX_DECIMALS);
1951   tm->tv_sec= mi_uint4korr(ptr);
1952   switch (dec)
1953   {
1954     case 0:
1955     default:
1956       tm->tv_usec= 0;
1957       break;
1958     case 1:
1959     case 2:
1960       tm->tv_usec= ((int) ptr[4]) * 10000;
1961       break;
1962     case 3:
1963     case 4:
1964       tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
1965       break;
1966     case 5:
1967     case 6:
1968       tm->tv_usec= mi_sint3korr(ptr + 4);
1969   }
1970 }
1971 
1972 
1973 /**
1974   Convert in-memory timestamp representation to on-disk representation.
1975 
1976   @param        tm   The value to convert.
1977   @param  OUT   ptr  The pointer to store the value to.
1978   @param        dec  Precision.
1979 */
my_timestamp_to_binary(const struct timeval * tm,uchar * ptr,uint dec)1980 void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
1981 {
1982   assert(dec <= DATETIME_MAX_DECIMALS);
1983   /* Stored value must have been previously properly rounded or truncated */
1984   assert((tm->tv_usec %
1985           (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1986   mi_int4store(ptr, tm->tv_sec);
1987   switch (dec)
1988   {
1989     case 0:
1990     default:
1991       break;
1992     case 1:
1993     case 2:
1994       ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
1995       break;
1996     case 3:
1997     case 4:
1998       mi_int2store(ptr + 4, tm->tv_usec / 100);
1999       break;
2000       /* Impossible second precision. Fall through */
2001     case 5:
2002     case 6:
2003       mi_int3store(ptr + 4, tm->tv_usec);
2004   }
2005 }
2006 
2007 /****************************************/
2008 
2009 
2010 /**
2011   Convert a temporal value to packed numeric temporal representation,
2012   depending on its time_type.
2013 
2014   @ltime   The value to convert.
2015   @return  Packed numeric time/date/datetime representation.
2016 */
TIME_to_longlong_packed(const MYSQL_TIME * ltime)2017 longlong TIME_to_longlong_packed(const MYSQL_TIME *ltime)
2018 {
2019   switch (ltime->time_type) {
2020   case MYSQL_TIMESTAMP_DATE:
2021     return TIME_to_longlong_date_packed(ltime);
2022   case MYSQL_TIMESTAMP_DATETIME:
2023     return TIME_to_longlong_datetime_packed(ltime);
2024   case MYSQL_TIMESTAMP_TIME:
2025     return TIME_to_longlong_time_packed(ltime);
2026   case MYSQL_TIMESTAMP_NONE:
2027   case MYSQL_TIMESTAMP_ERROR:
2028     return 0;
2029   }
2030   assert(0);
2031   return 0;
2032 }
2033 
2034 /*** End of low level format routines ***/
2035