1 /* Copyright (c) 2004, 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 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28 #include <my_time.h>
29 #include <m_string.h>
30 #include <m_ctype.h>
31 #include <myisampack.h>
32 /* Windows version of localtime_r() is declared in my_global.h */
33 #include <my_global.h>
34 #include "binary_log_types.h"
35
36 ulonglong log_10_int[20]=
37 {
38 1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
39 100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
40 1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
41 1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
42 1000000000000000000ULL, 10000000000000000000ULL
43 };
44
45
46 const char my_zero_datetime6[]= "0000-00-00 00:00:00.000000";
47
48
49 /* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
50
51 static uchar internal_format_positions[]=
52 {0, 1, 2, 3, 4, 5, 6, (uchar) 255};
53
54 static char time_separator=':';
55
56 static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */
57 uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
58
59 /*
60 Offset of system time zone from UTC in seconds used to speed up
61 work of my_system_gmt_sec() function.
62 */
63 static long my_time_zone=0;
64
65
66 /* Calc days in one year. works with 0 <= year <= 99 */
67
calc_days_in_year(uint year)68 uint calc_days_in_year(uint year)
69 {
70 return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
71 366 : 365);
72 }
73
74
75 /**
76 Set MYSQL_TIME structure to 0000-00-00 00:00:00.000000
77 @param tm[OUT] The value to set.
78 @param time_type Timestasmp type
79 */
set_zero_time(MYSQL_TIME * tm,enum enum_mysql_timestamp_type time_type)80 void set_zero_time(MYSQL_TIME *tm,
81 enum enum_mysql_timestamp_type time_type)
82 {
83 memset(tm, 0, sizeof(*tm));
84 tm->time_type= time_type;
85 }
86
87
88 /**
89 Set hour, minute and second of a MYSQL_TIME variable to maximum time value.
90 Unlike set_max_time(), does not touch the other structure members.
91 */
set_max_hhmmss(MYSQL_TIME * tm)92 void set_max_hhmmss(MYSQL_TIME *tm)
93 {
94 tm->hour= TIME_MAX_HOUR;
95 tm->minute= TIME_MAX_MINUTE;
96 tm->second= TIME_MAX_SECOND;
97 }
98
99
100 /**
101 Set MYSQL_TIME variable to maximum time value
102 @param tm OUT The variable to set.
103 @param neg Sign: 1 if negative, 0 if positive.
104 */
set_max_time(MYSQL_TIME * tm,my_bool neg)105 void set_max_time(MYSQL_TIME *tm, my_bool neg)
106 {
107 set_zero_time(tm, MYSQL_TIMESTAMP_TIME);
108 set_max_hhmmss(tm);
109 tm->neg= neg;
110 }
111
112
113 /**
114 @brief Check datetime value for validity according to flags.
115
116 @param[in] ltime Date to check.
117 @param[in] not_zero_date ltime is not the zero date
118 @param[in] flags flags to check
119 (see str_to_datetime() flags in my_time.h)
120 @param[out] was_cut set to 2 if value was invalid according to flags.
121 (Feb 29 in non-leap etc.) This remains unchanged
122 if value is not invalid.
123 @details Here we assume that year and month is ok!
124 If month is 0 we allow any date. (This only happens if we allow zero
125 date parts in str_to_datetime())
126 Disallow dates with zero year and non-zero month and/or day.
127
128 @return
129 0 OK
130 1 error
131 */
132
check_date(const MYSQL_TIME * ltime,my_bool not_zero_date,my_time_flags_t flags,int * was_cut)133 my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
134 my_time_flags_t flags, int *was_cut)
135 {
136 if (not_zero_date)
137 {
138 if (((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
139 (ltime->month == 0 || ltime->day == 0))
140 {
141 *was_cut= MYSQL_TIME_WARN_ZERO_IN_DATE;
142 return TRUE;
143 }
144 else if ((!(flags & TIME_INVALID_DATES) &&
145 ltime->month && ltime->day > days_in_month[ltime->month-1] &&
146 (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
147 ltime->day != 29)))
148 {
149 *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
150 return TRUE;
151 }
152 }
153 else if (flags & TIME_NO_ZERO_DATE)
154 {
155 *was_cut= MYSQL_TIME_WARN_ZERO_DATE;
156 return TRUE;
157 }
158 return FALSE;
159 }
160
161
162 /**
163 Check if TIME fields are fatally bad and cannot be further adjusted.
164 @param ltime Time value.
165 @retval TRUE if the value is fatally bad.
166 @retval FALSE if the value is Ok.
167 */
check_time_mmssff_range(const MYSQL_TIME * ltime)168 my_bool check_time_mmssff_range(const MYSQL_TIME *ltime)
169 {
170 return ltime->minute >= 60 || ltime->second >= 60 ||
171 ltime->second_part > 999999;
172 }
173
174
175 /**
176 Check TIME range. The value can include day part,
177 for example: '1 10:20:30.123456'.
178
179 minute, second and second_part values are not checked
180 unless hour is equal TIME_MAX_HOUR.
181
182 @param ltime Rime value.
183 @returns Test result.
184 @retval FALSE if value is Ok.
185 @retval TRUE if value is out of range.
186 */
check_time_range_quick(const MYSQL_TIME * ltime)187 my_bool check_time_range_quick(const MYSQL_TIME *ltime)
188 {
189 longlong hour= (longlong) ltime->hour + 24LL * ltime->day;
190 /* The input value should not be fatally bad */
191 assert(!check_time_mmssff_range(ltime));
192 if (hour <= TIME_MAX_HOUR &&
193 (hour != TIME_MAX_HOUR || ltime->minute != TIME_MAX_MINUTE ||
194 ltime->second != TIME_MAX_SECOND || !ltime->second_part))
195 return FALSE;
196 return TRUE;
197 }
198
199
200 /**
201 Check datetime, date, or normalized time (i.e. time without days) range.
202 @param ltime Datetime value.
203 @returns
204 @retval FALSE on success
205 @retval TRUE on error
206 */
check_datetime_range(const MYSQL_TIME * ltime)207 my_bool check_datetime_range(const MYSQL_TIME *ltime)
208 {
209 /*
210 In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
211 In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
212 */
213 return
214 ltime->year > 9999U || ltime->month > 12U || ltime->day > 31U ||
215 ltime->minute > 59U || ltime->second > 59U || ltime->second_part > 999999U ||
216 (ltime->hour >
217 (ltime->time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23U));
218 }
219
220
221 /*
222 Convert a timestamp string to a MYSQL_TIME value.
223
224 SYNOPSIS
225 str_to_datetime()
226 str String to parse
227 length Length of string
228 l_time Date is stored here
229 flags Bitmap of following items
230 TIME_FUZZY_DATE Set if we should allow partial dates
231 TIME_DATETIME_ONLY Set if we only allow full datetimes.
232 TIME_NO_ZERO_IN_DATE Don't allow partial dates
233 TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
234 TIME_INVALID_DATES Allow 2000-02-31
235 status Conversion status
236
237
238 DESCRIPTION
239 At least the following formats are recogniced (based on number of digits)
240 YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
241 YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
242 YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
243 Also dates where all parts are zero are allowed
244
245 The second part may have an optional .###### fraction part.
246
247 NOTES
248 This function should work with a format position vector as long as the
249 following things holds:
250 - All date are kept together and all time parts are kept together
251 - Date and time parts must be separated by blank
252 - Second fractions must come after second part and be separated
253 by a '.'. (The second fractions are optional)
254 - AM/PM must come after second fractions (or after seconds if no fractions)
255 - Year must always been specified.
256 - If time is before date, then we will use datetime format only if
257 the argument consist of two parts, separated by space.
258 Otherwise we will assume the argument is a date.
259 - The hour part must be specified in hour-minute-second order.
260
261 status->warnings is set to:
262 0 Value OK
263 MYSQL_TIME_WARN_TRUNCATED If value was cut during conversion
264 MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
265
266 l_time->time_type is set as follows:
267 MYSQL_TIMESTAMP_NONE String wasn't a timestamp, like
268 [DD [HH:[MM:[SS]]]].fraction.
269 l_time is not changed.
270 MYSQL_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
271 MYSQL_TIMESTAMP_DATETIME Full timestamp
272 MYSQL_TIMESTAMP_ERROR Timestamp with wrong values.
273 All elements in l_time is set to 0
274 RETURN VALUES
275 0 - Ok
276 1 - Error
277 */
278
279 #define MAX_DATE_PARTS 8
280
281 my_bool
str_to_datetime(const char * str,size_t length,MYSQL_TIME * l_time,my_time_flags_t flags,MYSQL_TIME_STATUS * status)282 str_to_datetime(const char *str, size_t length, MYSQL_TIME *l_time,
283 my_time_flags_t flags, MYSQL_TIME_STATUS *status)
284 {
285 uint field_length= 0, year_length= 0, digits, i, number_of_fields;
286 uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
287 uint add_hours= 0, start_loop;
288 ulong not_zero_date, allow_space;
289 my_bool is_internal_format;
290 const char *pos, *last_field_pos= NULL;
291 const char *end=str+length;
292 const uchar *format_position;
293 my_bool found_delimitier= 0, found_space= 0;
294 uint frac_pos, frac_len;
295 DBUG_ENTER("str_to_datetime");
296 DBUG_PRINT("ENTER", ("str: %.*s", (int)length, str));
297
298 my_time_status_init(status);
299
300 /* Skip space at start */
301 for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
302 ;
303 if (str == end || ! my_isdigit(&my_charset_latin1, *str))
304 {
305 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
306 l_time->time_type= MYSQL_TIMESTAMP_NONE;
307 DBUG_RETURN(1);
308 }
309
310 is_internal_format= 0;
311 /* This has to be changed if want to activate different timestamp formats */
312 format_position= internal_format_positions;
313
314 /*
315 Calculate number of digits in first part.
316 If length= 8 or >= 14 then year is of format YYYY.
317 (YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
318 */
319 for (pos=str;
320 pos != end && (my_isdigit(&my_charset_latin1,*pos) || *pos == 'T');
321 pos++)
322 ;
323
324 digits= (uint) (pos-str);
325 start_loop= 0; /* Start of scan loop */
326 date_len[format_position[0]]= 0; /* Length of year field */
327 if (pos == end || *pos == '.')
328 {
329 /* Found date in internal format (only numbers like YYYYMMDD) */
330 year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
331 field_length= year_length;
332 is_internal_format= 1;
333 format_position= internal_format_positions;
334 }
335 else
336 {
337 if (format_position[0] >= 3) /* If year is after HHMMDD */
338 {
339 /*
340 If year is not in first part then we have to determinate if we got
341 a date field or a datetime field.
342 We do this by checking if there is two numbers separated by
343 space in the input.
344 */
345 while (pos < end && !my_isspace(&my_charset_latin1, *pos))
346 pos++;
347 while (pos < end && !my_isdigit(&my_charset_latin1, *pos))
348 pos++;
349 if (pos == end)
350 {
351 if (flags & TIME_DATETIME_ONLY)
352 {
353 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
354 l_time->time_type= MYSQL_TIMESTAMP_NONE;
355 DBUG_RETURN(1); /* Can't be a full datetime */
356 }
357 /* Date field. Set hour, minutes and seconds to 0 */
358 date[0]= date[1]= date[2]= date[3]= date[4]= 0;
359 start_loop= 5; /* Start with first date part */
360 }
361 }
362
363 field_length= format_position[0] == 0 ? 4 : 2;
364 }
365
366 /*
367 Only allow space in the first "part" of the datetime field and:
368 - after days, part seconds
369 - before and after AM/PM (handled by code later)
370
371 2003-03-03 20:00:20 AM
372 20:00:20.000000 AM 03-03-2000
373 */
374 i= MY_MAX((uint) format_position[0], (uint) format_position[1]);
375 set_if_bigger(i, (uint) format_position[2]);
376 allow_space= ((1 << i) | (1 << format_position[6]));
377 allow_space&= (1 | 2 | 4 | 8 | 64);
378
379 not_zero_date= 0;
380 for (i = start_loop;
381 i < MAX_DATE_PARTS-1 && str != end &&
382 my_isdigit(&my_charset_latin1,*str);
383 i++)
384 {
385 const char *start= str;
386 ulong tmp_value= (uint) (uchar) (*str++ - '0');
387
388 /*
389 Internal format means no delimiters; every field has a fixed
390 width. Otherwise, we scan until we find a delimiter and discard
391 leading zeroes -- except for the microsecond part, where leading
392 zeroes are significant, and where we never process more than six
393 digits.
394 */
395 my_bool scan_until_delim= !is_internal_format &&
396 ((i != format_position[6]));
397
398 while (str != end && my_isdigit(&my_charset_latin1,str[0]) &&
399 (scan_until_delim || --field_length))
400 {
401 tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0');
402 str++;
403 }
404 date_len[i]= (uint) (str - start);
405 if (tmp_value > 999999) /* Impossible date part */
406 {
407 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
408 l_time->time_type= MYSQL_TIMESTAMP_NONE;
409 DBUG_RETURN(1);
410 }
411 date[i]=tmp_value;
412 not_zero_date|= tmp_value;
413
414 /* Length of next field */
415 field_length= format_position[i+1] == 0 ? 4 : 2;
416
417 if ((last_field_pos= str) == end)
418 {
419 i++; /* Register last found part */
420 break;
421 }
422 /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
423 if (i == format_position[2] && *str == 'T')
424 {
425 str++; /* ISO8601: CCYYMMDDThhmmss */
426 continue;
427 }
428 if (i == format_position[5]) /* Seconds */
429 {
430 if (*str == '.') /* Followed by part seconds */
431 {
432 str++;
433 /*
434 Shift last_field_pos, so '2001-01-01 00:00:00.'
435 is treated as a valid value
436 */
437 last_field_pos= str;
438 field_length= 6; /* 6 digits */
439 }
440 else if (my_isdigit(&my_charset_latin1,str[0]))
441 {
442 /*
443 We do not see a decimal point which would have indicated a
444 fractional second part in further read. So we skip the further
445 processing of digits.
446 */
447 i++;
448 break;
449 }
450 continue;
451 }
452 while (str != end &&
453 (my_ispunct(&my_charset_latin1,*str) ||
454 my_isspace(&my_charset_latin1,*str)))
455 {
456 if (my_isspace(&my_charset_latin1,*str))
457 {
458 if (!(allow_space & (1 << i)))
459 {
460 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
461 l_time->time_type= MYSQL_TIMESTAMP_NONE;
462 DBUG_RETURN(1);
463 }
464 found_space= 1;
465 }
466 str++;
467 found_delimitier= 1; /* Should be a 'normal' date */
468 }
469 /* Check if next position is AM/PM */
470 if (i == format_position[6]) /* Seconds, time for AM/PM */
471 {
472 i++; /* Skip AM/PM part */
473 if (format_position[7] != 255) /* If using AM/PM */
474 {
475 if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
476 {
477 if (str[0] == 'p' || str[0] == 'P')
478 add_hours= 12;
479 else if (str[0] != 'a' || str[0] != 'A')
480 continue; /* Not AM/PM */
481 str+= 2; /* Skip AM/PM */
482 /* Skip space after AM/PM */
483 while (str != end && my_isspace(&my_charset_latin1,*str))
484 str++;
485 }
486 }
487 }
488 last_field_pos= str;
489 }
490 if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
491 {
492 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
493 l_time->time_type= MYSQL_TIMESTAMP_NONE;
494 DBUG_RETURN(1); /* Can't be a datetime */
495 }
496
497 str= last_field_pos;
498
499 number_of_fields= i - start_loop;
500 while (i < MAX_DATE_PARTS)
501 {
502 date_len[i]= 0;
503 date[i++]= 0;
504 }
505
506 if (!is_internal_format)
507 {
508 year_length= date_len[(uint) format_position[0]];
509 if (!year_length) /* Year must be specified */
510 {
511 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
512 l_time->time_type= MYSQL_TIMESTAMP_NONE;
513 DBUG_RETURN(1);
514 }
515
516 l_time->year= date[(uint) format_position[0]];
517 l_time->month= date[(uint) format_position[1]];
518 l_time->day= date[(uint) format_position[2]];
519 l_time->hour= date[(uint) format_position[3]];
520 l_time->minute= date[(uint) format_position[4]];
521 l_time->second= date[(uint) format_position[5]];
522
523 frac_pos= (uint) format_position[6];
524 frac_len= date_len[frac_pos];
525 status->fractional_digits= frac_len;
526 if (frac_len < 6)
527 date[frac_pos]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - frac_len];
528 l_time->second_part= date[frac_pos];
529
530 if (format_position[7] != (uchar) 255)
531 {
532 if (l_time->hour > 12)
533 {
534 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
535 goto err;
536 }
537 l_time->hour= l_time->hour%12 + add_hours;
538 }
539 }
540 else
541 {
542 l_time->year= date[0];
543 l_time->month= date[1];
544 l_time->day= date[2];
545 l_time->hour= date[3];
546 l_time->minute= date[4];
547 l_time->second= date[5];
548 if (date_len[6] < 6)
549 date[6]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - date_len[6]];
550 l_time->second_part=date[6];
551 status->fractional_digits= date_len[6];
552 }
553 l_time->neg= 0;
554
555 if (year_length == 2 && not_zero_date)
556 l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
557
558 /*
559 Set time_type before check_datetime_range(),
560 as the latter relies on initialized time_type value.
561 */
562 l_time->time_type= (number_of_fields <= 3 ?
563 MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
564
565 if (number_of_fields < 3 || check_datetime_range(l_time))
566 {
567 /* Only give warning for a zero date if there is some garbage after */
568 if (!not_zero_date) /* If zero date */
569 {
570 for (; str != end ; str++)
571 {
572 if (!my_isspace(&my_charset_latin1, *str))
573 {
574 not_zero_date= 1; /* Give warning */
575 break;
576 }
577 }
578 }
579 status->warnings|= not_zero_date ? MYSQL_TIME_WARN_TRUNCATED :
580 MYSQL_TIME_WARN_ZERO_DATE;
581 goto err;
582 }
583
584 if (check_date(l_time, not_zero_date != 0, flags, &status->warnings))
585 goto err;
586
587 /* Scan all digits left after microseconds */
588 if (status->fractional_digits == 6 && str != end)
589 {
590 if (my_isdigit(&my_charset_latin1, *str))
591 {
592 /*
593 We don't need the exact nanoseconds value.
594 Knowing the first digit is enough for rounding.
595 */
596 status->nanoseconds= 100 * (int) (*str++ - '0');
597 for (; str != end && my_isdigit(&my_charset_latin1, *str); str++)
598 { }
599 }
600 }
601
602 for (; str != end ; str++)
603 {
604 if (!my_isspace(&my_charset_latin1,*str))
605 {
606 status->warnings= MYSQL_TIME_WARN_TRUNCATED;
607 break;
608 }
609 }
610
611 DBUG_RETURN(0);
612
613 err:
614 set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
615 DBUG_RETURN(1);
616 }
617
618
619 /*
620 Convert a time string to a MYSQL_TIME struct.
621
622 SYNOPSIS
623 str_to_time()
624 str A string in full TIMESTAMP format or
625 [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
626 [M]MSS or [S]S
627 There may be an optional [.second_part] after seconds
628 length Length of str
629 l_time Store result here
630 status Conversion status
631
632 status.warning is set to:
633 MYSQL_TIME_WARN_TRUNCATED flag if the input string
634 was cut during conversion, and/or
635 MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is out of range.
636
637 NOTES
638 Because of the extra days argument, this function can only
639 work with times where the time arguments are in the above order.
640
641 RETURN
642 0 ok
643 1 error
644 */
645
str_to_time(const char * str,size_t length,MYSQL_TIME * l_time,MYSQL_TIME_STATUS * status)646 my_bool str_to_time(const char *str, size_t length, MYSQL_TIME *l_time,
647 MYSQL_TIME_STATUS *status)
648 {
649 ulong date[5];
650 ulonglong value;
651 const char *end=str+length, *end_of_days;
652 my_bool found_days,found_hours;
653 uint state;
654
655 my_time_status_init(status);
656 l_time->neg=0;
657 for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
658 length--;
659 if (str != end && *str == '-')
660 {
661 l_time->neg=1;
662 str++;
663 length--;
664 }
665 if (str == end)
666 return 1;
667
668 /* Check first if this is a full TIMESTAMP */
669 if (length >= 12)
670 { /* Probably full timestamp */
671 (void) str_to_datetime(str, length, l_time,
672 (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), status);
673 if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
674 return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
675 my_time_status_init(status);
676 }
677
678 /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
679 for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
680 value=value*10L + (long) (*str - '0');
681
682 if (value > UINT_MAX)
683 return 1;
684
685 /* Skip all space after 'days' */
686 end_of_days= str;
687 for (; str != end && my_isspace(&my_charset_latin1, str[0]) ; str++)
688 ;
689
690 state= 0;
691 found_days=found_hours=0;
692 if ((uint) (end-str) > 1 && str != end_of_days &&
693 my_isdigit(&my_charset_latin1, *str))
694 { /* Found days part */
695 date[0]= (ulong) value;
696 state= 1; /* Assume next is hours */
697 found_days= 1;
698 }
699 else if ((end-str) > 1 && *str == time_separator &&
700 my_isdigit(&my_charset_latin1, str[1]))
701 {
702 date[0]= 0; /* Assume we found hours */
703 date[1]= (ulong) value;
704 state=2;
705 found_hours=1;
706 str++; /* skip ':' */
707 }
708 else
709 {
710 /* String given as one number; assume HHMMSS format */
711 date[0]= 0;
712 date[1]= (ulong) (value/10000);
713 date[2]= (ulong) (value/100 % 100);
714 date[3]= (ulong) (value % 100);
715 state=4;
716 goto fractional;
717 }
718
719 /* Read hours, minutes and seconds */
720 for (;;)
721 {
722 for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
723 value=value*10L + (long) (*str - '0');
724 date[state++]= (ulong) value;
725 if (state == 4 || (end-str) < 2 || *str != time_separator ||
726 !my_isdigit(&my_charset_latin1,str[1]))
727 break;
728 str++; /* Skip time_separator (':') */
729 }
730
731 if (state != 4)
732 { /* Not HH:MM:SS */
733 /* Fix the date to assume that seconds was given */
734 if (!found_hours && !found_days)
735 {
736 size_t len= sizeof(long) * (state - 1);
737 memmove((uchar*) (date+4) - len, (uchar*) (date+state) - len, len);
738 memset(date, 0, sizeof(long)*(4-state));
739 }
740 else
741 memset((date+state), 0, sizeof(long)*(4-state));
742 }
743
744 fractional:
745 /* Get fractional second part */
746 if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1]))
747 {
748 int field_length= 5;
749 str++; value=(uint) (uchar) (*str - '0');
750 while (++str != end && my_isdigit(&my_charset_latin1, *str))
751 {
752 if (field_length-- > 0)
753 value= value*10 + (uint) (uchar) (*str - '0');
754 }
755 if (field_length >= 0)
756 {
757 status->fractional_digits= DATETIME_MAX_DECIMALS - field_length;
758 if (field_length > 0)
759 value*= (long) log_10_int[field_length];
760 }
761 else
762 {
763 /* Scan digits left after microseconds */
764 status->fractional_digits= 6;
765 status->nanoseconds= 100 * (int) (str[-1] - '0');
766 for ( ; str != end && my_isdigit(&my_charset_latin1, *str); str++)
767 { }
768 }
769 date[4]= (ulong) value;
770 }
771 else if ((end - str) == 1 && *str == '.')
772 {
773 str++;
774 date[4]= 0;
775 }
776 else
777 date[4]=0;
778
779 /* Check for exponent part: E<gigit> | E<sign><digit> */
780 /* (may occur as result of %g formatting of time value) */
781 if ((end - str) > 1 &&
782 (*str == 'e' || *str == 'E') &&
783 (my_isdigit(&my_charset_latin1, str[1]) ||
784 ((str[1] == '-' || str[1] == '+') &&
785 (end - str) > 2 &&
786 my_isdigit(&my_charset_latin1, str[2]))))
787 return 1;
788
789 if (internal_format_positions[7] != 255)
790 {
791 /* Read a possible AM/PM */
792 while (str != end && my_isspace(&my_charset_latin1, *str))
793 str++;
794 if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
795 {
796 if (str[0] == 'p' || str[0] == 'P')
797 {
798 str+= 2;
799 date[1]= date[1]%12 + 12;
800 }
801 else if (str[0] == 'a' || str[0] == 'A')
802 str+=2;
803 }
804 }
805
806 /* Integer overflow checks */
807 if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
808 date[2] > UINT_MAX || date[3] > UINT_MAX ||
809 date[4] > UINT_MAX)
810 return 1;
811
812 l_time->year= 0; /* For protocol::store_time */
813 l_time->month= 0;
814
815 l_time->day= 0;
816 l_time->hour= date[1] + date[0] * 24; /* Mix days and hours */
817
818 l_time->minute= date[2];
819 l_time->second= date[3];
820 l_time->second_part= date[4];
821 l_time->time_type= MYSQL_TIMESTAMP_TIME;
822
823 if (check_time_mmssff_range(l_time))
824 {
825 status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
826 return TRUE;
827 }
828
829 /* Adjust the value into supported MYSQL_TIME range */
830 adjust_time_range(l_time, &status->warnings);
831
832 /* Check if there is garbage at end of the MYSQL_TIME specification */
833 if (str != end)
834 {
835 do
836 {
837 if (!my_isspace(&my_charset_latin1,*str))
838 {
839 status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
840 break;
841 }
842 } while (++str != end);
843 }
844 return 0;
845 }
846
847
848 /**
849 Convert number to TIME
850 @param nr Number to convert.
851 @param OUT ltime Variable to convert to.
852 @param OUT warnings Warning vector.
853
854 @retval false OK
855 @retval true No. is out of range
856 */
857 my_bool
number_to_time(longlong nr,MYSQL_TIME * ltime,int * warnings)858 number_to_time(longlong nr, MYSQL_TIME *ltime, int *warnings)
859 {
860 if (nr > TIME_MAX_VALUE)
861 {
862 /* For huge numbers try full DATETIME, like str_to_time does. */
863 if (nr >= 10000000000LL) /* '0001-00-00 00-00-00' */
864 {
865 int warnings_backup= *warnings;
866 if (number_to_datetime(nr, ltime, 0, warnings) != -1LL)
867 return FALSE;
868 *warnings= warnings_backup;
869 }
870 set_max_time(ltime, 0);
871 *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
872 return TRUE;
873 }
874 else if (nr < -TIME_MAX_VALUE)
875 {
876 set_max_time(ltime, 1);
877 *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
878 return TRUE;
879 }
880 if ((ltime->neg= (nr < 0)))
881 nr= -nr;
882 if (nr % 100 >= 60 || nr / 100 % 100 >= 60) /* Check hours and minutes */
883 {
884 set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
885 *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
886 return TRUE;
887 }
888 ltime->time_type= MYSQL_TIMESTAMP_TIME;
889 ltime->year= ltime->month= ltime->day= 0;
890 TIME_set_hhmmss(ltime, (uint)nr);
891 ltime->second_part= 0;
892 return FALSE;
893 }
894
895
896 /**
897 Adjust 'time' value to lie in the MYSQL_TIME range.
898 If the time value lies outside of the range [-838:59:59, 838:59:59],
899 set it to the closest endpoint of the range and set
900 MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
901
902 @param time pointer to MYSQL_TIME value
903 @param warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
904 */
adjust_time_range(struct st_mysql_time * my_time,int * warning)905 void adjust_time_range(struct st_mysql_time *my_time, int *warning)
906 {
907 assert(!check_time_mmssff_range(my_time));
908 if (check_time_range_quick(my_time))
909 {
910 my_time->day= my_time->second_part= 0;
911 set_max_hhmmss(my_time);
912 *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
913 }
914 }
915
916
917 /*
918 Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
919
920 SYNOPSIS
921 my_init_time()
922 */
my_init_time(void)923 void my_init_time(void)
924 {
925 time_t seconds;
926 struct tm *l_time,tm_tmp;
927 MYSQL_TIME my_time;
928 my_bool not_used;
929
930 seconds= (time_t) time((time_t*) 0);
931 localtime_r(&seconds,&tm_tmp);
932 l_time= &tm_tmp;
933 my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
934 my_time.year= (uint) l_time->tm_year+1900;
935 my_time.month= (uint) l_time->tm_mon+1;
936 my_time.day= (uint) l_time->tm_mday;
937 my_time.hour= (uint) l_time->tm_hour;
938 my_time.minute= (uint) l_time->tm_min;
939 my_time.second= (uint) l_time->tm_sec;
940 my_time.time_type= MYSQL_TIMESTAMP_DATETIME;
941 my_time.neg= 0;
942 my_time.second_part= 0;
943 my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
944 }
945
946
947 /*
948 Handle 2 digit year conversions
949
950 SYNOPSIS
951 year_2000_handling()
952 year 2 digit year
953
954 RETURN
955 Year between 1970-2069
956 */
957
year_2000_handling(uint year)958 uint year_2000_handling(uint year)
959 {
960 if ((year=year+1900) < 1900+YY_PART_YEAR)
961 year+=100;
962 return year;
963 }
964
965
966 /*
967 Calculate nr of day since year 0 in new date-system (from 1615)
968
969 SYNOPSIS
970 calc_daynr()
971 year Year (exact 4 digit year, no year conversions)
972 month Month
973 day Day
974
975 NOTES: 0000-00-00 is a valid date, and will return 0
976
977 RETURN
978 Days since 0000-00-00
979 */
980
calc_daynr(uint year,uint month,uint day)981 long calc_daynr(uint year,uint month,uint day)
982 {
983 long delsum;
984 int temp;
985 int y= year; /* may be < 0 temporarily */
986 DBUG_ENTER("calc_daynr");
987
988 if (y == 0 && month == 0)
989 DBUG_RETURN(0); /* Skip errors */
990 /* Cast to int to be able to handle month == 0 */
991 delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day);
992 if (month <= 2)
993 y--;
994 else
995 delsum-= (long) ((int) month * 4 + 23) / 10;
996 temp=(int) ((y/100+1)*3)/4;
997 DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld",
998 y+(month <= 2),month,day,delsum+y/4-temp));
999 assert(delsum+(int) y/4-temp >= 0);
1000 DBUG_RETURN(delsum+(int) y/4-temp);
1001 } /* calc_daynr */
1002
1003
1004 /*
1005 Convert time in MYSQL_TIME representation in system time zone to its
1006 my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
1007
1008 SYNOPSIS
1009 my_system_gmt_sec()
1010 t - time value to be converted
1011 my_timezone - pointer to long where offset of system time zone
1012 from UTC will be stored for caching
1013 in_dst_time_gap - set to true if time falls into spring time-gap
1014
1015 NOTES
1016 The idea is to cache the time zone offset from UTC (including daylight
1017 saving time) for the next call to make things faster. But currently we
1018 just calculate this offset during startup (by calling my_init_time()
1019 function) and use it all the time.
1020 Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
1021 is not allowed).
1022
1023 RETURN VALUE
1024 Time in UTC seconds since Unix Epoch representation.
1025 */
1026 my_time_t
my_system_gmt_sec(const MYSQL_TIME * t_src,long * my_timezone,my_bool * in_dst_time_gap)1027 my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
1028 my_bool *in_dst_time_gap)
1029 {
1030 uint loop;
1031 time_t tmp= 0;
1032 int shift= 0;
1033 MYSQL_TIME tmp_time;
1034 MYSQL_TIME *t= &tmp_time;
1035 struct tm *l_time,tm_tmp;
1036 long diff, current_timezone;
1037
1038 /*
1039 Use temp variable to avoid trashing input data, which could happen in
1040 case of shift required for boundary dates processing.
1041 */
1042 memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME));
1043
1044 if (!validate_timestamp_range(t))
1045 return 0;
1046
1047 /*
1048 Calculate the gmt time based on current time and timezone
1049 The -1 on the end is to ensure that if have a date that exists twice
1050 (like 2002-10-27 02:00:0 MET), we will find the initial date.
1051
1052 By doing -3600 we will have to call localtime_r() several times, but
1053 I couldn't come up with a better way to get a repeatable result :(
1054
1055 We can't use mktime() as it's buggy on many platforms and not thread safe.
1056
1057 Note: this code assumes that our time_t estimation is not too far away
1058 from real value (we assume that localtime_r(tmp) will return something
1059 within 24 hrs from t) which is probably true for all current time zones.
1060
1061 Note2: For the dates, which have time_t representation close to
1062 MAX_INT32 (efficient time_t limit for supported platforms), we should
1063 do a small trick to avoid overflow. That is, convert the date, which is
1064 two days earlier, and then add these days to the final value.
1065
1066 The same trick is done for the values close to 0 in time_t
1067 representation for platfroms with unsigned time_t (QNX).
1068
1069 To be more verbose, here is a sample (extracted from the code below):
1070 (calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
1071 would return -2147480896 because of the long type overflow. In result
1072 we would get 1901 year in localtime_r(), which is an obvious error.
1073
1074 Alike problem raises with the dates close to Epoch. E.g.
1075 (calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
1076 will give -3600.
1077
1078 On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
1079 wil give us a date around 2106 year. Which is no good.
1080
1081 Theoreticaly, there could be problems with the latter conversion:
1082 there are at least two timezones, which had time switches near 1 Jan
1083 of 1970 (because of political reasons). These are America/Hermosillo and
1084 America/Mazatlan time zones. They changed their offset on
1085 1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
1086 the code below will give incorrect results for dates close to
1087 1970-01-01, in the case OS takes into account these historical switches.
1088 Luckily, it seems that we support only one platform with unsigned
1089 time_t. It's QNX. And QNX does not support historical timezone data at all.
1090 E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
1091 historical information for localtime_r() etc. That is, the problem is not
1092 relevant to QNX.
1093
1094 We are safe with shifts close to MAX_INT32, as there are no known
1095 time switches on Jan 2038 yet :)
1096 */
1097 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
1098 {
1099 /*
1100 Below we will pass (uint) (t->day - shift) to calc_daynr.
1101 As we don't want to get an overflow here, we will shift
1102 only safe dates. That's why we have (t->day > 4) above.
1103 */
1104 t->day-= 2;
1105 shift= 2;
1106 }
1107
1108 tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
1109 (long) days_at_timestart) * SECONDS_IN_24H +
1110 (long) t->hour*3600L +
1111 (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
1112 3600);
1113
1114 current_timezone= my_time_zone;
1115 localtime_r(&tmp,&tm_tmp);
1116 l_time=&tm_tmp;
1117 for (loop=0;
1118 loop < 2 &&
1119 (t->hour != (uint) l_time->tm_hour ||
1120 t->minute != (uint) l_time->tm_min ||
1121 t->second != (uint) l_time->tm_sec);
1122 loop++)
1123 { /* One check should be enough ? */
1124 /* Get difference in days */
1125 int days= t->day - l_time->tm_mday;
1126 if (days < -1)
1127 days= 1; /* Month has wrapped */
1128 else if (days > 1)
1129 days= -1;
1130 diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
1131 (long) (60*((int) t->minute - (int) l_time->tm_min)) +
1132 (long) ((int) t->second - (int) l_time->tm_sec));
1133 current_timezone+= diff+3600; /* Compensate for -3600 above */
1134 tmp+= (time_t) diff;
1135 localtime_r(&tmp,&tm_tmp);
1136 l_time=&tm_tmp;
1137 }
1138 /*
1139 Fix that if we are in the non existing daylight saving time hour
1140 we move the start of the next real hour.
1141
1142 This code doesn't handle such exotical thing as time-gaps whose length
1143 is more than one hour or non-integer (latter can theoretically happen
1144 if one of seconds will be removed due leap correction, or because of
1145 general time correction like it happened for Africa/Monrovia time zone
1146 in year 1972).
1147 */
1148 if (loop == 2 && t->hour != (uint) l_time->tm_hour)
1149 {
1150 int days= t->day - l_time->tm_mday;
1151 if (days < -1)
1152 days=1; /* Month has wrapped */
1153 else if (days > 1)
1154 days= -1;
1155 diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
1156 (long) (60*((int) t->minute - (int) l_time->tm_min)) +
1157 (long) ((int) t->second - (int) l_time->tm_sec));
1158 if (diff == 3600)
1159 tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
1160 else if (diff == -3600)
1161 tmp-=t->minute*60 + t->second; /* Move to previous hour */
1162
1163 *in_dst_time_gap= 1;
1164 }
1165 *my_timezone= current_timezone;
1166
1167
1168 /* shift back, if we were dealing with boundary dates */
1169 tmp+= shift * SECONDS_IN_24H;
1170
1171 /*
1172 This is possible for dates, which slightly exceed boundaries.
1173 Conversion will pass ok for them, but we don't allow them.
1174 First check will pass for platforms with signed time_t.
1175 instruction above (tmp+= shift*86400L) could exceed
1176 MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
1177 So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
1178 with unsigned time_t tmp+= shift*86400L might result in a number,
1179 larger then TIMESTAMP_MAX_VALUE, so another check will work.
1180 */
1181 if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp))
1182 tmp= 0;
1183
1184 return (my_time_t) tmp;
1185 } /* my_system_gmt_sec */
1186
1187
1188 /**
1189 Print the microsecond part: ".NNN"
1190 @param to OUT The string pointer to print at
1191 @param useconds The microseconds value.
1192 @param dec Precision, between 1 and 6.
1193 @return The length of the result string.
1194 */
1195 static inline int
my_useconds_to_str(char * to,ulong useconds,uint dec)1196 my_useconds_to_str(char *to, ulong useconds, uint dec)
1197 {
1198 assert(dec <= DATETIME_MAX_DECIMALS);
1199 return sprintf(to, ".%0*lu", (int) dec,
1200 useconds / (ulong) log_10_int[DATETIME_MAX_DECIMALS - dec]);
1201 }
1202
1203
1204 /*
1205 Functions to convert time/date/datetime value to a string,
1206 using default format.
1207 This functions don't check that given MYSQL_TIME structure members are
1208 in valid range. If they are not, return value won't reflect any
1209 valid date either. Additionally, make_time doesn't take into
1210 account time->day member: it's assumed that days have been converted
1211 to hours already.
1212
1213 RETURN
1214 number of characters written to 'to'
1215 */
1216
my_time_to_str(const MYSQL_TIME * l_time,char * to,uint dec)1217 int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1218 {
1219 uint extra_hours= 0;
1220 int len= sprintf(to, "%s%02u:%02u:%02u", (l_time->neg ? "-" : ""),
1221 extra_hours + l_time->hour, l_time->minute, l_time->second);
1222 if (dec)
1223 len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1224 return len;
1225 }
1226
my_date_to_str(const MYSQL_TIME * l_time,char * to)1227 int my_date_to_str(const MYSQL_TIME *l_time, char *to)
1228 {
1229 return sprintf(to, "%04u-%02u-%02u",
1230 l_time->year, l_time->month, l_time->day);
1231 }
1232
1233
1234 /*
1235 Convert datetime to a string 'YYYY-MM-DD hh:mm:ss'.
1236 Open coded for better performance.
1237 This code previously resided in field.cc, in Field_timestamp::val_str().
1238
1239 @param to OUT The string pointer to print at.
1240 @param ltime The MYSQL_TIME value.
1241 @return The length of the result string.
1242 */
1243 static inline int
TIME_to_datetime_str(char * to,const MYSQL_TIME * ltime)1244 TIME_to_datetime_str(char *to, const MYSQL_TIME *ltime)
1245 {
1246 uint32 temp, temp2;
1247 /* Year */
1248 temp= ltime->year / 100;
1249 *to++= (char) ('0' + temp / 10);
1250 *to++= (char) ('0' + temp % 10);
1251 temp= ltime->year % 100;
1252 *to++= (char) ('0' + temp / 10);
1253 *to++= (char) ('0' + temp % 10);
1254 *to++= '-';
1255 /* Month */
1256 temp= ltime->month;
1257 temp2= temp / 10;
1258 temp= temp-temp2 * 10;
1259 *to++= (char) ('0' + (char) (temp2));
1260 *to++= (char) ('0' + (char) (temp));
1261 *to++= '-';
1262 /* Day */
1263 temp= ltime->day;
1264 temp2= temp / 10;
1265 temp= temp - temp2 * 10;
1266 *to++= (char) ('0' + (char) (temp2));
1267 *to++= (char) ('0' + (char) (temp));
1268 *to++= ' ';
1269 /* Hour */
1270 temp= ltime->hour;
1271 temp2= temp / 10;
1272 temp= temp - temp2 * 10;
1273 *to++= (char) ('0' + (char) (temp2));
1274 *to++= (char) ('0' + (char) (temp));
1275 *to++= ':';
1276 /* Minute */
1277 temp= ltime->minute;
1278 temp2= temp / 10;
1279 temp= temp - temp2 * 10;
1280 *to++= (char) ('0' + (char) (temp2));
1281 *to++= (char) ('0' + (char) (temp));
1282 *to++= ':';
1283 /* Second */
1284 temp= ltime->second;
1285 temp2=temp / 10;
1286 temp= temp - temp2 * 10;
1287 *to++= (char) ('0' + (char) (temp2));
1288 *to++= (char) ('0' + (char) (temp));
1289 return 19;
1290 }
1291
1292
1293 /**
1294 Print a datetime value with an optional fractional part.
1295
1296 @l_time The MYSQL_TIME value to print.
1297 @to OUT The string pointer to print at.
1298 @return The length of the result string.
1299 */
my_datetime_to_str(const MYSQL_TIME * l_time,char * to,uint dec)1300 int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1301 {
1302 int len= TIME_to_datetime_str(to, l_time);
1303 if (dec)
1304 len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1305 else
1306 to[len]= '\0';
1307 return len;
1308 }
1309
1310
1311 /*
1312 Convert struct DATE/TIME/DATETIME value to string using built-in
1313 MySQL time conversion formats.
1314
1315 SYNOPSIS
1316 my_TIME_to_string()
1317
1318 NOTE
1319 The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1320 */
1321
my_TIME_to_str(const MYSQL_TIME * l_time,char * to,uint dec)1322 int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1323 {
1324 switch (l_time->time_type) {
1325 case MYSQL_TIMESTAMP_DATETIME:
1326 return my_datetime_to_str(l_time, to, dec);
1327 case MYSQL_TIMESTAMP_DATE:
1328 return my_date_to_str(l_time, to);
1329 case MYSQL_TIMESTAMP_TIME:
1330 return my_time_to_str(l_time, to, dec);
1331 case MYSQL_TIMESTAMP_NONE:
1332 case MYSQL_TIMESTAMP_ERROR:
1333 to[0]='\0';
1334 return 0;
1335 default:
1336 assert(0);
1337 return 0;
1338 }
1339 }
1340
1341
1342 /**
1343 Print a timestamp with an oprional fractional part: XXXXX[.YYYYY]
1344
1345 @param tm The timestamp value to print.
1346 @param OUT to The string pointer to print at.
1347 @param dec Precision, in the range 0..6.
1348 @return The length of the result string.
1349 */
my_timeval_to_str(const struct timeval * tm,char * to,uint dec)1350 int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
1351 {
1352 int len= sprintf(to, "%d", (int) tm->tv_sec);
1353 if (dec)
1354 len+= my_useconds_to_str(to + len, tm->tv_usec, dec);
1355 return len;
1356 }
1357
1358
1359 /*
1360 Convert datetime value specified as number to broken-down TIME
1361 representation and form value of DATETIME type as side-effect.
1362
1363 SYNOPSIS
1364 number_to_datetime()
1365 nr - datetime value as number
1366 time_res - pointer for structure for broken-down representation
1367 flags - flags to use in validating date, as in str_to_datetime()
1368 was_cut 0 Value ok
1369 1 If value was cut during conversion
1370 2 check_date(date,flags) considers date invalid
1371
1372 DESCRIPTION
1373 Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1374 YYYYMMDDHHMMSS to broken-down MYSQL_TIME representation. Return value in
1375 YYYYMMDDHHMMSS format as side-effect.
1376
1377 This function also checks if datetime value fits in DATETIME range.
1378
1379 RETURN VALUE
1380 -1 Timestamp with wrong values
1381 anything else DATETIME as integer in YYYYMMDDHHMMSS format
1382 Datetime value in YYYYMMDDHHMMSS format.
1383
1384 was_cut if return value -1: one of
1385 - MYSQL_TIME_WARN_OUT_OF_RANGE
1386 - MYSQL_TIME_WARN_ZERO_DATE
1387 - MYSQL_TIME_WARN_TRUNCATED
1388 otherwise 0.
1389 */
1390
number_to_datetime(longlong nr,MYSQL_TIME * time_res,my_time_flags_t flags,int * was_cut)1391 longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
1392 my_time_flags_t flags, int *was_cut)
1393 {
1394 long part1,part2;
1395
1396 *was_cut= 0;
1397 memset(time_res, 0, sizeof(*time_res));
1398 time_res->time_type=MYSQL_TIMESTAMP_DATE;
1399
1400 if (nr == 0LL || nr >= 10000101000000LL)
1401 {
1402 time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1403 if (nr > 99999999999999LL) /* 9999-99-99 99:99:99 */
1404 {
1405 *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
1406 return -1LL;
1407 }
1408 goto ok;
1409 }
1410 if (nr < 101)
1411 goto err;
1412 if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1413 {
1414 nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1415 goto ok;
1416 }
1417 if (nr < (YY_PART_YEAR)*10000L+101L)
1418 goto err;
1419 if (nr <= 991231L)
1420 {
1421 nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1422 goto ok;
1423 }
1424 /*
1425 Though officially we support DATE values from 1000-01-01 only, one can
1426 easily insert a value like 1-1-1. So, for consistency reasons such dates
1427 are allowed when TIME_FUZZY_DATE is set.
1428 */
1429 if (nr < 10000101L && !(flags & TIME_FUZZY_DATE))
1430 goto err;
1431 if (nr <= 99991231L)
1432 {
1433 nr= nr*1000000L;
1434 goto ok;
1435 }
1436 if (nr < 101000000L)
1437 goto err;
1438
1439 time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1440
1441 if (nr <= (YY_PART_YEAR-1)*10000000000LL+1231235959LL)
1442 {
1443 nr= nr+20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1444 goto ok;
1445 }
1446 if (nr < YY_PART_YEAR*10000000000LL+ 101000000LL)
1447 goto err;
1448 if (nr <= 991231235959LL)
1449 nr= nr+19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1450
1451 ok:
1452 part1=(long) (nr/1000000LL);
1453 part2=(long) (nr - (longlong) part1*1000000LL);
1454 time_res->year= (int) (part1/10000L); part1%=10000L;
1455 time_res->month= (int) part1 / 100;
1456 time_res->day= (int) part1 % 100;
1457 time_res->hour= (int) (part2/10000L); part2%=10000L;
1458 time_res->minute=(int) part2 / 100;
1459 time_res->second=(int) part2 % 100;
1460
1461 if (!check_datetime_range(time_res) &&
1462 !check_date(time_res, (nr != 0), flags, was_cut))
1463 return nr;
1464
1465 /* Don't want to have was_cut get set if TIME_NO_ZERO_DATE was violated. */
1466 if (!nr && (flags & TIME_NO_ZERO_DATE))
1467 return -1LL;
1468
1469 err:
1470 *was_cut= MYSQL_TIME_WARN_TRUNCATED;
1471 return -1LL;
1472 }
1473
1474
1475 /**
1476 Convert time value to integer in YYYYMMDDHHMMSS.
1477 @param my_time The MYSQL_TIME value to convert.
1478 @return A number in format YYYYMMDDHHMMSS.
1479 */
TIME_to_ulonglong_datetime(const MYSQL_TIME * my_time)1480 ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)
1481 {
1482 return ((ulonglong) (my_time->year * 10000UL +
1483 my_time->month * 100UL +
1484 my_time->day) * 1000000ULL +
1485 (ulonglong) (my_time->hour * 10000UL +
1486 my_time->minute * 100UL +
1487 my_time->second));
1488 }
1489
1490
1491
1492 /**
1493 Convert MYSQL_TIME value to integer in YYYYMMDD format
1494 @param my_time The MYSQL_TIME value to convert.
1495 @return A number in format YYYYMMDD.
1496 */
TIME_to_ulonglong_date(const MYSQL_TIME * my_time)1497 ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *my_time)
1498 {
1499 return (ulonglong) (my_time->year * 10000UL + my_time->month * 100UL +
1500 my_time->day);
1501 }
1502
1503
1504 /**
1505 Convert MYSQL_TIME value to integer in HHMMSS format.
1506 This function doesn't take into account time->day member:
1507 it's assumed that days have been converted to hours already.
1508 @param my_time The TIME value to convert.
1509 @return The number in HHMMSS format.
1510 */
TIME_to_ulonglong_time(const MYSQL_TIME * my_time)1511 ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time)
1512 {
1513 return (ulonglong) (my_time->hour * 10000UL +
1514 my_time->minute * 100UL +
1515 my_time->second);
1516 }
1517
1518
1519 /**
1520 Set day, month and year from a number
1521 @param ltime MYSQL_TIME variable
1522 @param yymmdd Number in YYYYMMDD format
1523 */
TIME_set_yymmdd(MYSQL_TIME * ltime,uint yymmdd)1524 void TIME_set_yymmdd(MYSQL_TIME *ltime, uint yymmdd)
1525 {
1526 ltime->day= (int) (yymmdd % 100);
1527 ltime->month= (int) (yymmdd / 100) % 100;
1528 ltime->year= (int) (yymmdd / 10000);
1529 }
1530
1531
1532 /**
1533 Set hour, minute and secondr from a number
1534 @param ltime MYSQL_TIME variable
1535 @param hhmmss Number in HHMMSS format
1536 */
TIME_set_hhmmss(MYSQL_TIME * ltime,uint hhmmss)1537 void TIME_set_hhmmss(MYSQL_TIME *ltime, uint hhmmss)
1538 {
1539 ltime->second= (int) (hhmmss % 100);
1540 ltime->minute= (int) (hhmmss / 100) % 100;
1541 ltime->hour= (int) (hhmmss / 10000);
1542 }
1543
1544
1545 /*
1546 Convert struct MYSQL_TIME (date and time split into year/month/day/hour/...
1547 to a number in format YYYYMMDDHHMMSS (DATETIME),
1548 YYYYMMDD (DATE) or HHMMSS (TIME).
1549
1550 SYNOPSIS
1551 TIME_to_ulonglong()
1552
1553 DESCRIPTION
1554 The function is used when we need to convert value of time item
1555 to a number if it's used in numeric context, i. e.:
1556 SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1557 SELECT ?+1;
1558
1559 NOTE
1560 This function doesn't check that given MYSQL_TIME structure members are
1561 in valid range. If they are not, return value won't reflect any
1562 valid date either.
1563 */
1564
TIME_to_ulonglong(const MYSQL_TIME * my_time)1565 ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time)
1566 {
1567 switch (my_time->time_type) {
1568 case MYSQL_TIMESTAMP_DATETIME:
1569 return TIME_to_ulonglong_datetime(my_time);
1570 case MYSQL_TIMESTAMP_DATE:
1571 return TIME_to_ulonglong_date(my_time);
1572 case MYSQL_TIMESTAMP_TIME:
1573 return TIME_to_ulonglong_time(my_time);
1574 case MYSQL_TIMESTAMP_NONE:
1575 case MYSQL_TIMESTAMP_ERROR:
1576 return 0ULL;
1577 default:
1578 assert(0);
1579 }
1580 return 0;
1581 }
1582
1583
1584 /*** TIME low-level memory and disk representation routines ***/
1585
1586 /*
1587 In-memory format:
1588
1589 1 bit sign (Used for sign, when on disk)
1590 1 bit unused (Reserved for wider hour range, e.g. for intervals)
1591 10 bit hour (0-836)
1592 6 bit minute (0-59)
1593 6 bit second (0-59)
1594 24 bits microseconds (0-999999)
1595
1596 Total: 48 bits = 6 bytes
1597 Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1598 */
1599
1600
1601 /**
1602 Convert time value to numeric packed representation.
1603
1604 @param ltime The value to convert.
1605 @return Numeric packed representation.
1606 */
TIME_to_longlong_time_packed(const MYSQL_TIME * ltime)1607 longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
1608 {
1609 /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
1610 long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
1611 (ltime->minute << 6) | ltime->second;
1612 longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
1613 return ltime->neg ? -tmp : tmp;
1614 }
1615
1616
1617 /**
1618 Convert time packed numeric representation to time.
1619
1620 @param OUT ltime The MYSQL_TIME variable to set.
1621 @param tmp The packed numeric representation.
1622 */
TIME_from_longlong_time_packed(MYSQL_TIME * ltime,longlong tmp)1623 void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
1624 {
1625 longlong hms;
1626 if ((ltime->neg= (tmp < 0)))
1627 tmp= -tmp;
1628 hms= MY_PACKED_TIME_GET_INT_PART(tmp);
1629 ltime->year= (uint) 0;
1630 ltime->month= (uint) 0;
1631 ltime->day= (uint) 0;
1632 ltime->hour= (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
1633 ltime->minute= (uint) (hms >> 6) % (1 << 6); /* 6 bits starting at 6th */
1634 ltime->second= (uint) hms % (1 << 6); /* 6 bits starting at 0th */
1635 ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1636 ltime->time_type= MYSQL_TIMESTAMP_TIME;
1637 }
1638
1639 /*
1640 On disk we convert from signed representation to unsigned
1641 representation using TIMEF_OFS, so all values become binary comparable.
1642 */
1643 #define TIMEF_OFS 0x800000000000LL
1644 #define TIMEF_INT_OFS 0x800000LL
1645
1646
1647 /**
1648 Convert in-memory numeric time representation to on-disk representation
1649
1650 @param nr Value in packed numeric time format.
1651 @param OUT ptr The buffer to put value at.
1652 @param dec Precision.
1653 */
my_time_packed_to_binary(longlong nr,uchar * ptr,uint dec)1654 void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1655 {
1656 assert(dec <= DATETIME_MAX_DECIMALS);
1657 /* Make sure the stored value was previously properly rounded or truncated */
1658 assert((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1659 (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1660
1661 switch (dec)
1662 {
1663 case 0:
1664 default:
1665 mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1666 break;
1667
1668 case 1:
1669 case 2:
1670 mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1671 ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1672 break;
1673
1674 case 4:
1675 case 3:
1676 mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1677 mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1678 break;
1679
1680 case 5:
1681 case 6:
1682 mi_int6store(ptr, nr + TIMEF_OFS);
1683 break;
1684 }
1685 }
1686
1687
1688 /**
1689 Convert on-disk time representation to in-memory packed numeric
1690 representation.
1691
1692 @param ptr The pointer to read the value at.
1693 @param dec Precision.
1694 @return Packed numeric time representation.
1695 */
my_time_packed_from_binary(const uchar * ptr,uint dec)1696 longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
1697 {
1698 assert(dec <= DATETIME_MAX_DECIMALS);
1699
1700 switch (dec)
1701 {
1702 case 0:
1703 default:
1704 {
1705 longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1706 return MY_PACKED_TIME_MAKE_INT(intpart);
1707 }
1708 case 1:
1709 case 2:
1710 {
1711 longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1712 int frac= (uint) ptr[3];
1713 if (intpart < 0 && frac)
1714 {
1715 /*
1716 Negative values are stored with reverse fractional part order,
1717 for binary sort compatibility.
1718
1719 Disk value intpart frac Time value Memory value
1720 800000.00 0 0 00:00:00.00 0000000000.000000
1721 7FFFFF.FF -1 255 -00:00:00.01 FFFFFFFFFF.FFD8F0
1722 7FFFFF.9D -1 99 -00:00:00.99 FFFFFFFFFF.F0E4D0
1723 7FFFFF.00 -1 0 -00:00:01.00 FFFFFFFFFF.000000
1724 7FFFFE.FF -1 255 -00:00:01.01 FFFFFFFFFE.FFD8F0
1725 7FFFFE.F6 -2 246 -00:00:01.10 FFFFFFFFFE.FE7960
1726
1727 Formula to convert fractional part from disk format
1728 (now stored in "frac" variable) to absolute value: "0x100 - frac".
1729 To reconstruct in-memory value, we shift
1730 to the next integer value and then substruct fractional part.
1731 */
1732 intpart++; /* Shift to the next integer value */
1733 frac-= 0x100; /* -(0x100 - frac) */
1734 }
1735 return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
1736 }
1737
1738 case 3:
1739 case 4:
1740 {
1741 longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1742 int frac= mi_uint2korr(ptr + 3);
1743 if (intpart < 0 && frac)
1744 {
1745 /*
1746 Fix reverse fractional part order: "0x10000 - frac".
1747 See comments for FSP=1 and FSP=2 above.
1748 */
1749 intpart++; /* Shift to the next integer value */
1750 frac-= 0x10000; /* -(0x10000-frac) */
1751 }
1752 return MY_PACKED_TIME_MAKE(intpart, frac * 100);
1753 }
1754
1755 case 5:
1756 case 6:
1757 return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
1758 }
1759 }
1760
1761
1762 /*** DATETIME and DATE low-level memory and disk representation routines ***/
1763
1764 /*
1765 1 bit sign (used when on disk)
1766 17 bits year*13+month (year 0-9999, month 0-12)
1767 5 bits day (0-31)
1768 5 bits hour (0-23)
1769 6 bits minute (0-59)
1770 6 bits second (0-59)
1771 24 bits microseconds (0-999999)
1772
1773 Total: 64 bits = 8 bytes
1774
1775 SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1776 */
1777
1778 /**
1779 Convert datetime to packed numeric datetime representation.
1780 @param ltime The value to convert.
1781 @return Packed numeric representation of ltime.
1782 */
TIME_to_longlong_datetime_packed(const MYSQL_TIME * ltime)1783 longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
1784 {
1785 longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1786 longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
1787 longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
1788 assert(!check_datetime_range(ltime)); /* Make sure no overflow */
1789 return ltime->neg ? -tmp : tmp;
1790 }
1791
1792
1793 /**
1794 Convert date to packed numeric date representation.
1795 Numeric packed date format is similar to numeric packed datetime
1796 representation, with zero hhmmss part.
1797
1798 @param ltime The value to convert.
1799 @return Packed numeric representation of ltime.
1800 */
TIME_to_longlong_date_packed(const MYSQL_TIME * ltime)1801 longlong TIME_to_longlong_date_packed(const MYSQL_TIME *ltime)
1802 {
1803 longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1804 return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1805 }
1806
1807
1808 /**
1809 Convert year to packed numeric date representation.
1810 Packed value for YYYY is the same to packed value for date YYYY-00-00.
1811 */
year_to_longlong_datetime_packed(long year)1812 longlong year_to_longlong_datetime_packed(long year)
1813 {
1814 longlong ymd= ((year * 13) << 5);
1815 return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1816 }
1817
1818
1819 /**
1820 Convert packed numeric datetime representation to MYSQL_TIME.
1821 @param OUT ltime The datetime variable to convert to.
1822 @param tmp The packed numeric datetime value.
1823 */
TIME_from_longlong_datetime_packed(MYSQL_TIME * ltime,longlong tmp)1824 void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
1825 {
1826 longlong ymd, hms;
1827 longlong ymdhms, ym;
1828 if ((ltime->neg= (tmp < 0)))
1829 tmp= -tmp;
1830
1831 ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1832 ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
1833
1834 ymd= ymdhms >> 17;
1835 ym= ymd >> 5;
1836 hms= ymdhms % (1 << 17);
1837
1838 ltime->day= ymd % (1 << 5);
1839 ltime->month= ym % 13;
1840 ltime->year= (uint)(ym / 13);
1841
1842 ltime->second= hms % (1 << 6);
1843 ltime->minute= (hms >> 6) % (1 << 6);
1844 ltime->hour= (uint)(hms >> 12);
1845
1846 ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
1847 }
1848
1849
1850 /**
1851 Convert packed numeric date representation to MYSQL_TIME.
1852 @param OUT ltime The date variable to convert to.
1853 @param tmp The packed numeric date value.
1854 */
TIME_from_longlong_date_packed(MYSQL_TIME * ltime,longlong tmp)1855 void TIME_from_longlong_date_packed(MYSQL_TIME *ltime, longlong tmp)
1856 {
1857 TIME_from_longlong_datetime_packed(ltime, tmp);
1858 ltime->time_type= MYSQL_TIMESTAMP_DATE;
1859 }
1860
1861
1862 /*
1863 On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
1864 for HA_KETYPE_BINARY compatibilty purposes.
1865 */
1866 #define DATETIMEF_INT_OFS 0x8000000000LL
1867
1868
1869 /**
1870 Convert on-disk datetime representation
1871 to in-memory packed numeric representation.
1872
1873 @param ptr The pointer to read value at.
1874 @param dec Precision.
1875 @return In-memory packed numeric datetime representation.
1876 */
my_datetime_packed_from_binary(const uchar * ptr,uint dec)1877 longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
1878 {
1879 longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
1880 int frac;
1881 assert(dec <= DATETIME_MAX_DECIMALS);
1882 switch (dec)
1883 {
1884 case 0:
1885 default:
1886 return MY_PACKED_TIME_MAKE_INT(intpart);
1887 case 1:
1888 case 2:
1889 frac= ((int) (signed char) ptr[5]) * 10000;
1890 break;
1891 case 3:
1892 case 4:
1893 frac= mi_sint2korr(ptr + 5) * 100;
1894 break;
1895 case 5:
1896 case 6:
1897 frac= mi_sint3korr(ptr + 5);
1898 break;
1899 }
1900 return MY_PACKED_TIME_MAKE(intpart, frac);
1901 }
1902
1903
1904 /**
1905 Store in-memory numeric packed datetime representation to disk.
1906
1907 @param nr In-memory numeric packed datetime representation.
1908 @param OUT ptr The pointer to store at.
1909 @param dec Precision, 1-6.
1910 */
my_datetime_packed_to_binary(longlong nr,uchar * ptr,uint dec)1911 void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1912 {
1913 assert(dec <= DATETIME_MAX_DECIMALS);
1914 /* The value being stored must have been properly rounded or truncated */
1915 assert((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1916 (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1917
1918 mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
1919 switch (dec)
1920 {
1921 case 0:
1922 default:
1923 break;
1924 case 1:
1925 case 2:
1926 ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1927 break;
1928 case 3:
1929 case 4:
1930 mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1931 break;
1932 case 5:
1933 case 6:
1934 mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
1935 }
1936 }
1937
1938
1939 /*** TIMESTAMP low-level memory and disk representation routines ***/
1940
1941 /**
1942 Convert binary timestamp representation to in-memory representation.
1943
1944 @param OUT tm The variable to convert to.
1945 @param ptr The pointer to read the value from.
1946 @param dec Precision.
1947 */
my_timestamp_from_binary(struct timeval * tm,const uchar * ptr,uint dec)1948 void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
1949 {
1950 assert(dec <= DATETIME_MAX_DECIMALS);
1951 tm->tv_sec= mi_uint4korr(ptr);
1952 switch (dec)
1953 {
1954 case 0:
1955 default:
1956 tm->tv_usec= 0;
1957 break;
1958 case 1:
1959 case 2:
1960 tm->tv_usec= ((int) ptr[4]) * 10000;
1961 break;
1962 case 3:
1963 case 4:
1964 tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
1965 break;
1966 case 5:
1967 case 6:
1968 tm->tv_usec= mi_sint3korr(ptr + 4);
1969 }
1970 }
1971
1972
1973 /**
1974 Convert in-memory timestamp representation to on-disk representation.
1975
1976 @param tm The value to convert.
1977 @param OUT ptr The pointer to store the value to.
1978 @param dec Precision.
1979 */
my_timestamp_to_binary(const struct timeval * tm,uchar * ptr,uint dec)1980 void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
1981 {
1982 assert(dec <= DATETIME_MAX_DECIMALS);
1983 /* Stored value must have been previously properly rounded or truncated */
1984 assert((tm->tv_usec %
1985 (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1986 mi_int4store(ptr, tm->tv_sec);
1987 switch (dec)
1988 {
1989 case 0:
1990 default:
1991 break;
1992 case 1:
1993 case 2:
1994 ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
1995 break;
1996 case 3:
1997 case 4:
1998 mi_int2store(ptr + 4, tm->tv_usec / 100);
1999 break;
2000 /* Impossible second precision. Fall through */
2001 case 5:
2002 case 6:
2003 mi_int3store(ptr + 4, tm->tv_usec);
2004 }
2005 }
2006
2007 /****************************************/
2008
2009
2010 /**
2011 Convert a temporal value to packed numeric temporal representation,
2012 depending on its time_type.
2013
2014 @ltime The value to convert.
2015 @return Packed numeric time/date/datetime representation.
2016 */
TIME_to_longlong_packed(const MYSQL_TIME * ltime)2017 longlong TIME_to_longlong_packed(const MYSQL_TIME *ltime)
2018 {
2019 switch (ltime->time_type) {
2020 case MYSQL_TIMESTAMP_DATE:
2021 return TIME_to_longlong_date_packed(ltime);
2022 case MYSQL_TIMESTAMP_DATETIME:
2023 return TIME_to_longlong_datetime_packed(ltime);
2024 case MYSQL_TIMESTAMP_TIME:
2025 return TIME_to_longlong_time_packed(ltime);
2026 case MYSQL_TIMESTAMP_NONE:
2027 case MYSQL_TIMESTAMP_ERROR:
2028 return 0;
2029 }
2030 assert(0);
2031 return 0;
2032 }
2033
2034 /*** End of low level format routines ***/
2035