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