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, <ime->year, <ime->month,
1352 <ime->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,<ime->year,<ime->month,<ime->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(<ime, packed_value);
1718 return time2my_decimal(<ime, dec);
1719 case MYSQL_TYPE_DATE:
1720 TIME_from_longlong_date_packed(<ime, packed_value);
1721 ulonglong2decimal(TIME_to_ulonglong_date(<ime), dec);
1722 return dec;
1723 case MYSQL_TYPE_DATETIME:
1724 case MYSQL_TYPE_TIMESTAMP:
1725 TIME_from_longlong_datetime_packed(<ime, packed_value);
1726 return date2my_decimal(<ime, 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(<ime, packed_value);
1751 return TIME_to_ulonglong_time(<ime);
1752 case MYSQL_TYPE_DATE:
1753 TIME_from_longlong_date_packed(<ime, packed_value);
1754 return TIME_to_ulonglong_date(<ime);
1755 case MYSQL_TYPE_DATETIME:
1756 case MYSQL_TYPE_TIMESTAMP:
1757 TIME_from_longlong_datetime_packed(<ime, packed_value);
1758 return TIME_to_ulonglong_datetime(<ime);
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