1
/* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
19
#include <myisampack.h>
20
/* Windows version of localtime_r() is declared in my_ptrhead.h */
21
#include <my_pthread.h>
23
ulonglong log_10_int[20]=
25
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
26
ULL(100000000), ULL(1000000000), ULL(10000000000), ULL(100000000000),
27
ULL(1000000000000), ULL(10000000000000), ULL(100000000000000),
28
ULL(1000000000000000), ULL(10000000000000000), ULL(100000000000000000),
29
ULL(1000000000000000000), ULL(10000000000000000000)
33
const char my_zero_datetime6[]= "0000-00-00 00:00:00.000000";
36
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
38
static uchar internal_format_positions[]=
39
{0, 1, 2, 3, 4, 5, 6, (uchar) 255};
41
static char time_separator=':';
43
static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */
44
uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
47
Offset of system time zone from UTC in seconds used to speed up
48
work of my_system_gmt_sec() function.
50
static long my_time_zone=0;
53
/* Calc days in one year. works with 0 <= year <= 99 */
55
uint calc_days_in_year(uint year)
57
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
63
Set MYSQL_TIME structure to 0000-00-00 00:00:00.000000
64
@param tm[OUT] The value to set.
65
@param time_type Timestasmp type
67
inline void set_zero_time(MYSQL_TIME *tm,
68
enum enum_mysql_timestamp_type time_type)
70
memset(tm, 0, sizeof(*tm));
71
tm->time_type= time_type;
76
Set hour, minute and second of a MYSQL_TIME variable to maximum time value.
77
Unlike set_max_time(), does not touch the other structure members.
79
inline void set_max_hhmmss(MYSQL_TIME *tm)
81
tm->hour= TIME_MAX_HOUR;
82
tm->minute= TIME_MAX_MINUTE;
83
tm->second= TIME_MAX_SECOND;
88
Set MYSQL_TIME variable to maximum time value
89
@param tm OUT The variable to set.
90
@param neg Sign: 1 if negative, 0 if positive.
92
void set_max_time(MYSQL_TIME *tm, my_bool neg)
94
set_zero_time(tm, MYSQL_TIMESTAMP_TIME);
101
@brief Check datetime value for validity according to flags.
103
@param[in] ltime Date to check.
104
@param[in] not_zero_date ltime is not the zero date
105
@param[in] flags flags to check
106
(see str_to_datetime() flags in my_time.h)
107
@param[out] was_cut set to 2 if value was invalid according to flags.
108
(Feb 29 in non-leap etc.) This remains unchanged
109
if value is not invalid.
110
@details Here we assume that year and month is ok!
111
If month is 0 we allow any date. (This only happens if we allow zero
112
date parts in str_to_datetime())
113
Disallow dates with zero year and non-zero month and/or day.
120
my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
121
ulonglong flags, int *was_cut)
125
if (((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
126
(ltime->month == 0 || ltime->day == 0))
128
*was_cut= MYSQL_TIME_WARN_ZERO_IN_DATE;
131
else if ((!(flags & TIME_INVALID_DATES) &&
132
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
133
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
136
*was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
140
else if (flags & TIME_NO_ZERO_DATE)
142
*was_cut= MYSQL_TIME_WARN_ZERO_DATE;
150
Check if TIME fields are fatally bad and cannot be further adjusted.
151
@param ltime Time value.
152
@retval TRUE if the value is fatally bad.
153
@retval FALSE if the value is Ok.
155
inline my_bool check_time_mmssff_range(const MYSQL_TIME *ltime)
157
return ltime->minute >= 60 || ltime->second >= 60 ||
158
ltime->second_part > 999999;
163
Check TIME range. The value can include day part,
164
for example: '1 10:20:30.123456'.
166
minute, second and second_part values are not checked
167
unless hour is equal TIME_MAX_HOUR.
169
@param ltime Rime value.
170
@returns Test result.
171
@retval FALSE if value is Ok.
172
@retval TRUE if value is out of range.
174
inline my_bool check_time_range_quick(const MYSQL_TIME *ltime)
176
longlong hour= (longlong) ltime->hour + 24LL * ltime->day;
177
/* The input value should not be fatally bad */
178
DBUG_ASSERT(!check_time_mmssff_range(ltime));
179
if (hour <= TIME_MAX_HOUR &&
180
(hour != TIME_MAX_HOUR || ltime->minute != TIME_MAX_MINUTE ||
181
ltime->second != TIME_MAX_SECOND || !ltime->second_part))
188
Check datetime, date, or normalized time (i.e. time without days) range.
189
@param ltime Datetime value.
191
@retval FALSE on success
192
@retval TRUE on error
194
my_bool check_datetime_range(const MYSQL_TIME *ltime)
197
In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
198
In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
201
ltime->year > 9999 || ltime->month > 12 || ltime->day > 31 ||
202
ltime->minute > 59 || ltime->second > 59 || ltime->second_part > 999999 ||
204
(ltime->time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23));
209
Convert a timestamp string to a MYSQL_TIME value.
214
length Length of string
215
l_time Date is stored here
216
flags Bitmap of following items
217
TIME_FUZZY_DATE Set if we should allow partial dates
218
TIME_DATETIME_ONLY Set if we only allow full datetimes.
219
TIME_NO_ZERO_IN_DATE Don't allow partial dates
220
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
221
TIME_INVALID_DATES Allow 2000-02-31
222
status Conversion status
226
At least the following formats are recogniced (based on number of digits)
227
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
228
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
229
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
230
Also dates where all parts are zero are allowed
232
The second part may have an optional .###### fraction part.
235
This function should work with a format position vector as long as the
236
following things holds:
237
- All date are kept together and all time parts are kept together
238
- Date and time parts must be separated by blank
239
- Second fractions must come after second part and be separated
240
by a '.'. (The second fractions are optional)
241
- AM/PM must come after second fractions (or after seconds if no fractions)
242
- Year must always been specified.
243
- If time is before date, then we will use datetime format only if
244
the argument consist of two parts, separated by space.
245
Otherwise we will assume the argument is a date.
246
- The hour part must be specified in hour-minute-second order.
248
status->warnings is set to:
250
MYSQL_TIME_WARN_TRUNCATED If value was cut during conversion
251
MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
253
l_time->time_type is set as follows:
254
MYSQL_TIMESTAMP_NONE String wasn't a timestamp, like
255
[DD [HH:[MM:[SS]]]].fraction.
256
l_time is not changed.
257
MYSQL_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
258
MYSQL_TIMESTAMP_DATETIME Full timestamp
259
MYSQL_TIMESTAMP_ERROR Timestamp with wrong values.
260
All elements in l_time is set to 0
266
#define MAX_DATE_PARTS 8
269
str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
270
ulonglong flags, MYSQL_TIME_STATUS *status)
272
uint field_length, UNINIT_VAR(year_length), digits, i, number_of_fields;
273
uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
274
uint add_hours= 0, start_loop;
275
ulong not_zero_date, allow_space;
276
my_bool is_internal_format;
277
const char *pos, *UNINIT_VAR(last_field_pos);
278
const char *end=str+length;
279
const uchar *format_position;
280
my_bool found_delimitier= 0, found_space= 0;
281
uint frac_pos, frac_len;
282
DBUG_ENTER("str_to_datetime");
283
DBUG_PRINT("ENTER",("str: %.*s",length,str));
285
LINT_INIT(field_length);
287
my_time_status_init(status);
289
/* Skip space at start */
290
for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
292
if (str == end || ! my_isdigit(&my_charset_latin1, *str))
294
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
295
l_time->time_type= MYSQL_TIMESTAMP_NONE;
299
is_internal_format= 0;
300
/* This has to be changed if want to activate different timestamp formats */
301
format_position= internal_format_positions;
304
Calculate number of digits in first part.
305
If length= 8 or >= 14 then year is of format YYYY.
306
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
309
pos != end && (my_isdigit(&my_charset_latin1,*pos) || *pos == 'T');
313
digits= (uint) (pos-str);
314
start_loop= 0; /* Start of scan loop */
315
date_len[format_position[0]]= 0; /* Length of year field */
316
if (pos == end || *pos == '.')
318
/* Found date in internal format (only numbers like YYYYMMDD) */
319
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
320
field_length= year_length;
321
is_internal_format= 1;
322
format_position= internal_format_positions;
326
if (format_position[0] >= 3) /* If year is after HHMMDD */
329
If year is not in first part then we have to determinate if we got
330
a date field or a datetime field.
331
We do this by checking if there is two numbers separated by
334
while (pos < end && !my_isspace(&my_charset_latin1, *pos))
336
while (pos < end && !my_isdigit(&my_charset_latin1, *pos))
340
if (flags & TIME_DATETIME_ONLY)
342
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
343
l_time->time_type= MYSQL_TIMESTAMP_NONE;
344
DBUG_RETURN(1); /* Can't be a full datetime */
346
/* Date field. Set hour, minutes and seconds to 0 */
347
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
348
start_loop= 5; /* Start with first date part */
352
field_length= format_position[0] == 0 ? 4 : 2;
356
Only allow space in the first "part" of the datetime field and:
357
- after days, part seconds
358
- before and after AM/PM (handled by code later)
360
2003-03-03 20:00:20 AM
361
20:00:20.000000 AM 03-03-2000
363
i= MY_MAX((uint) format_position[0], (uint) format_position[1]);
364
set_if_bigger(i, (uint) format_position[2]);
365
allow_space= ((1 << i) | (1 << format_position[6]));
366
allow_space&= (1 | 2 | 4 | 8 | 64);
370
i < MAX_DATE_PARTS-1 && str != end &&
371
my_isdigit(&my_charset_latin1,*str);
374
const char *start= str;
375
ulong tmp_value= (uint) (uchar) (*str++ - '0');
378
Internal format means no delimiters; every field has a fixed
379
width. Otherwise, we scan until we find a delimiter and discard
380
leading zeroes -- except for the microsecond part, where leading
381
zeroes are significant, and where we never process more than six
384
my_bool scan_until_delim= !is_internal_format &&
385
((i != format_position[6]));
387
while (str != end && my_isdigit(&my_charset_latin1,str[0]) &&
388
(scan_until_delim || --field_length))
390
tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0');
393
date_len[i]= (uint) (str - start);
394
if (tmp_value > 999999) /* Impossible date part */
396
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
397
l_time->time_type= MYSQL_TIMESTAMP_NONE;
401
not_zero_date|= tmp_value;
403
/* Length of next field */
404
field_length= format_position[i+1] == 0 ? 4 : 2;
406
if ((last_field_pos= str) == end)
408
i++; /* Register last found part */
411
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
412
if (i == format_position[2] && *str == 'T')
414
str++; /* ISO8601: CCYYMMDDThhmmss */
417
if (i == format_position[5]) /* Seconds */
419
if (*str == '.') /* Followed by part seconds */
423
Shift last_field_pos, so '2001-01-01 00:00:00.'
424
is treated as a valid value
427
field_length= 6; /* 6 digits */
432
(my_ispunct(&my_charset_latin1,*str) ||
433
my_isspace(&my_charset_latin1,*str)))
435
if (my_isspace(&my_charset_latin1,*str))
437
if (!(allow_space & (1 << i)))
439
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
440
l_time->time_type= MYSQL_TIMESTAMP_NONE;
446
found_delimitier= 1; /* Should be a 'normal' date */
448
/* Check if next position is AM/PM */
449
if (i == format_position[6]) /* Seconds, time for AM/PM */
451
i++; /* Skip AM/PM part */
452
if (format_position[7] != 255) /* If using AM/PM */
454
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
456
if (str[0] == 'p' || str[0] == 'P')
458
else if (str[0] != 'a' || str[0] != 'A')
459
continue; /* Not AM/PM */
460
str+= 2; /* Skip AM/PM */
461
/* Skip space after AM/PM */
462
while (str != end && my_isspace(&my_charset_latin1,*str))
469
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
471
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
472
l_time->time_type= MYSQL_TIMESTAMP_NONE;
473
DBUG_RETURN(1); /* Can't be a datetime */
478
number_of_fields= i - start_loop;
479
while (i < MAX_DATE_PARTS)
485
if (!is_internal_format)
487
year_length= date_len[(uint) format_position[0]];
488
if (!year_length) /* Year must be specified */
490
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
491
l_time->time_type= MYSQL_TIMESTAMP_NONE;
495
l_time->year= date[(uint) format_position[0]];
496
l_time->month= date[(uint) format_position[1]];
497
l_time->day= date[(uint) format_position[2]];
498
l_time->hour= date[(uint) format_position[3]];
499
l_time->minute= date[(uint) format_position[4]];
500
l_time->second= date[(uint) format_position[5]];
502
frac_pos= (uint) format_position[6];
503
frac_len= date_len[frac_pos];
504
status->fractional_digits= frac_len;
506
date[frac_pos]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - frac_len];
507
l_time->second_part= date[frac_pos];
509
if (format_position[7] != (uchar) 255)
511
if (l_time->hour > 12)
513
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
516
l_time->hour= l_time->hour%12 + add_hours;
521
l_time->year= date[0];
522
l_time->month= date[1];
523
l_time->day= date[2];
524
l_time->hour= date[3];
525
l_time->minute= date[4];
526
l_time->second= date[5];
528
date[6]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - date_len[6]];
529
l_time->second_part=date[6];
530
status->fractional_digits= date_len[6];
534
if (year_length == 2 && not_zero_date)
535
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
538
Set time_type before check_datetime_range(),
539
as the latter relies on initialized time_type value.
541
l_time->time_type= (number_of_fields <= 3 ?
542
MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
544
if (number_of_fields < 3 || check_datetime_range(l_time))
546
/* Only give warning for a zero date if there is some garbage after */
547
if (!not_zero_date) /* If zero date */
549
for (; str != end ; str++)
551
if (!my_isspace(&my_charset_latin1, *str))
553
not_zero_date= 1; /* Give warning */
558
status->warnings|= not_zero_date ? MYSQL_TIME_WARN_TRUNCATED :
559
MYSQL_TIME_WARN_ZERO_DATE;
563
if (check_date(l_time, not_zero_date != 0, flags, &status->warnings))
566
/* Scan all digits left after microseconds */
567
if (status->fractional_digits == 6 && str != end)
569
if (my_isdigit(&my_charset_latin1, *str))
572
We don't need the exact nanoseconds value.
573
Knowing the first digit is enough for rounding.
575
status->nanoseconds= 100 * (int) (*str++ - '0');
576
for (; str != end && my_isdigit(&my_charset_latin1, *str); str++)
581
for (; str != end ; str++)
583
if (!my_isspace(&my_charset_latin1,*str))
585
status->warnings= MYSQL_TIME_WARN_TRUNCATED;
593
set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
599
Convert a time string to a MYSQL_TIME struct.
603
str A string in full TIMESTAMP format or
604
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
606
There may be an optional [.second_part] after seconds
608
l_time Store result here
609
status Conversion status
611
status.warning is set to:
612
MYSQL_TIME_WARN_TRUNCATED flag if the input string
613
was cut during conversion, and/or
614
MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is out of range.
617
Because of the extra days argument, this function can only
618
work with times where the time arguments are in the above order.
625
my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
626
MYSQL_TIME_STATUS *status)
630
const char *end=str+length, *end_of_days;
631
my_bool found_days,found_hours;
634
my_time_status_init(status);
636
for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
638
if (str != end && *str == '-')
647
/* Check first if this is a full TIMESTAMP */
649
{ /* Probably full timestamp */
650
(void) str_to_datetime(str, length, l_time,
651
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), status);
652
if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
653
return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
654
my_time_status_init(status);
657
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
658
for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
659
value=value*10L + (long) (*str - '0');
661
if (value > UINT_MAX)
664
/* Skip all space after 'days' */
666
for (; str != end && my_isspace(&my_charset_latin1, str[0]) ; str++)
670
found_days=found_hours=0;
671
if ((uint) (end-str) > 1 && str != end_of_days &&
672
my_isdigit(&my_charset_latin1, *str))
673
{ /* Found days part */
674
date[0]= (ulong) value;
675
state= 1; /* Assume next is hours */
678
else if ((end-str) > 1 && *str == time_separator &&
679
my_isdigit(&my_charset_latin1, str[1]))
681
date[0]= 0; /* Assume we found hours */
682
date[1]= (ulong) value;
685
str++; /* skip ':' */
689
/* String given as one number; assume HHMMSS format */
691
date[1]= (ulong) (value/10000);
692
date[2]= (ulong) (value/100 % 100);
693
date[3]= (ulong) (value % 100);
698
/* Read hours, minutes and seconds */
701
for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
702
value=value*10L + (long) (*str - '0');
703
date[state++]= (ulong) value;
704
if (state == 4 || (end-str) < 2 || *str != time_separator ||
705
!my_isdigit(&my_charset_latin1,str[1]))
707
str++; /* Skip time_separator (':') */
712
/* Fix the date to assume that seconds was given */
713
if (!found_hours && !found_days)
715
bmove_upp((uchar*) (date+4), (uchar*) (date+state),
716
sizeof(long)*(state-1));
717
memset(date, 0, sizeof(long)*(4-state));
720
memset((date+state), 0, sizeof(long)*(4-state));
724
/* Get fractional second part */
725
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1]))
728
str++; value=(uint) (uchar) (*str - '0');
729
while (++str != end && my_isdigit(&my_charset_latin1, *str))
731
if (field_length-- > 0)
732
value= value*10 + (uint) (uchar) (*str - '0');
734
if (field_length >= 0)
736
status->fractional_digits= DATETIME_MAX_DECIMALS - field_length;
737
if (field_length > 0)
738
value*= (long) log_10_int[field_length];
742
/* Scan digits left after microseconds */
743
status->fractional_digits= 6;
744
status->nanoseconds= 100 * (int) (str[-1] - '0');
745
for ( ; str != end && my_isdigit(&my_charset_latin1, *str); str++)
748
date[4]= (ulong) value;
750
else if ((end - str) == 1 && *str == '.')
758
/* Check for exponent part: E<gigit> | E<sign><digit> */
759
/* (may occur as result of %g formatting of time value) */
760
if ((end - str) > 1 &&
761
(*str == 'e' || *str == 'E') &&
762
(my_isdigit(&my_charset_latin1, str[1]) ||
763
((str[1] == '-' || str[1] == '+') &&
765
my_isdigit(&my_charset_latin1, str[2]))))
768
if (internal_format_positions[7] != 255)
770
/* Read a possible AM/PM */
771
while (str != end && my_isspace(&my_charset_latin1, *str))
773
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
775
if (str[0] == 'p' || str[0] == 'P')
778
date[1]= date[1]%12 + 12;
780
else if (str[0] == 'a' || str[0] == 'A')
785
/* Integer overflow checks */
786
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
787
date[2] > UINT_MAX || date[3] > UINT_MAX ||
791
l_time->year= 0; /* For protocol::store_time */
795
l_time->hour= date[1] + date[0] * 24; /* Mix days and hours */
797
l_time->minute= date[2];
798
l_time->second= date[3];
799
l_time->second_part= date[4];
800
l_time->time_type= MYSQL_TIMESTAMP_TIME;
802
if (check_time_mmssff_range(l_time))
804
status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
808
/* Adjust the value into supported MYSQL_TIME range */
809
adjust_time_range(l_time, &status->warnings);
811
/* Check if there is garbage at end of the MYSQL_TIME specification */
816
if (!my_isspace(&my_charset_latin1,*str))
818
status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
821
} while (++str != end);
828
Convert number to TIME
829
@param nr Number to convert.
830
@param OUT ltime Variable to convert to.
831
@param OUT warnings Warning vector.
834
@retval true No. is out of range
837
number_to_time(longlong nr, MYSQL_TIME *ltime, int *warnings)
839
if (nr > TIME_MAX_VALUE)
841
/* For huge numbers try full DATETIME, like str_to_time does. */
842
if (nr >= 10000000000LL) /* '0001-00-00 00-00-00' */
844
int warnings_backup= *warnings;
845
if (number_to_datetime(nr, ltime, 0, warnings) != LL(-1))
847
*warnings= warnings_backup;
849
set_max_time(ltime, 0);
850
*warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
853
else if (nr < -TIME_MAX_VALUE)
855
set_max_time(ltime, 1);
856
*warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
859
if ((ltime->neg= (nr < 0)))
861
if (nr % 100 >= 60 || nr / 100 % 100 >= 60) /* Check hours and minutes */
863
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
864
*warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
867
ltime->time_type= MYSQL_TIMESTAMP_TIME;
868
ltime->year= ltime->month= ltime->day= 0;
869
TIME_set_hhmmss(ltime, nr);
870
ltime->second_part= 0;
876
Adjust 'time' value to lie in the MYSQL_TIME range.
877
If the time value lies outside of the range [-838:59:59, 838:59:59],
878
set it to the closest endpoint of the range and set
879
MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
881
@param time pointer to MYSQL_TIME value
882
@param warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
884
void adjust_time_range(struct st_mysql_time *my_time, int *warning)
886
DBUG_ASSERT(!check_time_mmssff_range(my_time));
887
if (check_time_range_quick(my_time))
889
my_time->day= my_time->second_part= 0;
890
set_max_hhmmss(my_time);
891
*warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
897
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
902
void my_init_time(void)
905
struct tm *l_time,tm_tmp;
909
seconds= (time_t) time((time_t*) 0);
910
localtime_r(&seconds,&tm_tmp);
912
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
913
my_time.year= (uint) l_time->tm_year+1900;
914
my_time.month= (uint) l_time->tm_mon+1;
915
my_time.day= (uint) l_time->tm_mday;
916
my_time.hour= (uint) l_time->tm_hour;
917
my_time.minute= (uint) l_time->tm_min;
918
my_time.second= (uint) l_time->tm_sec;
919
my_time.time_type= MYSQL_TIMESTAMP_DATETIME;
921
my_time.second_part= 0;
922
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
927
Handle 2 digit year conversions
934
Year between 1970-2069
937
uint year_2000_handling(uint year)
939
if ((year=year+1900) < 1900+YY_PART_YEAR)
946
Calculate nr of day since year 0 in new date-system (from 1615)
950
year Year (exact 4 digit year, no year conversions)
954
NOTES: 0000-00-00 is a valid date, and will return 0
957
Days since 0000-00-00
960
long calc_daynr(uint year,uint month,uint day)
964
int y= year; /* may be < 0 temporarily */
965
DBUG_ENTER("calc_daynr");
967
if (y == 0 && month == 0)
968
DBUG_RETURN(0); /* Skip errors */
969
/* Cast to int to be able to handle month == 0 */
970
delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day);
974
delsum-= (long) ((int) month * 4 + 23) / 10;
975
temp=(int) ((y/100+1)*3)/4;
976
DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld",
977
y+(month <= 2),month,day,delsum+y/4-temp));
978
DBUG_ASSERT(delsum+(int) y/4-temp >= 0);
979
DBUG_RETURN(delsum+(int) y/4-temp);
984
Convert time in MYSQL_TIME representation in system time zone to its
985
my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
989
t - time value to be converted
990
my_timezone - pointer to long where offset of system time zone
991
from UTC will be stored for caching
992
in_dst_time_gap - set to true if time falls into spring time-gap
995
The idea is to cache the time zone offset from UTC (including daylight
996
saving time) for the next call to make things faster. But currently we
997
just calculate this offset during startup (by calling my_init_time()
998
function) and use it all the time.
999
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
1003
Time in UTC seconds since Unix Epoch representation.
1006
my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
1007
my_bool *in_dst_time_gap)
1012
MYSQL_TIME tmp_time;
1013
MYSQL_TIME *t= &tmp_time;
1014
struct tm *l_time,tm_tmp;
1015
long diff, current_timezone;
1018
Use temp variable to avoid trashing input data, which could happen in
1019
case of shift required for boundary dates processing.
1021
memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME));
1023
if (!validate_timestamp_range(t))
1027
Calculate the gmt time based on current time and timezone
1028
The -1 on the end is to ensure that if have a date that exists twice
1029
(like 2002-10-27 02:00:0 MET), we will find the initial date.
1031
By doing -3600 we will have to call localtime_r() several times, but
1032
I couldn't come up with a better way to get a repeatable result :(
1034
We can't use mktime() as it's buggy on many platforms and not thread safe.
1036
Note: this code assumes that our time_t estimation is not too far away
1037
from real value (we assume that localtime_r(tmp) will return something
1038
within 24 hrs from t) which is probably true for all current time zones.
1040
Note2: For the dates, which have time_t representation close to
1041
MAX_INT32 (efficient time_t limit for supported platforms), we should
1042
do a small trick to avoid overflow. That is, convert the date, which is
1043
two days earlier, and then add these days to the final value.
1045
The same trick is done for the values close to 0 in time_t
1046
representation for platfroms with unsigned time_t (QNX).
1048
To be more verbose, here is a sample (extracted from the code below):
1049
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
1050
would return -2147480896 because of the long type overflow. In result
1051
we would get 1901 year in localtime_r(), which is an obvious error.
1053
Alike problem raises with the dates close to Epoch. E.g.
1054
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
1057
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
1058
wil give us a date around 2106 year. Which is no good.
1060
Theoreticaly, there could be problems with the latter conversion:
1061
there are at least two timezones, which had time switches near 1 Jan
1062
of 1970 (because of political reasons). These are America/Hermosillo and
1063
America/Mazatlan time zones. They changed their offset on
1064
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
1065
the code below will give incorrect results for dates close to
1066
1970-01-01, in the case OS takes into account these historical switches.
1067
Luckily, it seems that we support only one platform with unsigned
1068
time_t. It's QNX. And QNX does not support historical timezone data at all.
1069
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
1070
historical information for localtime_r() etc. That is, the problem is not
1073
We are safe with shifts close to MAX_INT32, as there are no known
1074
time switches on Jan 2038 yet :)
1076
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
1079
Below we will pass (uint) (t->day - shift) to calc_daynr.
1080
As we don't want to get an overflow here, we will shift
1081
only safe dates. That's why we have (t->day > 4) above.
1086
#ifdef TIME_T_UNSIGNED
1090
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
1091
1970, 1 of Jan. For both dates we use shift, which is added
1092
to t->day in order to step out a bit from the border.
1093
This is required for platforms, where time_t is unsigned.
1094
As far as I know, among the platforms we support it's only QNX.
1095
Note: the order of below if-statements is significant.
1098
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
1105
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
1116
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
1117
(long) days_at_timestart) * SECONDS_IN_24H +
1118
(long) t->hour*3600L +
1119
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
1122
current_timezone= my_time_zone;
1123
localtime_r(&tmp,&tm_tmp);
1127
(t->hour != (uint) l_time->tm_hour ||
1128
t->minute != (uint) l_time->tm_min ||
1129
t->second != (uint) l_time->tm_sec);
1131
{ /* One check should be enough ? */
1132
/* Get difference in days */
1133
int days= t->day - l_time->tm_mday;
1135
days= 1; /* Month has wrapped */
1138
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
1139
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
1140
(long) ((int) t->second - (int) l_time->tm_sec));
1141
current_timezone+= diff+3600; /* Compensate for -3600 above */
1142
tmp+= (time_t) diff;
1143
localtime_r(&tmp,&tm_tmp);
1147
Fix that if we are in the non existing daylight saving time hour
1148
we move the start of the next real hour.
1150
This code doesn't handle such exotical thing as time-gaps whose length
1151
is more than one hour or non-integer (latter can theoretically happen
1152
if one of seconds will be removed due leap correction, or because of
1153
general time correction like it happened for Africa/Monrovia time zone
1156
if (loop == 2 && t->hour != (uint) l_time->tm_hour)
1158
int days= t->day - l_time->tm_mday;
1160
days=1; /* Month has wrapped */
1163
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
1164
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
1165
(long) ((int) t->second - (int) l_time->tm_sec));
1167
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
1168
else if (diff == -3600)
1169
tmp-=t->minute*60 + t->second; /* Move to previous hour */
1171
*in_dst_time_gap= 1;
1173
*my_timezone= current_timezone;
1176
/* shift back, if we were dealing with boundary dates */
1177
tmp+= shift * SECONDS_IN_24H;
1180
This is possible for dates, which slightly exceed boundaries.
1181
Conversion will pass ok for them, but we don't allow them.
1182
First check will pass for platforms with signed time_t.
1183
instruction above (tmp+= shift*86400L) could exceed
1184
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
1185
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
1186
with unsigned time_t tmp+= shift*86400L might result in a number,
1187
larger then TIMESTAMP_MAX_VALUE, so another check will work.
1189
if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp))
1192
return (my_time_t) tmp;
1193
} /* my_system_gmt_sec */
1197
Print the microsecond part: ".NNN"
1198
@param to OUT The string pointer to print at
1199
@param useconds The microseconds value.
1200
@param dec Precision, between 1 and 6.
1201
@return The length of the result string.
1204
my_useconds_to_str(char *to, ulong useconds, uint dec)
1206
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1207
return sprintf(to, ".%0*lu", (int) dec,
1208
useconds / (ulong) log_10_int[DATETIME_MAX_DECIMALS - dec]);
1213
Functions to convert time/date/datetime value to a string,
1214
using default format.
1215
This functions don't check that given MYSQL_TIME structure members are
1216
in valid range. If they are not, return value won't reflect any
1217
valid date either. Additionally, make_time doesn't take into
1218
account time->day member: it's assumed that days have been converted
1222
number of characters written to 'to'
1225
int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1227
uint extra_hours= 0;
1228
int len= sprintf(to, "%s%02u:%02u:%02u", (l_time->neg ? "-" : ""),
1229
extra_hours + l_time->hour, l_time->minute, l_time->second);
1231
len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1235
int my_date_to_str(const MYSQL_TIME *l_time, char *to)
1237
return sprintf(to, "%04u-%02u-%02u",
1238
l_time->year, l_time->month, l_time->day);
1243
Convert datetime to a string 'YYYY-MM-DD hh:mm:ss'.
1244
Open coded for better performance.
1245
This code previously resided in field.cc, in Field_timestamp::val_str().
1247
@param to OUT The string pointer to print at.
1248
@param ltime The MYSQL_TIME value.
1249
@return The length of the result string.
1252
TIME_to_datetime_str(char *to, const MYSQL_TIME *ltime)
1256
temp= ltime->year / 100;
1257
*to++= (char) ('0' + temp / 10);
1258
*to++= (char) ('0' + temp % 10);
1259
temp= ltime->year % 100;
1260
*to++= (char) ('0' + temp / 10);
1261
*to++= (char) ('0' + temp % 10);
1266
temp= temp-temp2 * 10;
1267
*to++= (char) ('0' + (char) (temp2));
1268
*to++= (char) ('0' + (char) (temp));
1273
temp= temp - temp2 * 10;
1274
*to++= (char) ('0' + (char) (temp2));
1275
*to++= (char) ('0' + (char) (temp));
1280
temp= temp - temp2 * 10;
1281
*to++= (char) ('0' + (char) (temp2));
1282
*to++= (char) ('0' + (char) (temp));
1285
temp= ltime->minute;
1287
temp= temp - temp2 * 10;
1288
*to++= (char) ('0' + (char) (temp2));
1289
*to++= (char) ('0' + (char) (temp));
1292
temp= ltime->second;
1294
temp= temp - temp2 * 10;
1295
*to++= (char) ('0' + (char) (temp2));
1296
*to++= (char) ('0' + (char) (temp));
1302
Print a datetime value with an optional fractional part.
1304
@l_time The MYSQL_TIME value to print.
1305
@to OUT The string pointer to print at.
1306
@return The length of the result string.
1308
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1310
int len= TIME_to_datetime_str(to, l_time);
1312
len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1320
Convert struct DATE/TIME/DATETIME value to string using built-in
1321
MySQL time conversion formats.
1327
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1330
int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1332
switch (l_time->time_type) {
1333
case MYSQL_TIMESTAMP_DATETIME:
1334
return my_datetime_to_str(l_time, to, dec);
1335
case MYSQL_TIMESTAMP_DATE:
1336
return my_date_to_str(l_time, to);
1337
case MYSQL_TIMESTAMP_TIME:
1338
return my_time_to_str(l_time, to, dec);
1339
case MYSQL_TIMESTAMP_NONE:
1340
case MYSQL_TIMESTAMP_ERROR:
1351
Print a timestamp with an oprional fractional part: XXXXX[.YYYYY]
1353
@param tm The timestamp value to print.
1354
@param OUT to The string pointer to print at.
1355
@param dec Precision, in the range 0..6.
1356
@return The length of the result string.
1358
int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
1360
int len= sprintf(to, "%d", (int) tm->tv_sec);
1362
len+= my_useconds_to_str(to + len, tm->tv_usec, dec);
1368
Convert datetime value specified as number to broken-down TIME
1369
representation and form value of DATETIME type as side-effect.
1372
number_to_datetime()
1373
nr - datetime value as number
1374
time_res - pointer for structure for broken-down representation
1375
flags - flags to use in validating date, as in str_to_datetime()
1377
1 If value was cut during conversion
1378
2 check_date(date,flags) considers date invalid
1381
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1382
YYYYMMDDHHMMSS to broken-down MYSQL_TIME representation. Return value in
1383
YYYYMMDDHHMMSS format as side-effect.
1385
This function also checks if datetime value fits in DATETIME range.
1388
-1 Timestamp with wrong values
1389
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1390
Datetime value in YYYYMMDDHHMMSS format.
1392
was_cut if return value -1: one of
1393
- MYSQL_TIME_WARN_OUT_OF_RANGE
1394
- MYSQL_TIME_WARN_ZERO_DATE
1395
- MYSQL_TIME_WARN_TRUNCATED
1399
longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
1400
ulonglong flags, int *was_cut)
1405
memset(time_res, 0, sizeof(*time_res));
1406
time_res->time_type=MYSQL_TIMESTAMP_DATE;
1408
if (nr == LL(0) || nr >= LL(10000101000000))
1410
time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1411
if (nr > 99999999999999LL) /* 9999-99-99 99:99:99 */
1413
*was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
1420
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1422
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1425
if (nr < (YY_PART_YEAR)*10000L+101L)
1429
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1433
Though officially we support DATE values from 1000-01-01 only, one can
1434
easily insert a value like 1-1-1. So, for consistency reasons such dates
1435
are allowed when TIME_FUZZY_DATE is set.
1437
if (nr < 10000101L && !(flags & TIME_FUZZY_DATE))
1439
if (nr <= 99991231L)
1444
if (nr < 101000000L)
1447
time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1449
if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
1451
nr= nr+LL(20000000000000); /* YYMMDDHHMMSS, 2000-2069 */
1454
if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
1456
if (nr <= LL(991231235959))
1457
nr= nr+LL(19000000000000); /* YYMMDDHHMMSS, 1970-1999 */
1460
part1=(long) (nr/LL(1000000));
1461
part2=(long) (nr - (longlong) part1*LL(1000000));
1462
time_res->year= (int) (part1/10000L); part1%=10000L;
1463
time_res->month= (int) part1 / 100;
1464
time_res->day= (int) part1 % 100;
1465
time_res->hour= (int) (part2/10000L); part2%=10000L;
1466
time_res->minute=(int) part2 / 100;
1467
time_res->second=(int) part2 % 100;
1469
if (!check_datetime_range(time_res) &&
1470
!check_date(time_res, (nr != 0), flags, was_cut))
1473
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1474
if (!nr && (flags & TIME_NO_ZERO_DATE))
1478
*was_cut= MYSQL_TIME_WARN_TRUNCATED;
1484
Convert time value to integer in YYYYMMDDHHMMSS.
1485
@param my_time The MYSQL_TIME value to convert.
1486
@return A number in format YYYYMMDDHHMMSS.
1488
ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)
1490
return ((ulonglong) (my_time->year * 10000UL +
1491
my_time->month * 100UL +
1492
my_time->day) * ULL(1000000) +
1493
(ulonglong) (my_time->hour * 10000UL +
1494
my_time->minute * 100UL +
1501
Convert MYSQL_TIME value to integer in YYYYMMDD format
1502
@param my_time The MYSQL_TIME value to convert.
1503
@return A number in format YYYYMMDD.
1505
ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *my_time)
1507
return (ulonglong) (my_time->year * 10000UL + my_time->month * 100UL +
1513
Convert MYSQL_TIME value to integer in HHMMSS format.
1514
This function doesn't take into account time->day member:
1515
it's assumed that days have been converted to hours already.
1516
@param my_time The TIME value to convert.
1517
@return The number in HHMMSS format.
1519
ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time)
1521
return (ulonglong) (my_time->hour * 10000UL +
1522
my_time->minute * 100UL +
1528
Set day, month and year from a number
1529
@param ltime MYSQL_TIME variable
1530
@param yymmdd Number in YYYYMMDD format
1532
void TIME_set_yymmdd(MYSQL_TIME *ltime, uint yymmdd)
1534
ltime->day= (int) (yymmdd % 100);
1535
ltime->month= (int) (yymmdd / 100) % 100;
1536
ltime->year= (int) (yymmdd / 10000);
1541
Set hour, minute and secondr from a number
1542
@param ltime MYSQL_TIME variable
1543
@param hhmmss Number in HHMMSS format
1545
void TIME_set_hhmmss(MYSQL_TIME *ltime, uint hhmmss)
1547
ltime->second= (int) (hhmmss % 100);
1548
ltime->minute= (int) (hhmmss / 100) % 100;
1549
ltime->hour= (int) (hhmmss / 10000);
1554
Convert struct MYSQL_TIME (date and time split into year/month/day/hour/...
1555
to a number in format YYYYMMDDHHMMSS (DATETIME),
1556
YYYYMMDD (DATE) or HHMMSS (TIME).
1562
The function is used when we need to convert value of time item
1563
to a number if it's used in numeric context, i. e.:
1564
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1568
This function doesn't check that given MYSQL_TIME structure members are
1569
in valid range. If they are not, return value won't reflect any
1573
ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time)
1575
switch (my_time->time_type) {
1576
case MYSQL_TIMESTAMP_DATETIME:
1577
return TIME_to_ulonglong_datetime(my_time);
1578
case MYSQL_TIMESTAMP_DATE:
1579
return TIME_to_ulonglong_date(my_time);
1580
case MYSQL_TIMESTAMP_TIME:
1581
return TIME_to_ulonglong_time(my_time);
1582
case MYSQL_TIMESTAMP_NONE:
1583
case MYSQL_TIMESTAMP_ERROR:
1592
/*** TIME low-level memory and disk representation routines ***/
1597
1 bit sign (Used for sign, when on disk)
1598
1 bit unused (Reserved for wider hour range, e.g. for intervals)
1602
24 bits microseconds (0-999999)
1604
Total: 48 bits = 6 bytes
1605
Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1610
Convert time value to numeric packed representation.
1612
@param ltime The value to convert.
1613
@return Numeric packed representation.
1615
longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
1617
/* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
1618
long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
1619
(ltime->minute << 6) | ltime->second;
1620
longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
1621
return ltime->neg ? -tmp : tmp;
1626
Convert time packed numeric representation to time.
1628
@param OUT ltime The MYSQL_TIME variable to set.
1629
@param tmp The packed numeric representation.
1631
void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
1634
if ((ltime->neg= (tmp < 0)))
1636
hms= MY_PACKED_TIME_GET_INT_PART(tmp);
1637
ltime->year= (uint) 0;
1638
ltime->month= (uint) 0;
1639
ltime->day= (uint) 0;
1640
ltime->hour= (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
1641
ltime->minute= (uint) (hms >> 6) % (1 << 6); /* 6 bits starting at 6th */
1642
ltime->second= (uint) hms % (1 << 6); /* 6 bits starting at 0th */
1643
ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1644
ltime->time_type= MYSQL_TIMESTAMP_TIME;
1649
Calculate binary size of packed numeric time representation.
1651
@param dec Precision.
1653
uint my_time_binary_length(uint dec)
1655
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1656
return 3 + (dec + 1) / 2;
1661
On disk we convert from signed representation to unsigned
1662
representation using TIMEF_OFS, so all values become binary comparable.
1664
#define TIMEF_OFS 0x800000000000LL
1665
#define TIMEF_INT_OFS 0x800000LL
1669
Convert in-memory numeric time representation to on-disk representation
1671
@param nr Value in packed numeric time format.
1672
@param OUT ptr The buffer to put value at.
1673
@param dec Precision.
1675
void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1677
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1678
/* Make sure the stored value was previously properly rounded or truncated */
1679
DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1680
(int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1686
mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1691
mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1692
ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1697
mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1698
mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1703
mi_int6store(ptr, nr + TIMEF_OFS);
1710
Convert on-disk time representation to in-memory packed numeric
1713
@param ptr The pointer to read the value at.
1714
@param dec Precision.
1715
@return Packed numeric time representation.
1717
longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
1719
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1726
longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1727
return MY_PACKED_TIME_MAKE_INT(intpart);
1732
longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1733
int frac= (uint) ptr[3];
1734
if (intpart < 0 && frac)
1737
Negative values are stored with reverse fractional part order,
1738
for binary sort compatibility.
1740
Disk value intpart frac Time value Memory value
1741
800000.00 0 0 00:00:00.00 0000000000.000000
1742
7FFFFF.FF -1 255 -00:00:00.01 FFFFFFFFFF.FFD8F0
1743
7FFFFF.9D -1 99 -00:00:00.99 FFFFFFFFFF.F0E4D0
1744
7FFFFF.00 -1 0 -00:00:01.00 FFFFFFFFFF.000000
1745
7FFFFE.FF -1 255 -00:00:01.01 FFFFFFFFFE.FFD8F0
1746
7FFFFE.F6 -2 246 -00:00:01.10 FFFFFFFFFE.FE7960
1748
Formula to convert fractional part from disk format
1749
(now stored in "frac" variable) to absolute value: "0x100 - frac".
1750
To reconstruct in-memory value, we shift
1751
to the next integer value and then substruct fractional part.
1753
intpart++; /* Shift to the next integer value */
1754
frac-= 0x100; /* -(0x100 - frac) */
1756
return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
1762
longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1763
int frac= mi_uint2korr(ptr + 3);
1764
if (intpart < 0 && frac)
1767
Fix reverse fractional part order: "0x10000 - frac".
1768
See comments for FSP=1 and FSP=2 above.
1770
intpart++; /* Shift to the next integer value */
1771
frac-= 0x10000; /* -(0x10000-frac) */
1773
return MY_PACKED_TIME_MAKE(intpart, frac * 100);
1778
return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
1783
/*** DATETIME and DATE low-level memory and disk representation routines ***/
1786
1 bit sign (used when on disk)
1787
17 bits year*13+month (year 0-9999, month 0-12)
1790
6 bits minute (0-59)
1791
6 bits second (0-59)
1792
24 bits microseconds (0-999999)
1794
Total: 64 bits = 8 bytes
1796
SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1800
Convert datetime to packed numeric datetime representation.
1801
@param ltime The value to convert.
1802
@return Packed numeric representation of ltime.
1804
longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
1806
longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1807
longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
1808
longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
1809
DBUG_ASSERT(!check_datetime_range(ltime)); /* Make sure no overflow */
1810
return ltime->neg ? -tmp : tmp;
1815
Convert date to packed numeric date representation.
1816
Numeric packed date format is similar to numeric packed datetime
1817
representation, with zero hhmmss part.
1819
@param ltime The value to convert.
1820
@return Packed numeric representation of ltime.
1822
longlong TIME_to_longlong_date_packed(const MYSQL_TIME *ltime)
1824
longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1825
return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1830
Convert year to packed numeric date representation.
1831
Packed value for YYYY is the same to packed value for date YYYY-00-00.
1833
longlong year_to_longlong_datetime_packed(long year)
1835
longlong ymd= ((year * 13) << 5);
1836
return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1841
Convert packed numeric datetime representation to MYSQL_TIME.
1842
@param OUT ltime The datetime variable to convert to.
1843
@param tmp The packed numeric datetime value.
1845
void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
1848
longlong ymdhms, ym;
1849
if ((ltime->neg= (tmp < 0)))
1852
ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1853
ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
1857
hms= ymdhms % (1 << 17);
1859
ltime->day= ymd % (1 << 5);
1860
ltime->month= ym % 13;
1861
ltime->year= ym / 13;
1863
ltime->second= hms % (1 << 6);
1864
ltime->minute= (hms >> 6) % (1 << 6);
1865
ltime->hour= (hms >> 12);
1867
ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
1872
Convert packed numeric date representation to MYSQL_TIME.
1873
@param OUT ltime The date variable to convert to.
1874
@param tmp The packed numeric date value.
1876
void TIME_from_longlong_date_packed(MYSQL_TIME *ltime, longlong tmp)
1878
TIME_from_longlong_datetime_packed(ltime, tmp);
1879
ltime->time_type= MYSQL_TIMESTAMP_DATE;
1884
Calculate binary size of packed datetime representation.
1885
@param dec Precision.
1887
uint my_datetime_binary_length(uint dec)
1889
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1890
return 5 + (dec + 1) / 2;
1895
On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
1896
for HA_KETYPE_BINARY compatibilty purposes.
1898
#define DATETIMEF_INT_OFS 0x8000000000LL
1902
Convert on-disk datetime representation
1903
to in-memory packed numeric representation.
1905
@param ptr The pointer to read value at.
1906
@param dec Precision.
1907
@return In-memory packed numeric datetime representation.
1909
longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
1911
longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
1913
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1918
return MY_PACKED_TIME_MAKE_INT(intpart);
1921
frac= ((int) (signed char) ptr[5]) * 10000;
1925
frac= mi_sint2korr(ptr + 5) * 100;
1929
frac= mi_sint3korr(ptr + 5);
1932
return MY_PACKED_TIME_MAKE(intpart, frac);
1937
Store in-memory numeric packed datetime representation to disk.
1939
@param nr In-memory numeric packed datetime representation.
1940
@param OUT ptr The pointer to store at.
1941
@param dec Precision, 1-6.
1943
void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1945
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1946
/* The value being stored must have been properly rounded or truncated */
1947
DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1948
(int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1950
mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
1958
ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1962
mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1966
mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
1971
/*** TIMESTAMP low-level memory and disk representation routines ***/
1974
Calculate on-disk size of a timestamp value.
1976
@param dec Precision.
1978
uint my_timestamp_binary_length(uint dec)
1980
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1981
return 4 + (dec + 1) / 2;
1986
Convert binary timestamp representation to in-memory representation.
1988
@param OUT tm The variable to convert to.
1989
@param ptr The pointer to read the value from.
1990
@param dec Precision.
1992
void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
1994
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1995
tm->tv_sec= mi_uint4korr(ptr);
2004
tm->tv_usec= ((int) ptr[4]) * 10000;
2008
tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
2012
tm->tv_usec= mi_sint3korr(ptr + 4);
2018
Convert in-memory timestamp representation to on-disk representation.
2020
@param tm The value to convert.
2021
@param OUT ptr The pointer to store the value to.
2022
@param dec Precision.
2024
void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
2026
DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
2027
/* Stored value must have been previously properly rounded or truncated */
2028
DBUG_ASSERT((tm->tv_usec %
2029
(int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
2030
mi_int4store(ptr, tm->tv_sec);
2038
ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
2042
mi_int2store(ptr + 4, tm->tv_usec / 100);
2044
/* Impossible second precision. Fall through */
2047
mi_int3store(ptr + 4, tm->tv_usec);
2051
/****************************************/
2055
Convert a temporal value to packed numeric temporal representation,
2056
depending on its time_type.
2058
@ltime The value to convert.
2059
@return Packed numeric time/date/datetime representation.
2061
longlong TIME_to_longlong_packed(const MYSQL_TIME *ltime)
2063
switch (ltime->time_type) {
2064
case MYSQL_TIMESTAMP_DATE:
2065
return TIME_to_longlong_date_packed(ltime);
2066
case MYSQL_TIMESTAMP_DATETIME:
2067
return TIME_to_longlong_datetime_packed(ltime);
2068
case MYSQL_TIMESTAMP_TIME:
2069
return TIME_to_longlong_time_packed(ltime);
2070
case MYSQL_TIMESTAMP_NONE:
2071
case MYSQL_TIMESTAMP_ERROR:
2078
/*** End of low level format routines ***/