1 /* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
2 Copyright (c) 2009, 2020, MariaDB Corporation.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
16
17
18 /* Functions to handle date and time */
19
20 #include "mariadb.h"
21 #include "sql_time.h"
22 #include "tztime.h" // struct Time_zone
23 #include "sql_class.h" // THD
24 #include <m_ctype.h>
25
26
27 #define MAX_DAY_NUMBER 3652424L
28
29 /* Some functions to calculate dates */
30
31 /*
32 Name description of interval names used in statements.
33
34 'interval_type_to_name' is ordered and sorted on interval size and
35 interval complexity.
36 Order of elements in 'interval_type_to_name' should correspond to
37 the order of elements in 'interval_type' enum
38
39 See also interval_type, interval_names, append_interval
40 */
41
42 LEX_CSTRING interval_type_to_name[INTERVAL_LAST] = {
43 { STRING_WITH_LEN("YEAR")},
44 { STRING_WITH_LEN("QUARTER")},
45 { STRING_WITH_LEN("MONTH")},
46 { STRING_WITH_LEN("WEEK")},
47 { STRING_WITH_LEN("DAY")},
48 { STRING_WITH_LEN("HOUR")},
49 { STRING_WITH_LEN("MINUTE")},
50 { STRING_WITH_LEN("SECOND")},
51 { STRING_WITH_LEN("MICROSECOND")},
52 { STRING_WITH_LEN("YEAR_MONTH")},
53 { STRING_WITH_LEN("DAY_HOUR")},
54 { STRING_WITH_LEN("DAY_MINUTE")},
55 { STRING_WITH_LEN("DAY_SECOND")},
56 { STRING_WITH_LEN("HOUR_MINUTE")},
57 { STRING_WITH_LEN("HOUR_SECOND")},
58 { STRING_WITH_LEN("MINUTE_SECOND")},
59 { STRING_WITH_LEN("DAY_MICROSECOND")},
60 { STRING_WITH_LEN("HOUR_MICROSECOND")},
61 { STRING_WITH_LEN("MINUTE_MICROSECOND")},
62 { STRING_WITH_LEN("SECOND_MICROSECOND")}
63 };
64
append_interval(String * str,interval_type int_type,const INTERVAL & interval)65 int append_interval(String *str, interval_type int_type, const INTERVAL &interval)
66 {
67 char buf[64];
68 size_t len;
69 switch (int_type) {
70 case INTERVAL_YEAR:
71 len= my_snprintf(buf,sizeof(buf),"%u", interval.year);
72 break;
73 case INTERVAL_QUARTER:
74 case INTERVAL_MONTH:
75 len= my_snprintf(buf,sizeof(buf),"%u", interval.month);
76 int_type=INTERVAL_MONTH;
77 break;
78 case INTERVAL_WEEK:
79 case INTERVAL_DAY:
80 len= my_snprintf(buf,sizeof(buf),"%u", interval.day);
81 int_type=INTERVAL_DAY;
82 break;
83 case INTERVAL_HOUR:
84 len= my_snprintf(buf,sizeof(buf),"%u", interval.hour);
85 break;
86 case INTERVAL_MINUTE:
87 len= my_snprintf(buf,sizeof(buf),"%u", interval.minute);
88 break;
89 case INTERVAL_SECOND:
90 len= my_snprintf(buf,sizeof(buf),"%u", interval.second);
91 break;
92 case INTERVAL_MICROSECOND:
93 len= my_snprintf(buf,sizeof(buf),"%u", interval.second_part);
94 break;
95 case INTERVAL_YEAR_MONTH:
96 len= my_snprintf(buf,sizeof(buf),"%u-%02u", interval.day, interval.month);
97 break;
98 case INTERVAL_DAY_HOUR:
99 len= my_snprintf(buf,sizeof(buf),"%u %u", interval.day, interval.hour);
100 break;
101 case INTERVAL_DAY_MINUTE:
102 len= my_snprintf(buf,sizeof(buf),"%u %u:%02u", interval.day, interval.hour, interval.minute);
103 break;
104 case INTERVAL_DAY_SECOND:
105 len= my_snprintf(buf,sizeof(buf),"%u %u:%02u:%02u", interval.day, interval.hour, interval.minute, interval.second);
106 break;
107 case INTERVAL_HOUR_MINUTE:
108 len= my_snprintf(buf,sizeof(buf),"%u:%02u", interval.hour, interval.minute);
109 break;
110 case INTERVAL_HOUR_SECOND:
111 len= my_snprintf(buf,sizeof(buf),"%u:%02u:%02u", interval.hour, interval.minute, interval.second);
112 break;
113 case INTERVAL_MINUTE_SECOND:
114 len= my_snprintf(buf,sizeof(buf),"%u:%02u", interval.minute, interval.second);
115 break;
116 case INTERVAL_DAY_MICROSECOND:
117 len= my_snprintf(buf,sizeof(buf),"%u %u:%02u:%02u.%06u", interval.day, interval.hour, interval.minute, interval.second, interval.second_part);
118 break;
119 case INTERVAL_HOUR_MICROSECOND:
120 len= my_snprintf(buf,sizeof(buf),"%u:%02u:%02u.%06u", interval.hour, interval.minute, interval.second, interval.second_part);
121 break;
122 case INTERVAL_MINUTE_MICROSECOND:
123 len= my_snprintf(buf,sizeof(buf),"%u:%02u.%06u", interval.minute, interval.second, interval.second_part);
124 break;
125 case INTERVAL_SECOND_MICROSECOND:
126 len= my_snprintf(buf,sizeof(buf),"%u.%06u", interval.second, interval.second_part);
127 break;
128 default:
129 DBUG_ASSERT(0);
130 len= 0;
131 }
132 return str->append(buf, len) || str->append(' ') ||
133 str->append(interval_type_to_name + int_type);
134 }
135
136
137 /*
138 Calc weekday from daynr
139 Returns 0 for monday, 1 for tuesday ...
140 */
141
calc_weekday(long daynr,bool sunday_first_day_of_week)142 int calc_weekday(long daynr,bool sunday_first_day_of_week)
143 {
144 DBUG_ENTER("calc_weekday");
145 DBUG_RETURN ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
146 }
147
148 /*
149 The bits in week_format has the following meaning:
150 WEEK_MONDAY_FIRST (0) If not set Sunday is first day of week
151 If set Monday is first day of week
152 WEEK_YEAR (1) If not set Week is in range 0-53
153
154 Week 0 is returned for the the last week of the previous year (for
155 a date at start of january) In this case one can get 53 for the
156 first week of next year. This flag ensures that the week is
157 relevant for the given year. Note that this flag is only
158 releveant if WEEK_JANUARY is not set.
159
160 If set Week is in range 1-53.
161
162 In this case one may get week 53 for a date in January (when
163 the week is that last week of previous year) and week 1 for a
164 date in December.
165
166 WEEK_FIRST_WEEKDAY (2) If not set Weeks are numbered according
167 to ISO 8601:1988
168 If set The week that contains the first
169 'first-day-of-week' is week 1.
170
171 ISO 8601:1988 means that if the week containing January 1 has
172 four or more days in the new year, then it is week 1;
173 Otherwise it is the last week of the previous year, and the
174 next week is week 1.
175 */
176
calc_week(const MYSQL_TIME * l_time,uint week_behaviour,uint * year)177 uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year)
178 {
179 uint days;
180 ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
181 ulong first_daynr=calc_daynr(l_time->year,1,1);
182 bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST);
183 bool week_year= MY_TEST(week_behaviour & WEEK_YEAR);
184 bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY);
185
186 uint weekday=calc_weekday(first_daynr, !monday_first);
187 *year=l_time->year;
188
189 if (l_time->month == 1 && l_time->day <= 7-weekday)
190 {
191 if (!week_year &&
192 ((first_weekday && weekday != 0) ||
193 (!first_weekday && weekday >= 4)))
194 return 0;
195 week_year= 1;
196 (*year)--;
197 first_daynr-= (days=calc_days_in_year(*year));
198 weekday= (weekday + 53*7- days) % 7;
199 }
200
201 if ((first_weekday && weekday != 0) ||
202 (!first_weekday && weekday >= 4))
203 days= daynr - (first_daynr+ (7-weekday));
204 else
205 days= daynr - (first_daynr - weekday);
206
207 if (week_year && days >= 52*7)
208 {
209 weekday= (weekday + calc_days_in_year(*year)) % 7;
210 if ((!first_weekday && weekday < 4) ||
211 (first_weekday && weekday == 0))
212 {
213 (*year)++;
214 return 1;
215 }
216 }
217 return days/7+1;
218 }
219
220 /* Change a daynr to year, month and day */
221 /* Daynr 0 is returned as date 00.00.00 */
222
get_date_from_daynr(long daynr,uint * ret_year,uint * ret_month,uint * ret_day)223 bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
224 uint *ret_day)
225 {
226 uint year,temp,leap_day,day_of_year,days_in_year;
227 uchar *month_pos;
228 DBUG_ENTER("get_date_from_daynr");
229
230 if (daynr < 366 || daynr > MAX_DAY_NUMBER)
231 DBUG_RETURN(1);
232
233 year= (uint) (daynr*100 / 36525L);
234 temp=(((year-1)/100+1)*3)/4;
235 day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
236 while (day_of_year > (days_in_year= calc_days_in_year(year)))
237 {
238 day_of_year-=days_in_year;
239 (year)++;
240 }
241 leap_day=0;
242 if (days_in_year == 366)
243 {
244 if (day_of_year > 31+28)
245 {
246 day_of_year--;
247 if (day_of_year == 31+28)
248 leap_day=1; /* Handle leapyears leapday */
249 }
250 }
251 *ret_month=1;
252 for (month_pos= days_in_month ;
253 day_of_year > (uint) *month_pos ;
254 day_of_year-= *(month_pos++), (*ret_month)++)
255 ;
256 *ret_year=year;
257 *ret_day=day_of_year+leap_day;
258 DBUG_RETURN(0);
259 }
260
261 /* Functions to handle periods */
262
convert_period_to_month(ulong period)263 ulong convert_period_to_month(ulong period)
264 {
265 ulong a,b;
266 if (period == 0 || period > 999912)
267 return 0L;
268 if ((a=period/100) < YY_PART_YEAR)
269 a+=2000;
270 else if (a < 100)
271 a+=1900;
272 b=period%100;
273 return a*12+b-1;
274 }
275
276
convert_month_to_period(ulong month)277 ulong convert_month_to_period(ulong month)
278 {
279 ulong year;
280 if (month == 0L)
281 return 0L;
282 if ((year=month/12) < 100)
283 {
284 year+=(year < YY_PART_YEAR) ? 2000 : 1900;
285 }
286 return year*100+month%12+1;
287 }
288
289
290 bool
check_date_with_warn(THD * thd,const MYSQL_TIME * ltime,date_conv_mode_t fuzzydate,timestamp_type ts_type)291 check_date_with_warn(THD *thd, const MYSQL_TIME *ltime,
292 date_conv_mode_t fuzzydate, timestamp_type ts_type)
293 {
294 int unused;
295 if (check_date(ltime, fuzzydate, &unused))
296 {
297 ErrConvTime str(ltime);
298 make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
299 &str, ts_type, nullptr, nullptr, nullptr);
300 return true;
301 }
302 return false;
303 }
304
305
306 bool
adjust_time_range_with_warn(THD * thd,MYSQL_TIME * ltime,uint dec)307 adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec)
308 {
309 MYSQL_TIME copy= *ltime;
310 ErrConvTime str(©);
311 int warnings= 0;
312 if (check_time_range(ltime, dec, &warnings))
313 return true;
314 if (warnings)
315 thd->push_warning_truncated_wrong_value("time", str.ptr());
316 return false;
317 }
318
319 /*
320 Convert a string to 8-bit representation,
321 for use in str_to_time/str_to_date/str_to_date.
322
323 In the future to_ascii() can be extended to convert
324 non-ASCII digits to ASCII digits
325 (for example, ARABIC-INDIC, DEVANAGARI, BENGALI, and so on)
326 so DATE/TIME/DATETIME values understand digits in the
327 respected scripts.
328 */
329 static uint
to_ascii(CHARSET_INFO * cs,const char * src,size_t src_length,char * dst,size_t dst_length)330 to_ascii(CHARSET_INFO *cs,
331 const char *src, size_t src_length,
332 char *dst, size_t dst_length)
333
334 {
335 int cnvres;
336 my_wc_t wc;
337 const char *srcend= src + src_length;
338 char *dst0= dst, *dstend= dst + dst_length - 1;
339 while (dst < dstend &&
340 (cnvres= (cs->cset->mb_wc)(cs, &wc,
341 (const uchar*) src,
342 (const uchar*) srcend)) > 0 &&
343 wc < 128)
344 {
345 src+= cnvres;
346 *dst++= static_cast<char>(wc);
347 }
348 *dst= '\0';
349 return (uint)(dst - dst0);
350 }
351
352
353 class TemporalAsciiBuffer: public LEX_CSTRING
354 {
355 char cnv[32];
356 public:
TemporalAsciiBuffer(const char * str,size_t length,CHARSET_INFO * cs)357 TemporalAsciiBuffer(const char *str, size_t length, CHARSET_INFO *cs)
358 {
359 if ((cs->state & MY_CS_NONASCII) != 0)
360 {
361 LEX_CSTRING::str= cnv;
362 LEX_CSTRING::length= to_ascii(cs, str, length, cnv, sizeof(cnv));
363 }
364 else
365 {
366 LEX_CSTRING::str= str;
367 LEX_CSTRING::length= length;
368 }
369 }
370 };
371
372
373 /* Character set-aware version of ascii_to_datetime_or_date_or_time() */
str_to_datetime_or_date_or_time(THD * thd,MYSQL_TIME_STATUS * st,const char * str,size_t length,CHARSET_INFO * cs,date_mode_t fuzzydate)374 bool Temporal::str_to_datetime_or_date_or_time(THD *thd, MYSQL_TIME_STATUS *st,
375 const char *str, size_t length,
376 CHARSET_INFO *cs,
377 date_mode_t fuzzydate)
378 {
379 TemporalAsciiBuffer tmp(str, length, cs);
380 return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate)||
381 add_nanoseconds(thd, &st->warnings, fuzzydate, st->nanoseconds);
382 }
383
384
385 /* Character set-aware version of str_to_datetime_or_date() */
str_to_datetime_or_date(THD * thd,MYSQL_TIME_STATUS * status,const char * str,size_t length,CHARSET_INFO * cs,date_mode_t flags)386 bool Temporal::str_to_datetime_or_date(THD *thd, MYSQL_TIME_STATUS *status,
387 const char *str, size_t length,
388 CHARSET_INFO *cs,
389 date_mode_t flags)
390 {
391 TemporalAsciiBuffer tmp(str, length, cs);
392 return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags) ||
393 add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds);
394 }
395
396
397 /* Character set-aware version of ascii_to_temporal() */
str_to_temporal(THD * thd,MYSQL_TIME_STATUS * status,const char * str,size_t length,CHARSET_INFO * cs,date_mode_t flags)398 bool Temporal::str_to_temporal(THD *thd, MYSQL_TIME_STATUS *status,
399 const char *str, size_t length, CHARSET_INFO *cs,
400 date_mode_t flags)
401 {
402 TemporalAsciiBuffer tmp(str, length, cs);
403 return ascii_to_temporal(status, tmp.str, tmp.length, flags) ||
404 add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds);
405 }
406
407
408 /* Character set-aware version of str_to_DDhhmmssff() */
str_to_DDhhmmssff(MYSQL_TIME_STATUS * status,const char * str,size_t length,CHARSET_INFO * cs,ulong max_hour)409 bool Interval_DDhhmmssff::str_to_DDhhmmssff(MYSQL_TIME_STATUS *status,
410 const char *str, size_t length,
411 CHARSET_INFO *cs, ulong max_hour)
412 {
413 TemporalAsciiBuffer tmp(str, length, cs);
414 bool rc= ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status);
415 DBUG_ASSERT(status->warnings || !rc);
416 return rc;
417 }
418
419
420 /*
421 Convert a timestamp string to a MYSQL_TIME value and produce a warning
422 if string was truncated during conversion.
423
424 NOTE
425 See description of str_to_datetime_xxx() for more information.
426 */
427
428 bool
str_to_datetime_with_warn(THD * thd,CHARSET_INFO * cs,const char * str,size_t length,MYSQL_TIME * to,date_mode_t mode)429 str_to_datetime_with_warn(THD *thd, CHARSET_INFO *cs,
430 const char *str, size_t length, MYSQL_TIME *to,
431 date_mode_t mode)
432 {
433 Temporal::Warn_push warn(thd, nullptr, nullptr, nullptr, to, mode);
434 Temporal_hybrid *t= new(to) Temporal_hybrid(thd, &warn, str, length, cs, mode);
435 return !t->is_valid_temporal();
436 }
437
438
double_to_datetime_with_warn(THD * thd,double value,MYSQL_TIME * ltime,date_mode_t fuzzydate,const TABLE_SHARE * s,const char * field_name)439 bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime,
440 date_mode_t fuzzydate,
441 const TABLE_SHARE *s, const char *field_name)
442 {
443 Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
444 s ? s->table_name.str : nullptr,
445 field_name, ltime, fuzzydate);
446 Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate);
447 return !t->is_valid_temporal();
448 }
449
450
decimal_to_datetime_with_warn(THD * thd,const my_decimal * value,MYSQL_TIME * ltime,date_mode_t fuzzydate,const TABLE_SHARE * s,const char * field_name)451 bool decimal_to_datetime_with_warn(THD *thd, const my_decimal *value,
452 MYSQL_TIME *ltime,
453 date_mode_t fuzzydate,
454 const TABLE_SHARE *s, const char *field_name)
455 {
456 Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
457 s ? s->table_name.str : nullptr,
458 field_name, ltime, fuzzydate);
459 Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate);
460 return !t->is_valid_temporal();
461 }
462
463
int_to_datetime_with_warn(THD * thd,const Longlong_hybrid & nr,MYSQL_TIME * ltime,date_mode_t fuzzydate,const TABLE_SHARE * s,const char * field_name)464 bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr,
465 MYSQL_TIME *ltime,
466 date_mode_t fuzzydate,
467 const TABLE_SHARE *s, const char *field_name)
468 {
469 /*
470 Note: conversion from an integer to TIME can overflow to '838:59:59.999999',
471 so the conversion result can have fractional digits.
472 */
473 Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
474 s ? s->table_name.str : nullptr,
475 field_name, ltime, fuzzydate);
476 Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, nr, fuzzydate);
477 return !t->is_valid_temporal();
478 }
479
480
481 /*
482 Convert a datetime from broken-down MYSQL_TIME representation to
483 corresponding TIMESTAMP value.
484
485 SYNOPSIS
486 TIME_to_timestamp()
487 thd - current thread
488 t - datetime in broken-down representation,
489 error_code - 0, if the conversion was successful;
490 ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value
491 which is out of TIMESTAMP range;
492 ER_WARN_INVALID_TIMESTAMP, if t represents value which
493 doesn't exists (falls into the spring time-gap).
494
495 RETURN
496 Number seconds in UTC since start of Unix Epoch corresponding to t.
497 0 - in case of ER_WARN_DATA_OUT_OF_RANGE
498 */
499
TIME_to_timestamp(THD * thd,const MYSQL_TIME * t,uint * error_code)500 my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code)
501 {
502 thd->time_zone_used= 1;
503 return thd->variables.time_zone->TIME_to_gmt_sec(t, error_code);
504 }
505
506
507 /*
508 Convert a system time structure to TIME
509 */
510
localtime_to_TIME(MYSQL_TIME * to,struct tm * from)511 void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
512 {
513 to->neg=0;
514 to->second_part=0;
515 to->year= (int) ((from->tm_year+1900) % 10000);
516 to->month= (int) from->tm_mon+1;
517 to->day= (int) from->tm_mday;
518 to->hour= (int) from->tm_hour;
519 to->minute= (int) from->tm_min;
520 to->second= (int) from->tm_sec;
521 }
522
523
calc_time_from_sec(MYSQL_TIME * to,ulong seconds,ulong microseconds)524 void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds)
525 {
526 long t_seconds;
527 // to->neg is not cleared, it may already be set to a useful value
528 to->time_type= MYSQL_TIMESTAMP_TIME;
529 to->year= 0;
530 to->month= 0;
531 to->day= 0;
532 to->hour= seconds/3600L;
533 t_seconds= seconds%3600L;
534 to->minute= t_seconds/60L;
535 to->second= t_seconds%60L;
536 to->second_part= microseconds;
537 }
538
539
540 /*
541 Parse a format string specification
542
543 SYNOPSIS
544 parse_date_time_format()
545 format_type Format of string (time, date or datetime)
546 format_str String to parse
547 format_length Length of string
548 date_time_format Format to fill in
549
550 NOTES
551 Fills in date_time_format->positions for all date time parts.
552
553 positions marks the position for a datetime element in the format string.
554 The position array elements are in the following order:
555 YYYY-DD-MM HH-MM-DD.FFFFFF AM
556 0 1 2 3 4 5 6 7
557
558 If positions[0]= 5, it means that year will be the forth element to
559 read from the parsed date string.
560
561 RETURN
562 0 ok
563 1 error
564 */
565
parse_date_time_format(timestamp_type format_type,const char * format,uint format_length,DATE_TIME_FORMAT * date_time_format)566 bool parse_date_time_format(timestamp_type format_type,
567 const char *format, uint format_length,
568 DATE_TIME_FORMAT *date_time_format)
569 {
570 uint offset= 0, separators= 0;
571 const char *ptr= format, *format_str;
572 const char *end= ptr+format_length;
573 uchar *dt_pos= date_time_format->positions;
574 /* need_p is set if we are using AM/PM format */
575 bool need_p= 0, allow_separator= 0;
576 ulong part_map= 0, separator_map= 0;
577 const char *parts[16];
578
579 date_time_format->time_separator= 0;
580 date_time_format->flag= 0; // For future
581
582 /*
583 Fill position with 'dummy' arguments to found out if a format tag is
584 used twice (This limit's the format to 255 characters, but this is ok)
585 */
586 dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]=
587 dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= 255;
588
589 for (; ptr != end; ptr++)
590 {
591 if (*ptr == '%' && ptr+1 != end)
592 {
593 uint UNINIT_VAR(position);
594 switch (*++ptr) {
595 case 'y': // Year
596 case 'Y':
597 position= 0;
598 break;
599 case 'c': // Month
600 case 'm':
601 position= 1;
602 break;
603 case 'd':
604 case 'e':
605 position= 2;
606 break;
607 case 'h':
608 case 'I':
609 case 'l':
610 need_p= 1; // Need AM/PM
611 /* Fall through */
612 case 'k':
613 case 'H':
614 position= 3;
615 break;
616 case 'i':
617 position= 4;
618 break;
619 case 's':
620 case 'S':
621 position= 5;
622 break;
623 case 'f':
624 position= 6;
625 if (dt_pos[5] != offset-1 || ptr[-2] != '.')
626 return 1; // Wrong usage of %f
627 break;
628 case 'p': // AM/PM
629 if (offset == 0) // Can't be first
630 return 0;
631 position= 7;
632 break;
633 default:
634 return 1; // Unknown controll char
635 }
636 if (dt_pos[position] != 255) // Don't allow same tag twice
637 return 1;
638 parts[position]= ptr-1;
639
640 /*
641 If switching from time to date, ensure that all time parts
642 are used
643 */
644 if (part_map && position <= 2 && !(part_map & (1 | 2 | 4)))
645 offset=5;
646 part_map|= (ulong) 1 << position;
647 dt_pos[position]= offset++;
648 allow_separator= 1;
649 }
650 else
651 {
652 /*
653 Don't allow any characters in format as this could easily confuse
654 the date reader
655 */
656 if (!allow_separator)
657 return 1; // No separator here
658 allow_separator= 0; // Don't allow two separators
659 separators++;
660 /* Store in separator_map which parts are punct characters */
661 if (my_ispunct(&my_charset_latin1, *ptr))
662 separator_map|= (ulong) 1 << (offset-1);
663 else if (!my_isspace(&my_charset_latin1, *ptr))
664 return 1;
665 }
666 }
667
668 /* If no %f, specify it after seconds. Move %p up, if necessary */
669 if ((part_map & 32) && !(part_map & 64))
670 {
671 dt_pos[6]= dt_pos[5] +1;
672 parts[6]= parts[5]; // For later test in (need_p)
673 if (dt_pos[6] == dt_pos[7]) // Move %p one step up if used
674 dt_pos[7]++;
675 }
676
677 /*
678 Check that we have not used a non legal format specifier and that all
679 format specifiers have been used
680
681 The last test is to ensure that %p is used if and only if
682 it's needed.
683 */
684 if ((format_type == MYSQL_TIMESTAMP_DATETIME &&
685 !test_all_bits(part_map, (1 | 2 | 4 | 8 | 16 | 32))) ||
686 (format_type == MYSQL_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
687 (format_type == MYSQL_TIMESTAMP_TIME &&
688 !test_all_bits(part_map, 8 | 16 | 32)) ||
689 !allow_separator || // %option should be last
690 (need_p && dt_pos[6] +1 != dt_pos[7]) ||
691 (need_p ^ (dt_pos[7] != 255)))
692 return 1;
693
694 if (dt_pos[6] != 255) // If fractional seconds
695 {
696 /* remove fractional seconds from later tests */
697 uint pos= dt_pos[6] -1;
698 /* Remove separator before %f from sep map */
699 separator_map= ((separator_map & ((ulong) (1 << pos)-1)) |
700 ((separator_map & ~((ulong) (1 << pos)-1)) >> 1));
701 if (part_map & 64)
702 {
703 separators--; // There is always a separator
704 need_p= 1; // force use of separators
705 }
706 }
707
708 /*
709 Remove possible separator before %p from sep_map
710 (This can either be at position 3, 4, 6 or 7) h.m.d.%f %p
711 */
712 if (dt_pos[7] != 255)
713 {
714 if (need_p && parts[7] != parts[6]+2)
715 separators--;
716 }
717 /*
718 Calculate if %p is in first or last part of the datetime field
719
720 At this point we have either %H-%i-%s %p 'year parts' or
721 'year parts' &H-%i-%s %p" as %f was removed above
722 */
723 offset= dt_pos[6] <= 3 ? 3 : 6;
724 /* Remove separator before %p from sep map */
725 separator_map= ((separator_map & ((ulong) (1 << offset)-1)) |
726 ((separator_map & ~((ulong) (1 << offset)-1)) >> 1));
727
728 format_str= 0;
729 switch (format_type) {
730 case MYSQL_TIMESTAMP_DATE:
731 format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
732 /* fall through */
733 case MYSQL_TIMESTAMP_TIME:
734 if (!format_str)
735 format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
736
737 /*
738 If there is no separators, allow the internal format as we can read
739 this. If separators are used, they must be between each part
740 */
741 if (format_length == 6 && !need_p &&
742 !my_strnncoll(&my_charset_bin,
743 (const uchar *) format, 6,
744 (const uchar *) format_str, 6))
745 return 0;
746 if (separator_map == (1 | 2))
747 {
748 if (format_type == MYSQL_TIMESTAMP_TIME)
749 {
750 if (*(format+2) != *(format+5))
751 break; // Error
752 /* Store the character used for time formats */
753 date_time_format->time_separator= *(format+2);
754 }
755 return 0;
756 }
757 break;
758 case MYSQL_TIMESTAMP_DATETIME:
759 /*
760 If there is no separators, allow the internal format as we can read
761 this. If separators are used, they must be between each part.
762 Between DATE and TIME we also allow space as separator
763 */
764 if ((format_length == 12 && !need_p &&
765 !my_strnncoll(&my_charset_bin,
766 (const uchar *) format, 12,
767 (const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
768 12)) ||
769 (separators == 5 && separator_map == (1 | 2 | 8 | 16)))
770 return 0;
771 break;
772 default:
773 DBUG_ASSERT(0);
774 break;
775 }
776 return 1; // Error
777 }
778
779
780 /*
781 Create a DATE_TIME_FORMAT object from a format string specification
782
783 SYNOPSIS
784 date_time_format_make()
785 format_type Format to parse (time, date or datetime)
786 format_str String to parse
787 format_length Length of string
788
789 NOTES
790 The returned object should be freed with my_free()
791
792 RETURN
793 NULL ponter: Error
794 new object
795 */
796
797 DATE_TIME_FORMAT
date_time_format_make(timestamp_type format_type,const char * format_str,uint format_length)798 *date_time_format_make(timestamp_type format_type,
799 const char *format_str, uint format_length)
800 {
801 DATE_TIME_FORMAT tmp;
802
803 if (format_length && format_length < 255 &&
804 !parse_date_time_format(format_type, format_str,
805 format_length, &tmp))
806 {
807 tmp.format.str= format_str;
808 tmp.format.length= format_length;
809 return date_time_format_copy((THD *)0, &tmp);
810 }
811 return 0;
812 }
813
814
815 /*
816 Create a copy of a DATE_TIME_FORMAT object
817
818 SYNOPSIS
819 date_and_time_format_copy()
820 thd Set if variable should be allocated in thread mem
821 format format to copy
822
823 NOTES
824 The returned object should be freed with my_free()
825
826 RETURN
827 NULL ponter: Error
828 new object
829 */
830
date_time_format_copy(THD * thd,DATE_TIME_FORMAT * format)831 DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
832 {
833 DATE_TIME_FORMAT *new_format;
834 size_t length= sizeof(*format) + format->format.length + 1;
835 char *format_pos;
836
837 if (thd)
838 new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
839 else
840 new_format= (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
841 if (new_format)
842 {
843 /* Put format string after current pos */
844 new_format->format.str= format_pos= (char*) (new_format+1);
845 memcpy((char*) new_format->positions, (char*) format->positions,
846 sizeof(format->positions));
847 new_format->time_separator= format->time_separator;
848 /* We make the string null terminated for easy printf in SHOW VARIABLES */
849 memcpy(format_pos, format->format.str, format->format.length);
850 format_pos[format->format.length]= 0;
851 new_format->format.length= format->format.length;
852 }
853 return new_format;
854 }
855
856
857 KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
858 {
859 {"USA", "%m.%d.%Y", "%Y-%m-%d %H.%i.%s", "%h:%i:%s %p" },
860 {"JIS", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
861 {"ISO", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
862 {"EUR", "%d.%m.%Y", "%Y-%m-%d %H.%i.%s", "%H.%i.%s" },
863 {"INTERNAL", "%Y%m%d", "%Y%m%d%H%i%s", "%H%i%s" },
864 { 0, 0, 0, 0 }
865 };
866
867
get_date_time_format_str(KNOWN_DATE_TIME_FORMAT * format,timestamp_type type)868 const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
869 timestamp_type type)
870 {
871 switch (type) {
872 case MYSQL_TIMESTAMP_DATE:
873 return format->date_format;
874 case MYSQL_TIMESTAMP_DATETIME:
875 return format->datetime_format;
876 case MYSQL_TIMESTAMP_TIME:
877 return format->time_format;
878 default:
879 DBUG_ASSERT(0); // Impossible
880 return 0;
881 }
882 }
883
884
885 /**
886 Convert TIME/DATE/DATETIME value to String.
887 @param l_time DATE value
888 @param OUT str String to convert to
889 @param dec Number of fractional digits.
890 */
my_TIME_to_str(const MYSQL_TIME * ltime,String * str,uint dec)891 bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec)
892 {
893 if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
894 return true;
895 str->set_charset(&my_charset_numeric);
896 str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), dec));
897 return false;
898 }
899
900
make_truncated_value_warning(THD * thd,Sql_condition::enum_warning_level level,const ErrConv * sval,timestamp_type time_type,const char * db_name,const char * table_name,const char * field_name)901 void make_truncated_value_warning(THD *thd,
902 Sql_condition::enum_warning_level level,
903 const ErrConv *sval,
904 timestamp_type time_type,
905 const char *db_name, const char *table_name,
906 const char *field_name)
907 {
908 const char *type_str= Temporal::type_name_by_timestamp_type(time_type);
909 return thd->push_warning_wrong_or_truncated_value
910 (level, time_type <= MYSQL_TIMESTAMP_ERROR, type_str, sval->ptr(),
911 db_name, table_name, field_name);
912 }
913
914
915 /* Daynumber from year 0 to 9999-12-31 */
916 #define COMBINE(X) \
917 (((((X)->day * 24LL + (X)->hour) * 60LL + \
918 (X)->minute) * 60LL + (X)->second)*1000000LL + \
919 (X)->second_part)
920 #define GET_PART(X, N) X % N ## LL; X/= N ## LL
921
date_add_interval(THD * thd,MYSQL_TIME * ltime,interval_type int_type,const INTERVAL & interval,bool push_warn)922 bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type,
923 const INTERVAL &interval, bool push_warn)
924 {
925 long period, sign;
926
927 sign= (interval.neg == (bool)ltime->neg ? 1 : -1);
928
929 switch (int_type) {
930 case INTERVAL_SECOND:
931 case INTERVAL_SECOND_MICROSECOND:
932 case INTERVAL_MICROSECOND:
933 case INTERVAL_MINUTE:
934 case INTERVAL_HOUR:
935 case INTERVAL_MINUTE_MICROSECOND:
936 case INTERVAL_MINUTE_SECOND:
937 case INTERVAL_HOUR_MICROSECOND:
938 case INTERVAL_HOUR_SECOND:
939 case INTERVAL_HOUR_MINUTE:
940 case INTERVAL_DAY_MICROSECOND:
941 case INTERVAL_DAY_SECOND:
942 case INTERVAL_DAY_MINUTE:
943 case INTERVAL_DAY_HOUR:
944 case INTERVAL_DAY:
945 {
946 longlong usec, daynr;
947 my_bool neg= 0;
948 enum enum_mysql_timestamp_type time_type= ltime->time_type;
949
950 if (((ulonglong) interval.day +
951 (ulonglong) interval.hour / 24 +
952 (ulonglong) interval.minute / 24 / 60 +
953 (ulonglong) interval.second / 24 / 60 / 60) > MAX_DAY_NUMBER)
954 goto invalid_date;
955
956 if (time_type != MYSQL_TIMESTAMP_TIME)
957 ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1;
958
959 usec= COMBINE(ltime) + sign*COMBINE(&interval);
960
961 if (usec < 0)
962 {
963 neg= 1;
964 usec= -usec;
965 }
966
967 ltime->second_part= GET_PART(usec, 1000000);
968 ltime->second= GET_PART(usec, 60);
969 ltime->minute= GET_PART(usec, 60);
970 ltime->neg^= neg;
971
972 if (time_type == MYSQL_TIMESTAMP_TIME)
973 {
974 if (usec > TIME_MAX_HOUR)
975 goto invalid_date;
976 ltime->hour= static_cast<uint>(usec);
977 ltime->day= 0;
978 return 0;
979 }
980 else if (ltime->neg)
981 goto invalid_date;
982
983 if (int_type != INTERVAL_DAY)
984 ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
985
986 ltime->hour= GET_PART(usec, 24);
987 daynr= usec;
988
989 /* Day number from year 0 to 9999-12-31 */
990 if (get_date_from_daynr((long) daynr, <ime->year, <ime->month,
991 <ime->day))
992 goto invalid_date;
993 break;
994 }
995 case INTERVAL_WEEK:
996 period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
997 sign * (long) interval.day);
998 /* Daynumber from year 0 to 9999-12-31 */
999 if (get_date_from_daynr((long) period,<ime->year,<ime->month,
1000 <ime->day))
1001 goto invalid_date;
1002 break;
1003 case INTERVAL_YEAR:
1004 ltime->year+= sign * (long) interval.year;
1005 if ((ulong) ltime->year >= 10000L)
1006 goto invalid_date;
1007 if (ltime->month == 2 && ltime->day == 29 &&
1008 calc_days_in_year(ltime->year) != 366)
1009 ltime->day=28; // Was leap-year
1010 break;
1011 case INTERVAL_YEAR_MONTH:
1012 case INTERVAL_QUARTER:
1013 case INTERVAL_MONTH:
1014 period= (ltime->year*12 + sign * (long) interval.year*12 +
1015 ltime->month-1 + sign * (long) interval.month);
1016 if ((ulong) period >= 120000L)
1017 goto invalid_date;
1018 ltime->year= (uint) (period / 12);
1019 ltime->month= (uint) (period % 12L)+1;
1020 /* Adjust day if the new month doesn't have enough days */
1021 if (ltime->day > days_in_month[ltime->month-1])
1022 {
1023 ltime->day = days_in_month[ltime->month-1];
1024 if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
1025 ltime->day++; // Leap-year
1026 }
1027 break;
1028 default:
1029 goto null_date;
1030 }
1031
1032 if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
1033 return 0; // Ok
1034
1035 invalid_date:
1036 if (push_warn)
1037 {
1038 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
1039 ER_DATETIME_FUNCTION_OVERFLOW,
1040 ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
1041 ltime->time_type == MYSQL_TIMESTAMP_TIME ?
1042 "time" : "datetime");
1043 }
1044 null_date:
1045 return 1;
1046 }
1047
1048
1049 /*
1050 Calculate difference between two datetime values as seconds + microseconds.
1051
1052 SYNOPSIS
1053 calc_time_diff()
1054 l_time1 - TIME/DATE/DATETIME value
1055 l_time2 - TIME/DATE/DATETIME value
1056 l_sign - 1 absolute values are substracted,
1057 -1 absolute values are added.
1058 seconds_out - Out parameter where difference between
1059 l_time1 and l_time2 in seconds is stored.
1060 microseconds_out- Out parameter where microsecond part of difference
1061 between l_time1 and l_time2 is stored.
1062
1063 NOTE
1064 This function calculates difference between l_time1 and l_time2 absolute
1065 values. So one should set l_sign and correct result if he want to take
1066 signs into account (i.e. for MYSQL_TIME values).
1067
1068 RETURN VALUES
1069 Returns sign of difference.
1070 1 means negative result
1071 0 means positive result
1072
1073 */
1074
1075 bool
calc_time_diff(const MYSQL_TIME * l_time1,const MYSQL_TIME * l_time2,int l_sign,ulonglong * seconds_out,ulong * microseconds_out)1076 calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
1077 int l_sign, ulonglong *seconds_out, ulong *microseconds_out)
1078 {
1079 long days;
1080 bool neg;
1081 longlong microseconds;
1082
1083 /*
1084 We suppose that if first argument is MYSQL_TIMESTAMP_TIME
1085 the second argument should be TIMESTAMP_TIME also.
1086 We should check it before calc_time_diff call.
1087 */
1088 if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
1089 days= (long)l_time1->day - l_sign * (long)l_time2->day;
1090 else
1091 {
1092 days= calc_daynr((uint) l_time1->year,
1093 (uint) l_time1->month,
1094 (uint) l_time1->day);
1095 if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
1096 days-= l_sign * (long)l_time2->day;
1097 else
1098 days-= l_sign*calc_daynr((uint) l_time2->year,
1099 (uint) l_time2->month,
1100 (uint) l_time2->day);
1101 }
1102
1103 microseconds= ((longlong)days * SECONDS_IN_24H +
1104 (longlong)(l_time1->hour*3600LL +
1105 l_time1->minute*60L +
1106 l_time1->second) -
1107 l_sign*(longlong)(l_time2->hour*3600LL +
1108 l_time2->minute*60L +
1109 l_time2->second)) * 1000000LL +
1110 (longlong)l_time1->second_part -
1111 l_sign*(longlong)l_time2->second_part;
1112
1113 neg= 0;
1114 if (microseconds < 0)
1115 {
1116 microseconds= -microseconds;
1117 neg= 1;
1118 }
1119 *seconds_out= (ulonglong) microseconds/1000000L;
1120 *microseconds_out= (ulong) (microseconds%1000000L);
1121 return neg;
1122 }
1123
1124
calc_time_diff(const MYSQL_TIME * l_time1,const MYSQL_TIME * l_time2,int l_sign,MYSQL_TIME * l_time3,date_mode_t fuzzydate)1125 bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
1126 int l_sign, MYSQL_TIME *l_time3, date_mode_t fuzzydate)
1127 {
1128 ulonglong seconds;
1129 ulong microseconds;
1130 bzero((char *) l_time3, sizeof(*l_time3));
1131 l_time3->neg= calc_time_diff(l_time1, l_time2, l_sign,
1132 &seconds, µseconds);
1133 /*
1134 For MYSQL_TIMESTAMP_TIME only:
1135 If first argument was negative and diff between arguments
1136 is non-zero we need to swap sign to get proper result.
1137 */
1138 if (l_time1->neg && (seconds || microseconds))
1139 l_time3->neg= 1 - l_time3->neg; // Swap sign of result
1140
1141 /*
1142 seconds is longlong, when casted to long it may become a small number
1143 even if the original seconds value was too large and invalid.
1144 as a workaround we limit seconds by a large invalid long number
1145 ("invalid" means > TIME_MAX_SECOND)
1146 */
1147 set_if_smaller(seconds, INT_MAX32);
1148 calc_time_from_sec(l_time3, (ulong) seconds, microseconds);
1149 return ((fuzzydate & TIME_NO_ZERO_DATE) && (seconds == 0) &&
1150 (microseconds == 0));
1151 }
1152
1153
1154 /*
1155 Compares 2 MYSQL_TIME structures
1156
1157 SYNOPSIS
1158 my_time_compare()
1159
1160 a - first time
1161 b - second time
1162
1163 RETURN VALUE
1164 -1 - a < b
1165 0 - a == b
1166 1 - a > b
1167
1168 */
1169
my_time_compare(const MYSQL_TIME * a,const MYSQL_TIME * b)1170 int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b)
1171 {
1172 ulonglong a_t= pack_time(a);
1173 ulonglong b_t= pack_time(b);
1174
1175 if (a_t < b_t)
1176 return -1;
1177 if (a_t > b_t)
1178 return 1;
1179
1180 return 0;
1181 }
1182
1183
1184 /**
1185 Convert TIME to DATETIME.
1186 @param ltime The value to convert.
1187 @return false on success, true of error (negative time).
1188 */
time_to_datetime(MYSQL_TIME * ltime)1189 bool time_to_datetime(MYSQL_TIME *ltime)
1190 {
1191 DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME);
1192 DBUG_ASSERT(ltime->year == 0);
1193 DBUG_ASSERT(ltime->month == 0);
1194 DBUG_ASSERT(ltime->day == 0);
1195 if (ltime->neg)
1196 return true;
1197 uint day= ltime->hour / 24;
1198 ltime->hour%= 24;
1199 ltime->month= day / 31;
1200 ltime->day= day % 31;
1201 return false;
1202 }
1203
1204
1205 /*** Conversion from TIME to DATETIME ***/
1206
1207 /*
1208 Simple case: TIME is within normal 24 hours internal.
1209 Mix DATE part of ldate and TIME part of ltime together.
1210 */
1211 static void
mix_date_and_time_simple(MYSQL_TIME * ldate,const MYSQL_TIME * ltime)1212 mix_date_and_time_simple(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
1213 {
1214 DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
1215 ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
1216 ldate->hour= ltime->hour;
1217 ldate->minute= ltime->minute;
1218 ldate->second= ltime->second;
1219 ldate->second_part= ltime->second_part;
1220 ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
1221 }
1222
1223
1224 /*
1225 Complex case: TIME is negative or outside of the 24 hour interval.
1226 */
1227 static void
mix_date_and_time_complex(MYSQL_TIME * ldate,const MYSQL_TIME * ltime)1228 mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
1229 {
1230 DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
1231 ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
1232 ulonglong seconds;
1233 ulong days, useconds;
1234 int sign= ltime->neg ? 1 : -1;
1235 ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds);
1236
1237 DBUG_ASSERT(!ldate->neg);
1238 DBUG_ASSERT(ldate->year > 0);
1239
1240 days= (long) (seconds / SECONDS_IN_24H);
1241 calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds);
1242 get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day);
1243 ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
1244 }
1245
1246
1247 /**
1248 Mix a date value and a time value.
1249
1250 @param IN/OUT ldate Date value.
1251 @param ltime Time value.
1252 */
1253 static void
mix_date_and_time(MYSQL_TIME * to,const MYSQL_TIME * from)1254 mix_date_and_time(MYSQL_TIME *to, const MYSQL_TIME *from)
1255 {
1256 if (!from->neg && from->hour < 24)
1257 mix_date_and_time_simple(to, from);
1258 else
1259 mix_date_and_time_complex(to, from);
1260 }
1261
1262
1263 /**
1264 Get current date in DATE format
1265 */
set_current_date(THD * thd,MYSQL_TIME * to)1266 void set_current_date(THD *thd, MYSQL_TIME *to)
1267 {
1268 thd->variables.time_zone->gmt_sec_to_TIME(to, thd->query_start());
1269 thd->time_zone_used= 1;
1270 datetime_to_date(to);
1271 }
1272
1273
1274 /**
1275 5.5 compatible conversion from TIME to DATETIME
1276 */
1277 static bool
time_to_datetime_old(THD * thd,const MYSQL_TIME * from,MYSQL_TIME * to)1278 time_to_datetime_old(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
1279 {
1280 DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
1281
1282 if (from->neg)
1283 return true;
1284
1285 /* Set the date part */
1286 uint day= from->hour / 24;
1287 to->day= day % 31;
1288 to->month= day / 31;
1289 to->year= 0;
1290 /* Set the time part */
1291 to->hour= from->hour % 24;
1292 to->minute= from->minute;
1293 to->second= from->second;
1294 to->second_part= from->second_part;
1295 /* set sign and type */
1296 to->neg= 0;
1297 to->time_type= MYSQL_TIMESTAMP_DATETIME;
1298 return false;
1299 }
1300
1301
1302 /**
1303 Convert time to datetime.
1304
1305 The time value is added to the current datetime value.
1306 @param IN ltime Time value to convert from.
1307 @param OUT ltime2 Datetime value to convert to.
1308 */
1309 bool
time_to_datetime(THD * thd,const MYSQL_TIME * from,MYSQL_TIME * to)1310 time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
1311 {
1312 if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)
1313 return time_to_datetime_old(thd, from, to);
1314 set_current_date(thd, to);
1315 mix_date_and_time(to, from);
1316 return false;
1317 }
1318
1319
1320 bool
time_to_datetime_with_warn(THD * thd,const MYSQL_TIME * from,MYSQL_TIME * to,date_conv_mode_t fuzzydate)1321 time_to_datetime_with_warn(THD *thd,
1322 const MYSQL_TIME *from, MYSQL_TIME *to,
1323 date_conv_mode_t fuzzydate)
1324 {
1325 int warn= 0;
1326 DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
1327 /*
1328 After time_to_datetime() we need to do check_date(), as
1329 the caller may want TIME_NO_ZERO_DATE or TIME_NO_ZERO_IN_DATE.
1330 Note, the SQL standard time->datetime conversion mode always returns
1331 a valid date based on CURRENT_DATE. So we need to do check_date()
1332 only in the old mode.
1333 */
1334 if (time_to_datetime(thd, from, to) ||
1335 ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) &&
1336 check_date(to, fuzzydate, &warn)))
1337 {
1338 ErrConvTime str(from);
1339 thd->push_warning_truncated_wrong_value("datetime", str.ptr());
1340 return true;
1341 }
1342 return false;
1343 }
1344
1345
pack_time(const MYSQL_TIME * my_time)1346 longlong pack_time(const MYSQL_TIME *my_time)
1347 {
1348 return ((((((my_time->year * 13ULL +
1349 my_time->month) * 32ULL +
1350 my_time->day) * 24ULL +
1351 my_time->hour) * 60ULL +
1352 my_time->minute) * 60ULL +
1353 my_time->second) * 1000000ULL +
1354 my_time->second_part) * (my_time->neg ? -1 : 1);
1355 }
1356
1357 #define get_one(WHERE, FACTOR) WHERE= (ulong)(packed % FACTOR); packed/= FACTOR
1358
unpack_time(longlong packed,MYSQL_TIME * my_time,enum_mysql_timestamp_type ts_type)1359 void unpack_time(longlong packed, MYSQL_TIME *my_time,
1360 enum_mysql_timestamp_type ts_type)
1361 {
1362 if ((my_time->neg= packed < 0))
1363 packed= -packed;
1364 get_one(my_time->second_part, 1000000ULL);
1365 get_one(my_time->second, 60U);
1366 get_one(my_time->minute, 60U);
1367 get_one(my_time->hour, 24U);
1368 get_one(my_time->day, 32U);
1369 get_one(my_time->month, 13U);
1370 my_time->year= (uint)packed;
1371 my_time->time_type= ts_type;
1372 switch (ts_type) {
1373 case MYSQL_TIMESTAMP_TIME:
1374 my_time->hour+= (my_time->month * 32 + my_time->day) * 24;
1375 my_time->month= my_time->day= 0;
1376 break;
1377 case MYSQL_TIMESTAMP_DATE:
1378 my_time->hour= my_time->minute= my_time->second= my_time->second_part= 0;
1379 break;
1380 case MYSQL_TIMESTAMP_NONE:
1381 case MYSQL_TIMESTAMP_ERROR:
1382 DBUG_ASSERT(0);
1383 case MYSQL_TIMESTAMP_DATETIME:
1384 break;
1385 }
1386 }
1387