1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 
24 /* Functions to handle date and time */
25 
26 #include "sql_time.h"
27 #include "tztime.h"                             // struct Time_zone
28 #include "sql_class.h"  // THD, MODE_STRICT_ALL_TABLES, MODE_STRICT_TRANS_TABLES
29 #include <m_ctype.h>
30 #include "item_timefunc.h"   // INTERNAL_FORMAT
31 
32 
33 	/* Some functions to calculate dates */
34 
35 /*
36   Name description of interval names used in statements.
37 
38   'interval_type_to_name' is ordered and sorted on interval size and
39   interval complexity.
40   Order of elements in 'interval_type_to_name' should correspond to
41   the order of elements in 'interval_type' enum
42 
43   See also interval_type, interval_names
44 */
45 
46 LEX_STRING interval_type_to_name[INTERVAL_LAST] = {
47   { C_STRING_WITH_LEN("YEAR")},
48   { C_STRING_WITH_LEN("QUARTER")},
49   { C_STRING_WITH_LEN("MONTH")},
50   { C_STRING_WITH_LEN("WEEK")},
51   { C_STRING_WITH_LEN("DAY")},
52   { C_STRING_WITH_LEN("HOUR")},
53   { C_STRING_WITH_LEN("MINUTE")},
54   { C_STRING_WITH_LEN("SECOND")},
55   { C_STRING_WITH_LEN("MICROSECOND")},
56   { C_STRING_WITH_LEN("YEAR_MONTH")},
57   { C_STRING_WITH_LEN("DAY_HOUR")},
58   { C_STRING_WITH_LEN("DAY_MINUTE")},
59   { C_STRING_WITH_LEN("DAY_SECOND")},
60   { C_STRING_WITH_LEN("HOUR_MINUTE")},
61   { C_STRING_WITH_LEN("HOUR_SECOND")},
62   { C_STRING_WITH_LEN("MINUTE_SECOND")},
63   { C_STRING_WITH_LEN("DAY_MICROSECOND")},
64   { C_STRING_WITH_LEN("HOUR_MICROSECOND")},
65   { C_STRING_WITH_LEN("MINUTE_MICROSECOND")},
66   { C_STRING_WITH_LEN("SECOND_MICROSECOND")}
67 };
68 
69 	/* Calc weekday from daynr */
70 	/* Returns 0 for monday, 1 for tuesday .... */
71 
calc_weekday(long daynr,bool sunday_first_day_of_week)72 int calc_weekday(long daynr,bool sunday_first_day_of_week)
73 {
74   DBUG_ENTER("calc_weekday");
75   DBUG_RETURN ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
76 }
77 
78 /*
79   The bits in week_format has the following meaning:
80    WEEK_MONDAY_FIRST (0)  If not set	Sunday is first day of week
81       		   	  If set	Monday is first day of week
82    WEEK_YEAR (1)	  If not set	Week is in range 0-53
83 
84    	Week 0 is returned for the the last week of the previous year (for
85 	a date at start of january) In this case one can get 53 for the
86 	first week of next year.  This flag ensures that the week is
87 	relevant for the given year. Note that this flag is only
88 	releveant if WEEK_JANUARY is not set.
89 
90 			  If set	 Week is in range 1-53.
91 
92 	In this case one may get week 53 for a date in January (when
93 	the week is that last week of previous year) and week 1 for a
94 	date in December.
95 
96   WEEK_FIRST_WEEKDAY (2)  If not set	Weeks are numbered according
97 			   		to ISO 8601:1988
98 			  If set	The week that contains the first
99 					'first-day-of-week' is week 1.
100 
101 	ISO 8601:1988 means that if the week containing January 1 has
102 	four or more days in the new year, then it is week 1;
103 	Otherwise it is the last week of the previous year, and the
104 	next week is week 1.
105 */
106 
calc_week(MYSQL_TIME * l_time,uint week_behaviour,uint * year)107 uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
108 {
109   uint days;
110   ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
111   ulong first_daynr=calc_daynr(l_time->year,1,1);
112   bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST);
113   bool week_year= MY_TEST(week_behaviour & WEEK_YEAR);
114   bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY);
115 
116   uint weekday=calc_weekday(first_daynr, !monday_first);
117   *year=l_time->year;
118 
119   if (l_time->month == 1 && l_time->day <= 7-weekday)
120   {
121     if (!week_year &&
122 	((first_weekday && weekday != 0) ||
123 	 (!first_weekday && weekday >= 4)))
124       return 0;
125     week_year= 1;
126     (*year)--;
127     first_daynr-= (days=calc_days_in_year(*year));
128     weekday= (weekday + 53*7- days) % 7;
129   }
130 
131   if ((first_weekday && weekday != 0) ||
132       (!first_weekday && weekday >= 4))
133     days= daynr - (first_daynr+ (7-weekday));
134   else
135     days= daynr - (first_daynr - weekday);
136 
137   if (week_year && days >= 52*7)
138   {
139     weekday= (weekday + calc_days_in_year(*year)) % 7;
140     if ((!first_weekday && weekday < 4) ||
141 	(first_weekday && weekday == 0))
142     {
143       (*year)++;
144       return 1;
145     }
146   }
147   return days/7+1;
148 }
149 
150 	/* Change a daynr to year, month and day */
151 	/* Daynr 0 is returned as date 00.00.00 */
152 
get_date_from_daynr(long daynr,uint * ret_year,uint * ret_month,uint * ret_day)153 void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
154 			 uint *ret_day)
155 {
156   uint year,temp,leap_day,day_of_year,days_in_year;
157   uchar *month_pos;
158   DBUG_ENTER("get_date_from_daynr");
159 
160   if (daynr <= 365L || daynr >= 3652500)
161   {						/* Fix if wrong daynr */
162     *ret_year= *ret_month = *ret_day =0;
163   }
164   else
165   {
166     year= (uint) (daynr*100 / 36525L);
167     temp=(((year-1)/100+1)*3)/4;
168     day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
169     while (day_of_year > (days_in_year= calc_days_in_year(year)))
170     {
171       day_of_year-=days_in_year;
172       (year)++;
173     }
174     leap_day=0;
175     if (days_in_year == 366)
176     {
177       if (day_of_year > 31+28)
178       {
179 	day_of_year--;
180 	if (day_of_year == 31+28)
181 	  leap_day=1;		/* Handle leapyears leapday */
182       }
183     }
184     *ret_month=1;
185     for (month_pos= days_in_month ;
186 	 day_of_year > (uint) *month_pos ;
187 	 day_of_year-= *(month_pos++), (*ret_month)++)
188       ;
189     *ret_year=year;
190     *ret_day=day_of_year+leap_day;
191   }
192   DBUG_VOID_RETURN;
193 }
194 
195 	/* Functions to handle periods */
196 
convert_period_to_month(ulong period)197 ulong convert_period_to_month(ulong period)
198 {
199   ulong a,b;
200   if (period == 0)
201     return 0L;
202   if ((a=period/100) < YY_PART_YEAR)
203     a+=2000;
204   else if (a < 100)
205     a+=1900;
206   b=period%100;
207   return a*12+b-1;
208 }
209 
210 
convert_month_to_period(ulong month)211 ulong convert_month_to_period(ulong month)
212 {
213   ulong year;
214   if (month == 0L)
215     return 0L;
216   if ((year=month/12) < 100)
217   {
218     year+=(year < YY_PART_YEAR) ? 2000 : 1900;
219   }
220   return year*100+month%12+1;
221 }
222 
223 
224 /*
225   Convert a string to 8-bit representation,
226   for use in str_to_time/str_to_date/str_to_date.
227 
228   In the future to_ascii() can be extended to convert
229   non-ASCII digits to ASCII digits
230   (for example, ARABIC-INDIC, DEVANAGARI, BENGALI, and so on)
231   so DATE/TIME/DATETIME values understand digits in the
232   respected scripts.
233 */
234 static uint
to_ascii(const CHARSET_INFO * cs,const char * src,size_t src_length,char * dst,size_t dst_length)235 to_ascii(const CHARSET_INFO *cs,
236          const char *src, size_t src_length,
237          char *dst, size_t dst_length)
238 {
239   int cnvres;
240   my_wc_t wc;
241   const char *srcend= src + src_length;
242   char *dst0= dst, *dstend= dst + dst_length - 1;
243   while (dst < dstend &&
244          (cnvres= (cs->cset->mb_wc)(cs, &wc,
245                                     (const uchar*) src,
246                                     (const uchar*) srcend)) > 0 &&
247          wc < 128)
248   {
249     src+= cnvres;
250     *dst++= static_cast<char>(wc);
251   }
252   *dst= '\0';
253   return dst - dst0;
254 }
255 
256 
257 /* Character set-aware version of str_to_time() */
str_to_time(const CHARSET_INFO * cs,const char * str,size_t length,MYSQL_TIME * l_time,my_time_flags_t flags,MYSQL_TIME_STATUS * status)258 bool str_to_time(const CHARSET_INFO *cs, const char *str, size_t length,
259                  MYSQL_TIME *l_time, my_time_flags_t flags,
260                  MYSQL_TIME_STATUS *status)
261 {
262   char cnv[MAX_TIME_FULL_WIDTH + 3]; // +3 for nanoseconds (for rounding)
263   if ((cs->state & MY_CS_NONASCII) != 0)
264   {
265     length= to_ascii(cs, str, length, cnv, sizeof(cnv));
266     str= cnv;
267   }
268   return str_to_time(str, length, l_time, status) ||
269          (!(flags & TIME_NO_NSEC_ROUNDING) &&
270           time_add_nanoseconds_with_round(l_time, status->nanoseconds,
271                                           &status->warnings));
272 }
273 
274 
275 /* Character set-aware version of str_to_datetime() */
str_to_datetime(const CHARSET_INFO * cs,const char * str,size_t length,MYSQL_TIME * l_time,my_time_flags_t flags,MYSQL_TIME_STATUS * status)276 bool str_to_datetime(const CHARSET_INFO *cs,
277                      const char *str, size_t length,
278                      MYSQL_TIME *l_time, my_time_flags_t flags,
279                      MYSQL_TIME_STATUS *status)
280 {
281   char cnv[MAX_DATETIME_FULL_WIDTH + 3]; // +3 for nanoseconds (for rounding)
282   if ((cs->state & MY_CS_NONASCII) != 0)
283   {
284     length= to_ascii(cs, str, length, cnv, sizeof(cnv));
285     str= cnv;
286   }
287   return str_to_datetime(str, length, l_time, flags, status) ||
288          (!(flags & TIME_NO_NSEC_ROUNDING) &&
289           datetime_add_nanoseconds_with_round(l_time,
290                                               status->nanoseconds,
291                                               &status->warnings));
292 }
293 
294 
295 /**
296   Add nanoseconds to a time value with rounding.
297 
298   @param IN/OUT ltime       MYSQL_TIME variable to add to.
299   @param        nanosecons  Nanosecons value.
300   @param IN/OUT warnings    Warning flag vector.
301   @retval                   False on success, true on error.
302 */
time_add_nanoseconds_with_round(MYSQL_TIME * ltime,uint nanoseconds,int * warnings)303 bool time_add_nanoseconds_with_round(MYSQL_TIME *ltime,
304                                      uint nanoseconds, int *warnings)
305 {
306   /* We expect correct input data */
307   assert(nanoseconds < 1000000000);
308   assert(!check_time_mmssff_range(ltime));
309 
310   if (nanoseconds < 500)
311     return false;
312 
313   ltime->second_part+= (nanoseconds + 500) / 1000;
314   if (ltime->second_part < 1000000)
315     goto ret;
316 
317   ltime->second_part%= 1000000;
318   if (ltime->second < 59)
319   {
320     ltime->second++;
321     goto ret;
322   }
323 
324   ltime->second= 0;
325   if (ltime->minute < 59)
326   {
327     ltime->minute++;
328     goto ret;
329   }
330   ltime->minute= 0;
331   ltime->hour++;
332 
333 ret:
334   /*
335     We can get '838:59:59.000001' at this point, which
336     is bigger than the maximum possible value '838:59:59.000000'.
337     Checking only "hour > 838" is not enough.
338     Do full adjust_time_range().
339   */
340   adjust_time_range(ltime, warnings);
341   return false;
342 }
343 
344 
345 /**
346   Add nanoseconds to a datetime value with rounding.
347 
348   @param IN/OUT ltime       MYSQL_TIME variable to add to.
349   @param        nanosecons  Nanosecons value.
350   @param IN/OUT warnings    Warning flag vector.
351   @retval                   False on success, true on error.
352 */
datetime_add_nanoseconds_with_round(MYSQL_TIME * ltime,uint nanoseconds,int * warnings)353 bool datetime_add_nanoseconds_with_round(MYSQL_TIME *ltime,
354                                          uint nanoseconds, int *warnings)
355 {
356   assert(nanoseconds < 1000000000);
357   if (nanoseconds < 500)
358     return false;
359 
360   ltime->second_part+= (nanoseconds + 500) / 1000;
361   if (ltime->second_part < 1000000)
362     return false;
363 
364   ltime->second_part%= 1000000;
365   Interval interval;
366   memset(&interval, 0, sizeof(interval));
367   interval.second= 1;
368   /* date_add_interval cannot handle bad dates */
369   if (check_date(ltime, non_zero_date(ltime),
370                  (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE), warnings))
371     return true;
372 
373   if (date_add_interval(ltime, INTERVAL_SECOND, interval))
374   {
375     *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
376     return true;
377   }
378   return false;
379 }
380 
381 
382 /*
383   Convert a timestamp string to a MYSQL_TIME value and produce a warning
384   if string was truncated during conversion.
385 
386   NOTE
387     See description of str_to_datetime() for more information.
388 */
389 bool
str_to_datetime_with_warn(String * str,MYSQL_TIME * l_time,my_time_flags_t flags)390 str_to_datetime_with_warn(String *str, MYSQL_TIME *l_time,
391                           my_time_flags_t flags)
392 {
393   MYSQL_TIME_STATUS status;
394   THD *thd= current_thd;
395   if (thd->variables.sql_mode & MODE_NO_ZERO_DATE)
396     flags|= TIME_NO_ZERO_DATE;
397   if (thd->variables.sql_mode & MODE_INVALID_DATES)
398     flags|= TIME_INVALID_DATES;
399   bool ret_val= str_to_datetime(str, l_time, flags, &status);
400   if (ret_val || status.warnings)
401     make_truncated_value_warning(ErrConvString(str), l_time->time_type);
402   return ret_val;
403 }
404 
405 
406 /**
407   Convert lldiv_t to datetime.
408 
409   @param         lld      The value to convert from.
410   @param[out]    ltime    The variable to convert to.
411   @param         flags    Conversion flags.
412   @param[in,out] warnings Warning flags.
413   @return                False on success, true on error.
414 */
lldiv_t_to_datetime(lldiv_t lld,MYSQL_TIME * ltime,my_time_flags_t flags,int * warnings)415 static bool lldiv_t_to_datetime(lldiv_t lld, MYSQL_TIME *ltime,
416                                 my_time_flags_t flags, int *warnings)
417 {
418   if (lld.rem < 0 || // Catch negative numbers with zero int part, e.g: -0.1
419       number_to_datetime(lld.quot, ltime, flags, warnings) == -1LL)
420   {
421     /* number_to_datetime does not clear ltime in case of ZERO DATE */
422     set_zero_time(ltime, MYSQL_TIMESTAMP_ERROR);
423     if (!*warnings) /* Neither sets warnings in case of ZERO DATE */
424       *warnings|= MYSQL_TIME_WARN_TRUNCATED;
425     return true;
426   }
427   else if (ltime->time_type == MYSQL_TIMESTAMP_DATE)
428   {
429     /*
430       Generate a warning in case of DATE with fractional part:
431         20011231.1234 -> '2001-12-31'
432       unless the caller does not want the warning: for example, CAST does.
433     */
434     if (lld.rem && !(flags & TIME_NO_DATE_FRAC_WARN))
435       *warnings|= MYSQL_TIME_WARN_TRUNCATED;
436   }
437   else if (!(flags & TIME_NO_NSEC_ROUNDING))
438   {
439     ltime->second_part= static_cast<ulong>(lld.rem / 1000);
440     return datetime_add_nanoseconds_with_round(ltime, lld.rem % 1000, warnings);
441   }
442   return false;
443 }
444 
445 
446 /**
447   Convert decimal value to datetime value with a warning.
448   @param       decimal The value to convert from.
449   @param[out]  ltime   The variable to convert to.
450   @param       flags   Conversion flags.
451   @return              False on success, true on error.
452 */
my_decimal_to_datetime_with_warn(const my_decimal * decimal,MYSQL_TIME * ltime,my_time_flags_t flags)453 bool my_decimal_to_datetime_with_warn(const my_decimal *decimal,
454                                       MYSQL_TIME *ltime, my_time_flags_t flags)
455 {
456   lldiv_t lld;
457   int warnings= 0;
458   bool rc;
459 
460   if ((rc= my_decimal2lldiv_t(0, decimal, &lld)))
461   {
462     warnings|= MYSQL_TIME_WARN_TRUNCATED;
463     set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
464   }
465   else
466     rc= lldiv_t_to_datetime(lld, ltime, flags, &warnings);
467 
468   if (warnings)
469     make_truncated_value_warning(ErrConvString(decimal), ltime->time_type);
470   return rc;
471 }
472 
473 
474 /**
475   Convert double value to datetime value with a warning.
476   @param       nr      The value to convert from.
477   @param[out]  ltime   The variable to convert to.
478   @param       flags   Conversion flags.
479   @return              False on success, true on error.
480 */
my_double_to_datetime_with_warn(double nr,MYSQL_TIME * ltime,my_time_flags_t flags)481 bool my_double_to_datetime_with_warn(double nr, MYSQL_TIME *ltime,
482                                      my_time_flags_t flags)
483 {
484   lldiv_t lld;
485   int warnings= 0;
486   bool rc;
487 
488   if ((rc= (double2lldiv_t(nr, &lld) != E_DEC_OK)))
489   {
490     warnings|= MYSQL_TIME_WARN_TRUNCATED;
491     set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
492   }
493   else
494     rc= lldiv_t_to_datetime(lld, ltime, flags, &warnings);
495 
496   if (warnings)
497     make_truncated_value_warning(ErrConvString(nr), ltime->time_type);
498   return rc;
499 }
500 
501 
502 /**
503   Convert longlong value to datetime value with a warning.
504   @param       nr      The value to convert from.
505   @param[out]  ltime   The variable to convert to.
506   @return              False on success, true on error.
507 */
my_longlong_to_datetime_with_warn(longlong nr,MYSQL_TIME * ltime,my_time_flags_t flags)508 bool my_longlong_to_datetime_with_warn(longlong nr, MYSQL_TIME *ltime,
509                                        my_time_flags_t flags)
510 {
511   int warnings= 0;
512   bool rc= number_to_datetime(nr, ltime, flags, &warnings) == -1LL;
513   if (warnings)
514     make_truncated_value_warning(ErrConvString(nr),  MYSQL_TIMESTAMP_NONE);
515   return rc;
516 }
517 
518 
519 /**
520   Convert lldiv_t value to time with nanosecond rounding.
521 
522   @param         lld      The value to convert from.
523   @param[out]    ltime    The variable to convert to,
524   @param         flags    Conversion flags.
525   @param[in,out] warnings Warning flags.
526   @return                 False on success, true on error.
527 */
lldiv_t_to_time(lldiv_t lld,MYSQL_TIME * ltime,int * warnings)528 static bool lldiv_t_to_time(lldiv_t lld, MYSQL_TIME *ltime, int *warnings)
529 {
530   if (number_to_time(lld.quot, ltime, warnings))
531     return true;
532   /*
533     Both lld.quot and lld.rem can give negative result value,
534     thus combine them using "|=".
535   */
536   if ((ltime->neg|= (lld.rem < 0)))
537     lld.rem= -lld.rem;
538   ltime->second_part= static_cast<ulong>(lld.rem / 1000);
539   return time_add_nanoseconds_with_round(ltime, lld.rem % 1000, warnings);
540 }
541 
542 
543 /**
544   Convert decimal number to TIME
545   @param      decimal_value  The number to convert from.
546   @param[out] ltime          The variable to convert to.
547   @return     False on success, true on error.
548 */
my_decimal_to_time_with_warn(const my_decimal * decimal,MYSQL_TIME * ltime)549 bool my_decimal_to_time_with_warn(const my_decimal *decimal, MYSQL_TIME *ltime)
550 {
551   lldiv_t lld;
552   int warnings= 0;
553   bool rc;
554 
555   if ((rc= my_decimal2lldiv_t(0, decimal, &lld)))
556   {
557     warnings|= MYSQL_TIME_WARN_TRUNCATED;
558     set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
559   }
560   else
561     rc= lldiv_t_to_time(lld, ltime, &warnings);
562 
563   if (warnings)
564     make_truncated_value_warning(ErrConvString(decimal), MYSQL_TIMESTAMP_TIME);
565   return rc;
566 }
567 
568 
569 /**
570   Convert double number to TIME
571 
572   @param      nr      The number to convert from.
573   @param[out] ltime   The variable to convert to.
574   @return     False on success, true on error.
575 */
my_double_to_time_with_warn(double nr,MYSQL_TIME * ltime)576 bool my_double_to_time_with_warn(double nr, MYSQL_TIME *ltime)
577 {
578   lldiv_t lld;
579   int warnings= 0;
580   bool rc;
581 
582   if ((rc= (double2lldiv_t(nr, &lld) != E_DEC_OK)))
583   {
584     warnings|= MYSQL_TIME_WARN_TRUNCATED;
585     set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
586   }
587   else
588     rc= lldiv_t_to_time(lld, ltime, &warnings);
589 
590   if (warnings)
591     make_truncated_value_warning(ErrConvString(nr), MYSQL_TIMESTAMP_TIME);
592   return rc;
593 }
594 
595 
596 
597 /**
598   Convert longlong number to TIME
599   @param      nr     The number to convert from.
600   @param[out] ltime  The variable to convert to.
601   @return     False on success, true on error.
602 */
my_longlong_to_time_with_warn(longlong nr,MYSQL_TIME * ltime)603 bool my_longlong_to_time_with_warn(longlong nr, MYSQL_TIME *ltime)
604 {
605   int warnings= 0;
606   bool rc= number_to_time(nr, ltime, &warnings);
607   if (warnings)
608     make_truncated_value_warning(ErrConvString(nr), MYSQL_TIMESTAMP_TIME);
609   return rc;
610 }
611 
612 
613 /**
614   Convert a datetime from broken-down MYSQL_TIME representation
615   to corresponding TIMESTAMP value.
616 
617   @param  thd             - current thread
618   @param  t               - datetime in broken-down representation,
619   @param  in_dst_time_gap - pointer to bool which is set to true if t represents
620                             value which doesn't exists (falls into the spring
621                             time-gap) or to false otherwise.
622   @return
623   @retval  Number seconds in UTC since start of Unix Epoch corresponding to t.
624   @retval  0 - t contains datetime value which is out of TIMESTAMP range.
625 */
TIME_to_timestamp(THD * thd,const MYSQL_TIME * t,my_bool * in_dst_time_gap)626 my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_gap)
627 {
628   my_time_t timestamp;
629 
630   *in_dst_time_gap= 0;
631 
632   timestamp= thd->time_zone()->TIME_to_gmt_sec(t, in_dst_time_gap);
633   if (timestamp)
634   {
635     return timestamp;
636   }
637 
638   /* If we are here we have range error. */
639   return(0);
640 }
641 
642 
643 /**
644   Convert a datetime MYSQL_TIME representation
645   to corresponding "struct timeval" value.
646 
647   ltime must previously be checked for TIME_NO_ZERO_IN_DATE.
648   Things like '0000-01-01', '2000-00-01', '2000-01-00' are not allowed
649   and asserted.
650 
651   Things like '0000-00-00 10:30:30' or '0000-00-00 00:00:00.123456'
652   (i.e. empty date with non-empty time) return error.
653 
654   Zero datetime '0000-00-00 00:00:00.000000'
655   is allowed and is mapper to {tv_sec=0, tv_usec=0}.
656 
657   Note: In case of error, tm value is not initialized.
658 
659   Note: "warnings" is not initialized to zero,
660   so new warnings are added to the old ones.
661   Caller must make sure to initialize "warnings".
662 
663   @param[in]  thd       current thd
664   @param[in]  ltime     datetime value
665   @param[out] tm        timeval value
666   @param[out] warnings  pointer to warnings vector
667   @return
668   @retval      false on success
669   @retval      true on error
670 */
datetime_with_no_zero_in_date_to_timeval(THD * thd,const MYSQL_TIME * ltime,struct timeval * tm,int * warnings)671 bool datetime_with_no_zero_in_date_to_timeval(THD *thd,
672                                               const MYSQL_TIME *ltime,
673                                               struct timeval *tm,
674                                               int *warnings)
675 {
676   if (!ltime->month) /* Zero date */
677   {
678     assert(!ltime->year && !ltime->day);
679     if (non_zero_time(ltime))
680     {
681       /*
682         Return error for zero date with non-zero time, e.g.:
683         '0000-00-00 10:20:30' or '0000-00-00 00:00:00.123456'
684       */
685       *warnings|= MYSQL_TIME_WARN_TRUNCATED;
686       return true;
687     }
688     tm->tv_sec= tm->tv_usec= 0; // '0000-00-00 00:00:00.000000'
689     return false;
690   }
691 
692   my_bool in_dst_time_gap;
693   if (!(tm->tv_sec= TIME_to_timestamp(current_thd, ltime, &in_dst_time_gap)))
694   {
695     /*
696       Date was outside of the supported timestamp range.
697       For example: '3001-01-01 00:00:00' or '1000-01-01 00:00:00'
698     */
699     *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
700     return true;
701   }
702   else if (in_dst_time_gap)
703   {
704     /*
705       Set MYSQL_TIME_WARN_INVALID_TIMESTAMP warning to indicate
706       that date was fine but pointed to winter/summer time switch gap.
707       In this case tm is set to the fist second after gap.
708       For example: '2003-03-30 02:30:00 MSK' -> '2003-03-30 03:00:00 MSK'
709     */
710     *warnings|= MYSQL_TIME_WARN_INVALID_TIMESTAMP;
711   }
712   tm->tv_usec= ltime->second_part;
713   return false;
714 }
715 
716 
717 /**
718   Convert a datetime MYSQL_TIME representation
719   to corresponding "struct timeval" value.
720 
721   Things like '0000-01-01', '2000-00-01', '2000-01-00'
722   (i.e. incomplete date) return error.
723 
724   Things like '0000-00-00 10:30:30' or '0000-00-00 00:00:00.123456'
725   (i.e. empty date with non-empty time) return error.
726 
727   Zero datetime '0000-00-00 00:00:00.000000'
728   is allowed and is mapper to {tv_sec=0, tv_usec=0}.
729 
730   Note: In case of error, tm value is not initialized.
731 
732   Note: "warnings" is not initialized to zero,
733   so new warnings are added to the old ones.
734   Caller must make sure to initialize "warnings".
735 
736   @param[in]  thd       current thd
737   @param[in]  ltime     datetime value
738   @param[out] tm        timeval value
739   @param[out] warnings  pointer to warnings vector
740   @return
741   @retval      false on success
742   @retval      true on error
743 */
datetime_to_timeval(THD * thd,const MYSQL_TIME * ltime,struct timeval * tm,int * warnings)744 bool datetime_to_timeval(THD *thd, const MYSQL_TIME *ltime,
745                          struct timeval *tm, int *warnings)
746 {
747   return
748     check_date(ltime, non_zero_date(ltime), TIME_NO_ZERO_IN_DATE, warnings) ||
749     datetime_with_no_zero_in_date_to_timeval(current_thd, ltime, tm, warnings);
750 }
751 
752 
753 /*
754   Convert a time string to a MYSQL_TIME struct and produce a warning
755   if string was cut during conversion.
756 
757   NOTE
758     See str_to_time() for more info.
759 */
760 bool
str_to_time_with_warn(String * str,MYSQL_TIME * l_time)761 str_to_time_with_warn(String *str, MYSQL_TIME *l_time)
762 {
763   MYSQL_TIME_STATUS status;
764   bool ret_val= str_to_time(str, l_time, 0, &status);
765   if (ret_val || status.warnings)
766     make_truncated_value_warning(ErrConvString(str), MYSQL_TIMESTAMP_TIME);
767   return ret_val;
768 }
769 
770 
771 /**
772   Convert time to datetime.
773 
774   The time value is added to the current datetime value.
775   @param  IN  ltime    Time value to convert from.
776   @param  OUT ltime2   Datetime value to convert to.
777 */
time_to_datetime(THD * thd,const MYSQL_TIME * ltime,MYSQL_TIME * ltime2)778 void time_to_datetime(THD *thd, const MYSQL_TIME *ltime, MYSQL_TIME *ltime2)
779 {
780   thd->variables.time_zone->gmt_sec_to_TIME(ltime2,
781     static_cast<my_time_t>(thd->query_start()));
782   ltime2->hour= ltime2->minute= ltime2->second= ltime2->second_part= 0;
783   ltime2->time_type= MYSQL_TIMESTAMP_DATE;
784   mix_date_and_time(ltime2, ltime);
785 }
786 
787 
788 /*
789   Convert a system time structure to TIME
790 */
791 
localtime_to_TIME(MYSQL_TIME * to,struct tm * from)792 void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
793 {
794   to->neg=0;
795   to->second_part=0;
796   to->year=	(int) ((from->tm_year+1900) % 10000);
797   to->month=	(int) from->tm_mon+1;
798   to->day=	(int) from->tm_mday;
799   to->hour=	(int) from->tm_hour;
800   to->minute=	(int) from->tm_min;
801   to->second=   (int) from->tm_sec;
802 }
803 
calc_time_from_sec(MYSQL_TIME * to,longlong seconds,long microseconds)804 void calc_time_from_sec(MYSQL_TIME *to, longlong seconds, long microseconds)
805 {
806   long t_seconds;
807   // to->neg is not cleared, it may already be set to a useful value
808   to->time_type= MYSQL_TIMESTAMP_TIME;
809   to->year= 0;
810   to->month= 0;
811   to->day= 0;
812   assert(seconds < (0xFFFFFFFFLL * 3600LL));
813   to->hour=  (long) (seconds / 3600L);
814   t_seconds= (long) (seconds % 3600L);
815   to->minute= t_seconds/60L;
816   to->second= t_seconds%60L;
817   to->second_part= microseconds;
818 }
819 
820 
821 /**
822   Parse a format string specification
823 
824   @param format_type  Format of string (time, date or datetime)
825   @date_time_format   Format to fill in
826 
827   Fills in date_time_format->positions for all date time parts.
828 
829   positions marks the position for a datetime element in the format string.
830   The position array elements are in the following order:
831   YYYY-DD-MM HH-MM-DD.FFFFFF AM
832   0    1  2  3  4  5  6      7
833 
834   If positions[0]= 5, it means that year will be the forth element to
835   read from the parsed date string.
836 
837   @return true if error, false otherwise.
838 */
839 
parse_date_time_format(timestamp_type format_type,Date_time_format * date_time_format)840 bool parse_date_time_format(timestamp_type format_type,
841 			    Date_time_format *date_time_format)
842 {
843   const char *format= date_time_format->format.str;
844   size_t format_length= date_time_format->format.length;
845   uint offset= 0, separators= 0;
846   const char *ptr= format, *format_str;
847   const char *end= ptr+format_length;
848   uchar *dt_pos= date_time_format->positions;
849   /* need_p is set if we are using AM/PM format */
850   bool need_p= 0, allow_separator= 0;
851   ulong part_map= 0, separator_map= 0;
852   const char *parts[16];
853 
854   date_time_format->time_separator= 0;
855   date_time_format->flag= 0;			// For future
856 
857   /*
858     Fill position with 'dummy' arguments to found out if a format tag is
859     used twice (This limit's the format to 255 characters, but this is ok)
860   */
861   dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]=
862     dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= 255;
863 
864   for (; ptr != end; ptr++)
865   {
866     if (*ptr == '%' && ptr+1 != end)
867     {
868       uint position= 0;
869       switch (*++ptr) {
870       case 'y':					// Year
871       case 'Y':
872 	position= 0;
873 	break;
874       case 'c':					// Month
875       case 'm':
876 	position= 1;
877 	break;
878       case 'd':
879       case 'e':
880 	position= 2;
881 	break;
882       case 'h':
883       case 'I':
884       case 'l':
885 	need_p= 1;				// Need AM/PM
886 	/* Fall through */
887       case 'k':
888       case 'H':
889 	position= 3;
890 	break;
891       case 'i':
892 	position= 4;
893 	break;
894       case 's':
895       case 'S':
896 	position= 5;
897 	break;
898       case 'f':
899 	position= 6;
900 	if (dt_pos[5] != offset-1 || ptr[-2] != '.')
901 	  return 1;				// Wrong usage of %f
902 	break;
903       case 'p':					// AM/PM
904 	if (offset == 0)			// Can't be first
905 	  return 0;
906 	position= 7;
907 	break;
908       default:
909 	return 1;				// Unknown controll char
910       }
911       if (dt_pos[position] != 255)		// Don't allow same tag twice
912 	return 1;
913       parts[position]= ptr-1;
914 
915       /*
916 	If switching from time to date, ensure that all time parts
917 	are used
918       */
919       if (part_map && position <= 2 && !(part_map & (1 | 2 | 4)))
920 	offset=5;
921       part_map|= (ulong) 1 << position;
922       dt_pos[position]= offset++;
923       allow_separator= 1;
924     }
925     else
926     {
927       /*
928 	Don't allow any characters in format as this could easily confuse
929 	the date reader
930       */
931       if (!allow_separator)
932 	return 1;				// No separator here
933       allow_separator= 0;			// Don't allow two separators
934       separators++;
935       /* Store in separator_map which parts are punct characters */
936       if (my_ispunct(&my_charset_latin1, *ptr))
937 	separator_map|= (ulong) 1 << (offset-1);
938       else if (!my_isspace(&my_charset_latin1, *ptr))
939 	return 1;
940     }
941   }
942 
943   /* If no %f, specify it after seconds.  Move %p up, if necessary */
944   if ((part_map & 32) && !(part_map & 64))
945   {
946     dt_pos[6]= dt_pos[5] +1;
947     parts[6]= parts[5];				// For later test in (need_p)
948     if (dt_pos[6] == dt_pos[7])			// Move %p one step up if used
949       dt_pos[7]++;
950   }
951 
952   /*
953     Check that we have not used a non legal format specifier and that all
954     format specifiers have been used
955 
956     The last test is to ensure that %p is used if and only if
957     it's needed.
958   */
959   if ((format_type == MYSQL_TIMESTAMP_DATETIME &&
960        !test_all_bits(part_map, (1 | 2 | 4 | 8 | 16 | 32))) ||
961       (format_type == MYSQL_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
962       (format_type == MYSQL_TIMESTAMP_TIME &&
963        !test_all_bits(part_map, 8 | 16 | 32)) ||
964       !allow_separator ||			// %option should be last
965       (need_p && dt_pos[6] +1 != dt_pos[7]) ||
966       (need_p ^ (dt_pos[7] != 255)))
967     return 1;
968 
969   if (dt_pos[6] != 255)				// If fractional seconds
970   {
971     /* remove fractional seconds from later tests */
972     uint pos= dt_pos[6] -1;
973     /* Remove separator before %f from sep map */
974     separator_map= ((separator_map & ((ulong) (1 << pos)-1)) |
975 		    ((separator_map & ~((ulong) (1 << pos)-1)) >> 1));
976     if (part_map & 64)
977     {
978       separators--;				// There is always a separator
979       need_p= 1;				// force use of separators
980     }
981   }
982 
983   /*
984     Remove possible separator before %p from sep_map
985     (This can either be at position 3, 4, 6 or 7) h.m.d.%f %p
986   */
987   if (dt_pos[7] != 255)
988   {
989     if (need_p && parts[7] != parts[6]+2)
990       separators--;
991   }
992   /*
993     Calculate if %p is in first or last part of the datetime field
994 
995     At this point we have either %H-%i-%s %p 'year parts' or
996     'year parts' &H-%i-%s %p" as %f was removed above
997   */
998   offset= dt_pos[6] <= 3 ? 3 : 6;
999   /* Remove separator before %p from sep map */
1000   separator_map= ((separator_map & ((ulong) (1 << offset)-1)) |
1001 		  ((separator_map & ~((ulong) (1 << offset)-1)) >> 1));
1002 
1003   format_str= 0;
1004   switch (format_type) {
1005   case MYSQL_TIMESTAMP_DATE:
1006     format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
1007     /* fall through */
1008   case MYSQL_TIMESTAMP_TIME:
1009     if (!format_str)
1010       format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
1011 
1012     /*
1013       If there is no separators, allow the internal format as we can read
1014       this.  If separators are used, they must be between each part
1015     */
1016     if (format_length == 6 && !need_p &&
1017 	!my_strnncoll(&my_charset_bin,
1018 		      (const uchar *) format, 6,
1019 		      (const uchar *) format_str, 6))
1020       return 0;
1021     if (separator_map == (1 | 2))
1022     {
1023       if (format_type == MYSQL_TIMESTAMP_TIME)
1024       {
1025 	if (*(format+2) != *(format+5))
1026 	  break;				// Error
1027 	/* Store the character used for time formats */
1028 	date_time_format->time_separator= *(format+2);
1029       }
1030       return 0;
1031     }
1032     break;
1033   case MYSQL_TIMESTAMP_DATETIME:
1034     /*
1035       If there is no separators, allow the internal format as we can read
1036       this.  If separators are used, they must be between each part.
1037       Between DATE and TIME we also allow space as separator
1038     */
1039     if ((format_length == 12 && !need_p &&
1040 	 !my_strnncoll(&my_charset_bin,
1041 		       (const uchar *) format, 12,
1042 		       (const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
1043 		       12)) ||
1044 	(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
1045       return 0;
1046     break;
1047   default:
1048     assert(1);
1049     break;
1050   }
1051   return 1;					// Error
1052 }
1053 
1054 
1055 /*
1056   Create a copy of a Date_time_format object
1057 
1058   SYNOPSIS
1059     date_and_time_format_copy()
1060     thd			Set if variable should be allocated in thread mem
1061     format		format to copy
1062 
1063   NOTES
1064     The returned object should be freed with my_free()
1065 
1066   RETURN
1067     NULL ponter:	Error
1068     new object
1069 */
1070 
date_time_format_copy(THD * thd,Date_time_format * format)1071 Date_time_format *date_time_format_copy(THD *thd, Date_time_format *format)
1072 {
1073   Date_time_format *new_format;
1074   size_t length= sizeof(*format) + format->format.length + 1;
1075 
1076   if (thd)
1077     new_format= (Date_time_format *) thd->alloc(length);
1078   else
1079     new_format=  (Date_time_format *) my_malloc(key_memory_DATE_TIME_FORMAT,
1080                                                 length, MYF(MY_WME));
1081   if (new_format)
1082   {
1083     /* Put format string after current pos */
1084     new_format->format.str= (char*) (new_format+1);
1085     memcpy((char*) new_format->positions, (char*) format->positions,
1086 	   sizeof(format->positions));
1087     new_format->time_separator= format->time_separator;
1088     /* We make the string null terminated for easy printf in SHOW VARIABLES */
1089     memcpy(new_format->format.str, format->format.str,
1090 	   format->format.length);
1091     new_format->format.str[format->format.length]= 0;
1092     new_format->format.length= format->format.length;
1093   }
1094   return new_format;
1095 }
1096 
1097 
1098 Known_date_time_format known_date_time_formats[6]=
1099 {
1100   {"USA", "%m.%d.%Y", "%Y-%m-%d %H.%i.%s", "%h:%i:%s %p" },
1101   {"JIS", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
1102   {"ISO", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
1103   {"EUR", "%d.%m.%Y", "%Y-%m-%d %H.%i.%s", "%H.%i.%s" },
1104   {"INTERNAL", "%Y%m%d",   "%Y%m%d%H%i%s", "%H%i%s" },
1105   { 0, 0, 0, 0 }
1106 };
1107 
1108 
1109 /*
1110    Return format string according format name.
1111    If name is unknown, result is NULL
1112 */
1113 
get_date_time_format_str(Known_date_time_format * format,timestamp_type type)1114 const char *get_date_time_format_str(Known_date_time_format *format,
1115 				     timestamp_type type)
1116 {
1117   switch (type) {
1118   case MYSQL_TIMESTAMP_DATE:
1119     return format->date_format;
1120   case MYSQL_TIMESTAMP_DATETIME:
1121     return format->datetime_format;
1122   case MYSQL_TIMESTAMP_TIME:
1123     return format->time_format;
1124   default:
1125     assert(0);				// Impossible
1126     return 0;
1127   }
1128 }
1129 
1130 /****************************************************************************
1131   Functions to create default time/date/datetime strings
1132 
1133   NOTE:
1134     For the moment the Date_time_format argument is ignored becasue
1135     MySQL doesn't support comparing of date/time/datetime strings that
1136     are not in arbutary order as dates are compared as strings in some
1137     context)
1138     This functions don't check that given MYSQL_TIME structure members are
1139     in valid range. If they are not, return value won't reflect any
1140     valid date either. Additionally, make_time doesn't take into
1141     account time->day member: it's assumed that days have been converted
1142     to hours already.
1143 ****************************************************************************/
1144 
1145 /**
1146   Convert TIME value to String.
1147   @param      format   Format (unused, see comments above)
1148   @param      l_time   TIME value
1149   @param[out] str      String to convert to
1150   @param      dec      Number of fractional digits.
1151 */
make_time(const Date_time_format * format MY_ATTRIBUTE ((unused)),const MYSQL_TIME * l_time,String * str,uint dec)1152 void make_time(const Date_time_format *format MY_ATTRIBUTE((unused)),
1153                const MYSQL_TIME *l_time, String *str, uint dec)
1154 {
1155   uint length= (uint) my_time_to_str(l_time, (char*) str->ptr(), dec);
1156   str->length(length);
1157   str->set_charset(&my_charset_numeric);
1158 }
1159 
1160 
1161 /**
1162   Convert DATE value to String.
1163   @param      format   Format (unused, see comments above)
1164   @param      l_time   DATE value
1165   @param[out] str      String to convert to
1166 */
make_date(const Date_time_format * format MY_ATTRIBUTE ((unused)),const MYSQL_TIME * l_time,String * str)1167 void make_date(const Date_time_format *format MY_ATTRIBUTE((unused)),
1168                const MYSQL_TIME *l_time, String *str)
1169 {
1170   uint length= (uint) my_date_to_str(l_time, (char*) str->ptr());
1171   str->length(length);
1172   str->set_charset(&my_charset_numeric);
1173 }
1174 
1175 
1176 /**
1177   Convert DATETIME value to String.
1178   @param      format   Format (unused, see comments above)
1179   @param      l_time   DATE value
1180   @param[out] str      String to convert to
1181   @param      dec      Number of fractional digits.
1182 */
make_datetime(const Date_time_format * format MY_ATTRIBUTE ((unused)),const MYSQL_TIME * l_time,String * str,uint dec)1183 void make_datetime(const Date_time_format *format MY_ATTRIBUTE((unused)),
1184                    const MYSQL_TIME *l_time, String *str, uint dec)
1185 {
1186   uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr(), dec);
1187   str->length(length);
1188   str->set_charset(&my_charset_numeric);
1189 }
1190 
1191 
1192 /**
1193   Convert TIME/DATE/DATETIME value to String.
1194   @param      l_time   DATE value
1195   @param[out] str      String to convert to
1196   @param      dec      Number of fractional digits.
1197 */
my_TIME_to_str(const MYSQL_TIME * ltime,String * str,uint dec)1198 bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec)
1199 {
1200   if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
1201     return true;
1202   str->set_charset(&my_charset_numeric);
1203   str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), dec));
1204   return false;
1205 }
1206 
1207 
make_truncated_value_warning(THD * thd,Sql_condition::enum_severity_level level,ErrConvString val,timestamp_type time_type,const char * field_name)1208 void make_truncated_value_warning(THD *thd,
1209                                   Sql_condition::enum_severity_level level,
1210                                   ErrConvString val, timestamp_type time_type,
1211                                   const char *field_name)
1212 {
1213   char warn_buff[MYSQL_ERRMSG_SIZE];
1214   const char *type_str;
1215   CHARSET_INFO *cs= system_charset_info;
1216 
1217   switch (time_type) {
1218     case MYSQL_TIMESTAMP_DATE:
1219       type_str= "date";
1220       break;
1221     case MYSQL_TIMESTAMP_TIME:
1222       type_str= "time";
1223       break;
1224     case MYSQL_TIMESTAMP_DATETIME:  // FALLTHROUGH
1225     default:
1226       type_str= "datetime";
1227       break;
1228   }
1229   if (field_name)
1230     cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
1231                        ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
1232                        type_str, val.ptr(), field_name,
1233                        (long) thd->get_stmt_da()->current_row_for_condition());
1234   else
1235   {
1236     if (time_type > MYSQL_TIMESTAMP_ERROR)
1237       cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
1238                          ER(ER_TRUNCATED_WRONG_VALUE),
1239                          type_str, val.ptr());
1240     else
1241       cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
1242                          ER(ER_WRONG_VALUE), type_str, val.ptr());
1243   }
1244   push_warning(thd, level, ER_TRUNCATED_WRONG_VALUE, warn_buff);
1245 }
1246 
1247 
1248 /* Daynumber from year 0 to 9999-12-31 */
1249 #define MAX_DAY_NUMBER 3652424L
1250 
date_add_interval(MYSQL_TIME * ltime,interval_type int_type,Interval interval)1251 bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
1252                        Interval interval)
1253 {
1254   long period, sign;
1255 
1256   ltime->neg= 0;
1257 
1258   sign= (interval.neg ? -1 : 1);
1259 
1260   switch (int_type) {
1261   case INTERVAL_SECOND:
1262   case INTERVAL_SECOND_MICROSECOND:
1263   case INTERVAL_MICROSECOND:
1264   case INTERVAL_MINUTE:
1265   case INTERVAL_HOUR:
1266   case INTERVAL_MINUTE_MICROSECOND:
1267   case INTERVAL_MINUTE_SECOND:
1268   case INTERVAL_HOUR_MICROSECOND:
1269   case INTERVAL_HOUR_SECOND:
1270   case INTERVAL_HOUR_MINUTE:
1271   case INTERVAL_DAY_MICROSECOND:
1272   case INTERVAL_DAY_SECOND:
1273   case INTERVAL_DAY_MINUTE:
1274   case INTERVAL_DAY_HOUR:
1275   {
1276     longlong sec, days, daynr, microseconds, extra_sec;
1277     ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
1278     microseconds= ltime->second_part + sign*interval.second_part;
1279     extra_sec= microseconds/1000000L;
1280     microseconds= microseconds%1000000L;
1281 
1282     sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
1283 	 ltime->second +
1284 	 sign* (longlong) (interval.day*3600*24L +
1285                            interval.hour*3600LL+interval.minute*60LL+
1286                            interval.second))+ extra_sec;
1287     if (microseconds < 0)
1288     {
1289       microseconds+= 1000000LL;
1290       sec--;
1291     }
1292     days= sec/(3600*24LL);
1293     sec-= days*3600*24LL;
1294     if (sec < 0)
1295     {
1296       days--;
1297       sec+= 3600*24LL;
1298     }
1299     ltime->second_part= (uint) microseconds;
1300     ltime->second= (uint) (sec % 60);
1301     ltime->minute= (uint) (sec/60 % 60);
1302     ltime->hour=   (uint) (sec/3600);
1303     daynr= calc_daynr(ltime->year,ltime->month,1) + days;
1304     /* Day number from year 0 to 9999-12-31 */
1305     if ((ulonglong) daynr > MAX_DAY_NUMBER)
1306       goto invalid_date;
1307     get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
1308                         &ltime->day);
1309     break;
1310   }
1311   case INTERVAL_DAY:
1312   case INTERVAL_WEEK:
1313     period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
1314              sign * (long) interval.day);
1315     /* Daynumber from year 0 to 9999-12-31 */
1316     if ((ulong) period > MAX_DAY_NUMBER)
1317       goto invalid_date;
1318     get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
1319     break;
1320   case INTERVAL_YEAR:
1321     ltime->year+= sign * (long) interval.year;
1322     if ((ulong) ltime->year >= 10000L)
1323       goto invalid_date;
1324     if (ltime->month == 2 && ltime->day == 29 &&
1325 	calc_days_in_year(ltime->year) != 366)
1326       ltime->day=28;				// Was leap-year
1327     break;
1328   case INTERVAL_YEAR_MONTH:
1329   case INTERVAL_QUARTER:
1330   case INTERVAL_MONTH:
1331     period= (ltime->year*12 + sign * (long) interval.year*12 +
1332 	     ltime->month-1 + sign * (long) interval.month);
1333     if ((ulong) period >= 120000L)
1334       goto invalid_date;
1335     ltime->year= (uint) (period / 12);
1336     ltime->month= (uint) (period % 12L)+1;
1337     /* Adjust day if the new month doesn't have enough days */
1338     if (ltime->day > days_in_month[ltime->month-1])
1339     {
1340       ltime->day = days_in_month[ltime->month-1];
1341       if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
1342 	ltime->day++;				// Leap-year
1343     }
1344     break;
1345   default:
1346     goto null_date;
1347   }
1348 
1349   return 0;					// Ok
1350 
1351 invalid_date:
1352   push_warning_printf(current_thd, Sql_condition::SL_WARNING,
1353                       ER_DATETIME_FUNCTION_OVERFLOW,
1354                       ER(ER_DATETIME_FUNCTION_OVERFLOW),
1355                       "datetime");
1356 null_date:
1357   return 1;
1358 }
1359 
1360 
1361 /*
1362   Calculate difference between two datetime values as seconds + microseconds.
1363 
1364   SYNOPSIS
1365     calc_time_diff()
1366       l_time1         - TIME/DATE/DATETIME value
1367       l_time2         - TIME/DATE/DATETIME value
1368       l_sign          - 1 absolute values are substracted,
1369                         -1 absolute values are added.
1370       seconds_out     - Out parameter where difference between
1371                         l_time1 and l_time2 in seconds is stored.
1372       microseconds_out- Out parameter where microsecond part of difference
1373                         between l_time1 and l_time2 is stored.
1374 
1375   NOTE
1376     This function calculates difference between l_time1 and l_time2 absolute
1377     values. So one should set l_sign and correct result if he want to take
1378     signs into account (i.e. for MYSQL_TIME values).
1379 
1380   RETURN VALUES
1381     Returns sign of difference.
1382     1 means negative result
1383     0 means positive result
1384 
1385 */
1386 
1387 bool
calc_time_diff(const MYSQL_TIME * l_time1,const MYSQL_TIME * l_time2,int l_sign,longlong * seconds_out,long * microseconds_out)1388 calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
1389                int l_sign, longlong *seconds_out, long *microseconds_out)
1390 {
1391   long days;
1392   bool neg;
1393   longlong microseconds;
1394 
1395   /*
1396     We suppose that if first argument is MYSQL_TIMESTAMP_TIME
1397     the second argument should be TIMESTAMP_TIME also.
1398     We should check it before calc_time_diff call.
1399   */
1400   if (l_time1->time_type == MYSQL_TIMESTAMP_TIME)  // Time value
1401     days= (long)l_time1->day - l_sign * (long)l_time2->day;
1402   else
1403   {
1404     days= calc_daynr((uint) l_time1->year,
1405 		     (uint) l_time1->month,
1406 		     (uint) l_time1->day);
1407     if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
1408       days-= l_sign * (long)l_time2->day;
1409     else
1410       days-= l_sign*calc_daynr((uint) l_time2->year,
1411 			       (uint) l_time2->month,
1412 			       (uint) l_time2->day);
1413   }
1414 
1415   microseconds= ((longlong)days * SECONDS_IN_24H +
1416                  (longlong)(l_time1->hour*3600L +
1417                             l_time1->minute*60L +
1418                             l_time1->second) -
1419                  l_sign*(longlong)(l_time2->hour*3600L +
1420                                    l_time2->minute*60L +
1421                                    l_time2->second)) * 1000000LL +
1422                 (longlong)l_time1->second_part -
1423                 l_sign*(longlong)l_time2->second_part;
1424 
1425   neg= 0;
1426   if (microseconds < 0)
1427   {
1428     microseconds= -microseconds;
1429     neg= 1;
1430   }
1431   *seconds_out= microseconds/1000000L;
1432   *microseconds_out= (long) (microseconds%1000000L);
1433   return neg;
1434 }
1435 
1436 
1437 /*
1438   Compares 2 MYSQL_TIME structures
1439 
1440   SYNOPSIS
1441     my_time_compare()
1442 
1443       a - first time
1444       b - second time
1445 
1446   RETURN VALUE
1447    -1   - a < b
1448     0   - a == b
1449     1   - a > b
1450 
1451 */
1452 
my_time_compare(MYSQL_TIME * a,MYSQL_TIME * b)1453 int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
1454 {
1455   ulonglong a_t= TIME_to_ulonglong_datetime(a);
1456   ulonglong b_t= TIME_to_ulonglong_datetime(b);
1457 
1458   if (a_t < b_t)
1459     return -1;
1460   if (a_t > b_t)
1461     return 1;
1462 
1463   if (a->second_part < b->second_part)
1464     return -1;
1465   if (a->second_part > b->second_part)
1466     return 1;
1467 
1468   return 0;
1469 }
1470 
1471 
1472 /* Rounding functions */
1473 static uint msec_round_add[7]=
1474 {
1475   500000000,
1476   50000000,
1477   5000000,
1478   500000,
1479   50000,
1480   5000,
1481   0
1482 };
1483 
1484 
1485 /**
1486   Round time value to the given precision.
1487 
1488   @param IN/OUT  ltime    The value to round.
1489   @param         dec      Precision.
1490   @return        False on success, true on error.
1491 */
my_time_round(MYSQL_TIME * ltime,uint dec)1492 bool my_time_round(MYSQL_TIME *ltime, uint dec)
1493 {
1494   int warnings= 0;
1495   assert(dec <= DATETIME_MAX_DECIMALS);
1496   /* Add half away from zero */
1497   bool rc= time_add_nanoseconds_with_round(ltime,
1498                                            msec_round_add[dec], &warnings);
1499   /* Truncate non-significant digits */
1500   my_time_trunc(ltime, dec);
1501   return rc;
1502 }
1503 
1504 
1505 /**
1506   Round datetime value to the given precision.
1507 
1508   @param IN/OUT  ltime    The value to round.
1509   @param         dec      Precision.
1510   @return        False on success, true on error.
1511 */
my_datetime_round(MYSQL_TIME * ltime,uint dec,int * warnings)1512 bool my_datetime_round(MYSQL_TIME *ltime, uint dec, int *warnings)
1513 {
1514   assert(dec <= DATETIME_MAX_DECIMALS);
1515   /* Add half away from zero */
1516   bool rc= datetime_add_nanoseconds_with_round(ltime,
1517                                                msec_round_add[dec], warnings);
1518   /* Truncate non-significant digits */
1519   my_time_trunc(ltime, dec);
1520   return rc;
1521 }
1522 
1523 
1524 /**
1525   Round timeval value to the given precision.
1526 
1527   @param IN/OUT  ts       The value to round.
1528   @param         dec      Precision.
1529   @return        False on success, true on error.
1530 */
my_timeval_round(struct timeval * tv,uint decimals)1531 bool my_timeval_round(struct timeval *tv, uint decimals)
1532 {
1533   assert(decimals <= DATETIME_MAX_DECIMALS);
1534   uint nanoseconds= msec_round_add[decimals];
1535   tv->tv_usec+= (nanoseconds + 500) / 1000;
1536   if (tv->tv_usec < 1000000)
1537     goto ret;
1538 
1539   tv->tv_usec= 0;
1540   tv->tv_sec++;
1541   if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tv->tv_sec))
1542   {
1543     tv->tv_sec= TIMESTAMP_MAX_VALUE;
1544     return true;
1545   }
1546 
1547 ret:
1548   my_timeval_trunc(tv, decimals);
1549   return false;
1550 }
1551 
1552 
1553 /**
1554   Mix a date value and a time value.
1555 
1556   @param  IN/OUT  ldate  Date value.
1557   @param          ltime  Time value.
1558 */
mix_date_and_time(MYSQL_TIME * ldate,const MYSQL_TIME * ltime)1559 void mix_date_and_time(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
1560 {
1561   assert(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
1562          ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
1563 
1564   if (!ltime->neg && ltime->hour < 24)
1565   {
1566     /*
1567       Simple case: TIME is within normal 24 hours internal.
1568       Mix DATE part of ltime2 and TIME part of ltime together.
1569     */
1570     ldate->hour= ltime->hour;
1571     ldate->minute= ltime->minute;
1572     ldate->second= ltime->second;
1573     ldate->second_part= ltime->second_part;
1574   }
1575   else
1576   {
1577     /* Complex case: TIME is negative or outside of 24 hours internal. */
1578     longlong seconds;
1579     long days, useconds;
1580     int sign= ltime->neg ? 1 : -1;
1581     ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds);
1582     assert(!ldate->neg);
1583 
1584     /*
1585       We pass current date to mix_date_and_time. If we want to use
1586       this function with arbitrary dates, this code will need
1587       to cover cases when ltime is negative and "ldate < -ltime".
1588     */
1589     assert(ldate->year > 0);
1590 
1591     days= (long) (seconds / SECONDS_IN_24H);
1592     calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds);
1593     get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day);
1594   }
1595   ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
1596 }
1597 
1598 
1599 /**
1600   Convert MYSQL_TIME value to its packed numeric representation,
1601   using field type.
1602   @param ltime  The value to convert.
1603   @param type   MySQL field type.
1604   @retval       Packed numeric representation.
1605 */
TIME_to_longlong_packed(const MYSQL_TIME * ltime,enum enum_field_types type)1606 longlong TIME_to_longlong_packed(const MYSQL_TIME *ltime,
1607                                  enum enum_field_types type)
1608 {
1609   switch (type)
1610   {
1611   case MYSQL_TYPE_TIME:
1612     return TIME_to_longlong_time_packed(ltime);
1613   case MYSQL_TYPE_DATETIME:
1614   case MYSQL_TYPE_TIMESTAMP:
1615     return TIME_to_longlong_datetime_packed(ltime);
1616   case MYSQL_TYPE_DATE:
1617     return TIME_to_longlong_date_packed(ltime);
1618   default:
1619     return TIME_to_longlong_packed(ltime);
1620   }
1621 }
1622 
1623 
1624 /**
1625   Convert packed numeric temporal representation to time, date or datetime,
1626   using field type.
1627   @param[out] ltime        The variable to write to.
1628   @param      type         MySQL field type.
1629   @param      packed_value Numeric datetype representation.
1630 */
TIME_from_longlong_packed(MYSQL_TIME * ltime,enum enum_field_types type,longlong packed_value)1631 void TIME_from_longlong_packed(MYSQL_TIME *ltime,
1632                                enum enum_field_types type,
1633                                longlong packed_value)
1634 {
1635   switch (type)
1636   {
1637   case MYSQL_TYPE_TIME:
1638     TIME_from_longlong_time_packed(ltime, packed_value);
1639     break;
1640   case MYSQL_TYPE_DATE:
1641     TIME_from_longlong_date_packed(ltime, packed_value);
1642     break;
1643   case MYSQL_TYPE_DATETIME:
1644   case MYSQL_TYPE_TIMESTAMP:
1645     TIME_from_longlong_datetime_packed(ltime, packed_value);
1646     break;
1647   default:
1648     assert(0);
1649     set_zero_time(ltime, MYSQL_TIMESTAMP_ERROR);
1650     break;
1651   }
1652 }
1653 
1654 
1655 /**
1656   Unpack packed numeric temporal value to date/time value
1657   and then convert to decimal representation.
1658 
1659   @param  OUT dec          The variable to write to.
1660   @param      type         MySQL field type.
1661   @param      packed_value Packed numeric temporal representation.
1662   @return     A decimal value in on of the following formats, depending
1663               on type: YYYYMMDD, hhmmss.ffffff or YYMMDDhhmmss.ffffff.
1664 */
my_decimal_from_datetime_packed(my_decimal * dec,enum enum_field_types type,longlong packed_value)1665 my_decimal *my_decimal_from_datetime_packed(my_decimal *dec,
1666                                             enum enum_field_types type,
1667                                             longlong packed_value)
1668 {
1669   MYSQL_TIME ltime;
1670   switch (type)
1671   {
1672     case MYSQL_TYPE_TIME:
1673       TIME_from_longlong_time_packed(&ltime, packed_value);
1674       return time2my_decimal(&ltime, dec);
1675     case MYSQL_TYPE_DATE:
1676       TIME_from_longlong_date_packed(&ltime, packed_value);
1677       ulonglong2decimal(TIME_to_ulonglong_date(&ltime), dec);
1678       return dec;
1679     case MYSQL_TYPE_DATETIME:
1680     case MYSQL_TYPE_TIMESTAMP:
1681       TIME_from_longlong_datetime_packed(&ltime, packed_value);
1682       return date2my_decimal(&ltime, dec);
1683     default:
1684       assert(0);
1685       ulonglong2decimal(0, dec);
1686       return dec;
1687   }
1688 }
1689 
1690 
1691 /**
1692   Convert packed numeric representation to
1693   unpacked numeric representation.
1694   @param type           MySQL field type.
1695   @param paacked_value  Packed numeric temporal value.
1696   @return               Number in one of the following formats,
1697                         depending on type: YYMMDD, YYMMDDhhmmss, hhmmss.
1698 */
longlong_from_datetime_packed(enum enum_field_types type,longlong packed_value)1699 longlong longlong_from_datetime_packed(enum enum_field_types type,
1700                                        longlong packed_value)
1701 {
1702   MYSQL_TIME ltime;
1703   switch (type)
1704   {
1705     case MYSQL_TYPE_TIME:
1706       TIME_from_longlong_time_packed(&ltime, packed_value);
1707       return TIME_to_ulonglong_time(&ltime);
1708     case MYSQL_TYPE_DATE:
1709       TIME_from_longlong_date_packed(&ltime, packed_value);
1710       return TIME_to_ulonglong_date(&ltime);
1711     case MYSQL_TYPE_DATETIME:
1712     case MYSQL_TYPE_TIMESTAMP:
1713       TIME_from_longlong_datetime_packed(&ltime, packed_value);
1714       return TIME_to_ulonglong_datetime(&ltime);
1715     default:
1716       assert(0);
1717       return 0;
1718   }
1719 }
1720 
1721 
1722 /**
1723   Convert packed numeric temporal representation to unpacked numeric
1724   representation.
1725   @param type           MySQL field type.
1726   @param packed_value   Numeric packed temporal representation.
1727   @return               A double value in on of the following formats,
1728                         depending  on type:
1729                         YYYYMMDD, hhmmss.ffffff or YYMMDDhhmmss.ffffff.
1730 */
double_from_datetime_packed(enum enum_field_types type,longlong packed_value)1731 double double_from_datetime_packed(enum enum_field_types type,
1732                                    longlong packed_value)
1733 {
1734   longlong result= longlong_from_datetime_packed(type, packed_value);
1735   return result +
1736         ((double) MY_PACKED_TIME_GET_FRAC_PART(packed_value)) / 1000000;
1737 }
1738