1
/* Copyright (C) 2004-2006 MySQL AB
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
#include <mystrings/m_string.h>
18
#include <mystrings/m_ctype.h>
19
/* Windows version of localtime_r() is declared in my_ptrhead.h */
21
uint64_t log_10_int[20]=
23
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
24
100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
25
1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
26
1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
27
1000000000000000000ULL, 10000000000000000000ULL
31
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
33
static unsigned char internal_format_positions[]=
34
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
36
static char time_separator=':';
38
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
39
unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
42
Offset of system time zone from UTC in seconds used to speed up
43
work of my_system_gmt_sec() function.
45
static long my_time_zone=0;
48
/* Calc days in one year. works with 0 <= year <= 99 */
50
uint32_t calc_days_in_year(uint32_t year)
52
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
57
@brief Check datetime value for validity according to flags.
59
@param[in] ltime Date to check.
60
@param[in] not_zero_date ltime is not the zero date
61
@param[in] flags flags to check
62
(see str_to_datetime() flags in my_time.h)
63
@param[out] was_cut set to 2 if value was invalid according to flags.
64
(Feb 29 in non-leap etc.) This remains unchanged
65
if value is not invalid.
67
@details Here we assume that year and month is ok!
68
If month is 0 we allow any date. (This only happens if we allow zero
69
date parts in str_to_datetime())
70
Disallow dates with zero year and non-zero month and/or day.
77
bool check_date(const DRIZZLE_TIME *ltime, bool not_zero_date,
78
uint32_t flags, int *was_cut)
82
if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
83
(ltime->month == 0 || ltime->day == 0)) ||
84
(!(flags & TIME_INVALID_DATES) &&
85
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
86
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
93
else if (flags & TIME_NO_ZERO_DATE)
96
We don't set *was_cut here to signal that the problem was a zero date
97
and not an invalid date
106
Convert a timestamp string to a DRIZZLE_TIME value.
111
length Length of string
112
l_time Date is stored here
113
flags Bitmap of following items
114
TIME_FUZZY_DATE Set if we should allow partial dates
115
TIME_DATETIME_ONLY Set if we only allow full datetimes.
116
TIME_NO_ZERO_IN_DATE Don't allow partial dates
117
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
118
TIME_INVALID_DATES Allow 2000-02-31
120
1 If value was cut during conversion
121
2 check_date(date,flags) considers date invalid
124
At least the following formats are recogniced (based on number of digits)
125
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
126
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
127
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
128
Also dates where all parts are zero are allowed
130
The second part may have an optional .###### fraction part.
133
This function should work with a format position vector as long as the
134
following things holds:
135
- All date are kept together and all time parts are kept together
136
- Date and time parts must be separated by blank
137
- Second fractions must come after second part and be separated
138
by a '.'. (The second fractions are optional)
139
- AM/PM must come after second fractions (or after seconds if no fractions)
140
- Year must always been specified.
141
- If time is before date, then we will use datetime format only if
142
the argument consist of two parts, separated by space.
143
Otherwise we will assume the argument is a date.
144
- The hour part must be specified in hour-minute-second order.
147
DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
148
[DD [HH:[MM:[SS]]]].fraction.
149
l_time is not changed.
150
DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
151
DRIZZLE_TIMESTAMP_DATETIME Full timestamp
152
DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
153
All elements in l_time is set to 0
156
#define MAX_DATE_PARTS 8
158
enum enum_drizzle_timestamp_type
159
str_to_datetime(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
160
uint32_t flags, int *was_cut)
162
uint32_t field_length, year_length=4, digits, i, number_of_fields;
163
uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
164
uint32_t add_hours= 0, start_loop;
165
uint32_t not_zero_date, allow_space;
166
bool is_internal_format;
167
const char *pos, *last_field_pos=NULL;
168
const char *end=str+length;
169
const unsigned char *format_position;
170
bool found_delimitier= 0, found_space= 0;
171
uint32_t frac_pos, frac_len;
175
/* Skip space at start */
176
for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
178
if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
181
return(DRIZZLE_TIMESTAMP_NONE);
184
is_internal_format= 0;
185
/* This has to be changed if want to activate different timestamp formats */
186
format_position= internal_format_positions;
189
Calculate number of digits in first part.
190
If length= 8 or >= 14 then year is of format YYYY.
191
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
194
pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
198
digits= (uint) (pos-str);
199
start_loop= 0; /* Start of scan loop */
200
date_len[format_position[0]]= 0; /* Length of year field */
201
if (pos == end || *pos == '.')
203
/* Found date in internal format (only numbers like YYYYMMDD) */
204
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
205
field_length= year_length;
206
is_internal_format= 1;
207
format_position= internal_format_positions;
211
if (format_position[0] >= 3) /* If year is after HHMMDD */
214
If year is not in first part then we have to determinate if we got
215
a date field or a datetime field.
216
We do this by checking if there is two numbers separated by
219
while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
221
while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
225
if (flags & TIME_DATETIME_ONLY)
228
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
230
/* Date field. Set hour, minutes and seconds to 0 */
231
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
232
start_loop= 5; /* Start with first date part */
236
field_length= format_position[0] == 0 ? 4 : 2;
240
Only allow space in the first "part" of the datetime field and:
241
- after days, part seconds
242
- before and after AM/PM (handled by code later)
244
2003-03-03 20:00:20 AM
245
20:00:20.000000 AM 03-03-2000
247
i= cmax((uint) format_position[0], (uint) format_position[1]);
248
set_if_bigger(i, (uint) format_position[2]);
249
allow_space= ((1 << i) | (1 << format_position[6]));
250
allow_space&= (1 | 2 | 4 | 8);
254
i < MAX_DATE_PARTS-1 && str != end &&
255
my_isdigit(&my_charset_utf8_general_ci,*str);
258
const char *start= str;
259
uint32_t tmp_value= (uint) (unsigned char) (*str++ - '0');
260
while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
261
(!is_internal_format || --field_length))
263
tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
266
date_len[i]= (uint) (str - start);
267
if (tmp_value > 999999) /* Impossible date part */
270
return(DRIZZLE_TIMESTAMP_NONE);
273
not_zero_date|= tmp_value;
275
/* Length of next field */
276
field_length= format_position[i+1] == 0 ? 4 : 2;
278
if ((last_field_pos= str) == end)
280
i++; /* Register last found part */
283
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
284
if (i == format_position[2] && *str == 'T')
286
str++; /* ISO8601: CCYYMMDDThhmmss */
289
if (i == format_position[5]) /* Seconds */
291
if (*str == '.') /* Followed by part seconds */
294
field_length= 6; /* 6 digits */
299
(my_ispunct(&my_charset_utf8_general_ci,*str) ||
300
my_isspace(&my_charset_utf8_general_ci,*str)))
302
if (my_isspace(&my_charset_utf8_general_ci,*str))
304
if (!(allow_space & (1 << i)))
307
return(DRIZZLE_TIMESTAMP_NONE);
312
found_delimitier= 1; /* Should be a 'normal' date */
314
/* Check if next position is AM/PM */
315
if (i == format_position[6]) /* Seconds, time for AM/PM */
317
i++; /* Skip AM/PM part */
318
if (format_position[7] != 255) /* If using AM/PM */
320
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
322
if (str[0] == 'p' || str[0] == 'P')
324
else if (str[0] != 'a' || str[0] != 'A')
325
continue; /* Not AM/PM */
326
str+= 2; /* Skip AM/PM */
327
/* Skip space after AM/PM */
328
while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
335
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
338
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
343
number_of_fields= i - start_loop;
344
while (i < MAX_DATE_PARTS)
350
if (!is_internal_format)
352
year_length= date_len[(uint) format_position[0]];
353
if (!year_length) /* Year must be specified */
356
return(DRIZZLE_TIMESTAMP_NONE);
359
l_time->year= date[(uint) format_position[0]];
360
l_time->month= date[(uint) format_position[1]];
361
l_time->day= date[(uint) format_position[2]];
362
l_time->hour= date[(uint) format_position[3]];
363
l_time->minute= date[(uint) format_position[4]];
364
l_time->second= date[(uint) format_position[5]];
366
frac_pos= (uint) format_position[6];
367
frac_len= date_len[frac_pos];
369
date[frac_pos]*= (uint) log_10_int[6 - frac_len];
370
l_time->second_part= date[frac_pos];
372
if (format_position[7] != (unsigned char) 255)
374
if (l_time->hour > 12)
379
l_time->hour= l_time->hour%12 + add_hours;
384
l_time->year= date[0];
385
l_time->month= date[1];
386
l_time->day= date[2];
387
l_time->hour= date[3];
388
l_time->minute= date[4];
389
l_time->second= date[5];
391
date[6]*= (uint) log_10_int[6 - date_len[6]];
392
l_time->second_part=date[6];
396
if (year_length == 2 && not_zero_date)
397
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
399
if (number_of_fields < 3 ||
400
l_time->year > 9999 || l_time->month > 12 ||
401
l_time->day > 31 || l_time->hour > 23 ||
402
l_time->minute > 59 || l_time->second > 59)
404
/* Only give warning for a zero date if there is some garbage after */
405
if (!not_zero_date) /* If zero date */
407
for (; str != end ; str++)
409
if (!my_isspace(&my_charset_utf8_general_ci, *str))
411
not_zero_date= 1; /* Give warning */
416
*was_cut= test(not_zero_date);
420
if (check_date(l_time, not_zero_date != 0, flags, was_cut))
423
l_time->time_type= (number_of_fields <= 3 ?
424
DRIZZLE_TIMESTAMP_DATE : DRIZZLE_TIMESTAMP_DATETIME);
426
for (; str != end ; str++)
428
if (!my_isspace(&my_charset_utf8_general_ci,*str))
435
return(l_time->time_type=
436
(number_of_fields <= 3 ? DRIZZLE_TIMESTAMP_DATE :
437
DRIZZLE_TIMESTAMP_DATETIME));
440
memset(l_time, 0, sizeof(*l_time));
441
return(DRIZZLE_TIMESTAMP_ERROR);
446
Convert a time string to a DRIZZLE_TIME struct.
450
str A string in full TIMESTAMP format or
451
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
453
There may be an optional [.second_part] after seconds
455
l_time Store result here
456
warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
457
was cut during conversion, and/or
458
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
462
Because of the extra days argument, this function can only
463
work with times where the time arguments are in the above order.
470
bool str_to_time(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
475
const char *end=str+length, *end_of_days;
476
bool found_days,found_hours;
481
for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
483
if (str != end && *str == '-')
492
/* Check first if this is a full TIMESTAMP */
494
{ /* Probably full timestamp */
496
enum enum_drizzle_timestamp_type
497
res= str_to_datetime(str, length, l_time,
498
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
499
if ((int) res >= (int) DRIZZLE_TIMESTAMP_ERROR)
502
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
503
return res == DRIZZLE_TIMESTAMP_ERROR;
507
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
508
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
509
value=value*10L + (long) (*str - '0');
511
/* Skip all space after 'days' */
513
for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
516
found_days=found_hours=0;
517
if ((uint) (end-str) > 1 && str != end_of_days &&
518
my_isdigit(&my_charset_utf8_general_ci, *str))
519
{ /* Found days part */
520
date[0]= (uint32_t) value;
521
state= 1; /* Assume next is hours */
524
else if ((end-str) > 1 && *str == time_separator &&
525
my_isdigit(&my_charset_utf8_general_ci, str[1]))
527
date[0]= 0; /* Assume we found hours */
528
date[1]= (uint32_t) value;
531
str++; /* skip ':' */
535
/* String given as one number; assume HHMMSS format */
537
date[1]= (uint32_t) (value/10000);
538
date[2]= (uint32_t) (value/100 % 100);
539
date[3]= (uint32_t) (value % 100);
544
/* Read hours, minutes and seconds */
547
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
548
value=value*10L + (long) (*str - '0');
549
date[state++]= (uint32_t) value;
550
if (state == 4 || (end-str) < 2 || *str != time_separator ||
551
!my_isdigit(&my_charset_utf8_general_ci,str[1]))
553
str++; /* Skip time_separator (':') */
558
/* Fix the date to assume that seconds was given */
559
if (!found_hours && !found_days)
561
bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
562
sizeof(long)*(state-1));
563
memset(date, 0, sizeof(long)*(4-state));
566
memset(date+state, 0, sizeof(long)*(4-state));
570
/* Get fractional second part */
571
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
574
str++; value=(uint) (unsigned char) (*str - '0');
575
while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
577
if (field_length-- > 0)
578
value= value*10 + (uint) (unsigned char) (*str - '0');
580
if (field_length > 0)
581
value*= (long) log_10_int[field_length];
582
else if (field_length < 0)
583
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
584
date[4]= (uint32_t) value;
589
/* Check for exponent part: E<gigit> | E<sign><digit> */
590
/* (may occur as result of %g formatting of time value) */
591
if ((end - str) > 1 &&
592
(*str == 'e' || *str == 'E') &&
593
(my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
594
((str[1] == '-' || str[1] == '+') &&
596
my_isdigit(&my_charset_utf8_general_ci, str[2]))))
599
if (internal_format_positions[7] != 255)
601
/* Read a possible AM/PM */
602
while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
604
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
606
if (str[0] == 'p' || str[0] == 'P')
609
date[1]= date[1]%12 + 12;
611
else if (str[0] == 'a' || str[0] == 'A')
616
/* Integer overflow checks */
617
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
618
date[2] > UINT_MAX || date[3] > UINT_MAX ||
622
l_time->year= 0; /* For protocol::store_time */
624
l_time->day= date[0];
625
l_time->hour= date[1];
626
l_time->minute= date[2];
627
l_time->second= date[3];
628
l_time->second_part= date[4];
629
l_time->time_type= DRIZZLE_TIMESTAMP_TIME;
631
/* Check if the value is valid and fits into DRIZZLE_TIME range */
632
if (check_time_range(l_time, warning))
635
/* Check if there is garbage at end of the DRIZZLE_TIME specification */
640
if (!my_isspace(&my_charset_utf8_general_ci,*str))
642
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
645
} while (++str != end);
652
Check 'time' value to lie in the DRIZZLE_TIME range
656
time pointer to DRIZZLE_TIME value
657
warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
660
If the time value lies outside of the range [-838:59:59, 838:59:59],
661
set it to the closest endpoint of the range and set
662
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
665
0 time value is valid, but was possibly truncated
666
1 time value is invalid
669
int check_time_range(DRIZZLE_TIME *my_time, int *warning)
673
if (my_time->minute >= 60 || my_time->second >= 60)
676
hour= my_time->hour + (24*my_time->day);
677
if (hour <= TIME_MAX_HOUR &&
678
(hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
679
my_time->second != TIME_MAX_SECOND || !my_time->second_part))
683
my_time->hour= TIME_MAX_HOUR;
684
my_time->minute= TIME_MAX_MINUTE;
685
my_time->second= TIME_MAX_SECOND;
686
my_time->second_part= 0;
687
*warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
693
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
701
struct tm *l_time,tm_tmp;
702
DRIZZLE_TIME my_time;
705
seconds= (time_t) time((time_t*) 0);
706
localtime_r(&seconds,&tm_tmp);
708
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
709
my_time.year= (uint) l_time->tm_year+1900;
710
my_time.month= (uint) l_time->tm_mon+1;
711
my_time.day= (uint) l_time->tm_mday;
712
my_time.hour= (uint) l_time->tm_hour;
713
my_time.minute= (uint) l_time->tm_min;
714
my_time.second= (uint) l_time->tm_sec;
715
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
720
Handle 2 digit year conversions
727
Year between 1970-2069
730
uint32_t year_2000_handling(uint32_t year)
732
if ((year=year+1900) < 1900+YY_PART_YEAR)
739
Calculate nr of day since year 0 in new date-system (from 1615)
743
year Year (exact 4 digit year, no year conversions)
747
NOTES: 0000-00-00 is a valid date, and will return 0
750
Days since 0000-00-00
753
long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
758
if (year == 0 && month == 0 && day == 0)
759
return(0); /* Skip errors */
760
delsum= (long) (365L * year+ 31*(month-1) +day);
764
delsum-= (long) (month*4+23)/10;
765
temp=(int) ((year/100+1)*3)/4;
766
return(delsum+(int) year/4-temp);
771
Convert time in DRIZZLE_TIME representation in system time zone to its
772
my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
776
t - time value to be converted
777
my_timezone - pointer to long where offset of system time zone
778
from UTC will be stored for caching
779
in_dst_time_gap - set to true if time falls into spring time-gap
782
The idea is to cache the time zone offset from UTC (including daylight
783
saving time) for the next call to make things faster. But currently we
784
just calculate this offset during startup (by calling init_time()
785
function) and use it all the time.
786
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
790
Time in UTC seconds since Unix Epoch representation.
793
my_system_gmt_sec(const DRIZZLE_TIME *t_src, long *my_timezone,
794
bool *in_dst_time_gap)
799
DRIZZLE_TIME tmp_time;
800
DRIZZLE_TIME *t= &tmp_time;
801
struct tm *l_time,tm_tmp;
802
long diff, current_timezone;
805
Use temp variable to avoid trashing input data, which could happen in
806
case of shift required for boundary dates processing.
808
memcpy(&tmp_time, t_src, sizeof(DRIZZLE_TIME));
810
if (!validate_timestamp_range(t))
814
Calculate the gmt time based on current time and timezone
815
The -1 on the end is to ensure that if have a date that exists twice
816
(like 2002-10-27 02:00:0 MET), we will find the initial date.
818
By doing -3600 we will have to call localtime_r() several times, but
819
I couldn't come up with a better way to get a repeatable result :(
821
We can't use mktime() as it's buggy on many platforms and not thread safe.
823
Note: this code assumes that our time_t estimation is not too far away
824
from real value (we assume that localtime_r(tmp) will return something
825
within 24 hrs from t) which is probably true for all current time zones.
827
Note2: For the dates, which have time_t representation close to
828
MAX_INT32 (efficient time_t limit for supported platforms), we should
829
do a small trick to avoid overflow. That is, convert the date, which is
830
two days earlier, and then add these days to the final value.
832
The same trick is done for the values close to 0 in time_t
833
representation for platfroms with unsigned time_t (QNX).
835
To be more verbose, here is a sample (extracted from the code below):
836
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
837
would return -2147480896 because of the long type overflow. In result
838
we would get 1901 year in localtime_r(), which is an obvious error.
840
Alike problem raises with the dates close to Epoch. E.g.
841
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
844
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
845
wil give us a date around 2106 year. Which is no good.
847
Theoreticaly, there could be problems with the latter conversion:
848
there are at least two timezones, which had time switches near 1 Jan
849
of 1970 (because of political reasons). These are America/Hermosillo and
850
America/Mazatlan time zones. They changed their offset on
851
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
852
the code below will give incorrect results for dates close to
853
1970-01-01, in the case OS takes into account these historical switches.
854
Luckily, it seems that we support only one platform with unsigned
855
time_t. It's QNX. And QNX does not support historical timezone data at all.
856
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
857
historical information for localtime_r() etc. That is, the problem is not
860
We are safe with shifts close to MAX_INT32, as there are no known
861
time switches on Jan 2038 yet :)
863
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
866
Below we will pass (uint) (t->day - shift) to calc_daynr.
867
As we don't want to get an overflow here, we will shift
868
only safe dates. That's why we have (t->day > 4) above.
873
#ifdef TIME_T_UNSIGNED
877
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
878
1970, 1 of Jan. For both dates we use shift, which is added
879
to t->day in order to step out a bit from the border.
880
This is required for platforms, where time_t is unsigned.
881
As far as I know, among the platforms we support it's only QNX.
882
Note: the order of below if-statements is significant.
885
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
892
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
903
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
904
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
905
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
908
current_timezone= my_time_zone;
909
localtime_r(&tmp,&tm_tmp);
913
(t->hour != (uint) l_time->tm_hour ||
914
t->minute != (uint) l_time->tm_min ||
915
t->second != (uint) l_time->tm_sec);
917
{ /* One check should be enough ? */
918
/* Get difference in days */
919
int days= t->day - l_time->tm_mday;
921
days= 1; /* Month has wrapped */
924
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
925
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
926
(long) ((int) t->second - (int) l_time->tm_sec));
927
current_timezone+= diff+3600; /* Compensate for -3600 above */
929
localtime_r(&tmp,&tm_tmp);
933
Fix that if we are in the non existing daylight saving time hour
934
we move the start of the next real hour.
936
This code doesn't handle such exotical thing as time-gaps whose length
937
is more than one hour or non-integer (latter can theoretically happen
938
if one of seconds will be removed due leap correction, or because of
939
general time correction like it happened for Africa/Monrovia time zone
942
if (loop == 2 && t->hour != (uint) l_time->tm_hour)
944
int days= t->day - l_time->tm_mday;
946
days=1; /* Month has wrapped */
949
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
950
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
951
(long) ((int) t->second - (int) l_time->tm_sec));
953
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
954
else if (diff == -3600)
955
tmp-=t->minute*60 + t->second; /* Move to previous hour */
957
*in_dst_time_gap= true;
959
*my_timezone= current_timezone;
962
/* shift back, if we were dealing with boundary dates */
966
This is possible for dates, which slightly exceed boundaries.
967
Conversion will pass ok for them, but we don't allow them.
968
First check will pass for platforms with signed time_t.
969
instruction above (tmp+= shift*86400L) could exceed
970
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
971
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
972
with unsigned time_t tmp+= shift*86400L might result in a number,
973
larger then TIMESTAMP_MAX_VALUE, so another check will work.
975
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
978
return (my_time_t) tmp;
979
} /* my_system_gmt_sec */
982
/* Set DRIZZLE_TIME structure to 0000-00-00 00:00:00.000000 */
984
void set_zero_time(DRIZZLE_TIME *tm, enum enum_drizzle_timestamp_type time_type)
986
memset(tm, 0, sizeof(*tm));
987
tm->time_type= time_type;
992
Functions to convert time/date/datetime value to a string,
993
using default format.
994
This functions don't check that given DRIZZLE_TIME structure members are
995
in valid range. If they are not, return value won't reflect any
996
valid date either. Additionally, make_time doesn't take into
997
account time->day member: it's assumed that days have been converted
1001
number of characters written to 'to'
1004
int my_time_to_str(const DRIZZLE_TIME *l_time, char *to)
1006
uint32_t extra_hours= 0;
1007
return sprintf(to, "%s%02u:%02u:%02u",
1008
(l_time->neg ? "-" : ""),
1009
extra_hours+ l_time->hour,
1014
int my_date_to_str(const DRIZZLE_TIME *l_time, char *to)
1016
return sprintf(to, "%04u-%02u-%02u",
1022
int my_datetime_to_str(const DRIZZLE_TIME *l_time, char *to)
1024
return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u",
1035
Convert struct DATE/TIME/DATETIME value to string using built-in
1036
MySQL time conversion formats.
1042
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1045
int my_TIME_to_str(const DRIZZLE_TIME *l_time, char *to)
1047
switch (l_time->time_type) {
1048
case DRIZZLE_TIMESTAMP_DATETIME:
1049
return my_datetime_to_str(l_time, to);
1050
case DRIZZLE_TIMESTAMP_DATE:
1051
return my_date_to_str(l_time, to);
1052
case DRIZZLE_TIMESTAMP_TIME:
1053
return my_time_to_str(l_time, to);
1054
case DRIZZLE_TIMESTAMP_NONE:
1055
case DRIZZLE_TIMESTAMP_ERROR:
1066
Convert datetime value specified as number to broken-down TIME
1067
representation and form value of DATETIME type as side-effect.
1070
number_to_datetime()
1071
nr - datetime value as number
1072
time_res - pointer for structure for broken-down representation
1073
flags - flags to use in validating date, as in str_to_datetime()
1075
1 If value was cut during conversion
1076
2 check_date(date,flags) considers date invalid
1079
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1080
YYYYMMDDHHMMSS to broken-down DRIZZLE_TIME representation. Return value in
1081
YYYYMMDDHHMMSS format as side-effect.
1083
This function also checks if datetime value fits in DATETIME range.
1086
-1 Timestamp with wrong values
1087
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1088
Datetime value in YYYYMMDDHHMMSS format.
1091
int64_t number_to_datetime(int64_t nr, DRIZZLE_TIME *time_res,
1092
uint32_t flags, int *was_cut)
1097
memset(time_res, 0, sizeof(*time_res));
1098
time_res->time_type=DRIZZLE_TIMESTAMP_DATE;
1100
if (nr == 0LL || nr >= 10000101000000LL)
1102
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1107
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1109
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1112
if (nr < (YY_PART_YEAR)*10000L+101L)
1116
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1121
if (nr <= 99991231L)
1126
if (nr < 101000000L)
1129
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1131
if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1133
nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1136
if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1138
if (nr <= 991231235959LL)
1139
nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1142
part1=(long) (nr / 1000000LL);
1143
part2=(long) (nr - (int64_t) part1 * 1000000LL);
1144
time_res->year= (int) (part1/10000L); part1%=10000L;
1145
time_res->month= (int) part1 / 100;
1146
time_res->day= (int) part1 % 100;
1147
time_res->hour= (int) (part2/10000L); part2%=10000L;
1148
time_res->minute=(int) part2 / 100;
1149
time_res->second=(int) part2 % 100;
1151
if (time_res->year <= 9999 && time_res->month <= 12 &&
1152
time_res->day <= 31 && time_res->hour <= 23 &&
1153
time_res->minute <= 59 && time_res->second <= 59 &&
1154
!check_date(time_res, (nr != 0), flags, was_cut))
1157
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1158
if (!nr && (flags & TIME_NO_ZERO_DATE))
1167
/* Convert time value to integer in YYYYMMDDHHMMSS format */
1169
uint64_t TIME_to_uint64_t_datetime(const DRIZZLE_TIME *my_time)
1171
return ((uint64_t) (my_time->year * 10000UL +
1172
my_time->month * 100UL +
1173
my_time->day) * 1000000ULL +
1174
(uint64_t) (my_time->hour * 10000UL +
1175
my_time->minute * 100UL +
1180
/* Convert DRIZZLE_TIME value to integer in YYYYMMDD format */
1182
uint64_t TIME_to_uint64_t_date(const DRIZZLE_TIME *my_time)
1184
return (uint64_t) (my_time->year * 10000UL + my_time->month * 100UL +
1190
Convert DRIZZLE_TIME value to integer in HHMMSS format.
1191
This function doesn't take into account time->day member:
1192
it's assumed that days have been converted to hours already.
1195
uint64_t TIME_to_uint64_t_time(const DRIZZLE_TIME *my_time)
1197
return (uint64_t) (my_time->hour * 10000UL +
1198
my_time->minute * 100UL +
1204
Convert struct DRIZZLE_TIME (date and time split into year/month/day/hour/...
1205
to a number in format YYYYMMDDHHMMSS (DATETIME),
1206
YYYYMMDD (DATE) or HHMMSS (TIME).
1212
The function is used when we need to convert value of time item
1213
to a number if it's used in numeric context, i. e.:
1214
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1218
This function doesn't check that given DRIZZLE_TIME structure members are
1219
in valid range. If they are not, return value won't reflect any
1223
uint64_t TIME_to_uint64_t(const DRIZZLE_TIME *my_time)
1225
switch (my_time->time_type) {
1226
case DRIZZLE_TIMESTAMP_DATETIME:
1227
return TIME_to_uint64_t_datetime(my_time);
1228
case DRIZZLE_TIMESTAMP_DATE:
1229
return TIME_to_uint64_t_date(my_time);
1230
case DRIZZLE_TIMESTAMP_TIME:
1231
return TIME_to_uint64_t_time(my_time);
1232
case DRIZZLE_TIMESTAMP_NONE:
1233
case DRIZZLE_TIMESTAMP_ERROR: