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