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
#include <drizzled/util/test.h>
20
/* Windows version of localtime_r() is declared in my_ptrhead.h */
22
uint64_t log_10_int[20]=
24
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
25
100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
26
1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
27
1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
28
1000000000000000000ULL, 10000000000000000000ULL
32
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
34
static unsigned char internal_format_positions[]=
35
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
37
static char time_separator=':';
39
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
40
unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
43
Offset of system time zone from UTC in seconds used to speed up
44
work of my_system_gmt_sec() function.
46
static long my_time_zone=0;
49
/* Calc days in one year. works with 0 <= year <= 99 */
51
uint32_t calc_days_in_year(uint32_t year)
53
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
58
@brief Check datetime value for validity according to flags.
60
@param[in] ltime Date to check.
61
@param[in] not_zero_date ltime is not the zero date
62
@param[in] flags flags to check
63
(see str_to_datetime() flags in my_time.h)
64
@param[out] was_cut set to 2 if value was invalid according to flags.
65
(Feb 29 in non-leap etc.) This remains unchanged
66
if value is not invalid.
68
@details Here we assume that year and month is ok!
69
If month is 0 we allow any date. (This only happens if we allow zero
70
date parts in str_to_datetime())
71
Disallow dates with zero year and non-zero month and/or day.
78
bool check_date(const DRIZZLE_TIME *ltime, bool not_zero_date,
79
uint32_t flags, int *was_cut)
83
if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
84
(ltime->month == 0 || ltime->day == 0)) ||
85
(!(flags & TIME_INVALID_DATES) &&
86
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
87
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
94
else if (flags & TIME_NO_ZERO_DATE)
97
We don't set *was_cut here to signal that the problem was a zero date
98
and not an invalid date
107
Convert a timestamp string to a DRIZZLE_TIME value.
112
length Length of string
113
l_time Date is stored here
114
flags Bitmap of following items
115
TIME_FUZZY_DATE Set if we should allow partial dates
116
TIME_DATETIME_ONLY Set if we only allow full datetimes.
117
TIME_NO_ZERO_IN_DATE Don't allow partial dates
118
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
119
TIME_INVALID_DATES Allow 2000-02-31
121
1 If value was cut during conversion
122
2 check_date(date,flags) considers date invalid
125
At least the following formats are recogniced (based on number of digits)
126
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
127
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
128
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
129
Also dates where all parts are zero are allowed
131
The second part may have an optional .###### fraction part.
134
This function should work with a format position vector as long as the
135
following things holds:
136
- All date are kept together and all time parts are kept together
137
- Date and time parts must be separated by blank
138
- Second fractions must come after second part and be separated
139
by a '.'. (The second fractions are optional)
140
- AM/PM must come after second fractions (or after seconds if no fractions)
141
- Year must always been specified.
142
- If time is before date, then we will use datetime format only if
143
the argument consist of two parts, separated by space.
144
Otherwise we will assume the argument is a date.
145
- The hour part must be specified in hour-minute-second order.
148
DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
149
[DD [HH:[MM:[SS]]]].fraction.
150
l_time is not changed.
151
DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
152
DRIZZLE_TIMESTAMP_DATETIME Full timestamp
153
DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
154
All elements in l_time is set to 0
157
#define MAX_DATE_PARTS 8
159
enum enum_drizzle_timestamp_type
160
str_to_datetime(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
161
uint32_t flags, int *was_cut)
163
uint32_t field_length, year_length=4, digits, i, number_of_fields;
164
uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
165
uint32_t add_hours= 0, start_loop;
166
uint32_t not_zero_date, allow_space;
167
bool is_internal_format;
168
const char *pos, *last_field_pos=NULL;
169
const char *end=str+length;
170
const unsigned char *format_position;
171
bool found_delimitier= 0, found_space= 0;
172
uint32_t frac_pos, frac_len;
176
/* Skip space at start */
177
for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
179
if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
182
return(DRIZZLE_TIMESTAMP_NONE);
185
is_internal_format= 0;
186
/* This has to be changed if want to activate different timestamp formats */
187
format_position= internal_format_positions;
190
Calculate number of digits in first part.
191
If length= 8 or >= 14 then year is of format YYYY.
192
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
195
pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
199
digits= (uint) (pos-str);
200
start_loop= 0; /* Start of scan loop */
201
date_len[format_position[0]]= 0; /* Length of year field */
202
if (pos == end || *pos == '.')
204
/* Found date in internal format (only numbers like YYYYMMDD) */
205
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
206
field_length= year_length;
207
is_internal_format= 1;
208
format_position= internal_format_positions;
212
if (format_position[0] >= 3) /* If year is after HHMMDD */
215
If year is not in first part then we have to determinate if we got
216
a date field or a datetime field.
217
We do this by checking if there is two numbers separated by
220
while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
222
while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
226
if (flags & TIME_DATETIME_ONLY)
229
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
231
/* Date field. Set hour, minutes and seconds to 0 */
232
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
233
start_loop= 5; /* Start with first date part */
237
field_length= format_position[0] == 0 ? 4 : 2;
241
Only allow space in the first "part" of the datetime field and:
242
- after days, part seconds
243
- before and after AM/PM (handled by code later)
245
2003-03-03 20:00:20 AM
246
20:00:20.000000 AM 03-03-2000
248
i= cmax((uint) format_position[0], (uint) format_position[1]);
249
set_if_bigger(i, (uint) format_position[2]);
250
allow_space= ((1 << i) | (1 << format_position[6]));
251
allow_space&= (1 | 2 | 4 | 8);
255
i < MAX_DATE_PARTS-1 && str != end &&
256
my_isdigit(&my_charset_utf8_general_ci,*str);
259
const char *start= str;
260
uint32_t tmp_value= (uint) (unsigned char) (*str++ - '0');
261
while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
262
(!is_internal_format || --field_length))
264
tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
267
date_len[i]= (uint) (str - start);
268
if (tmp_value > 999999) /* Impossible date part */
271
return(DRIZZLE_TIMESTAMP_NONE);
274
not_zero_date|= tmp_value;
276
/* Length of next field */
277
field_length= format_position[i+1] == 0 ? 4 : 2;
279
if ((last_field_pos= str) == end)
281
i++; /* Register last found part */
284
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
285
if (i == format_position[2] && *str == 'T')
287
str++; /* ISO8601: CCYYMMDDThhmmss */
290
if (i == format_position[5]) /* Seconds */
292
if (*str == '.') /* Followed by part seconds */
295
field_length= 6; /* 6 digits */
300
(my_ispunct(&my_charset_utf8_general_ci,*str) ||
301
my_isspace(&my_charset_utf8_general_ci,*str)))
303
if (my_isspace(&my_charset_utf8_general_ci,*str))
305
if (!(allow_space & (1 << i)))
308
return(DRIZZLE_TIMESTAMP_NONE);
313
found_delimitier= 1; /* Should be a 'normal' date */
315
/* Check if next position is AM/PM */
316
if (i == format_position[6]) /* Seconds, time for AM/PM */
318
i++; /* Skip AM/PM part */
319
if (format_position[7] != 255) /* If using AM/PM */
321
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
323
if (str[0] == 'p' || str[0] == 'P')
325
else if (str[0] != 'a' || str[0] != 'A')
326
continue; /* Not AM/PM */
327
str+= 2; /* Skip AM/PM */
328
/* Skip space after AM/PM */
329
while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
336
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
339
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
344
number_of_fields= i - start_loop;
345
while (i < MAX_DATE_PARTS)
351
if (!is_internal_format)
353
year_length= date_len[(uint) format_position[0]];
354
if (!year_length) /* Year must be specified */
357
return(DRIZZLE_TIMESTAMP_NONE);
360
l_time->year= date[(uint) format_position[0]];
361
l_time->month= date[(uint) format_position[1]];
362
l_time->day= date[(uint) format_position[2]];
363
l_time->hour= date[(uint) format_position[3]];
364
l_time->minute= date[(uint) format_position[4]];
365
l_time->second= date[(uint) format_position[5]];
367
frac_pos= (uint) format_position[6];
368
frac_len= date_len[frac_pos];
370
date[frac_pos]*= (uint) log_10_int[6 - frac_len];
371
l_time->second_part= date[frac_pos];
373
if (format_position[7] != (unsigned char) 255)
375
if (l_time->hour > 12)
380
l_time->hour= l_time->hour%12 + add_hours;
385
l_time->year= date[0];
386
l_time->month= date[1];
387
l_time->day= date[2];
388
l_time->hour= date[3];
389
l_time->minute= date[4];
390
l_time->second= date[5];
392
date[6]*= (uint) log_10_int[6 - date_len[6]];
393
l_time->second_part=date[6];
397
if (year_length == 2 && not_zero_date)
398
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
400
if (number_of_fields < 3 ||
401
l_time->year > 9999 || l_time->month > 12 ||
402
l_time->day > 31 || l_time->hour > 23 ||
403
l_time->minute > 59 || l_time->second > 59)
405
/* Only give warning for a zero date if there is some garbage after */
406
if (!not_zero_date) /* If zero date */
408
for (; str != end ; str++)
410
if (!my_isspace(&my_charset_utf8_general_ci, *str))
412
not_zero_date= 1; /* Give warning */
417
*was_cut= test(not_zero_date);
421
if (check_date(l_time, not_zero_date != 0, flags, was_cut))
424
l_time->time_type= (number_of_fields <= 3 ?
425
DRIZZLE_TIMESTAMP_DATE : DRIZZLE_TIMESTAMP_DATETIME);
427
for (; str != end ; str++)
429
if (!my_isspace(&my_charset_utf8_general_ci,*str))
436
return(l_time->time_type=
437
(number_of_fields <= 3 ? DRIZZLE_TIMESTAMP_DATE :
438
DRIZZLE_TIMESTAMP_DATETIME));
441
memset(l_time, 0, sizeof(*l_time));
442
return(DRIZZLE_TIMESTAMP_ERROR);
447
Convert a time string to a DRIZZLE_TIME struct.
451
str A string in full TIMESTAMP format or
452
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
454
There may be an optional [.second_part] after seconds
456
l_time Store result here
457
warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
458
was cut during conversion, and/or
459
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
463
Because of the extra days argument, this function can only
464
work with times where the time arguments are in the above order.
471
bool str_to_time(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
476
const char *end=str+length, *end_of_days;
477
bool found_days,found_hours;
482
for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
484
if (str != end && *str == '-')
493
/* Check first if this is a full TIMESTAMP */
495
{ /* Probably full timestamp */
497
enum enum_drizzle_timestamp_type
498
res= str_to_datetime(str, length, l_time,
499
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
500
if ((int) res >= (int) DRIZZLE_TIMESTAMP_ERROR)
503
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
504
return res == DRIZZLE_TIMESTAMP_ERROR;
508
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
509
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
510
value=value*10L + (long) (*str - '0');
512
/* Skip all space after 'days' */
514
for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
517
found_days=found_hours=0;
518
if ((uint) (end-str) > 1 && str != end_of_days &&
519
my_isdigit(&my_charset_utf8_general_ci, *str))
520
{ /* Found days part */
521
date[0]= (uint32_t) value;
522
state= 1; /* Assume next is hours */
525
else if ((end-str) > 1 && *str == time_separator &&
526
my_isdigit(&my_charset_utf8_general_ci, str[1]))
528
date[0]= 0; /* Assume we found hours */
529
date[1]= (uint32_t) value;
532
str++; /* skip ':' */
536
/* String given as one number; assume HHMMSS format */
538
date[1]= (uint32_t) (value/10000);
539
date[2]= (uint32_t) (value/100 % 100);
540
date[3]= (uint32_t) (value % 100);
545
/* Read hours, minutes and seconds */
548
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
549
value=value*10L + (long) (*str - '0');
550
date[state++]= (uint32_t) value;
551
if (state == 4 || (end-str) < 2 || *str != time_separator ||
552
!my_isdigit(&my_charset_utf8_general_ci,str[1]))
554
str++; /* Skip time_separator (':') */
559
/* Fix the date to assume that seconds was given */
560
if (!found_hours && !found_days)
562
bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
563
sizeof(long)*(state-1));
564
memset(date, 0, sizeof(long)*(4-state));
567
memset(date+state, 0, sizeof(long)*(4-state));
571
/* Get fractional second part */
572
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
575
str++; value=(uint) (unsigned char) (*str - '0');
576
while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
578
if (field_length-- > 0)
579
value= value*10 + (uint) (unsigned char) (*str - '0');
581
if (field_length > 0)
582
value*= (long) log_10_int[field_length];
583
else if (field_length < 0)
584
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
585
date[4]= (uint32_t) value;
590
/* Check for exponent part: E<gigit> | E<sign><digit> */
591
/* (may occur as result of %g formatting of time value) */
592
if ((end - str) > 1 &&
593
(*str == 'e' || *str == 'E') &&
594
(my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
595
((str[1] == '-' || str[1] == '+') &&
597
my_isdigit(&my_charset_utf8_general_ci, str[2]))))
600
if (internal_format_positions[7] != 255)
602
/* Read a possible AM/PM */
603
while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
605
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
607
if (str[0] == 'p' || str[0] == 'P')
610
date[1]= date[1]%12 + 12;
612
else if (str[0] == 'a' || str[0] == 'A')
617
/* Integer overflow checks */
618
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
619
date[2] > UINT_MAX || date[3] > UINT_MAX ||
623
l_time->year= 0; /* For protocol::store_time */
625
l_time->day= date[0];
626
l_time->hour= date[1];
627
l_time->minute= date[2];
628
l_time->second= date[3];
629
l_time->second_part= date[4];
630
l_time->time_type= DRIZZLE_TIMESTAMP_TIME;
632
/* Check if the value is valid and fits into DRIZZLE_TIME range */
633
if (check_time_range(l_time, warning))
636
/* Check if there is garbage at end of the DRIZZLE_TIME specification */
641
if (!my_isspace(&my_charset_utf8_general_ci,*str))
643
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
646
} while (++str != end);
653
Check 'time' value to lie in the DRIZZLE_TIME range
657
time pointer to DRIZZLE_TIME value
658
warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
661
If the time value lies outside of the range [-838:59:59, 838:59:59],
662
set it to the closest endpoint of the range and set
663
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
666
0 time value is valid, but was possibly truncated
667
1 time value is invalid
670
int check_time_range(DRIZZLE_TIME *my_time, int *warning)
674
if (my_time->minute >= 60 || my_time->second >= 60)
677
hour= my_time->hour + (24*my_time->day);
678
if (hour <= TIME_MAX_HOUR &&
679
(hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
680
my_time->second != TIME_MAX_SECOND || !my_time->second_part))
684
my_time->hour= TIME_MAX_HOUR;
685
my_time->minute= TIME_MAX_MINUTE;
686
my_time->second= TIME_MAX_SECOND;
687
my_time->second_part= 0;
688
*warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
694
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
702
struct tm *l_time,tm_tmp;
703
DRIZZLE_TIME my_time;
706
seconds= (time_t) time((time_t*) 0);
707
localtime_r(&seconds,&tm_tmp);
709
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
710
my_time.year= (uint) l_time->tm_year+1900;
711
my_time.month= (uint) l_time->tm_mon+1;
712
my_time.day= (uint) l_time->tm_mday;
713
my_time.hour= (uint) l_time->tm_hour;
714
my_time.minute= (uint) l_time->tm_min;
715
my_time.second= (uint) l_time->tm_sec;
716
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
721
Handle 2 digit year conversions
728
Year between 1970-2069
731
uint32_t year_2000_handling(uint32_t year)
733
if ((year=year+1900) < 1900+YY_PART_YEAR)
740
Calculate nr of day since year 0 in new date-system (from 1615)
744
year Year (exact 4 digit year, no year conversions)
748
NOTES: 0000-00-00 is a valid date, and will return 0
751
Days since 0000-00-00
754
long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
759
if (year == 0 && month == 0 && day == 0)
760
return(0); /* Skip errors */
761
delsum= (long) (365L * year+ 31*(month-1) +day);
765
delsum-= (long) (month*4+23)/10;
766
temp=(int) ((year/100+1)*3)/4;
767
return(delsum+(int) year/4-temp);
772
Convert time in DRIZZLE_TIME representation in system time zone to its
773
my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
777
t - time value to be converted
778
my_timezone - pointer to long where offset of system time zone
779
from UTC will be stored for caching
780
in_dst_time_gap - set to true if time falls into spring time-gap
783
The idea is to cache the time zone offset from UTC (including daylight
784
saving time) for the next call to make things faster. But currently we
785
just calculate this offset during startup (by calling init_time()
786
function) and use it all the time.
787
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
791
Time in UTC seconds since Unix Epoch representation.
794
my_system_gmt_sec(const DRIZZLE_TIME *t_src, long *my_timezone,
795
bool *in_dst_time_gap)
800
DRIZZLE_TIME tmp_time;
801
DRIZZLE_TIME *t= &tmp_time;
802
struct tm *l_time,tm_tmp;
803
long diff, current_timezone;
806
Use temp variable to avoid trashing input data, which could happen in
807
case of shift required for boundary dates processing.
809
memcpy(&tmp_time, t_src, sizeof(DRIZZLE_TIME));
811
if (!validate_timestamp_range(t))
815
Calculate the gmt time based on current time and timezone
816
The -1 on the end is to ensure that if have a date that exists twice
817
(like 2002-10-27 02:00:0 MET), we will find the initial date.
819
By doing -3600 we will have to call localtime_r() several times, but
820
I couldn't come up with a better way to get a repeatable result :(
822
We can't use mktime() as it's buggy on many platforms and not thread safe.
824
Note: this code assumes that our time_t estimation is not too far away
825
from real value (we assume that localtime_r(tmp) will return something
826
within 24 hrs from t) which is probably true for all current time zones.
828
Note2: For the dates, which have time_t representation close to
829
MAX_INT32 (efficient time_t limit for supported platforms), we should
830
do a small trick to avoid overflow. That is, convert the date, which is
831
two days earlier, and then add these days to the final value.
833
The same trick is done for the values close to 0 in time_t
834
representation for platfroms with unsigned time_t (QNX).
836
To be more verbose, here is a sample (extracted from the code below):
837
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
838
would return -2147480896 because of the long type overflow. In result
839
we would get 1901 year in localtime_r(), which is an obvious error.
841
Alike problem raises with the dates close to Epoch. E.g.
842
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
845
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
846
wil give us a date around 2106 year. Which is no good.
848
Theoreticaly, there could be problems with the latter conversion:
849
there are at least two timezones, which had time switches near 1 Jan
850
of 1970 (because of political reasons). These are America/Hermosillo and
851
America/Mazatlan time zones. They changed their offset on
852
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
853
the code below will give incorrect results for dates close to
854
1970-01-01, in the case OS takes into account these historical switches.
855
Luckily, it seems that we support only one platform with unsigned
856
time_t. It's QNX. And QNX does not support historical timezone data at all.
857
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
858
historical information for localtime_r() etc. That is, the problem is not
861
We are safe with shifts close to MAX_INT32, as there are no known
862
time switches on Jan 2038 yet :)
864
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
867
Below we will pass (uint) (t->day - shift) to calc_daynr.
868
As we don't want to get an overflow here, we will shift
869
only safe dates. That's why we have (t->day > 4) above.
874
#ifdef TIME_T_UNSIGNED
878
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
879
1970, 1 of Jan. For both dates we use shift, which is added
880
to t->day in order to step out a bit from the border.
881
This is required for platforms, where time_t is unsigned.
882
As far as I know, among the platforms we support it's only QNX.
883
Note: the order of below if-statements is significant.
886
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
893
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
904
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
905
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
906
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
909
current_timezone= my_time_zone;
910
localtime_r(&tmp,&tm_tmp);
914
(t->hour != (uint) l_time->tm_hour ||
915
t->minute != (uint) l_time->tm_min ||
916
t->second != (uint) l_time->tm_sec);
918
{ /* One check should be enough ? */
919
/* Get difference in days */
920
int days= t->day - l_time->tm_mday;
922
days= 1; /* Month has wrapped */
925
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
926
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
927
(long) ((int) t->second - (int) l_time->tm_sec));
928
current_timezone+= diff+3600; /* Compensate for -3600 above */
930
localtime_r(&tmp,&tm_tmp);
934
Fix that if we are in the non existing daylight saving time hour
935
we move the start of the next real hour.
937
This code doesn't handle such exotical thing as time-gaps whose length
938
is more than one hour or non-integer (latter can theoretically happen
939
if one of seconds will be removed due leap correction, or because of
940
general time correction like it happened for Africa/Monrovia time zone
943
if (loop == 2 && t->hour != (uint) l_time->tm_hour)
945
int days= t->day - l_time->tm_mday;
947
days=1; /* Month has wrapped */
950
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
951
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
952
(long) ((int) t->second - (int) l_time->tm_sec));
954
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
955
else if (diff == -3600)
956
tmp-=t->minute*60 + t->second; /* Move to previous hour */
958
*in_dst_time_gap= true;
960
*my_timezone= current_timezone;
963
/* shift back, if we were dealing with boundary dates */
967
This is possible for dates, which slightly exceed boundaries.
968
Conversion will pass ok for them, but we don't allow them.
969
First check will pass for platforms with signed time_t.
970
instruction above (tmp+= shift*86400L) could exceed
971
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
972
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
973
with unsigned time_t tmp+= shift*86400L might result in a number,
974
larger then TIMESTAMP_MAX_VALUE, so another check will work.
976
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
979
return (my_time_t) tmp;
980
} /* my_system_gmt_sec */
983
/* Set DRIZZLE_TIME structure to 0000-00-00 00:00:00.000000 */
985
void set_zero_time(DRIZZLE_TIME *tm, enum enum_drizzle_timestamp_type time_type)
987
memset(tm, 0, sizeof(*tm));
988
tm->time_type= time_type;
993
Functions to convert time/date/datetime value to a string,
994
using default format.
995
This functions don't check that given DRIZZLE_TIME structure members are
996
in valid range. If they are not, return value won't reflect any
997
valid date either. Additionally, make_time doesn't take into
998
account time->day member: it's assumed that days have been converted
1002
number of characters written to 'to'
1005
int my_time_to_str(const DRIZZLE_TIME *l_time, char *to)
1007
uint32_t extra_hours= 0;
1008
return sprintf(to, "%s%02u:%02u:%02u",
1009
(l_time->neg ? "-" : ""),
1010
extra_hours+ l_time->hour,
1015
int my_date_to_str(const DRIZZLE_TIME *l_time, char *to)
1017
return sprintf(to, "%04u-%02u-%02u",
1023
int my_datetime_to_str(const DRIZZLE_TIME *l_time, char *to)
1025
return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u",
1036
Convert struct DATE/TIME/DATETIME value to string using built-in
1037
MySQL time conversion formats.
1043
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1046
int my_TIME_to_str(const DRIZZLE_TIME *l_time, char *to)
1048
switch (l_time->time_type) {
1049
case DRIZZLE_TIMESTAMP_DATETIME:
1050
return my_datetime_to_str(l_time, to);
1051
case DRIZZLE_TIMESTAMP_DATE:
1052
return my_date_to_str(l_time, to);
1053
case DRIZZLE_TIMESTAMP_TIME:
1054
return my_time_to_str(l_time, to);
1055
case DRIZZLE_TIMESTAMP_NONE:
1056
case DRIZZLE_TIMESTAMP_ERROR:
1067
Convert datetime value specified as number to broken-down TIME
1068
representation and form value of DATETIME type as side-effect.
1071
number_to_datetime()
1072
nr - datetime value as number
1073
time_res - pointer for structure for broken-down representation
1074
flags - flags to use in validating date, as in str_to_datetime()
1076
1 If value was cut during conversion
1077
2 check_date(date,flags) considers date invalid
1080
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1081
YYYYMMDDHHMMSS to broken-down DRIZZLE_TIME representation. Return value in
1082
YYYYMMDDHHMMSS format as side-effect.
1084
This function also checks if datetime value fits in DATETIME range.
1087
-1 Timestamp with wrong values
1088
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1089
Datetime value in YYYYMMDDHHMMSS format.
1092
int64_t number_to_datetime(int64_t nr, DRIZZLE_TIME *time_res,
1093
uint32_t flags, int *was_cut)
1098
memset(time_res, 0, sizeof(*time_res));
1099
time_res->time_type=DRIZZLE_TIMESTAMP_DATE;
1101
if (nr == 0LL || nr >= 10000101000000LL)
1103
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1108
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1110
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1113
if (nr < (YY_PART_YEAR)*10000L+101L)
1117
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1122
if (nr <= 99991231L)
1127
if (nr < 101000000L)
1130
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1132
if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1134
nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1137
if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1139
if (nr <= 991231235959LL)
1140
nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1143
part1=(long) (nr / 1000000LL);
1144
part2=(long) (nr - (int64_t) part1 * 1000000LL);
1145
time_res->year= (int) (part1/10000L); part1%=10000L;
1146
time_res->month= (int) part1 / 100;
1147
time_res->day= (int) part1 % 100;
1148
time_res->hour= (int) (part2/10000L); part2%=10000L;
1149
time_res->minute=(int) part2 / 100;
1150
time_res->second=(int) part2 % 100;
1152
if (time_res->year <= 9999 && time_res->month <= 12 &&
1153
time_res->day <= 31 && time_res->hour <= 23 &&
1154
time_res->minute <= 59 && time_res->second <= 59 &&
1155
!check_date(time_res, (nr != 0), flags, was_cut))
1158
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1159
if (!nr && (flags & TIME_NO_ZERO_DATE))
1168
/* Convert time value to integer in YYYYMMDDHHMMSS format */
1170
uint64_t TIME_to_uint64_t_datetime(const DRIZZLE_TIME *my_time)
1172
return ((uint64_t) (my_time->year * 10000UL +
1173
my_time->month * 100UL +
1174
my_time->day) * 1000000ULL +
1175
(uint64_t) (my_time->hour * 10000UL +
1176
my_time->minute * 100UL +
1181
/* Convert DRIZZLE_TIME value to integer in YYYYMMDD format */
1183
uint64_t TIME_to_uint64_t_date(const DRIZZLE_TIME *my_time)
1185
return (uint64_t) (my_time->year * 10000UL + my_time->month * 100UL +
1191
Convert DRIZZLE_TIME value to integer in HHMMSS format.
1192
This function doesn't take into account time->day member:
1193
it's assumed that days have been converted to hours already.
1196
uint64_t TIME_to_uint64_t_time(const DRIZZLE_TIME *my_time)
1198
return (uint64_t) (my_time->hour * 10000UL +
1199
my_time->minute * 100UL +
1205
Convert struct DRIZZLE_TIME (date and time split into year/month/day/hour/...
1206
to a number in format YYYYMMDDHHMMSS (DATETIME),
1207
YYYYMMDD (DATE) or HHMMSS (TIME).
1213
The function is used when we need to convert value of time item
1214
to a number if it's used in numeric context, i. e.:
1215
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1219
This function doesn't check that given DRIZZLE_TIME structure members are
1220
in valid range. If they are not, return value won't reflect any
1224
uint64_t TIME_to_uint64_t(const DRIZZLE_TIME *my_time)
1226
switch (my_time->time_type) {
1227
case DRIZZLE_TIMESTAMP_DATETIME:
1228
return TIME_to_uint64_t_datetime(my_time);
1229
case DRIZZLE_TIMESTAMP_DATE:
1230
return TIME_to_uint64_t_date(my_time);
1231
case DRIZZLE_TIMESTAMP_TIME:
1232
return TIME_to_uint64_t_time(my_time);
1233
case DRIZZLE_TIMESTAMP_NONE:
1234
case DRIZZLE_TIMESTAMP_ERROR: