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