1 /* Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved.
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 /**
29   @defgroup MY_TIME Mysys time utilities
30   @ingroup MYSYS
31   @{
32 
33   @file mysys/my_time.cc
34 
35   Implementation of low level time utilities.
36 */
37 
38 /**
39    @ingroup MY_TIME
40    @page LOW_LEVEL_FORMATS Low-level memory and disk formats
41 
42    - @subpage datetime_and_date_low_level_rep
43    - @subpage time_low_level_rep
44  */
45 
46 #include "my_time.h"
47 
48 #include <assert.h>   // assert
49 #include <algorithm>  // std::max
50 #include <cctype>     // std::isspace
51 #include <climits>    // UINT_MAX
52 #include <cstdio>     // std::sprintf
53 #include <cstring>    // std::memset
54 
55 #include "field_types.h"     // enum_field_types
56 #include "integer_digits.h"  // count_digits, write_digits, write_two_digits
57 #include "my_byteorder.h"    // int3store
58 #include "my_systime.h"      // localtime_r
59 #include "myisampack.h"      // mi_int2store
60 #include "template_utils.h"  // pointer_cast
61 
62 const ulonglong log_10_int[20] = {1,
63                                   10,
64                                   100,
65                                   1000,
66                                   10000UL,
67                                   100000UL,
68                                   1000000UL,
69                                   10000000UL,
70                                   100000000ULL,
71                                   1000000000ULL,
72                                   10000000000ULL,
73                                   100000000000ULL,
74                                   1000000000000ULL,
75                                   10000000000000ULL,
76                                   100000000000000ULL,
77                                   1000000000000000ULL,
78                                   10000000000000000ULL,
79                                   100000000000000000ULL,
80                                   1000000000000000000ULL,
81                                   10000000000000000000ULL};
82 
83 const char my_zero_datetime6[] = "0000-00-00 00:00:00.000000";
84 
85 /**
86    Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format.
87 */
88 static constexpr const uchar internal_format_positions[] = {0, 1, 2, 3,
89                                                             4, 5, 6, 255};
90 
91 static constexpr const char time_separator = ':';
92 
93 /** Day number with 1970-01-01 as  base. */
94 static constexpr ulong const days_at_timestart = 719528;
95 const uchar days_in_month[] = {31, 28, 31, 30, 31, 30, 31,
96                                31, 30, 31, 30, 31, 0};
97 
98 /**
99    Offset of system time zone from UTC in seconds used to speed up
100    work of my_system_gmt_sec() function.
101 */
102 static long my_time_zone = 0;
103 
104 // Right-shift of a negative value is implementation-defined
105 // Assert that we have arithmetic shift of negative numbers
106 static_assert((-2 >> 1) == -1, "Right shift of negative numbers is arithmetic");
my_packed_time_get_int_part(longlong i)107 static longlong my_packed_time_get_int_part(longlong i) { return (i >> 24); }
108 
my_packed_time_make(longlong i,longlong f)109 static longlong my_packed_time_make(longlong i, longlong f) {
110   assert(std::abs(f) <= 0xffffffLL);
111   return (static_cast<ulonglong>(i) << 24) + f;
112 }
113 
my_packed_time_make_int(longlong i)114 static longlong my_packed_time_make_int(longlong i) {
115   return (static_cast<ulonglong>(i) << 24);
116 }
117 
118 // The behavior of <cctype> functions is undefined if the argument's value is
119 // neither representable as unsigned char nor equal to EOF. To use these
120 // functions safely with plain chars, cast to unsigned char.
isspace_char(char ch)121 static inline int isspace_char(char ch) {
122   return std::isspace(static_cast<unsigned char>(ch));
123 }
124 
isdigit_char(char ch)125 static inline int isdigit_char(char ch) {
126   return std::isdigit(static_cast<unsigned char>(ch));
127 }
128 
ispunct_char(char ch)129 static inline int ispunct_char(char ch) {
130   return std::ispunct(static_cast<unsigned char>(ch));
131 }
132 
133 /**
134    Calc days in one year.
135    @note Works with both two and four digit years.
136 
137    @return number of days in that year
138 */
calc_days_in_year(uint year)139 uint calc_days_in_year(uint year) {
140   return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)) ? 366
141                                                                        : 365);
142 }
143 
144 /**
145    Set MYSQL_TIME structure to 0000-00-00 00:00:00.000000
146    @param [out] tm    The value to set.
147    @param time_type  Timestasmp type
148 */
set_zero_time(MYSQL_TIME * tm,enum enum_mysql_timestamp_type time_type)149 void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type) {
150   memset(tm, 0, sizeof(*tm));
151   tm->time_type = time_type;
152 }
153 
154 /**
155   Set hour, minute and second of a MYSQL_TIME variable to maximum time value.
156   Unlike set_max_time(), does not touch the other structure members.
157 */
set_max_hhmmss(MYSQL_TIME * tm)158 void set_max_hhmmss(MYSQL_TIME *tm) {
159   tm->hour = TIME_MAX_HOUR;
160   tm->minute = TIME_MAX_MINUTE;
161   tm->second = TIME_MAX_SECOND;
162 }
163 
164 /**
165   Set MYSQL_TIME variable to maximum time value
166   @param tm    OUT  The variable to set.
167   @param neg        Sign: 1 if negative, 0 if positive.
168 */
set_max_time(MYSQL_TIME * tm,bool neg)169 void set_max_time(MYSQL_TIME *tm, bool neg) {
170   set_zero_time(tm, MYSQL_TIMESTAMP_TIME);
171   set_max_hhmmss(tm);
172   tm->neg = neg;
173 }
174 
175 /**
176   @brief Check datetime value for validity according to flags.
177 
178   @param[in]  my_time        Date to check.
179   @param[in]  not_zero_date  my_time is not the zero date
180   @param[in]  flags          flags to check
181                              (see str_to_datetime() flags in my_time.h)
182   @param[out] was_cut        set to 2 if value was invalid according to flags.
183                              (Feb 29 in non-leap etc.). This remains unchanged
184                              if value is not invalid.
185 
186   @details Here we assume that year and month is ok!
187     If month is 0 we allow any date. (This only happens if we allow zero
188     date parts in str_to_datetime())
189     Disallow dates with zero year and non-zero month and/or day.
190 
191   @retval false  OK
192   @retval true   error
193 */
check_date(const MYSQL_TIME & my_time,bool not_zero_date,my_time_flags_t flags,int * was_cut)194 bool check_date(const MYSQL_TIME &my_time, bool not_zero_date,
195                 my_time_flags_t flags, int *was_cut) {
196   if (not_zero_date) {
197     if (((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
198         (my_time.month == 0 || my_time.day == 0)) {
199       *was_cut = MYSQL_TIME_WARN_ZERO_IN_DATE;
200       return true;
201     } else if ((!(flags & TIME_INVALID_DATES) && my_time.month &&
202                 my_time.day > days_in_month[my_time.month - 1] &&
203                 (my_time.month != 2 || calc_days_in_year(my_time.year) != 366 ||
204                  my_time.day != 29))) {
205       *was_cut = MYSQL_TIME_WARN_OUT_OF_RANGE;
206       return true;
207     }
208   } else if (flags & TIME_NO_ZERO_DATE) {
209     *was_cut = MYSQL_TIME_WARN_ZERO_DATE;
210     return true;
211   }
212   return false;
213 }
214 
215 /**
216   Check if TIME fields can be adjusted to make the time value valid.
217 
218   @param  my_time Time value.
219   @retval true    if the value cannot be made valid.
220   @retval false   if the value is already valid or can be adjusted to
221                   become valid.
222 */
check_time_mmssff_range(const MYSQL_TIME & my_time)223 bool check_time_mmssff_range(const MYSQL_TIME &my_time) {
224   return my_time.minute >= 60 || my_time.second >= 60 ||
225          my_time.second_part > 999999;
226 }
227 
228 /**
229   Check TIME range. The value can include day part,
230   for example:  '1 10:20:30.123456'.
231 
232   minute, second and second_part values are not checked
233   unless hour is equal TIME_MAX_HOUR.
234 
235   @param my_time Time value.
236   @returns       Test result.
237   @retval        false if value is Ok.
238   @retval        true if value is out of range.
239 */
check_time_range_quick(const MYSQL_TIME & my_time)240 bool check_time_range_quick(const MYSQL_TIME &my_time) {
241   longlong hour = static_cast<longlong>(my_time.hour) + 24LL * my_time.day;
242   /* The input value should not be fatally bad */
243   assert(!check_time_mmssff_range(my_time));
244   if (hour <= TIME_MAX_HOUR &&
245       (hour != TIME_MAX_HOUR || my_time.minute != TIME_MAX_MINUTE ||
246        my_time.second != TIME_MAX_SECOND || !my_time.second_part))
247     return false;
248   return true;
249 }
250 
251 /**
252   Check datetime, date, or normalized time (i.e. time without days) range.
253   @param my_time  Datetime value.
254   @retval false on success
255   @retval true  on error
256 */
check_datetime_range(const MYSQL_TIME & my_time)257 bool check_datetime_range(const MYSQL_TIME &my_time) {
258   /*
259     In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
260     In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
261   */
262   return my_time.year > 9999U || my_time.month > 12U || my_time.day > 31U ||
263          my_time.minute > 59U || my_time.second > 59U ||
264          my_time.second_part > 999999U ||
265          (my_time.hour >
266           (my_time.time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23U));
267 }
268 
269 #define MAX_DATE_PARTS 8
270 
271 /**
272   Parses a time zone displacement string on the form `{+-}HH:MM`, converting
273   to seconds.
274 
275   @param[in]  str    Time zone displacement string.
276   @param[in]  length Length of said string.
277   @param[out] result Calculated displacement in seconds.
278 
279   @retval false Ok.
280   @retval true  Not a valid time zone displacement string.
281 */
time_zone_displacement_to_seconds(const char * str,size_t length,int * result)282 bool time_zone_displacement_to_seconds(const char *str, size_t length,
283                                        int *result) {
284   if (length < 6) return true;
285 
286   int sign = str[0] == '+' ? 1 : (str[0] == '-' ? -1 : 0);
287   if (sign == 0) return true;
288 
289   if (!(std::isdigit(str[1]) && std::isdigit(str[2]))) return true;
290   int hours = (str[1] - '0') * 10 + str[2] - '0';
291 
292   if (str[3] != ':') return true;
293 
294   if (!(std::isdigit(str[4]) && std::isdigit(str[5]))) return true;
295   int minutes = (str[4] - '0') * 10 + str[5] - '0';
296   if (minutes >= MINS_PER_HOUR) return true;
297   int seconds = hours * SECS_PER_HOUR + minutes * SECS_PER_MIN;
298 
299   if (seconds > MAX_TIME_ZONE_HOURS * SECS_PER_HOUR) return true;
300 
301   // The SQL standard forbids -00:00.
302   if (sign == -1 && hours == 0 && minutes == 0) return true;
303 
304   for (size_t i = 6; i < length; ++i)
305     if (!std::isspace(str[i])) return true;
306 
307   *result = seconds * sign;
308   return false;
309 }
310 
311 /**
312    Convert a timestamp string to a MYSQL_TIME value.
313 
314    DESCRIPTION
315       At least the following formats are recogniced (based on number of digits)
316       YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
317       YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
318       YYYYMMDDTHHMMSS  where T is a the character T (ISO8601)
319       Also dates where all parts are zero are allowed
320 
321       The second part may have an optional .###### fraction part.
322       The datetime value may be followed by a time zone displacement +/-HH:MM.
323 
324     NOTES
325      This function should work with a format position vector as long as the
326      following things holds:
327      - All date are kept together and all time parts are kept together
328      - Date and time parts must be separated by blank
329      - Second fractions must come after second part and be separated
330        by a '.'.  (The second fractions are optional)
331      - AM/PM must come after second fractions (or after seconds if no fractions)
332      - Year must always been specified.
333      - If time is before date, then we will use datetime format only if
334        the argument consist of two parts, separated by space.
335        Otherwise we will assume the argument is a date.
336      - The hour part must be specified in hour-minute-second order.
337 
338       status->warnings is set to:
339       0                            Value OK
340       MYSQL_TIME_WARN_TRUNCATED    If value was cut during conversion
341       MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
342 
343       l_time->time_type is set as follows:
344       MYSQL_TIMESTAMP_NONE        String wasn't a timestamp, like
345                                   [DD [HH:[MM:[SS]]]].fraction.
346                                   l_time is not changed.
347       MYSQL_TIMESTAMP_DATE        DATE string (YY MM and DD parts ok)
348       MYSQL_TIMESTAMP_DATETIME    Full timestamp
349       MYSQL_TIMESTAMP_ERROR       Timestamp with wrong values.
350                                   All elements in l_time is set to 0
351 
352       flags is a bit field with the follwing possible values:
353        TIME_FUZZY_DATE
354        TIME_DATETIME_ONLY
355        TIME_NO_ZERO_IN_DATE
356        TIME_NO_ZERO_DATE
357        TIME_INVALID_DATES
358 
359     @param str          String to parse
360     @param length       Length of string
361     @param[out] l_time  Date is stored here
362     @param flags        Bitfield
363    TIME_FUZZY_DATE|TIME_DATETIME_ONLY|TIME_NO_ZERO_IN_DATE|TIME_NO_ZERO_DATE|TIME_INVALID_DATES
364    (described above)
365     @param status Conversion status and warnings
366 
367     @retval false Ok
368     @retval true  Error
369   */
str_to_datetime(const char * str,std::size_t length,MYSQL_TIME * l_time,my_time_flags_t flags,MYSQL_TIME_STATUS * status)370 bool str_to_datetime(const char *str, std::size_t length, MYSQL_TIME *l_time,
371                      my_time_flags_t flags, MYSQL_TIME_STATUS *status) {
372   uint field_length = 0;
373   uint year_length = 0;
374   uint digits;
375   uint i;
376   uint number_of_fields;
377   uint date[MAX_DATE_PARTS];
378   uint date_len[MAX_DATE_PARTS];
379   uint add_hours = 0;
380   uint start_loop;
381   ulong not_zero_date;
382   ulong allow_space;
383   bool is_internal_format = false;
384   const char *pos;
385   const char *last_field_pos = nullptr;
386   const char *end = str + length;
387   const uchar *format_position;
388   bool found_delimiter = false;
389   bool found_space = false;
390   bool found_displacement = false;
391   uint frac_pos;
392   uint frac_len;
393   int displacement = 0;
394 
395   assert(status->warnings == 0 && status->fractional_digits == 0 &&
396          status->nanoseconds == 0);
397 
398   /* Skip space at start */
399   for (; str != end && isspace_char(*str); str++)
400     ;
401   if (str == end || !isdigit_char(*str)) {
402     status->warnings = MYSQL_TIME_WARN_TRUNCATED;
403     l_time->time_type = MYSQL_TIMESTAMP_NONE;
404     return true;
405   }
406 
407   is_internal_format = false;
408   /* This has to be changed if want to activate different timestamp formats */
409   format_position = internal_format_positions;
410 
411   /*
412     Calculate number of digits in first part.
413     If length= 8 or >= 14 then year is of format YYYY.
414     (YYYY-MM-DD,  YYYYMMDD, YYYYYMMDDHHMMSS)
415   */
416   for (pos = str; pos != end && (isdigit_char(*pos) || *pos == 'T'); pos++)
417     ;
418 
419   digits = static_cast<uint>(pos - str);
420   start_loop = 0;                   /* Start of scan loop */
421   date_len[format_position[0]] = 0; /* Length of year field */
422   if (pos == end || *pos == '.') {
423     /* Found date in internal format (only numbers like YYYYMMDD) */
424     year_length = (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
425     field_length = year_length;
426     is_internal_format = true;
427     format_position = internal_format_positions;
428   } else {
429     if (format_position[0] >= 3) /* If year is after HHMMDD */
430     {
431       /*
432         If year is not in first part then we have to determinate if we got
433         a date field or a datetime field.
434         We do this by checking if there is two numbers separated by
435         space in the input.
436       */
437       while (pos < end && !isspace_char(*pos)) pos++;
438       while (pos < end && !isdigit_char(*pos)) pos++;
439       if (pos == end) {
440         if (flags & TIME_DATETIME_ONLY) {
441           status->warnings = MYSQL_TIME_WARN_TRUNCATED;
442           l_time->time_type = MYSQL_TIMESTAMP_NONE;
443           return true; /* Can't be a full datetime */
444         }
445         /* Date field.  Set hour, minutes and seconds to 0 */
446         date[0] = 0;
447         date[1] = 0;
448         date[2] = 0;
449         date[3] = 0;
450         date[4] = 0;
451         start_loop = 5; /* Start with first date part */
452       }
453     }
454 
455     field_length = format_position[0] == 0 ? 4 : 2;
456   }
457 
458   /*
459     Only allow space in the first "part" of the datetime field and:
460     - after days, part seconds
461     - before and after AM/PM (handled by code later)
462 
463     2003-03-03 20:00:20 AM
464     20:00:20.000000 AM 03-03-2000
465   */
466   i = *std::max_element(format_position, format_position + 3);
467 
468   allow_space = ((1 << i) | (1 << format_position[6]));
469   allow_space &= (1 | 2 | 4 | 8 | 64);
470 
471   not_zero_date = 0;
472   for (i = start_loop;
473        i < MAX_DATE_PARTS - 1 && str != end && isdigit_char(*str); i++) {
474     const char *start = str;
475     ulong tmp_value = static_cast<uchar>(*str++ - '0');
476 
477     /*
478       Internal format means no delimiters; every field has a fixed
479       width. Otherwise, we scan until we find a delimiter and discard
480       leading zeroes -- except for the microsecond part, where leading
481       zeroes are significant, and where we never process more than six
482       digits.
483     */
484     bool scan_until_delim = !is_internal_format && (i != format_position[6]);
485 
486     while (str != end && isdigit_char(str[0]) &&
487            (scan_until_delim || --field_length)) {
488       tmp_value =
489           tmp_value * 10 + static_cast<ulong>(static_cast<uchar>(*str - '0'));
490       str++;
491       if (tmp_value > 999999) /* Impossible date part */
492       {
493         status->warnings = MYSQL_TIME_WARN_TRUNCATED;
494         l_time->time_type = MYSQL_TIMESTAMP_NONE;
495         return true;
496       }
497     }
498     date_len[i] = static_cast<uint>(str - start);
499     date[i] = tmp_value;
500     not_zero_date |= tmp_value;
501 
502     /* Length of next field */
503     field_length = format_position[i + 1] == 0 ? 4 : 2;
504 
505     if ((last_field_pos = str) == end) {
506       i++; /* Register last found part */
507       break;
508     }
509     /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
510     if (i == format_position[2] && *str == 'T') {
511       str++; /* ISO8601:  CCYYMMDDThhmmss */
512       continue;
513     }
514     if (i == format_position[5]) /* Seconds */
515     {
516       if (*str == '.') /* Followed by part seconds */
517       {
518         str++;
519         /*
520           Shift last_field_pos, so '2001-01-01 00:00:00.'
521           is treated as a valid value
522         */
523         last_field_pos = str;
524         field_length = 6; /* 6 digits */
525       } else if (isdigit_char(str[0])) {
526         /*
527           We do not see a decimal point which would have indicated a
528           fractional second part in further read. So we skip the further
529           processing of digits.
530         */
531         i++;
532         break;
533       } else if (str[0] == '+' || str[0] == '-') {
534         if (!time_zone_displacement_to_seconds(str, end - str, &displacement)) {
535           found_displacement = true;
536           str += end - str;
537           last_field_pos = str;
538         } else {
539           status->warnings = MYSQL_TIME_WARN_TRUNCATED;
540           l_time->time_type = MYSQL_TIMESTAMP_NONE;
541           return true;
542         }
543       }
544       continue;
545     }
546     if (i == format_position[6] && (str[0] == '+' || str[0] == '-')) {
547       if (!time_zone_displacement_to_seconds(str, end - str, &displacement)) {
548         found_displacement = true;
549         str += end - str;
550         last_field_pos = str;
551       } else {
552         status->warnings = MYSQL_TIME_WARN_TRUNCATED;
553         l_time->time_type = MYSQL_TIMESTAMP_NONE;
554         return true;
555       }
556     }
557 
558     while (str != end && (ispunct_char(*str) || isspace_char(*str))) {
559       if (isspace_char(*str)) {
560         if (!(allow_space & (1 << i))) {
561           status->warnings = MYSQL_TIME_WARN_TRUNCATED;
562           l_time->time_type = MYSQL_TIMESTAMP_NONE;
563           return true;
564         }
565         found_space = true;
566       }
567       str++;
568       found_delimiter = true; /* Should be a 'normal' date */
569     }
570     /* Check if next position is AM/PM */
571     if (i == format_position[6]) /* Seconds, time for AM/PM */
572     {
573       i++;                           /* Skip AM/PM part */
574       if (format_position[7] != 255) /* If using AM/PM */
575       {
576         if (str + 2 <= end && (str[1] == 'M' || str[1] == 'm')) {
577           if (str[0] == 'p' || str[0] == 'P')
578             add_hours = 12;
579           else if (str[0] != 'a' && str[0] != 'A')
580             continue; /* Not AM/PM */
581           str += 2;   /* Skip AM/PM */
582           /* Skip space after AM/PM */
583           while (str != end && isspace_char(*str)) str++;
584         }
585       }
586     }
587     last_field_pos = str;
588   }
589   if (found_delimiter && !found_space && (flags & TIME_DATETIME_ONLY)) {
590     status->warnings = MYSQL_TIME_WARN_TRUNCATED;
591     l_time->time_type = MYSQL_TIMESTAMP_NONE;
592     return true; /* Can't be a datetime */
593   }
594 
595   str = last_field_pos;
596 
597   number_of_fields = i - start_loop;
598   while (i < MAX_DATE_PARTS) {
599     date_len[i] = 0;
600     date[i++] = 0;
601   }
602 
603   if (!is_internal_format) {
604     year_length = date_len[static_cast<uint>(format_position[0])];
605     if (!year_length) /* Year must be specified */
606     {
607       status->warnings = MYSQL_TIME_WARN_TRUNCATED;
608       l_time->time_type = MYSQL_TIMESTAMP_NONE;
609       return true;
610     }
611 
612     l_time->year = date[static_cast<uint>(format_position[0])];
613     l_time->month = date[static_cast<uint>(format_position[1])];
614     l_time->day = date[static_cast<uint>(format_position[2])];
615     l_time->hour = date[static_cast<uint>(format_position[3])];
616     l_time->minute = date[static_cast<uint>(format_position[4])];
617     l_time->second = date[static_cast<uint>(format_position[5])];
618     l_time->time_zone_displacement = displacement;
619 
620     frac_pos = static_cast<uint>(format_position[6]);
621     frac_len = date_len[frac_pos];
622     status->fractional_digits = frac_len;
623     if (frac_len < 6)
624       date[frac_pos] *=
625           static_cast<uint>(log_10_int[DATETIME_MAX_DECIMALS - frac_len]);
626     l_time->second_part = date[frac_pos];
627 
628     if (format_position[7] != static_cast<uchar>(255)) {
629       if (l_time->hour > 12) {
630         status->warnings = MYSQL_TIME_WARN_TRUNCATED;
631         goto err;
632       }
633       l_time->hour = l_time->hour % 12 + add_hours;
634     }
635   } else {
636     l_time->year = date[0];
637     l_time->month = date[1];
638     l_time->day = date[2];
639     l_time->hour = date[3];
640     l_time->minute = date[4];
641     l_time->second = date[5];
642     if (date_len[6] < 6)
643       date[6] *=
644           static_cast<uint>(log_10_int[DATETIME_MAX_DECIMALS - date_len[6]]);
645     l_time->second_part = date[6];
646     l_time->time_zone_displacement = displacement;
647     status->fractional_digits = date_len[6];
648   }
649   l_time->neg = false;
650 
651   if (year_length == 2 && not_zero_date)
652     l_time->year += (l_time->year < YY_PART_YEAR ? 2000 : 1900);
653 
654   /*
655     Set time_type before check_datetime_range(),
656     as the latter relies on initialized time_type value.
657   */
658   l_time->time_type =
659       (number_of_fields <= 3 ? MYSQL_TIMESTAMP_DATE
660                              : (found_displacement ? MYSQL_TIMESTAMP_DATETIME_TZ
661                                                    : MYSQL_TIMESTAMP_DATETIME));
662 
663   if (number_of_fields < 3 || check_datetime_range(*l_time)) {
664     /* Only give warning for a zero date if there is some garbage after */
665     if (!not_zero_date) /* If zero date */
666     {
667       for (; str != end; str++) {
668         if (!isspace_char(*str)) {
669           not_zero_date = 1; /* Give warning */
670           break;
671         }
672       }
673     }
674     status->warnings |=
675         not_zero_date ? MYSQL_TIME_WARN_TRUNCATED : MYSQL_TIME_WARN_ZERO_DATE;
676     goto err;
677   }
678 
679   if (check_date(*l_time, not_zero_date != 0, flags, &status->warnings))
680     goto err;
681 
682   /* Scan all digits left after microseconds */
683   if (status->fractional_digits == 6 && str != end) {
684     if (isdigit_char(*str)) {
685       /*
686         We don't need the exact nanoseconds value.
687         Knowing the first digit is enough for rounding.
688       */
689       status->nanoseconds = 100 * (*str++ - '0');
690       for (; str != end && isdigit_char(*str); str++) {
691       }
692     }
693   }
694 
695   if (str != end && (str[0] == '+' || str[0] == '-')) {
696     if (time_zone_displacement_to_seconds(str, end - str, &displacement)) {
697       status->warnings = MYSQL_TIME_WARN_TRUNCATED;
698       l_time->time_type = MYSQL_TIMESTAMP_NONE;
699       return true;
700     } else {
701       l_time->time_type = MYSQL_TIMESTAMP_DATETIME_TZ;
702       l_time->time_zone_displacement = displacement;
703       return false;
704     }
705   }
706 
707   for (; str != end; str++) {
708     if (!isspace_char(*str)) {
709       status->warnings = MYSQL_TIME_WARN_TRUNCATED;
710       break;
711     }
712   }
713 
714   return false;
715 
716 err:
717   set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
718   return true;
719 }
720 
721 /**
722  Convert a time string to a MYSQL_TIME struct.
723 
724  status.warning is set to:
725      MYSQL_TIME_WARN_TRUNCATED flag if the input string
726                         was cut during conversion, and/or
727      MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is out of range.
728 
729  @note
730      Because of the extra days argument, this function can only
731      work with times where the time arguments are in the above order.
732 
733  @param      str     A string in full TIMESTAMP format or
734                          [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
735                          [M]MSS or [S]S
736  @param      length  Length of str
737  @param[out] l_time  Store result here
738  @param[out] status  Conversion status, including warnings.
739  @param      flags   Optional flags to control conversion
740 
741  @retval false  Ok
742  @retval true   Error
743 */
str_to_time(const char * str,std::size_t length,MYSQL_TIME * l_time,MYSQL_TIME_STATUS * status,my_time_flags_t flags)744 bool str_to_time(const char *str, std::size_t length, MYSQL_TIME *l_time,
745                  MYSQL_TIME_STATUS *status, my_time_flags_t flags) {
746   ulong date[5];
747   ulonglong value;
748   const char *end = str + length;
749   const char *end_of_days;
750   bool found_days;
751   bool found_hours;
752   uint state;
753   const char *start;
754   bool seen_colon = false;
755 
756   assert(status->warnings == 0 && status->fractional_digits == 0 &&
757          status->nanoseconds == 0);
758 
759   l_time->neg = false;
760   for (; str != end && isspace_char(*str); str++) length--;
761   if (str != end && *str == '-') {
762     l_time->neg = true;
763     str++;
764     length--;
765   }
766   if (str == end) return true;
767 
768   // Remember beginning of first non-space/- char.
769   start = str;
770 
771   /* Check first if this is a full TIMESTAMP */
772   if (length >= 12) { /* Probably full timestamp */
773     MYSQL_TIME_STATUS tmpstatus;
774     (void)str_to_datetime(str, length, l_time,
775                           (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &tmpstatus);
776     if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR) {
777       *status = tmpstatus;
778       return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
779     }
780     assert(status->warnings == 0 && status->fractional_digits == 0 &&
781            status->nanoseconds == 0);
782   }
783 
784   /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
785   for (value = 0; str != end && isdigit_char(*str); str++)
786     value = value * 10L + static_cast<long>(*str - '0');
787 
788   if (value > UINT_MAX) return true;
789 
790   /* Skip all space after 'days' */
791   end_of_days = str;
792   for (; str != end && isspace_char(str[0]); str++)
793     ;
794 
795   state = 0;
796   found_days = found_hours = false;
797   if (static_cast<uint>(end - str) > 1 && str != end_of_days &&
798       isdigit_char(*str)) { /* Found days part */
799     date[0] = static_cast<ulong>(value);
800     state = 1; /* Assume next is hours */
801     found_days = true;
802   } else if ((end - str) > 1 && *str == time_separator &&
803              isdigit_char(str[1])) {
804     date[0] = 0; /* Assume we found hours */
805     date[1] = static_cast<ulong>(value);
806     state = 2;
807     found_hours = true;
808     str++; /* skip ':' */
809     seen_colon = true;
810   } else {
811     /* String given as one number; assume HHMMSS format */
812     date[0] = 0;
813     date[1] = static_cast<ulong>(value / 10000);
814     date[2] = static_cast<ulong>(value / 100 % 100);
815     date[3] = static_cast<ulong>(value % 100);
816     state = 4;
817     goto fractional;
818   }
819 
820   /* Read hours, minutes and seconds */
821   for (;;) {
822     for (value = 0; str != end && isdigit_char(*str); str++)
823       value = value * 10L + static_cast<long>(*str - '0');
824     date[state++] = static_cast<ulong>(value);
825     if (state == 4 || (end - str) < 2 || *str != time_separator ||
826         !isdigit_char(str[1]))
827       break;
828     str++; /* Skip time_separator (':') */
829     seen_colon = true;
830   }
831 
832   if (state != 4) { /* Not HH:MM:SS */
833     /* Fix the date to assume that seconds was given */
834     if (!found_hours && !found_days) {
835       std::size_t len = sizeof(long) * (state - 1);
836       memmove(pointer_cast<uchar *>(date + 4) - len,
837               pointer_cast<uchar *>(date + state) - len, len);
838       memset(date, 0, sizeof(long) * (4 - state));
839     } else
840       memset((date + state), 0, sizeof(long) * (4 - state));
841   }
842 
843 fractional:
844   /* Get fractional second part */
845   if ((end - str) >= 2 && *str == '.' && isdigit_char(str[1])) {
846     int field_length = 5;
847     str++;
848     value = static_cast<uint>(static_cast<uchar>(*str - '0'));
849     while (++str != end && isdigit_char(*str)) {
850       if (field_length-- > 0)
851         value = value * 10 + static_cast<uint>(static_cast<uchar>(*str - '0'));
852     }
853     if (field_length >= 0) {
854       status->fractional_digits = DATETIME_MAX_DECIMALS - field_length;
855       if (field_length > 0)
856         value *= static_cast<long>(log_10_int[field_length]);
857     } else {
858       /* Scan digits left after microseconds */
859       status->fractional_digits = 6;
860       status->nanoseconds = 100 * (str[-1] - '0');
861       for (; str != end && isdigit_char(*str); str++) {
862       }
863     }
864     date[4] = static_cast<ulong>(value);
865   } else if ((end - str) == 1 && *str == '.') {
866     str++;
867     date[4] = 0;
868   } else
869     date[4] = 0;
870 
871   /* Check for exponent part: E<gigit> | E<sign><digit> */
872   /* (may occur as result of %g formatting of time value) */
873   if ((end - str) > 1 && (*str == 'e' || *str == 'E') &&
874       (isdigit_char(str[1]) || ((str[1] == '-' || str[1] == '+') &&
875                                 (end - str) > 2 && isdigit_char(str[2]))))
876     return true;
877 
878   if (internal_format_positions[7] != 255) {
879     /* Read a possible AM/PM */
880     while (str != end && isspace_char(*str)) str++;
881     if (str + 2 <= end && (str[1] == 'M' || str[1] == 'm')) {
882       if (str[0] == 'p' || str[0] == 'P') {
883         str += 2;
884         date[1] = date[1] % 12 + 12;
885       } else if (str[0] == 'a' || str[0] == 'A')
886         str += 2;
887     }
888   }
889 
890   /* Integer overflow checks */
891   if (date[0] > UINT_MAX || date[1] > UINT_MAX || date[2] > UINT_MAX ||
892       date[3] > UINT_MAX || date[4] > UINT_MAX)
893     return true;
894 
895   if (!seen_colon && (flags & TIME_STRICT_COLON)) {
896     memset(l_time, 0, sizeof(*l_time));
897     status->warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
898     return true;
899   }
900 
901   l_time->year = 0; /* For protocol::store_time */
902   l_time->month = 0;
903 
904   l_time->day = 0;
905   l_time->hour = date[1] + date[0] * 24; /* Mix days and hours */
906 
907   l_time->minute = date[2];
908   l_time->second = date[3];
909   l_time->second_part = date[4];
910   l_time->time_type = MYSQL_TIMESTAMP_TIME;
911   l_time->time_zone_displacement = 0;
912 
913   if (check_time_mmssff_range(*l_time)) {
914     status->warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
915     return true;
916   }
917 
918   /* Adjust the value into supported MYSQL_TIME range */
919   adjust_time_range(l_time, &status->warnings);
920 
921   /* Check if there is garbage at end of the MYSQL_TIME specification */
922   if (str != end) {
923     do {
924       if (!isspace_char(*str)) {
925         status->warnings |= MYSQL_TIME_WARN_TRUNCATED;
926         // No char was actually used in conversion - bad value
927         if (str == start) {
928           l_time->time_type = MYSQL_TIMESTAMP_NONE;
929           return true;
930         }
931         break;
932       }
933     } while (++str != end);
934   }
935   return false;
936 }
937 
938 /**
939   Convert number to TIME
940   @param nr            Number to convert.
941   @param [out] ltime     Variable to convert to.
942   @param [out] warnings  Warning vector.
943 
944   @retval false OK
945   @retval true No. is out of range
946 */
number_to_time(longlong nr,MYSQL_TIME * ltime,int * warnings)947 bool number_to_time(longlong nr, MYSQL_TIME *ltime, int *warnings) {
948   if (nr > TIME_MAX_VALUE) {
949     /* For huge numbers try full DATETIME, like str_to_time does. */
950     if (nr >= 10000000000LL) /* '0001-00-00 00-00-00' */
951     {
952       int warnings_backup = *warnings;
953       if (number_to_datetime(nr, ltime, 0, warnings) != -1LL) return false;
954       *warnings = warnings_backup;
955     }
956     set_max_time(ltime, false);
957     *warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
958     return true;
959   } else if (nr < -TIME_MAX_VALUE) {
960     set_max_time(ltime, true);
961     *warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
962     return true;
963   }
964   if ((ltime->neg = (nr < 0))) nr = -nr;
965   if (nr % 100 >= 60 || nr / 100 % 100 >= 60) /* Check hours and minutes */
966   {
967     set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
968     *warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
969     return true;
970   }
971   ltime->time_type = MYSQL_TIMESTAMP_TIME;
972   ltime->year = ltime->month = ltime->day = 0;
973   TIME_set_hhmmss(ltime, static_cast<uint>(nr));
974   ltime->second_part = 0;
975   return false;
976 }
977 
978 /**
979   Adjust 'time' value to lie in the MYSQL_TIME range.
980   If the time value lies outside of the range [-838:59:59, 838:59:59],
981   set it to the closest endpoint of the range and set
982   MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
983 
984   @param[in,out]  my_time  pointer to MYSQL_TIME value
985   @param[out]  warning  set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is
986   out of range
987 */
adjust_time_range(MYSQL_TIME * my_time,int * warning)988 void adjust_time_range(MYSQL_TIME *my_time, int *warning) {
989   assert(!check_time_mmssff_range(*my_time));
990   if (check_time_range_quick(*my_time)) {
991     my_time->day = my_time->second_part = 0;
992     set_max_hhmmss(my_time);
993     *warning |= MYSQL_TIME_WARN_OUT_OF_RANGE;
994   }
995 }
996 
997 /**
998    Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
999 */
my_init_time()1000 void my_init_time() {
1001   time_t seconds;
1002   struct tm *l_time;
1003   struct tm tm_tmp;
1004   MYSQL_TIME my_time;
1005   bool not_used;
1006 
1007   seconds = time(nullptr);
1008   localtime_r(&seconds, &tm_tmp);
1009   l_time = &tm_tmp;
1010   my_time_zone = 3600; /* Comp. for -3600 in my_gmt_sec */
1011   my_time.year = static_cast<uint>(l_time->tm_year) + 1900;
1012   my_time.month = static_cast<uint>(l_time->tm_mon) + 1;
1013   my_time.day = static_cast<uint>(l_time->tm_mday);
1014   my_time.hour = static_cast<uint>(l_time->tm_hour);
1015   my_time.minute = static_cast<uint>(l_time->tm_min);
1016   my_time.second = static_cast<uint>(l_time->tm_sec);
1017   my_time.time_type = MYSQL_TIMESTAMP_DATETIME;
1018   my_time.neg = false;
1019   my_time.second_part = 0;
1020   my_system_gmt_sec(my_time, &my_time_zone, &not_used); /* Init my_time_zone */
1021 }
1022 
1023 /**
1024   Handle 2 digit year conversions.
1025 
1026   @param year 2 digit year
1027   @return Year between 1970-2069
1028 */
year_2000_handling(uint year)1029 uint year_2000_handling(uint year) {
1030   if ((year = year + 1900) < 1900 + YY_PART_YEAR) year += 100;
1031   return year;
1032 }
1033 
1034 /**
1035   Calculate nr of day since year 0 in new date-system (from 1615).
1036 
1037   @param year	  Year (exact 4 digit year, no year conversions)
1038   @param month  Month
1039   @param day	  Day
1040 
1041   @note 0000-00-00 is a valid date, and will return 0
1042 
1043   @return Days since 0000-00-00
1044 */
calc_daynr(uint year,uint month,uint day)1045 long calc_daynr(uint year, uint month, uint day) {
1046   long delsum;
1047   int temp;
1048   int y = year; /* may be < 0 temporarily */
1049 
1050   if (y == 0 && month == 0) return 0; /* Skip errors */
1051   /* Cast to int to be able to handle month == 0 */
1052   delsum = static_cast<long>(365 * y + 31 * (static_cast<int>(month) - 1) +
1053                              static_cast<int>(day));
1054   if (month <= 2)
1055     y--;
1056   else
1057     delsum -= static_cast<long>(static_cast<int>(month) * 4 + 23) / 10;
1058   temp = ((y / 100 + 1) * 3) / 4;
1059   assert(delsum + static_cast<int>(y) / 4 - temp >= 0);
1060   return (delsum + static_cast<int>(y) / 4 - temp);
1061 } /* calc_daynr */
1062 
1063 /**
1064   Convert time in MYSQL_TIME representation in system time zone to its
1065   my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
1066 
1067   @param my_time         - time value to be converted
1068   @param my_timezone     - pointer to long where offset of system time zone
1069                            from UTC will be stored for caching
1070   @param in_dst_time_gap - set to true if time falls into spring time-gap
1071 
1072   @note
1073     The idea is to cache the time zone offset from UTC (including daylight
1074     saving time) for the next call to make things faster. But currently we
1075     just calculate this offset during startup (by calling my_init_time()
1076     function) and use it all the time.
1077     Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
1078     is not allowed).
1079 
1080   @return Time in UTC seconds since Unix Epoch representation.
1081 */
my_system_gmt_sec(const MYSQL_TIME & my_time,long * my_timezone,bool * in_dst_time_gap)1082 my_time_t my_system_gmt_sec(const MYSQL_TIME &my_time, long *my_timezone,
1083                             bool *in_dst_time_gap) {
1084   uint loop;
1085   time_t tmp = 0;
1086   int shift = 0;
1087   MYSQL_TIME tmp_time;
1088   MYSQL_TIME *t = &tmp_time;
1089   struct tm *l_time;
1090   struct tm tm_tmp;
1091   long diff, current_timezone;
1092 
1093   /*
1094     Use temp variable to avoid trashing input data, which could happen in
1095     case of shift required for boundary dates processing.
1096   */
1097   // memcpy(&tmp_time, &my_time, sizeof(MYSQL_TIME));
1098   tmp_time = my_time;
1099 
1100   if (!validate_timestamp_range(*t)) return 0;
1101 
1102   /*
1103     Calculate the gmt time based on current time and timezone
1104     The -1 on the end is to ensure that if have a date that exists twice
1105     (like 2002-10-27 02:00:0 MET), we will find the initial date.
1106 
1107     By doing -3600 we will have to call localtime_r() several times, but
1108     I couldn't come up with a better way to get a repeatable result :(
1109 
1110     We can't use mktime() as it's buggy on many platforms and not thread safe.
1111 
1112     Note: this code assumes that our time_t estimation is not too far away
1113     from real value (we assume that localtime_r(tmp) will return something
1114     within 24 hrs from t) which is probably true for all current time zones.
1115 
1116     Note2: For the dates, which have time_t representation close to
1117     MAX_INT32 (efficient time_t limit for supported platforms), we should
1118     do a small trick to avoid overflow. That is, convert the date, which is
1119     two days earlier, and then add these days to the final value.
1120 
1121     The same trick is done for the values close to 0 in time_t
1122     representation for platfroms with unsigned time_t (QNX).
1123 
1124     To be more verbose, here is a sample (extracted from the code below):
1125     (calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
1126     would return -2147480896 because of the long type overflow. In result
1127     we would get 1901 year in localtime_r(), which is an obvious error.
1128 
1129     Alike problem raises with the dates close to Epoch. E.g.
1130     (calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
1131     will give -3600.
1132 
1133     On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
1134     wil give us a date around 2106 year. Which is no good.
1135 
1136     Theoreticaly, there could be problems with the latter conversion:
1137     there are at least two timezones, which had time switches near 1 Jan
1138     of 1970 (because of political reasons). These are America/Hermosillo and
1139     America/Mazatlan time zones. They changed their offset on
1140     1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
1141     the code below will give incorrect results for dates close to
1142     1970-01-01, in the case OS takes into account these historical switches.
1143     Luckily, it seems that we support only one platform with unsigned
1144     time_t. It's QNX. And QNX does not support historical timezone data at all.
1145     E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
1146     historical information for localtime_r() etc. That is, the problem is not
1147     relevant to QNX.
1148 
1149     We are safe with shifts close to MAX_INT32, as there are no known
1150     time switches on Jan 2038 yet :)
1151   */
1152   if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4)) {
1153     /*
1154       Below we will pass static_cast<uint>(t->day - shift) to calc_daynr.
1155       As we don't want to get an overflow here, we will shift
1156       only safe dates. That's why we have (t->day > 4) above.
1157     */
1158     t->day -= 2;
1159     shift = 2;
1160   }
1161 
1162   tmp = static_cast<time_t>(
1163       ((calc_daynr(static_cast<uint>(t->year), static_cast<uint>(t->month),
1164                    static_cast<uint>(t->day)) -
1165         static_cast<long>(days_at_timestart)) *
1166            SECONDS_IN_24H +
1167        static_cast<long>(t->hour) * 3600L +
1168        static_cast<long>(t->minute * 60 + t->second)) +
1169       static_cast<time_t>(my_time_zone) - 3600);
1170 
1171   current_timezone = my_time_zone;
1172   localtime_r(&tmp, &tm_tmp);
1173   l_time = &tm_tmp;
1174   for (loop = 0; loop < 2 && (t->hour != static_cast<uint>(l_time->tm_hour) ||
1175                               t->minute != static_cast<uint>(l_time->tm_min) ||
1176                               t->second != static_cast<uint>(l_time->tm_sec));
1177        loop++) { /* One check should be enough ? */
1178     /* Get difference in days */
1179     int days = t->day - l_time->tm_mday;
1180     if (days < -1)
1181       days = 1; /* Month has wrapped */
1182     else if (days > 1)
1183       days = -1;
1184     diff = (3600L * static_cast<long>(days * 24 + (static_cast<int>(t->hour) -
1185                                                    l_time->tm_hour)) +
1186             static_cast<long>(60 *
1187                               (static_cast<int>(t->minute) - l_time->tm_min)) +
1188             static_cast<long>(static_cast<int>(t->second) - l_time->tm_sec));
1189     current_timezone += diff + 3600; /* Compensate for -3600 above */
1190     tmp += static_cast<time_t>(diff);
1191     localtime_r(&tmp, &tm_tmp);
1192     l_time = &tm_tmp;
1193   }
1194   /*
1195     Fix that if we are in the non existing daylight saving time hour
1196     we move the start of the next real hour.
1197 
1198     This code doesn't handle such exotical thing as time-gaps whose length
1199     is more than one hour or non-integer (latter can theoretically happen
1200     if one of seconds will be removed due leap correction, or because of
1201     general time correction like it happened for Africa/Monrovia time zone
1202     in year 1972).
1203   */
1204   if (loop == 2 && t->hour != static_cast<uint>(l_time->tm_hour)) {
1205     int days = t->day - l_time->tm_mday;
1206     if (days < -1)
1207       days = 1; /* Month has wrapped */
1208     else if (days > 1)
1209       days = -1;
1210     diff = (3600L * static_cast<long>(days * 24 + (static_cast<int>(t->hour) -
1211                                                    l_time->tm_hour)) +
1212             static_cast<long>(60 *
1213                               (static_cast<int>(t->minute) - l_time->tm_min)) +
1214             static_cast<long>(static_cast<int>(t->second) - l_time->tm_sec));
1215     if (diff == 3600)
1216       tmp += 3600 - t->minute * 60 - t->second; /* Move to next hour */
1217     else if (diff == -3600)
1218       tmp -= t->minute * 60 + t->second; /* Move to previous hour */
1219 
1220     *in_dst_time_gap = true;
1221   }
1222   *my_timezone = current_timezone;
1223 
1224   /* shift back, if we were dealing with boundary dates */
1225   tmp += shift * SECONDS_IN_24H;
1226 
1227   /*
1228     This is possible for dates, which slightly exceed boundaries.
1229     Conversion will pass ok for them, but we don't allow them.
1230     First check will pass for platforms with signed time_t.
1231     instruction above (tmp+= shift*86400L) could exceed
1232     MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
1233     So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
1234     with unsigned time_t tmp+= shift*86400L might result in a number,
1235     larger then TIMESTAMP_MAX_VALUE, so another check will work.
1236   */
1237   if (!is_time_t_valid_for_timestamp(tmp)) tmp = 0;
1238 
1239   return static_cast<my_time_t>(tmp);
1240 } /* my_system_gmt_sec */
1241 
1242 /**
1243   Writes a two-digit number to a string, padded with zero if it is less than 10.
1244   If the number is greater than or equal to 100, "00" is written to the string.
1245   The number should be less than 100 for valid temporal values, but the
1246   formatting functions need to handle invalid values too, since they are used
1247   for formatting the values in error/warning messages when invalid values have
1248   been given by the user.
1249 */
format_two_digits(int value,char * to)1250 static char *format_two_digits(int value, char *to) {
1251   if (value < 0 || value >= 100) value = 0;
1252   return write_two_digits(value, to);
1253 }
1254 
1255 /**
1256   Print the microsecond part with the specified precision.
1257 
1258   @param[out] to   The string pointer to print at
1259   @param useconds  The microseconds value
1260   @param dec       Precision, between 1 and 6
1261 
1262   @return          The length of the result string
1263 */
my_useconds_to_str(char * to,unsigned useconds,unsigned dec)1264 static int my_useconds_to_str(char *to, unsigned useconds, unsigned dec) {
1265   assert(dec <= DATETIME_MAX_DECIMALS);
1266 
1267   // Write the decimal point and the terminating zero character.
1268   to[0] = '.';
1269   to[dec + 1] = '\0';
1270 
1271   // Write the dec most significant digits of the microsecond value.
1272   for (int i = DATETIME_MAX_DECIMALS - dec; i > 0; --i) useconds /= 10;
1273   write_digits(useconds, dec, to + 1);
1274   return dec + 1;
1275 }
1276 
1277 /**
1278   Converts a time value to a string with the format HH:MM:SS[.fraction].
1279 
1280   This function doesn't check that the given MYSQL_TIME structure members
1281   are in the valid range. If they are not, the returned value won't reflect
1282   any valid time either. Additionally, it doesn't take into
1283   account time->day member: it's assumed that days have been converted
1284   to hours already.
1285 
1286   @param      my_time Source time value
1287   @param[out] to      Destnation char array
1288   @param      dec     Precision, in the range 0..6
1289 
1290   @return number of characters written to 'to'
1291 */
1292 
my_time_to_str(const MYSQL_TIME & my_time,char * to,uint dec)1293 int my_time_to_str(const MYSQL_TIME &my_time, char *to, uint dec) {
1294   const char *const start = to;
1295   if (my_time.neg) *to++ = '-';
1296 
1297   // Hours should be zero-padded up to two digits. It might have more digits.
1298   to = write_digits(my_time.hour, std::max(2, count_digits(my_time.hour)), to);
1299 
1300   *to++ = ':';
1301   to = format_two_digits(my_time.minute, to);
1302   *to++ = ':';
1303   to = format_two_digits(my_time.second, to);
1304 
1305   const int length = to - start;
1306   if (dec) return length + my_useconds_to_str(to, my_time.second_part, dec);
1307   *to = '\0';
1308   return length;
1309 }
1310 
1311 /**
1312   Converts a date value to a string with the format 'YYYY-MM-DD'.
1313 
1314   This function doesn't check that the given MYSQL_TIME structure members are
1315   in the valid range. If they are not, the returned value won't reflect any
1316   valid date either.
1317 
1318   @param      my_time Source time value
1319   @param[out] to      Destination character array
1320 
1321   @return number of characters written to 'to'
1322 */
my_date_to_str(const MYSQL_TIME & my_time,char * to)1323 int my_date_to_str(const MYSQL_TIME &my_time, char *to) {
1324   const char *const start = to;
1325   to = format_two_digits(my_time.year / 100, to);
1326   to = format_two_digits(my_time.year % 100, to);
1327   *to++ = '-';
1328   to = format_two_digits(my_time.month, to);
1329   *to++ = '-';
1330   to = format_two_digits(my_time.day, to);
1331   *to = '\0';
1332   return to - start;
1333 }
1334 
1335 /**
1336   Convert datetime to a string 'YYYY-MM-DD hh:mm:ss'.
1337   Open coded for better performance.
1338   This code previously resided in field.cc, in Field_timestamp::val_str().
1339 
1340   @param      my_time  The src MYSQL_TIME value.
1341   @param[out] to       The string pointer to print at.
1342   @return The length of the result string.
1343 */
TIME_to_datetime_str(const MYSQL_TIME & my_time,char * to)1344 static int TIME_to_datetime_str(const MYSQL_TIME &my_time, char *to) {
1345   /* Year */
1346   to = format_two_digits(my_time.year / 100, to);
1347   to = format_two_digits(my_time.year % 100, to);
1348   *to++ = '-';
1349   /* Month */
1350   to = format_two_digits(my_time.month, to);
1351   *to++ = '-';
1352   /* Day */
1353   to = format_two_digits(my_time.day, to);
1354   *to++ = ' ';
1355   /* Hour */
1356   to = format_two_digits(my_time.hour, to);
1357   *to++ = ':';
1358   /* Minute */
1359   to = format_two_digits(my_time.minute, to);
1360   *to++ = ':';
1361   /* Second */
1362   format_two_digits(my_time.second, to);
1363   return 19;
1364 }
1365 
1366 /**
1367   Print a datetime value with an optional fractional part.
1368 
1369   @param       my_time The MYSQL_TIME value to print
1370   @param [out] to      The string pointer to print at
1371   @param       dec     Precision, in the range 0..6
1372 
1373   @return The length of the result string.
1374 */
my_datetime_to_str(const MYSQL_TIME & my_time,char * to,uint dec)1375 int my_datetime_to_str(const MYSQL_TIME &my_time, char *to, uint dec) {
1376   int len = TIME_to_datetime_str(my_time, to);
1377   if (dec) len += my_useconds_to_str(to + len, my_time.second_part, dec);
1378   if (my_time.time_type == MYSQL_TIMESTAMP_DATETIME_TZ) {
1379     int tzd = my_time.time_zone_displacement;
1380     len += sprintf(to + len, "%+02i:%02i", tzd / SECS_PER_HOUR,
1381                    std::abs(tzd) / SECS_PER_MIN % MINS_PER_HOUR);
1382   } else
1383     to[len] = '\0';
1384   return len;
1385 }
1386 
1387 /**
1388   Convert struct DATE/TIME/DATETIME value to string using built-in
1389   MySQL time conversion formats.
1390 
1391   @note The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1392   @param my_time      The MYSQL_TIME value to print
1393   @param [out] to     The string pointer to print at
1394   @param dec          Precision, in the range 0..6
1395 
1396   @return number of bytes written
1397 */
my_TIME_to_str(const MYSQL_TIME & my_time,char * to,uint dec)1398 int my_TIME_to_str(const MYSQL_TIME &my_time, char *to, uint dec) {
1399   switch (my_time.time_type) {
1400     case MYSQL_TIMESTAMP_DATETIME:
1401     case MYSQL_TIMESTAMP_DATETIME_TZ:
1402       return my_datetime_to_str(my_time, to, dec);
1403     case MYSQL_TIMESTAMP_DATE:
1404       return my_date_to_str(my_time, to);
1405     case MYSQL_TIMESTAMP_TIME:
1406       return my_time_to_str(my_time, to, dec);
1407     case MYSQL_TIMESTAMP_NONE:
1408     case MYSQL_TIMESTAMP_ERROR:
1409       to[0] = '\0';
1410       return 0;
1411     default:
1412       assert(false);
1413       return 0;
1414   }
1415 }
1416 
1417 /**
1418   Print a timestamp with an oprional fractional part: XXXXX[.YYYYY]
1419 
1420   @param      tm  The timestamp value to print.
1421   @param [out] to  The string pointer to print at.
1422   @param      dec Precision, in the range 0..6.
1423   @return         The length of the result string.
1424 */
my_timeval_to_str(const struct timeval * tm,char * to,uint dec)1425 int my_timeval_to_str(const struct timeval *tm, char *to, uint dec) {
1426   int len = sprintf(to, "%d", static_cast<int>(tm->tv_sec));
1427   if (dec) len += my_useconds_to_str(to + len, tm->tv_usec, dec);
1428   return len;
1429 }
1430 
1431 /**
1432   Convert datetime value specified as number to broken-down TIME
1433   representation and form value of DATETIME type as side-effect.
1434 
1435   Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1436   YYYYMMDDHHMMSS to broken-down MYSQL_TIME representation. Return value in
1437   YYYYMMDDHHMMSS format as side-effect.
1438 
1439   This function also checks if datetime value fits in DATETIME range.
1440 
1441     Datetime value in YYYYMMDDHHMMSS format.
1442 
1443     was_cut         if return value -1: one of
1444                       - MYSQL_TIME_WARN_OUT_OF_RANGE
1445                       - MYSQL_TIME_WARN_ZERO_DATE
1446                       - MYSQL_TIME_WARN_TRUNCATED
1447                     otherwise 0.
1448 
1449   @param         nr        datetime value as number
1450   @param[in,out] time_res  pointer for structure for broken-down
1451                             representation
1452   @param         flags     TIME_NO_ZERO_DATE and flags used by check_date()
1453   @param[out]    was_cut   0 Value ok
1454                            1 If value was cut during conversion
1455                            2 check_date(date,flags) considers date invalid
1456 
1457   @retval -1              Timestamp with wrong values, e.g. nr == 0 with
1458                           TIME_NO_ZERO_DATE
1459   @retval anything else   DATETIME as integer in YYYYMMDDHHMMSS format
1460 */
number_to_datetime(longlong nr,MYSQL_TIME * time_res,my_time_flags_t flags,int * was_cut)1461 longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
1462                             my_time_flags_t flags, int *was_cut) {
1463   long part1;
1464   long part2;
1465 
1466   *was_cut = 0;
1467   memset(time_res, 0, sizeof(*time_res));
1468   time_res->time_type = MYSQL_TIMESTAMP_DATE;
1469 
1470   if (nr == 0LL || nr >= 10000101000000LL) {
1471     time_res->time_type = MYSQL_TIMESTAMP_DATETIME;
1472     if (nr > 99999999999999LL) /* 9999-99-99 99:99:99 */
1473     {
1474       *was_cut = MYSQL_TIME_WARN_OUT_OF_RANGE;
1475       return -1LL;
1476     }
1477     goto ok;
1478   }
1479   if (nr < 101) goto err;
1480   if (nr <= (YY_PART_YEAR - 1) * 10000L + 1231L) {
1481     nr = (nr + 20000000L) * 1000000L; /* YYMMDD, year: 2000-2069 */
1482     goto ok;
1483   }
1484   if (nr < (YY_PART_YEAR)*10000L + 101L) goto err;
1485   if (nr <= 991231L) {
1486     nr = (nr + 19000000L) * 1000000L; /* YYMMDD, year: 1970-1999 */
1487     goto ok;
1488   }
1489   /*
1490     Though officially we support DATE values from 1000-01-01 only, one can
1491     easily insert a value like 1-1-1. So, for consistency reasons such dates
1492     are allowed when TIME_FUZZY_DATE is set.
1493   */
1494   if (nr < 10000101L && !(flags & TIME_FUZZY_DATE)) goto err;
1495   if (nr <= 99991231L) {
1496     nr = nr * 1000000L;
1497     goto ok;
1498   }
1499   if (nr < 101000000L) goto err;
1500 
1501   time_res->time_type = MYSQL_TIMESTAMP_DATETIME;
1502 
1503   if (nr <= (YY_PART_YEAR - 1) * 10000000000LL + 1231235959LL) {
1504     nr = nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1505     goto ok;
1506   }
1507   if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL) goto err;
1508   if (nr <= 991231235959LL)
1509     nr = nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1510 
1511 ok:
1512   part1 = static_cast<long>(nr / 1000000LL);
1513   part2 = static_cast<long>(nr - static_cast<longlong>(part1) * 1000000LL);
1514   time_res->year = static_cast<int>(part1 / 10000L);
1515   part1 %= 10000L;
1516   time_res->month = static_cast<int>(part1) / 100;
1517   time_res->day = static_cast<int>(part1) % 100;
1518   time_res->hour = static_cast<int>(part2 / 10000L);
1519   part2 %= 10000L;
1520   time_res->minute = static_cast<int>(part2) / 100;
1521   time_res->second = static_cast<int>(part2) % 100;
1522 
1523   if (!check_datetime_range(*time_res) &&
1524       !check_date(*time_res, (nr != 0), flags, was_cut))
1525     return nr;
1526 
1527   /* Don't want to have was_cut get set if TIME_NO_ZERO_DATE was violated. */
1528   if (!nr && (flags & TIME_NO_ZERO_DATE)) return -1LL;
1529 
1530 err:
1531   *was_cut = MYSQL_TIME_WARN_TRUNCATED;
1532   return -1LL;
1533 }
1534 
1535 /**
1536   Convert time value to integer in YYYYMMDDHHMMSS.
1537 
1538   @param  my_time  The MYSQL_TIME value to convert.
1539 
1540   @return          A number in format YYYYMMDDHHMMSS.
1541 */
TIME_to_ulonglong_datetime(const MYSQL_TIME & my_time)1542 ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME &my_time) {
1543   return (static_cast<ulonglong>(my_time.year * 10000UL +
1544                                  my_time.month * 100UL + my_time.day) *
1545               1000000ULL +
1546           static_cast<ulonglong>(my_time.hour * 10000UL +
1547                                  my_time.minute * 100UL + my_time.second));
1548 }
1549 
1550 /**
1551   Convert MYSQL_TIME value to integer in YYYYMMDD format
1552 
1553   @param my_time  The MYSQL_TIME value to convert.
1554   @return         A number in format YYYYMMDD.
1555 */
TIME_to_ulonglong_date(const MYSQL_TIME & my_time)1556 ulonglong TIME_to_ulonglong_date(const MYSQL_TIME &my_time) {
1557   return static_cast<ulonglong>(my_time.year * 10000UL + my_time.month * 100UL +
1558                                 my_time.day);
1559 }
1560 
1561 /**
1562   Convert MYSQL_TIME value to integer in HHMMSS format.
1563   This function doesn't take into account time->day member:
1564   it's assumed that days have been converted to hours already.
1565 
1566   @param my_time  The TIME value to convert.
1567   @return         The number in HHMMSS format.
1568 */
TIME_to_ulonglong_time(const MYSQL_TIME & my_time)1569 ulonglong TIME_to_ulonglong_time(const MYSQL_TIME &my_time) {
1570   return static_cast<ulonglong>(my_time.hour * 10000UL +
1571                                 my_time.minute * 100UL + my_time.second);
1572 }
1573 
1574 /**
1575   Set day, month and year from a number.
1576 
1577   @param ltime    MYSQL_TIME variable
1578   @param yymmdd   Number in YYYYMMDD format
1579 */
TIME_set_yymmdd(MYSQL_TIME * ltime,uint yymmdd)1580 void TIME_set_yymmdd(MYSQL_TIME *ltime, uint yymmdd) {
1581   ltime->day = static_cast<int>(yymmdd % 100);
1582   ltime->month = static_cast<int>(yymmdd / 100) % 100;
1583   ltime->year = static_cast<int>(yymmdd / 10000);
1584 }
1585 
1586 /**
1587   Set hour, minute and secondr from a number.
1588 
1589   @param ltime    MYSQL_TIME variable
1590   @param hhmmss   Number in HHMMSS format
1591 */
TIME_set_hhmmss(MYSQL_TIME * ltime,uint hhmmss)1592 void TIME_set_hhmmss(MYSQL_TIME *ltime, uint hhmmss) {
1593   ltime->second = static_cast<int>(hhmmss % 100);
1594   ltime->minute = static_cast<int>(hhmmss / 100) % 100;
1595   ltime->hour = static_cast<int>(hhmmss / 10000);
1596 }
1597 
1598 /**
1599   Convert struct MYSQL_TIME (date and time split into year/month/day/hour/...
1600   to a number in format YYYYMMDDHHMMSS (DATETIME),
1601   YYYYMMDD (DATE) or HHMMSS (TIME).
1602 
1603   The function is used when we need to convert value of time item
1604   to a number if it's used in numeric context, i. e.:
1605   SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1606   SELECT ?+1;
1607 
1608   @param my_time Source time value
1609 
1610   @retval 0 in case of errors!
1611   @retval number in format YYYYMMDDHHMMSS (DATETIME), YYYYMMDD (DATE) or HHMMSS
1612   (TIME), otherwise.
1613 
1614   @note
1615     This function doesn't check that given MYSQL_TIME structure members are
1616     in valid range. If they are not, return value won't reflect any
1617     valid date either.
1618 */
TIME_to_ulonglong(const MYSQL_TIME & my_time)1619 ulonglong TIME_to_ulonglong(const MYSQL_TIME &my_time) {
1620   switch (my_time.time_type) {
1621     case MYSQL_TIMESTAMP_DATETIME:
1622       return TIME_to_ulonglong_datetime(my_time);
1623     case MYSQL_TIMESTAMP_DATE:
1624       return TIME_to_ulonglong_date(my_time);
1625     case MYSQL_TIMESTAMP_TIME:
1626       return TIME_to_ulonglong_time(my_time);
1627     case MYSQL_TIMESTAMP_NONE:
1628     case MYSQL_TIMESTAMP_ERROR:
1629       return 0ULL;
1630     default:
1631       assert(false);
1632   }
1633   return 0;
1634 }
1635 
1636 /**
1637    Round MYSQL_TIME datetime value and convert to ulonglong representation.
1638 
1639    @param my_time input time
1640    @param[out] warnings warning vector
1641 
1642    @return time in (u)longlong format
1643  */
TIME_to_ulonglong_datetime_round(const MYSQL_TIME & my_time,int * warnings)1644 ulonglong TIME_to_ulonglong_datetime_round(const MYSQL_TIME &my_time,
1645                                            int *warnings) {
1646   // Catch simple cases
1647   if (my_time.second_part < 500000) return TIME_to_ulonglong_datetime(my_time);
1648   if (my_time.second < 59) return TIME_to_ulonglong_datetime(my_time) + 1;
1649   // Corner case e.g. 'YYYY-MM-DD hh:mm:59.5'. Proceed with slower method.
1650   MYSQL_TIME tmp = my_time;
1651   my_datetime_adjust_frac(&tmp, 0, warnings, false);
1652   return TIME_to_ulonglong_datetime(tmp);  // + TIME_microseconds_round(ltime);
1653 }
1654 
1655 /**
1656    Round MYSQL_TIME time value and convert to to ulonglong representation.
1657 
1658    @param my_time input time
1659    @return time in (u)longlong format
1660  */
TIME_to_ulonglong_time_round(const MYSQL_TIME & my_time)1661 ulonglong TIME_to_ulonglong_time_round(const MYSQL_TIME &my_time) {
1662   if (my_time.second_part < 500000) return TIME_to_ulonglong_time(my_time);
1663   if (my_time.second < 59) return TIME_to_ulonglong_time(my_time) + 1;
1664   // Corner case e.g. 'hh:mm:59.5'. Proceed with slower method.
1665   MYSQL_TIME tmp = my_time;
1666   my_time_adjust_frac(&tmp, 0, false);
1667   return TIME_to_ulonglong_time(tmp);
1668 }
1669 
1670 /**
1671    @page time_low_level_rep TIME
1672 
1673   In-memory format:
1674 
1675 | Bits  | Field         | Value range |
1676 | ----: | :----         | :---- |
1677 |   1   | sign          |(Used for sign, when on disk) |
1678 |   1   | unused        |(Reserved for wider hour range, e.g. for intervals) |
1679 |   10  | hour          |(0-838) |
1680 |   6   | minute        |(0-59) |
1681 |   6   | second        |(0-59) |
1682 |  24   | microseconds  |(0-999999) |
1683 
1684  Total: 48 bits = 6 bytes
1685 
1686 @verbatim
1687 Format: Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1688 @endverbatim
1689 */
1690 
1691 /**
1692   Convert time value to numeric packed representation.
1693 
1694   @param    my_time The value to convert.
1695   @return           Numeric packed representation.
1696 */
TIME_to_longlong_time_packed(const MYSQL_TIME & my_time)1697 longlong TIME_to_longlong_time_packed(const MYSQL_TIME &my_time) {
1698   /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
1699   long hms = (((my_time.month ? 0 : my_time.day * 24) + my_time.hour) << 12) |
1700              (my_time.minute << 6) | my_time.second;
1701   longlong tmp = my_packed_time_make(hms, my_time.second_part);
1702   return my_time.neg ? -tmp : tmp;
1703 }
1704 
1705 /**
1706   Convert time packed numeric representation to time.
1707 
1708   @param [out] ltime  The MYSQL_TIME variable to set.
1709   @param      tmp    The packed numeric representation.
1710 */
TIME_from_longlong_time_packed(MYSQL_TIME * ltime,longlong tmp)1711 void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp) {
1712   longlong hms;
1713   if ((ltime->neg = (tmp < 0))) tmp = -tmp;
1714   hms = my_packed_time_get_int_part(tmp);
1715   ltime->year = static_cast<uint>(0);
1716   ltime->month = static_cast<uint>(0);
1717   ltime->day = static_cast<uint>(0);
1718   ltime->hour =
1719       static_cast<uint>(hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
1720   ltime->minute =
1721       static_cast<uint>(hms >> 6) % (1 << 6); /* 6 bits starting at 6th   */
1722   ltime->second =
1723       static_cast<uint>(hms) % (1 << 6); /* 6 bits starting at 0th   */
1724   ltime->second_part = my_packed_time_get_frac_part(tmp);
1725   ltime->time_type = MYSQL_TIMESTAMP_TIME;
1726 }
1727 
1728 /**
1729   On disk we convert from signed representation to unsigned
1730   representation using TIMEF_OFS, so all values become binary comparable.
1731 */
1732 #define TIMEF_OFS 0x800000000000LL
1733 #define TIMEF_INT_OFS 0x800000LL
1734 
1735 /**
1736   Convert in-memory numeric time representation to on-disk representation
1737 
1738   @param       nr   Value in packed numeric time format.
1739   @param [out] ptr  The buffer to put value at.
1740   @param       dec  Precision.
1741 */
my_time_packed_to_binary(longlong nr,uchar * ptr,uint dec)1742 void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec) {
1743   assert(dec <= DATETIME_MAX_DECIMALS);
1744   /* Make sure the stored value was previously properly rounded or truncated */
1745   assert((my_packed_time_get_frac_part(nr) %
1746           static_cast<int>(log_10_int[DATETIME_MAX_DECIMALS - dec])) == 0);
1747 
1748   switch (dec) {
1749     case 0:
1750     default:
1751       mi_int3store(ptr, TIMEF_INT_OFS + my_packed_time_get_int_part(nr));
1752       break;
1753 
1754     case 1:
1755     case 2:
1756       mi_int3store(ptr, TIMEF_INT_OFS + my_packed_time_get_int_part(nr));
1757       ptr[3] = static_cast<unsigned char>(
1758           static_cast<char>(my_packed_time_get_frac_part(nr) / 10000));
1759       break;
1760 
1761     case 4:
1762     case 3:
1763       mi_int3store(ptr, TIMEF_INT_OFS + my_packed_time_get_int_part(nr));
1764       mi_int2store(ptr + 3, my_packed_time_get_frac_part(nr) / 100);
1765       break;
1766 
1767     case 5:
1768     case 6:
1769       mi_int6store(ptr, nr + TIMEF_OFS);
1770       break;
1771   }
1772 }
1773 
1774 /**
1775   Convert on-disk time representation to in-memory packed numeric
1776   representation.
1777 
1778   @param   ptr  The pointer to read the value at.
1779   @param   dec  Precision.
1780   @return       Packed numeric time representation.
1781 */
my_time_packed_from_binary(const uchar * ptr,uint dec)1782 longlong my_time_packed_from_binary(const uchar *ptr, uint dec) {
1783   assert(dec <= DATETIME_MAX_DECIMALS);
1784 
1785   switch (dec) {
1786     case 0:
1787     default: {
1788       longlong intpart = mi_uint3korr(ptr) - TIMEF_INT_OFS;
1789       return my_packed_time_make_int(intpart);
1790     }
1791     case 1:
1792     case 2: {
1793       longlong intpart = mi_uint3korr(ptr) - TIMEF_INT_OFS;
1794       int frac = static_cast<uint>(ptr[3]);
1795       if (intpart < 0 && frac) {
1796         /*
1797            Negative values are stored with
1798            reverse fractional part order,
1799            for binary sort compatibility.
1800 
1801             Disk value  intpart frac   Time value   Memory value
1802             800000.00    0      0      00:00:00.00  0000000000.000000
1803             7FFFFF.FF   -1      255   -00:00:00.01  FFFFFFFFFF.FFD8F0
1804             7FFFFF.9D   -1      99    -00:00:00.99  FFFFFFFFFF.F0E4D0
1805             7FFFFF.00   -1      0     -00:00:01.00  FFFFFFFFFF.000000
1806             7FFFFE.FF   -1      255   -00:00:01.01  FFFFFFFFFE.FFD8F0
1807             7FFFFE.F6   -2      246   -00:00:01.10  FFFFFFFFFE.FE7960
1808 
1809             Formula to convert fractional part from disk format
1810             (now stored in "frac" variable) to absolute value: "0x100 - frac".
1811             To reconstruct in-memory value, we shift
1812             to the next integer value and then substruct fractional part.
1813         */
1814         intpart++;     /* Shift to the next integer value */
1815         frac -= 0x100; /* -(0x100 - frac) */
1816       }
1817       return my_packed_time_make(intpart, frac * 10000);
1818     }
1819 
1820     case 3:
1821     case 4: {
1822       longlong intpart = mi_uint3korr(ptr) - TIMEF_INT_OFS;
1823       int frac = mi_uint2korr(ptr + 3);
1824       if (intpart < 0 && frac) {
1825         /*
1826           Fix reverse fractional part order: "0x10000 - frac".
1827           See comments for FSP=1 and FSP=2 above.
1828         */
1829         intpart++;       /* Shift to the next integer value */
1830         frac -= 0x10000; /* -(0x10000-frac) */
1831       }
1832       return my_packed_time_make(intpart, frac * 100);
1833     }
1834 
1835     case 5:
1836     case 6:
1837       return (static_cast<longlong>(mi_uint6korr(ptr))) - TIMEF_OFS;
1838   }
1839 }
1840 
1841 /**
1842    @page datetime_and_date_low_level_rep DATETIME and DATE
1843 
1844 | Bits  | Field         | Value |
1845 | ----: | :----         | :---- |
1846 |    1  | sign          |(used when on disk) |
1847 |   17  | year*13+month |(year 0-9999, month 0-12) |
1848 |    5  | day           |(0-31)|
1849 |    5  | hour          |(0-23)|
1850 |    6  | minute        |(0-59)|
1851 |    6  | second        |(0-59)|
1852 |   24  | microseconds  |(0-999999)|
1853 
1854    Total: 64 bits = 8 bytes
1855 
1856 @verbatim
1857 Format: SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1858 @endverbatim
1859 
1860 */
1861 
1862 /**
1863   Convert datetime to packed numeric datetime representation.
1864 
1865   @param my_time  The value to convert.
1866   @return       Packed numeric representation of my_time.
1867 */
TIME_to_longlong_datetime_packed(const MYSQL_TIME & my_time)1868 longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME &my_time) {
1869   longlong ymd = ((my_time.year * 13 + my_time.month) << 5) | my_time.day;
1870   longlong hms = (my_time.hour << 12) | (my_time.minute << 6) | my_time.second;
1871   longlong tmp = my_packed_time_make(((ymd << 17) | hms), my_time.second_part);
1872   assert(!check_datetime_range(my_time)); /* Make sure no overflow */
1873   return my_time.neg ? -tmp : tmp;
1874 }
1875 
1876 /**
1877   Convert date to packed numeric date representation.
1878   Numeric packed date format is similar to numeric packed datetime
1879   representation, with zero hhmmss part.
1880 
1881   @param my_time The value to convert.
1882   @return      Packed numeric representation of ltime.
1883 */
TIME_to_longlong_date_packed(const MYSQL_TIME & my_time)1884 longlong TIME_to_longlong_date_packed(const MYSQL_TIME &my_time) {
1885   longlong ymd = ((my_time.year * 13 + my_time.month) << 5) | my_time.day;
1886   return my_packed_time_make_int(ymd << 17);
1887 }
1888 
1889 /**
1890   Convert year to packed numeric date representation.
1891   Packed value for YYYY is the same to packed value for date YYYY-00-00.
1892 
1893   @return packed value for date YYYY-00-00.
1894 */
year_to_longlong_datetime_packed(long year)1895 longlong year_to_longlong_datetime_packed(long year) {
1896   longlong ymd = ((year * 13) << 5);
1897   return my_packed_time_make_int(ymd << 17);
1898 }
1899 
1900 /**
1901   Convert packed numeric datetime representation to MYSQL_TIME.
1902 
1903   @param [out] ltime The datetime variable to convert to.
1904   @param      tmp   The packed numeric datetime value.
1905 */
TIME_from_longlong_datetime_packed(MYSQL_TIME * ltime,longlong tmp)1906 void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp) {
1907   longlong ymd;
1908   longlong hms;
1909   longlong ymdhms;
1910   longlong ym;
1911 
1912   if ((ltime->neg = (tmp < 0))) tmp = -tmp;
1913 
1914   ltime->second_part = my_packed_time_get_frac_part(tmp);
1915   ymdhms = my_packed_time_get_int_part(tmp);
1916 
1917   ymd = ymdhms >> 17;
1918   ym = ymd >> 5;
1919   hms = ymdhms % (1 << 17);
1920 
1921   ltime->day = ymd % (1 << 5);
1922   ltime->month = ym % 13;
1923   ltime->year = static_cast<uint>(ym / 13);
1924 
1925   ltime->second = hms % (1 << 6);
1926   ltime->minute = (hms >> 6) % (1 << 6);
1927   ltime->hour = static_cast<uint>(hms >> 12);
1928 
1929   ltime->time_type = MYSQL_TIMESTAMP_DATETIME;
1930   ltime->time_zone_displacement = 0;
1931 }
1932 
1933 /**
1934   Convert packed numeric date representation to MYSQL_TIME.
1935 
1936   @param [out] ltime The date variable to convert to.
1937   @param      tmp   The packed numeric date value.
1938 */
TIME_from_longlong_date_packed(MYSQL_TIME * ltime,longlong tmp)1939 void TIME_from_longlong_date_packed(MYSQL_TIME *ltime, longlong tmp) {
1940   TIME_from_longlong_datetime_packed(ltime, tmp);
1941   ltime->time_type = MYSQL_TIMESTAMP_DATE;
1942 }
1943 
1944 /**
1945   On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
1946   for HA_KETYPE_BINARY compatibilty purposes.
1947 */
1948 #define DATETIMEF_INT_OFS 0x8000000000LL
1949 
1950 /**
1951   Convert on-disk datetime representation
1952   to in-memory packed numeric representation.
1953 
1954   @param ptr   The pointer to read value at.
1955   @param dec   Precision.
1956   @return      In-memory packed numeric datetime representation.
1957 */
my_datetime_packed_from_binary(const uchar * ptr,uint dec)1958 longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec) {
1959   longlong intpart = mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
1960   int frac;
1961   assert(dec <= DATETIME_MAX_DECIMALS);
1962   switch (dec) {
1963     case 0:
1964     default:
1965       return my_packed_time_make_int(intpart);
1966     case 1:
1967     case 2:
1968       frac = (static_cast<int>(static_cast<signed char>(ptr[5]))) * 10000;
1969       break;
1970     case 3:
1971     case 4:
1972       frac = mi_sint2korr(ptr + 5) * 100;
1973       break;
1974     case 5:
1975     case 6:
1976       frac = mi_sint3korr(ptr + 5);
1977       break;
1978   }
1979   return my_packed_time_make(intpart, frac);
1980 }
1981 
1982 /**
1983   Store in-memory numeric packed datetime representation to disk.
1984 
1985   @param      nr  In-memory numeric packed datetime representation.
1986   @param [out] ptr The pointer to store at.
1987   @param      dec Precision, 1-6.
1988 */
my_datetime_packed_to_binary(longlong nr,uchar * ptr,uint dec)1989 void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec) {
1990   assert(dec <= DATETIME_MAX_DECIMALS);
1991   /* The value being stored must have been properly rounded or truncated */
1992   assert((my_packed_time_get_frac_part(nr) %
1993           static_cast<int>(log_10_int[DATETIME_MAX_DECIMALS - dec])) == 0);
1994 
1995   mi_int5store(ptr, my_packed_time_get_int_part(nr) + DATETIMEF_INT_OFS);
1996   switch (dec) {
1997     case 0:
1998     default:
1999       break;
2000     case 1:
2001     case 2:
2002       ptr[5] = static_cast<unsigned char>(
2003           static_cast<char>(my_packed_time_get_frac_part(nr) / 10000));
2004       break;
2005     case 3:
2006     case 4:
2007       mi_int2store(ptr + 5, my_packed_time_get_frac_part(nr) / 100);
2008       break;
2009     case 5:
2010     case 6:
2011       mi_int3store(ptr + 5, my_packed_time_get_frac_part(nr));
2012   }
2013 }
2014 
2015 /*** TIMESTAMP low-level memory and disk representation routines ***/
2016 
2017 /**
2018   Convert binary timestamp representation to in-memory representation.
2019 
2020   @param [out] tm  The variable to convert to.
2021   @param      ptr The pointer to read the value from.
2022   @param      dec Precision.
2023 */
my_timestamp_from_binary(struct timeval * tm,const uchar * ptr,uint dec)2024 void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec) {
2025   assert(dec <= DATETIME_MAX_DECIMALS);
2026   tm->tv_sec = mi_uint4korr(ptr);
2027   switch (dec) {
2028     case 0:
2029     default:
2030       tm->tv_usec = 0;
2031       break;
2032     case 1:
2033     case 2:
2034       tm->tv_usec = (static_cast<int>(ptr[4])) * 10000;
2035       break;
2036     case 3:
2037     case 4:
2038       tm->tv_usec = mi_sint2korr(ptr + 4) * 100;
2039       break;
2040     case 5:
2041     case 6:
2042       tm->tv_usec = mi_sint3korr(ptr + 4);
2043   }
2044 }
2045 
2046 /**
2047   Convert in-memory timestamp representation to on-disk representation.
2048 
2049   @param        tm   The value to convert.
2050   @param [out]  ptr  The pointer to store the value to.
2051   @param        dec  Precision.
2052 */
my_timestamp_to_binary(const struct timeval * tm,uchar * ptr,uint dec)2053 void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec) {
2054   assert(dec <= DATETIME_MAX_DECIMALS);
2055   /* Stored value must have been previously properly rounded or truncated */
2056   assert((tm->tv_usec %
2057           static_cast<int>(log_10_int[DATETIME_MAX_DECIMALS - dec])) == 0);
2058   mi_int4store(ptr, tm->tv_sec);
2059   switch (dec) {
2060     case 0:
2061     default:
2062       break;
2063     case 1:
2064     case 2:
2065       ptr[4] =
2066           static_cast<unsigned char>(static_cast<char>(tm->tv_usec / 10000));
2067       break;
2068     case 3:
2069     case 4:
2070       mi_int2store(ptr + 4, tm->tv_usec / 100);
2071       break;
2072       /* Impossible second precision. Fall through */
2073     case 5:
2074     case 6:
2075       mi_int3store(ptr + 4, tm->tv_usec);
2076   }
2077 }
2078 /**
2079   Convert in-memory date representation to on-disk representation.
2080 
2081   @param        ltime The value to convert.
2082   @param [out]  ptr   The pointer to store the value to.
2083 */
my_date_to_binary(const MYSQL_TIME * ltime,uchar * ptr)2084 void my_date_to_binary(const MYSQL_TIME *ltime, uchar *ptr) {
2085   long tmp = ltime->day + ltime->month * 32 + ltime->year * 16 * 32;
2086   int3store(ptr, tmp);
2087 }
2088 
2089 /**
2090   Convert a temporal value to packed numeric temporal representation,
2091   depending on its time_type.
2092 
2093   @param my_time   The value to convert.
2094   @return  Packed numeric time/date/datetime representation.
2095 */
TIME_to_longlong_packed(const MYSQL_TIME & my_time)2096 longlong TIME_to_longlong_packed(const MYSQL_TIME &my_time) {
2097   switch (my_time.time_type) {
2098     case MYSQL_TIMESTAMP_DATE:
2099       return TIME_to_longlong_date_packed(my_time);
2100     case MYSQL_TIMESTAMP_DATETIME_TZ:
2101       assert(false);  // Should not be this type at this point.
2102     case MYSQL_TIMESTAMP_DATETIME:
2103       return TIME_to_longlong_datetime_packed(my_time);
2104     case MYSQL_TIMESTAMP_TIME:
2105       return TIME_to_longlong_time_packed(my_time);
2106     case MYSQL_TIMESTAMP_NONE:
2107     case MYSQL_TIMESTAMP_ERROR:
2108       return 0;
2109   }
2110   assert(false);
2111   return 0;
2112 }
2113 
2114 /**
2115     Change a daynr to year, month and day. Daynr 0 is returned as date
2116     00.00.00
2117 */
get_date_from_daynr(int64_t daynr,uint * ret_year,uint * ret_month,uint * ret_day)2118 void get_date_from_daynr(int64_t daynr, uint *ret_year, uint *ret_month,
2119                          uint *ret_day) {
2120   uint year;
2121   uint temp;
2122   uint leap_day;
2123   uint day_of_year;
2124   uint days_in_year;
2125   const uchar *month_pos;
2126 
2127   if (daynr <= 365L || daynr >= 3652500) { /* Fix if wrong daynr */
2128     *ret_year = *ret_month = *ret_day = 0;
2129   } else {
2130     year = static_cast<uint>(daynr * 100 / 36525L);
2131     temp = (((year - 1) / 100 + 1) * 3) / 4;
2132     day_of_year = static_cast<uint>(daynr - static_cast<long>(year) * 365L) -
2133                   (year - 1) / 4 + temp;
2134     while (day_of_year > (days_in_year = calc_days_in_year(year))) {
2135       day_of_year -= days_in_year;
2136       (year)++;
2137     }
2138     leap_day = 0;
2139     if (days_in_year == 366) {
2140       if (day_of_year > 31 + 28) {
2141         day_of_year--;
2142         if (day_of_year == 31 + 28) leap_day = 1; /* Handle leapyears leapday */
2143       }
2144     }
2145     *ret_month = 1;
2146     for (month_pos = days_in_month; day_of_year > static_cast<uint>(*month_pos);
2147          day_of_year -= *(month_pos++), (*ret_month)++)
2148       ;
2149     *ret_year = year;
2150     *ret_day = day_of_year + leap_day;
2151   }
2152 }
2153 
2154 /**
2155    Calc weekday from daynr.
2156 
2157    @retval 0 for Monday
2158    @retval 6 for Sunday
2159 */
calc_weekday(long daynr,bool sunday_first_day_of_week)2160 int calc_weekday(long daynr, bool sunday_first_day_of_week) {
2161   return (static_cast<int>((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) %
2162                            7));
2163 }
2164 
2165 /**
2166   Calculate the week number from a MYSQL_TIME value.
2167 
2168   The bits in week_format has the following meaning:
2169    WEEK_MONDAY_FIRST (0)  If not set	Sunday is first day of week
2170                           If set	Monday is first day of week
2171    WEEK_YEAR (1)	  If not set	Week is in range 0-53
2172 
2173         Week 0 is returned for the the last week of the previous year (for
2174         a date at start of january) In this case one can get 53 for the
2175         first week of next year.  This flag ensures that the week is
2176         relevant for the given year. Note that this flag is only
2177         releveant if WEEK_JANUARY is not set.
2178 
2179                           If set	 Week is in range 1-53.
2180 
2181         In this case one may get week 53 for a date in January (when
2182         the week is that last week of previous year) and week 1 for a
2183         date in December.
2184 
2185   WEEK_FIRST_WEEKDAY (2)  If not set	Weeks are numbered according
2186                                         to ISO 8601:1988
2187                           If set	The week that contains the first
2188                                         'first-day-of-week' is week 1.
2189 
2190         ISO 8601:1988 means that if the week containing January 1 has
2191         four or more days in the new year, then it is week 1;
2192         Otherwise it is the last week of the previous year, and the
2193         next week is week 1.
2194 
2195    @param my_time         Source time value
2196    @param week_behaviour  Parameter controlling how weeks are counted
2197    @param[out] year       The year of the week number (which may be different
2198                           from my_time.year as descibed above)
2199 
2200    @return week number
2201 */
calc_week(const MYSQL_TIME & my_time,uint week_behaviour,uint * year)2202 uint calc_week(const MYSQL_TIME &my_time, uint week_behaviour, uint *year) {
2203   uint days;
2204   ulong daynr = calc_daynr(my_time.year, my_time.month, my_time.day);
2205   ulong first_daynr = calc_daynr(my_time.year, 1, 1);
2206   bool monday_first = (week_behaviour & WEEK_MONDAY_FIRST);
2207   bool week_year = (week_behaviour & WEEK_YEAR);
2208   bool first_weekday = (week_behaviour & WEEK_FIRST_WEEKDAY);
2209 
2210   uint weekday = calc_weekday(first_daynr, !monday_first);
2211   *year = my_time.year;
2212 
2213   if (my_time.month == 1 && my_time.day <= 7 - weekday) {
2214     if (!week_year &&
2215         ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4)))
2216       return 0;
2217     week_year = true;
2218     (*year)--;
2219     first_daynr -= (days = calc_days_in_year(*year));
2220     weekday = (weekday + 53 * 7 - days) % 7;
2221   }
2222 
2223   if ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4))
2224     days = daynr - (first_daynr + (7 - weekday));
2225   else
2226     days = daynr - (first_daynr - weekday);
2227 
2228   if (week_year && days >= 52 * 7) {
2229     weekday = (weekday + calc_days_in_year(*year)) % 7;
2230     if ((!first_weekday && weekday < 4) || (first_weekday && weekday == 0)) {
2231       (*year)++;
2232       return 1;
2233     }
2234   }
2235   return days / 7 + 1;
2236 }
2237 
2238 /**
2239    Predicate for the validity of a period.
2240  */
valid_period(long long period)2241 bool valid_period(long long period) {
2242   if (period <= 0) return false;
2243   if ((period % 100) == 0) return false;
2244   if ((period % 100) > 12) return false;
2245   return true;
2246 }
2247 
2248 /**
2249    Calculate month from period.
2250 
2251    @return month
2252  */
convert_period_to_month(ulong period)2253 ulong convert_period_to_month(ulong period) {
2254   ulong a;
2255   ulong b;
2256   if (period == 0) return 0L;
2257   if ((a = period / 100) < YY_PART_YEAR)
2258     a += 2000;
2259   else if (a < 100)
2260     a += 1900;
2261   b = period % 100;
2262   return a * 12 + b - 1;
2263 }
2264 
2265 /**
2266    Convert month to period.
2267 
2268    @return period
2269  */
convert_month_to_period(ulong month)2270 ulong convert_month_to_period(ulong month) {
2271   ulong year;
2272   if (month == 0L) return 0L;
2273   if ((year = month / 12) < 100) {
2274     year += (year < YY_PART_YEAR) ? 2000 : 1900;
2275   }
2276   return year * 100 + month % 12 + 1;
2277 }
2278 
2279 /**
2280    Add an interval to a MYSQL_TIME struct.
2281 
2282    @retval true if error
2283    @retval false otherwise
2284  */
date_add_interval(MYSQL_TIME * ltime,interval_type int_type,Interval interval,int * warnings)2285 bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
2286                        Interval interval, int *warnings) {
2287   ltime->neg = false;
2288 
2289   long long sign = (interval.neg ? -1 : 1);
2290 
2291   switch (int_type) {
2292     case INTERVAL_SECOND:
2293     case INTERVAL_SECOND_MICROSECOND:
2294     case INTERVAL_MICROSECOND:
2295     case INTERVAL_MINUTE:
2296     case INTERVAL_HOUR:
2297     case INTERVAL_MINUTE_MICROSECOND:
2298     case INTERVAL_MINUTE_SECOND:
2299     case INTERVAL_HOUR_MICROSECOND:
2300     case INTERVAL_HOUR_SECOND:
2301     case INTERVAL_HOUR_MINUTE:
2302     case INTERVAL_DAY_MICROSECOND:
2303     case INTERVAL_DAY_SECOND:
2304     case INTERVAL_DAY_MINUTE:
2305     case INTERVAL_DAY_HOUR: {
2306       longlong sec, days, daynr, microseconds, extra_sec;
2307       ltime->time_type = MYSQL_TIMESTAMP_DATETIME;  // Return full date
2308       microseconds = ltime->second_part + sign * interval.second_part;
2309       extra_sec = microseconds / 1000000L;
2310       microseconds = microseconds % 1000000L;
2311 
2312       if (interval.day > MAX_DAY_NUMBER) goto invalid_date;
2313       if (interval.hour > MAX_DAY_NUMBER * 24ULL) goto invalid_date;
2314       if (interval.minute > MAX_DAY_NUMBER * 24ULL * 60ULL) goto invalid_date;
2315       if (interval.second > MAX_DAY_NUMBER * 24ULL * 60ULL * 60ULL)
2316         goto invalid_date;
2317       sec =
2318           ((ltime->day - 1) * 3600LL * 24LL + ltime->hour * 3600LL +
2319            ltime->minute * 60LL + ltime->second +
2320            sign * static_cast<longlong>(
2321                       interval.day * 3600ULL * 24ULL + interval.hour * 3600ULL +
2322                       interval.minute * 60ULL + interval.second)) +
2323           extra_sec;
2324       if (microseconds < 0) {
2325         microseconds += 1000000LL;
2326         sec--;
2327       }
2328       days = sec / (3600 * 24LL);
2329       sec -= days * 3600 * 24LL;
2330       if (sec < 0) {
2331         days--;
2332         sec += 3600 * 24LL;
2333       }
2334       ltime->second_part = static_cast<uint>(microseconds);
2335       ltime->second = static_cast<uint>(sec % 60);
2336       ltime->minute = static_cast<uint>(sec / 60 % 60);
2337       ltime->hour = static_cast<uint>(sec / 3600);
2338       daynr = calc_daynr(ltime->year, ltime->month, 1) + days;
2339       /* Day number from year 0 to 9999-12-31 */
2340       if (daynr < 0 || daynr > MAX_DAY_NUMBER) goto invalid_date;
2341       get_date_from_daynr(daynr, &ltime->year, &ltime->month, &ltime->day);
2342       break;
2343     }
2344     case INTERVAL_DAY:
2345     case INTERVAL_WEEK: {
2346       unsigned long period;
2347       period = calc_daynr(ltime->year, ltime->month, ltime->day);
2348       if (interval.neg) {
2349         if (period < interval.day)  // Before 0.
2350           goto invalid_date;
2351         period -= interval.day;
2352       } else {
2353         if (period + interval.day < period)  // Overflow.
2354           goto invalid_date;
2355         if (period + interval.day > MAX_DAY_NUMBER)  // After 9999-12-31.
2356           goto invalid_date;
2357         period += interval.day;
2358       }
2359       get_date_from_daynr(period, &ltime->year, &ltime->month, &ltime->day);
2360     } break;
2361     case INTERVAL_YEAR:
2362       if (interval.year > 10000UL) goto invalid_date;
2363       ltime->year += sign * static_cast<long>(interval.year);
2364       if (static_cast<ulong>(ltime->year) >= 10000L) goto invalid_date;
2365       if (ltime->month == 2 && ltime->day == 29 &&
2366           calc_days_in_year(ltime->year) != 366)
2367         ltime->day = 28;  // Was leap-year
2368       break;
2369     case INTERVAL_YEAR_MONTH:
2370     case INTERVAL_QUARTER:
2371     case INTERVAL_MONTH: {
2372       unsigned long long period;
2373 
2374       // Simple guards against arithmetic overflow when calculating period.
2375       if (interval.month >= UINT_MAX / 2) goto invalid_date;
2376       if (interval.year >= UINT_MAX / 12) goto invalid_date;
2377 
2378       period = (ltime->year * 12ULL +
2379                 sign * static_cast<unsigned long long>(interval.year) * 12ULL +
2380                 ltime->month - 1ULL +
2381                 sign * static_cast<unsigned long long>(interval.month));
2382       if (period >= 120000LL) goto invalid_date;
2383       ltime->year = period / 12;
2384       ltime->month = (period % 12L) + 1;
2385       /* Adjust day if the new month doesn't have enough days */
2386       if (ltime->day > days_in_month[ltime->month - 1]) {
2387         ltime->day = days_in_month[ltime->month - 1];
2388         if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
2389           ltime->day++;  // Leap-year
2390       }
2391     } break;
2392     default:
2393       fprintf(stderr, "Unexpected interval type: %u\n",
2394               static_cast<unsigned int>(int_type));
2395       assert(false);
2396       goto null_date;
2397   }
2398 
2399   return false;  // Ok
2400 
2401 invalid_date:
2402   if (warnings) {
2403     *warnings |= MYSQL_TIME_WARN_DATETIME_OVERFLOW;
2404   }
2405 
2406 null_date:
2407 
2408   return true;
2409 }
2410 
2411 /**
2412   Add nanoseconds to a time value with truncation.
2413 
2414   @param [in,out] ltime        MYSQL_TIME variable to add to.
2415   @param          nanoseconds  Nanoseconds value.
2416   @param [in,out] warnings     Warning flag vector.
2417   @retval                      False on success. No real failure case here.
2418 */
time_add_nanoseconds_with_truncate(MYSQL_TIME * ltime,uint nanoseconds,int * warnings)2419 bool time_add_nanoseconds_with_truncate(MYSQL_TIME *ltime, uint nanoseconds,
2420                                         int *warnings) {
2421   /*
2422     If second_part is not set then only add nanoseconds to it.
2423     If second_part is already set and then nanoseconds just represent
2424     additional numbers which help rounding, so we can ignore them.
2425   */
2426   if (ltime->second_part == 0) ltime->second_part = nanoseconds / 1000;
2427 
2428   adjust_time_range(ltime, warnings);
2429   return false;
2430 }
2431 
2432 /**
2433    Add nanoseconds to a datetime value with truncation.
2434 
2435    @param [in,out] ltime        MYSQL_TIME variable to add to.
2436    @param          nanoseconds  Nanoseconds value.
2437    @retval                      False on success. No real failure case here.
2438 */
datetime_add_nanoseconds_with_truncate(MYSQL_TIME * ltime,uint nanoseconds)2439 bool datetime_add_nanoseconds_with_truncate(MYSQL_TIME *ltime,
2440                                             uint nanoseconds) {
2441   /*
2442     If second_part is not set then only add nanoseconds to it.
2443     If second_part is already set and then nanoseconds just represent
2444     additional numbers which help rounding, so we can ignore them.
2445   */
2446   if (ltime->second_part == 0) ltime->second_part = nanoseconds / 1000;
2447   return false;
2448 }
2449 
2450 /**
2451   Add nanoseconds to a time value with rounding.
2452 
2453   @param [in,out] ltime        MYSQL_TIME variable to add to.
2454   @param          nanoseconds  Nanoseconds value.
2455   @param [in,out] warnings     Warning flag vector.
2456   @retval                      False on success, true on error.
2457 */
time_add_nanoseconds_with_round(MYSQL_TIME * ltime,uint nanoseconds,int * warnings)2458 bool time_add_nanoseconds_with_round(MYSQL_TIME *ltime, uint nanoseconds,
2459                                      int *warnings) {
2460   /* We expect correct input data */
2461   assert(nanoseconds < 1000000000);
2462   assert(!check_time_mmssff_range(*ltime));
2463 
2464   if (nanoseconds < 500) return false;
2465 
2466   ltime->second_part += (nanoseconds + 500) / 1000;
2467   if (ltime->second_part < 1000000) goto ret;
2468 
2469   ltime->second_part %= 1000000;
2470   if (ltime->second < 59) {
2471     ltime->second++;
2472     goto ret;
2473   }
2474 
2475   ltime->second = 0;
2476   if (ltime->minute < 59) {
2477     ltime->minute++;
2478     goto ret;
2479   }
2480   ltime->minute = 0;
2481   ltime->hour++;
2482 
2483 ret:
2484   /*
2485     We can get '838:59:59.000001' at this point, which
2486     is bigger than the maximum possible value '838:59:59.000000'.
2487     Checking only "hour > 838" is not enough.
2488     Do full adjust_time_range().
2489   */
2490   adjust_time_range(ltime, warnings);
2491   return false;
2492 }
2493 
2494 /**
2495   Add nanoseconds to a datetime value with rounding.
2496 
2497   @param [in,out] ltime        MYSQL_TIME variable to add to.
2498   @param          nanoseconds  Nanoseconds value.
2499   @param [in,out] warnings     Warning flag vector.
2500   @retval                      False on success, true on error.
2501 */
datetime_add_nanoseconds_with_round(MYSQL_TIME * ltime,uint nanoseconds,int * warnings)2502 bool datetime_add_nanoseconds_with_round(MYSQL_TIME *ltime, uint nanoseconds,
2503                                          int *warnings) {
2504   assert(nanoseconds < 1000000000);
2505   if (nanoseconds < 500) return false;
2506 
2507   ltime->second_part += (nanoseconds + 500) / 1000;
2508   if (ltime->second_part < 1000000) return false;
2509 
2510   ltime->second_part %= 1000000;
2511   Interval interval;
2512   memset(&interval, 0, sizeof(interval));
2513   interval.second = 1;
2514   /* date_add_interval cannot handle bad dates */
2515   if (check_date(*ltime, non_zero_date(*ltime),
2516                  (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE), warnings))
2517     return true;
2518 
2519   if (date_add_interval(ltime, INTERVAL_SECOND, interval, warnings)) {
2520     *warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
2521     return true;
2522   }
2523   return false;
2524 }
2525 
2526 /**
2527   Add nanoseconds to time and round or tuncate as indicated by argument.
2528 
2529   @param [in,out] ltime        MYSQL_TIME variable to add to.
2530   @param          nanoseconds  Nanosecons value.
2531   @param [in,out] warnings     Warning flag vector.
2532   @param          truncate     Decides whether fractional part of seconds will
2533                                be truncated/rounded.
2534   @retval                      False on success. No real failure case here.
2535 */
time_add_nanoseconds_adjust_frac(MYSQL_TIME * ltime,uint nanoseconds,int * warnings,bool truncate)2536 bool time_add_nanoseconds_adjust_frac(MYSQL_TIME *ltime, uint nanoseconds,
2537                                       int *warnings, bool truncate) {
2538   if (truncate)
2539     return time_add_nanoseconds_with_truncate(ltime, nanoseconds, warnings);
2540   else
2541     return time_add_nanoseconds_with_round(ltime, nanoseconds, warnings);
2542 }
2543 
2544 /**
2545    Add nanoseconds to datetime and round or tuncate as indicated by argument.
2546 
2547   @param [in,out] ltime        MYSQL_TIME variable to add to.
2548   @param          nanoseconds  Nanoseconds value.
2549   @param [in,out] warnings     Warning flag vector.
2550   @param          truncate     Decides whether fractional part of seconds will
2551                                be truncated/rounded.
2552   @retval                      False on success, true on error.
2553 */
datetime_add_nanoseconds_adjust_frac(MYSQL_TIME * ltime,uint nanoseconds,int * warnings,bool truncate)2554 bool datetime_add_nanoseconds_adjust_frac(MYSQL_TIME *ltime, uint nanoseconds,
2555                                           int *warnings, bool truncate) {
2556   if (truncate)
2557     return datetime_add_nanoseconds_with_truncate(ltime, nanoseconds);
2558   else
2559     return datetime_add_nanoseconds_with_round(ltime, nanoseconds, warnings);
2560 }
2561 
2562 /** Rounding functions */
2563 static constexpr const uint msec_round_add[7] = {
2564     500000000, 50000000, 5000000, 500000, 50000, 5000, 0};
2565 
2566 /**
2567   Round/Truncate time value to the given precision.
2568 
2569   @param [in,out]  ltime    The value to round.
2570   @param           dec      Precision.
2571   @param           truncate Decides whether fractional part of seconds will be
2572                             truncated/rounded.
2573   @return                   False on success, true on error.
2574 */
my_time_adjust_frac(MYSQL_TIME * ltime,uint dec,bool truncate)2575 bool my_time_adjust_frac(MYSQL_TIME *ltime, uint dec, bool truncate) {
2576   int warnings = 0;
2577   assert(dec <= DATETIME_MAX_DECIMALS);
2578   /* Add half away from zero */
2579   bool rc = time_add_nanoseconds_adjust_frac(ltime, msec_round_add[dec],
2580                                              &warnings, truncate);
2581 
2582   /* Truncate non-significant digits */
2583   my_time_trunc(ltime, dec);
2584   return rc;
2585 }
2586 
2587 /**
2588   Round/Truncate datetime value to the given precision.
2589 
2590   @param [in,out]  ltime    The value to round.
2591   @param           dec      Precision.
2592   @param [in,out]  warnings Warning flag vector
2593   @param           truncate Decides whether fractional part of seconds will be
2594                             truncated/rounded.
2595   @return                   False on success, true on error.
2596 */
my_datetime_adjust_frac(MYSQL_TIME * ltime,uint dec,int * warnings,bool truncate)2597 bool my_datetime_adjust_frac(MYSQL_TIME *ltime, uint dec, int *warnings,
2598                              bool truncate) {
2599   assert(dec <= DATETIME_MAX_DECIMALS);
2600   /* Add half away from zero */
2601   bool rc = datetime_add_nanoseconds_adjust_frac(ltime, msec_round_add[dec],
2602                                                  warnings, truncate);
2603   /* Truncate non-significant digits */
2604   my_time_trunc(ltime, dec);
2605   return rc;
2606 }
2607 
2608 /**
2609   Round timeval value to the given precision.
2610 
2611   @param [in,out]  tv       The value to round.
2612   @param           decimals Precision.
2613   @return                   False on success, true on error.
2614 */
my_timeval_round(struct timeval * tv,uint decimals)2615 bool my_timeval_round(struct timeval *tv, uint decimals) {
2616   assert(decimals <= DATETIME_MAX_DECIMALS);
2617   uint nanoseconds = msec_round_add[decimals];
2618   tv->tv_usec += (nanoseconds + 500) / 1000;
2619   if (tv->tv_usec < 1000000) goto ret;
2620 
2621   tv->tv_usec = 0;
2622   tv->tv_sec++;
2623   if (!is_time_t_valid_for_timestamp(tv->tv_sec)) {
2624     tv->tv_sec = TIMESTAMP_MAX_VALUE;
2625     return true;
2626   }
2627 
2628 ret:
2629   my_timeval_trunc(tv, decimals);
2630   return false;
2631 }
2632 
2633 /**
2634   Mix a date value and a time value.
2635 
2636   @param [in,out] ldate    Date value.
2637   @param          my_time  Time value.
2638 */
mix_date_and_time(MYSQL_TIME * ldate,const MYSQL_TIME & my_time)2639 void mix_date_and_time(MYSQL_TIME *ldate, const MYSQL_TIME &my_time) {
2640   assert(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
2641          ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
2642 
2643   if (!my_time.neg && my_time.hour < 24) {
2644     /*
2645       Simple case: TIME is within normal 24 hours internal.
2646       Mix DATE part of ltime2 and TIME part of ltime together.
2647     */
2648     ldate->hour = my_time.hour;
2649     ldate->minute = my_time.minute;
2650     ldate->second = my_time.second;
2651     ldate->second_part = my_time.second_part;
2652   } else {
2653     /* Complex case: TIME is negative or outside of 24 hours internal. */
2654     longlong seconds;
2655     long days, useconds;
2656     int sign = my_time.neg ? 1 : -1;
2657     ldate->neg = calc_time_diff(*ldate, my_time, sign, &seconds, &useconds);
2658     assert(!ldate->neg);
2659 
2660     /*
2661       We pass current date to mix_date_and_time. If we want to use
2662       this function with arbitrary dates, this code will need
2663       to cover cases when ltime is negative and "ldate < -ltime".
2664     */
2665     assert(ldate->year > 0);
2666 
2667     days = static_cast<long>(seconds / SECONDS_IN_24H);
2668     calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds);
2669     get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day);
2670   }
2671   ldate->time_type = MYSQL_TIMESTAMP_DATETIME;
2672 }
2673 
2674 /**
2675    Converts a timepoint in a posix tm struct to a MYSQL_TIME struct.
2676 
2677    @param [out] to store converted timepoint here
2678    @param from posix tm struct holding a valid timepoint
2679  */
localtime_to_TIME(MYSQL_TIME * to,const struct tm * from)2680 void localtime_to_TIME(MYSQL_TIME *to, const struct tm *from) {
2681   to->neg = false;
2682   to->second_part = 0;
2683   to->year = ((from->tm_year + 1900) % 10000);
2684   to->month = from->tm_mon + 1;
2685   to->day = from->tm_mday;
2686   to->hour = from->tm_hour;
2687   to->minute = from->tm_min;
2688   to->second = from->tm_sec;
2689   to->time_zone_displacement = 0;
2690 }
2691 
2692 /**
2693    Initialize MYSQL_TIME with MYSQL_TIMESTAMP_TIME from given number
2694    of seconds and microseconds.
2695  */
calc_time_from_sec(MYSQL_TIME * to,longlong seconds,long microseconds)2696 void calc_time_from_sec(MYSQL_TIME *to, longlong seconds, long microseconds) {
2697   long t_seconds;
2698   // to->neg is not cleared, it may already be set to a useful value
2699   to->time_type = MYSQL_TIMESTAMP_TIME;
2700   to->year = 0;
2701   to->month = 0;
2702   to->day = 0;
2703   assert(seconds < (0xFFFFFFFFLL * 3600LL));
2704   to->hour = static_cast<long>(seconds / 3600L);
2705   t_seconds = static_cast<long>(seconds % 3600L);
2706   to->minute = t_seconds / 60L;
2707   to->second = t_seconds % 60L;
2708   to->second_part = microseconds;
2709 }
2710 
2711 /**
2712   Calculate difference between two datetime values as seconds + microseconds.
2713 
2714   @param my_time1         - TIME/DATE/DATETIME value
2715   @param my_time2         - TIME/DATE/DATETIME value
2716   @param l_sign           - 1 absolute values are substracted, -1 absolute
2717   values are added.
2718   @param[out] seconds_out - where difference between my_time1 and my_time2
2719                             in seconds is stored.
2720   @param[out] microseconds_out - where microsecond part of difference between
2721                                  my_time1 and my_time2 is stored.
2722 
2723   @note This function calculates difference between my_time1 and
2724     my_time2 absolute values. So one should set l_sign and correct
2725     result if he want to take signs into account (i.e. for MYSQL_TIME
2726     values).
2727 
2728   @returns Sign of difference.
2729     @retval 1 means negative result
2730     @retval 0 means positive result
2731 */
calc_time_diff(const MYSQL_TIME & my_time1,const MYSQL_TIME & my_time2,int l_sign,longlong * seconds_out,long * microseconds_out)2732 bool calc_time_diff(const MYSQL_TIME &my_time1, const MYSQL_TIME &my_time2,
2733                     int l_sign, longlong *seconds_out, long *microseconds_out) {
2734   long days;
2735   bool neg;
2736   longlong microseconds;
2737 
2738   /*
2739     We suppose that if first argument is MYSQL_TIMESTAMP_TIME
2740     the second argument should be TIMESTAMP_TIME also.
2741     We should check it before calc_time_diff call.
2742   */
2743   if (my_time1.time_type == MYSQL_TIMESTAMP_TIME)  // Time value
2744     days = static_cast<long>(my_time1.day) -
2745            l_sign * static_cast<long>(my_time2.day);
2746   else {
2747     days = calc_daynr(static_cast<uint>(my_time1.year),
2748                       static_cast<uint>(my_time1.month),
2749                       static_cast<uint>(my_time1.day));
2750     if (my_time2.time_type == MYSQL_TIMESTAMP_TIME)
2751       days -= l_sign * static_cast<long>(my_time2.day);
2752     else
2753       days -= l_sign * calc_daynr(static_cast<uint>(my_time2.year),
2754                                   static_cast<uint>(my_time2.month),
2755                                   static_cast<uint>(my_time2.day));
2756   }
2757 
2758   microseconds =
2759       (static_cast<longlong>(days) * SECONDS_IN_24H +
2760        static_cast<longlong>(my_time1.hour * 3600L + my_time1.minute * 60L +
2761                              my_time1.second) -
2762        l_sign *
2763            static_cast<longlong>(my_time2.hour * 3600L + my_time2.minute * 60L +
2764                                  my_time2.second)) *
2765           1000000LL +
2766       static_cast<longlong>(my_time1.second_part) -
2767       l_sign * static_cast<longlong>(my_time2.second_part);
2768 
2769   neg = false;
2770   if (microseconds < 0) {
2771     microseconds = -microseconds;
2772     neg = true;
2773   }
2774   *seconds_out = microseconds / 1000000L;
2775   *microseconds_out = static_cast<long>(microseconds % 1000000L);
2776   return neg;
2777 }
2778 
2779 /**
2780    Compare tow MYSQL_TIME objects.
2781 
2782    @retval 0 if a and b are equal
2783    @retval -1 if a comes before b
2784    @retval 1 if b comes before a
2785  */
my_time_compare(const MYSQL_TIME & my_time_a,const MYSQL_TIME & my_time_b)2786 int my_time_compare(const MYSQL_TIME &my_time_a, const MYSQL_TIME &my_time_b) {
2787   ulonglong a_t = TIME_to_ulonglong_datetime(my_time_a);
2788   ulonglong b_t = TIME_to_ulonglong_datetime(my_time_b);
2789 
2790   if (a_t < b_t) return -1;
2791   if (a_t > b_t) return 1;
2792 
2793   if (my_time_a.second_part < my_time_b.second_part) return -1;
2794   if (my_time_a.second_part > my_time_b.second_part) return 1;
2795 
2796   return 0;
2797 }
2798 
2799 /**
2800   Convert MYSQL_TIME value to its packed numeric representation,
2801   using field type.
2802 
2803   @param my_time The time value to convert.
2804   @param type    MySQL field type.
2805   @return        Packed numeric representation.
2806 */
TIME_to_longlong_packed(const MYSQL_TIME & my_time,enum enum_field_types type)2807 longlong TIME_to_longlong_packed(const MYSQL_TIME &my_time,
2808                                  enum enum_field_types type) {
2809   switch (type) {
2810     case MYSQL_TYPE_TIME:
2811       return TIME_to_longlong_time_packed(my_time);
2812     case MYSQL_TYPE_DATETIME:
2813     case MYSQL_TYPE_TIMESTAMP:
2814       return TIME_to_longlong_datetime_packed(my_time);
2815     case MYSQL_TYPE_DATE:
2816       return TIME_to_longlong_date_packed(my_time);
2817     default:
2818       return TIME_to_longlong_packed(my_time);
2819   }
2820 }
2821 
2822 /**
2823   Convert packed numeric temporal representation to time, date or datetime,
2824   using field type.
2825 
2826   @param[out] ltime        The variable to write to.
2827   @param      type         MySQL field type.
2828   @param      packed_value Numeric datetype representation.
2829 */
TIME_from_longlong_packed(MYSQL_TIME * ltime,enum enum_field_types type,longlong packed_value)2830 void TIME_from_longlong_packed(MYSQL_TIME *ltime, enum enum_field_types type,
2831                                longlong packed_value) {
2832   switch (type) {
2833     case MYSQL_TYPE_TIME:
2834       TIME_from_longlong_time_packed(ltime, packed_value);
2835       break;
2836     case MYSQL_TYPE_DATE:
2837       TIME_from_longlong_date_packed(ltime, packed_value);
2838       break;
2839     case MYSQL_TYPE_DATETIME:
2840     case MYSQL_TYPE_TIMESTAMP:
2841       TIME_from_longlong_datetime_packed(ltime, packed_value);
2842       break;
2843     default:
2844       assert(false);
2845       set_zero_time(ltime, MYSQL_TIMESTAMP_ERROR);
2846       break;
2847   }
2848 }
2849 
2850 /**
2851   Convert packed numeric representation to
2852   unpacked numeric representation.
2853 
2854   @param type           MySQL field type.
2855   @param packed_value   Packed numeric temporal value.
2856   @return               Number in one of the following formats,
2857                         depending on type: YYMMDD, YYMMDDhhmmss, hhmmss.
2858 */
longlong_from_datetime_packed(enum enum_field_types type,longlong packed_value)2859 longlong longlong_from_datetime_packed(enum enum_field_types type,
2860                                        longlong packed_value) {
2861   MYSQL_TIME ltime;
2862   switch (type) {
2863     case MYSQL_TYPE_TIME:
2864       TIME_from_longlong_time_packed(&ltime, packed_value);
2865       return TIME_to_ulonglong_time(ltime);
2866     case MYSQL_TYPE_DATE:
2867       TIME_from_longlong_date_packed(&ltime, packed_value);
2868       return TIME_to_ulonglong_date(ltime);
2869     case MYSQL_TYPE_DATETIME:
2870     case MYSQL_TYPE_TIMESTAMP:
2871       TIME_from_longlong_datetime_packed(&ltime, packed_value);
2872       return TIME_to_ulonglong_datetime(ltime);
2873     default:
2874       assert(false);
2875       return 0;
2876   }
2877 }
2878 
2879 /**
2880   Convert packed numeric temporal representation to unpacked numeric
2881   representation.
2882 
2883   @param type           MySQL field type.
2884   @param packed_value   Numeric packed temporal representation.
2885   @return               A double value in on of the following formats,
2886                         depending  on type:
2887                         YYYYMMDD, hhmmss.ffffff or YYMMDDhhmmss.ffffff.
2888 */
double_from_datetime_packed(enum enum_field_types type,longlong packed_value)2889 double double_from_datetime_packed(enum enum_field_types type,
2890                                    longlong packed_value) {
2891   longlong result = longlong_from_datetime_packed(type, packed_value);
2892   return result +
2893          (static_cast<double>(my_packed_time_get_frac_part(packed_value))) /
2894              1000000;
2895 }
2896 
2897 /**
2898    @} (end of defgroup MY_TIME)
2899 */
2900 
2901 // Non-static driver functions for unit tests
2902 namespace mysys_my_time {
DRV_my_packed_time_get_int_part(longlong i)2903 longlong DRV_my_packed_time_get_int_part(longlong i) {
2904   return my_packed_time_get_int_part(i);
2905 }
DRV_my_packed_time_make(longlong i,longlong f)2906 longlong DRV_my_packed_time_make(longlong i, longlong f) {
2907   return my_packed_time_make(i, f);
2908 }
DRV_my_packed_time_make_int(longlong i)2909 longlong DRV_my_packed_time_make_int(longlong i) {
2910   return my_packed_time_make_int(i);
2911 }
2912 }  // namespace mysys_my_time
2913