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 */
16
#include <drizzled/global.h>
18
#include <mystrings/m_string.h>
19
#include <mystrings/m_ctype.h>
21
#include <drizzled/util/test.h>
22
/* Windows version of localtime_r() is declared in my_ptrhead.h */
24
uint64_t log_10_int[20]=
26
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
27
100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
28
1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
29
1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
30
1000000000000000000ULL, 10000000000000000000ULL
34
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
36
static unsigned char internal_format_positions[]=
37
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
39
static char time_separator=':';
41
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
42
unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
45
Offset of system time zone from UTC in seconds used to speed up
46
work of my_system_gmt_sec() function.
48
static long my_time_zone=0;
51
/* Calc days in one year. works with 0 <= year <= 99 */
53
uint32_t calc_days_in_year(uint32_t year)
55
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
60
@brief Check datetime value for validity according to flags.
62
@param[in] ltime Date to check.
63
@param[in] not_zero_date ltime is not the zero date
64
@param[in] flags flags to check
65
(see str_to_datetime() flags in my_time.h)
66
@param[out] was_cut set to 2 if value was invalid according to flags.
67
(Feb 29 in non-leap etc.) This remains unchanged
68
if value is not invalid.
70
@details Here we assume that year and month is ok!
71
If month is 0 we allow any date. (This only happens if we allow zero
72
date parts in str_to_datetime())
73
Disallow dates with zero year and non-zero month and/or day.
80
bool check_date(const DRIZZLE_TIME *ltime, bool not_zero_date,
81
uint32_t flags, int *was_cut)
85
if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
86
(ltime->month == 0 || ltime->day == 0)) ||
87
(!(flags & TIME_INVALID_DATES) &&
88
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
89
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
96
else if (flags & TIME_NO_ZERO_DATE)
99
We don't set *was_cut here to signal that the problem was a zero date
100
and not an invalid date
109
Convert a timestamp string to a DRIZZLE_TIME value.
114
length Length of string
115
l_time Date is stored here
116
flags Bitmap of following items
117
TIME_FUZZY_DATE Set if we should allow partial dates
118
TIME_DATETIME_ONLY Set if we only allow full datetimes.
119
TIME_NO_ZERO_IN_DATE Don't allow partial dates
120
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
121
TIME_INVALID_DATES Allow 2000-02-31
123
1 If value was cut during conversion
124
2 check_date(date,flags) considers date invalid
127
At least the following formats are recogniced (based on number of digits)
128
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
129
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
130
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
131
Also dates where all parts are zero are allowed
133
The second part may have an optional .###### fraction part.
136
This function should work with a format position vector as long as the
137
following things holds:
138
- All date are kept together and all time parts are kept together
139
- Date and time parts must be separated by blank
140
- Second fractions must come after second part and be separated
141
by a '.'. (The second fractions are optional)
142
- AM/PM must come after second fractions (or after seconds if no fractions)
143
- Year must always been specified.
144
- If time is before date, then we will use datetime format only if
145
the argument consist of two parts, separated by space.
146
Otherwise we will assume the argument is a date.
147
- The hour part must be specified in hour-minute-second order.
150
DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
151
[DD [HH:[MM:[SS]]]].fraction.
152
l_time is not changed.
153
DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
154
DRIZZLE_TIMESTAMP_DATETIME Full timestamp
155
DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
156
All elements in l_time is set to 0
159
#define MAX_DATE_PARTS 8
161
enum enum_drizzle_timestamp_type
162
str_to_datetime(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
163
uint32_t flags, int *was_cut)
165
uint32_t field_length, year_length=4, digits, i, number_of_fields;
166
uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
167
uint32_t add_hours= 0, start_loop;
168
uint32_t not_zero_date, allow_space;
169
bool is_internal_format;
170
const char *pos, *last_field_pos=NULL;
171
const char *end=str+length;
172
const unsigned char *format_position;
173
bool found_delimitier= 0, found_space= 0;
174
uint32_t frac_pos, frac_len;
178
/* Skip space at start */
179
for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
181
if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
184
return(DRIZZLE_TIMESTAMP_NONE);
187
is_internal_format= 0;
188
/* This has to be changed if want to activate different timestamp formats */
189
format_position= internal_format_positions;
192
Calculate number of digits in first part.
193
If length= 8 or >= 14 then year is of format YYYY.
194
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
197
pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
201
digits= (uint) (pos-str);
202
start_loop= 0; /* Start of scan loop */
203
date_len[format_position[0]]= 0; /* Length of year field */
204
if (pos == end || *pos == '.')
206
/* Found date in internal format (only numbers like YYYYMMDD) */
207
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
208
field_length= year_length;
209
is_internal_format= 1;
210
format_position= internal_format_positions;
214
if (format_position[0] >= 3) /* If year is after HHMMDD */
217
If year is not in first part then we have to determinate if we got
218
a date field or a datetime field.
219
We do this by checking if there is two numbers separated by
222
while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
224
while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
228
if (flags & TIME_DATETIME_ONLY)
231
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
233
/* Date field. Set hour, minutes and seconds to 0 */
234
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
235
start_loop= 5; /* Start with first date part */
239
field_length= format_position[0] == 0 ? 4 : 2;
243
Only allow space in the first "part" of the datetime field and:
244
- after days, part seconds
245
- before and after AM/PM (handled by code later)
247
2003-03-03 20:00:20 AM
248
20:00:20.000000 AM 03-03-2000
250
i= cmax((uint) format_position[0], (uint) format_position[1]);
251
set_if_bigger(i, (uint) format_position[2]);
252
allow_space= ((1 << i) | (1 << format_position[6]));
253
allow_space&= (1 | 2 | 4 | 8);
257
i < MAX_DATE_PARTS-1 && str != end &&
258
my_isdigit(&my_charset_utf8_general_ci,*str);
261
const char *start= str;
262
uint32_t tmp_value= (uint) (unsigned char) (*str++ - '0');
263
while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
264
(!is_internal_format || --field_length))
266
tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
269
date_len[i]= (uint) (str - start);
270
if (tmp_value > 999999) /* Impossible date part */
273
return(DRIZZLE_TIMESTAMP_NONE);
276
not_zero_date|= tmp_value;
278
/* Length of next field */
279
field_length= format_position[i+1] == 0 ? 4 : 2;
281
if ((last_field_pos= str) == end)
283
i++; /* Register last found part */
286
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
287
if (i == format_position[2] && *str == 'T')
289
str++; /* ISO8601: CCYYMMDDThhmmss */
292
if (i == format_position[5]) /* Seconds */
294
if (*str == '.') /* Followed by part seconds */
297
field_length= 6; /* 6 digits */
302
(my_ispunct(&my_charset_utf8_general_ci,*str) ||
303
my_isspace(&my_charset_utf8_general_ci,*str)))
305
if (my_isspace(&my_charset_utf8_general_ci,*str))
307
if (!(allow_space & (1 << i)))
310
return(DRIZZLE_TIMESTAMP_NONE);
315
found_delimitier= 1; /* Should be a 'normal' date */
317
/* Check if next position is AM/PM */
318
if (i == format_position[6]) /* Seconds, time for AM/PM */
320
i++; /* Skip AM/PM part */
321
if (format_position[7] != 255) /* If using AM/PM */
323
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
325
if (str[0] == 'p' || str[0] == 'P')
327
else if (str[0] != 'a' || str[0] != 'A')
328
continue; /* Not AM/PM */
329
str+= 2; /* Skip AM/PM */
330
/* Skip space after AM/PM */
331
while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
338
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
341
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
346
number_of_fields= i - start_loop;
347
while (i < MAX_DATE_PARTS)
353
if (!is_internal_format)
355
year_length= date_len[(uint) format_position[0]];
356
if (!year_length) /* Year must be specified */
359
return(DRIZZLE_TIMESTAMP_NONE);
362
l_time->year= date[(uint) format_position[0]];
363
l_time->month= date[(uint) format_position[1]];
364
l_time->day= date[(uint) format_position[2]];
365
l_time->hour= date[(uint) format_position[3]];
366
l_time->minute= date[(uint) format_position[4]];
367
l_time->second= date[(uint) format_position[5]];
369
frac_pos= (uint) format_position[6];
370
frac_len= date_len[frac_pos];
372
date[frac_pos]*= (uint) log_10_int[6 - frac_len];
373
l_time->second_part= date[frac_pos];
375
if (format_position[7] != (unsigned char) 255)
377
if (l_time->hour > 12)
382
l_time->hour= l_time->hour%12 + add_hours;
387
l_time->year= date[0];
388
l_time->month= date[1];
389
l_time->day= date[2];
390
l_time->hour= date[3];
391
l_time->minute= date[4];
392
l_time->second= date[5];
394
date[6]*= (uint) log_10_int[6 - date_len[6]];
395
l_time->second_part=date[6];
399
if (year_length == 2 && not_zero_date)
400
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
402
if (number_of_fields < 3 ||
403
l_time->year > 9999 || l_time->month > 12 ||
404
l_time->day > 31 || l_time->hour > 23 ||
405
l_time->minute > 59 || l_time->second > 59)
407
/* Only give warning for a zero date if there is some garbage after */
408
if (!not_zero_date) /* If zero date */
410
for (; str != end ; str++)
412
if (!my_isspace(&my_charset_utf8_general_ci, *str))
414
not_zero_date= 1; /* Give warning */
419
*was_cut= test(not_zero_date);
423
if (check_date(l_time, not_zero_date != 0, flags, was_cut))
426
l_time->time_type= (number_of_fields <= 3 ?
427
DRIZZLE_TIMESTAMP_DATE : DRIZZLE_TIMESTAMP_DATETIME);
429
for (; str != end ; str++)
431
if (!my_isspace(&my_charset_utf8_general_ci,*str))
438
return(l_time->time_type=
439
(number_of_fields <= 3 ? DRIZZLE_TIMESTAMP_DATE :
440
DRIZZLE_TIMESTAMP_DATETIME));
443
memset(l_time, 0, sizeof(*l_time));
444
return(DRIZZLE_TIMESTAMP_ERROR);
449
Convert a time string to a DRIZZLE_TIME struct.
453
str A string in full TIMESTAMP format or
454
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
456
There may be an optional [.second_part] after seconds
458
l_time Store result here
459
warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
460
was cut during conversion, and/or
461
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
465
Because of the extra days argument, this function can only
466
work with times where the time arguments are in the above order.
473
bool str_to_time(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
478
const char *end=str+length, *end_of_days;
479
bool found_days,found_hours;
484
for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
486
if (str != end && *str == '-')
495
/* Check first if this is a full TIMESTAMP */
497
{ /* Probably full timestamp */
499
enum enum_drizzle_timestamp_type
500
res= str_to_datetime(str, length, l_time,
501
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
502
if ((int) res >= (int) DRIZZLE_TIMESTAMP_ERROR)
505
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
506
return res == DRIZZLE_TIMESTAMP_ERROR;
510
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
511
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
512
value=value*10L + (long) (*str - '0');
514
/* Skip all space after 'days' */
516
for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
519
found_days=found_hours=0;
520
if ((uint) (end-str) > 1 && str != end_of_days &&
521
my_isdigit(&my_charset_utf8_general_ci, *str))
522
{ /* Found days part */
523
date[0]= (uint32_t) value;
524
state= 1; /* Assume next is hours */
527
else if ((end-str) > 1 && *str == time_separator &&
528
my_isdigit(&my_charset_utf8_general_ci, str[1]))
530
date[0]= 0; /* Assume we found hours */
531
date[1]= (uint32_t) value;
534
str++; /* skip ':' */
538
/* String given as one number; assume HHMMSS format */
540
date[1]= (uint32_t) (value/10000);
541
date[2]= (uint32_t) (value/100 % 100);
542
date[3]= (uint32_t) (value % 100);
547
/* Read hours, minutes and seconds */
550
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
551
value=value*10L + (long) (*str - '0');
552
date[state++]= (uint32_t) value;
553
if (state == 4 || (end-str) < 2 || *str != time_separator ||
554
!my_isdigit(&my_charset_utf8_general_ci,str[1]))
556
str++; /* Skip time_separator (':') */
561
/* Fix the date to assume that seconds was given */
562
if (!found_hours && !found_days)
564
bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
565
sizeof(long)*(state-1));
566
memset(date, 0, sizeof(long)*(4-state));
569
memset(date+state, 0, sizeof(long)*(4-state));
573
/* Get fractional second part */
574
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
577
str++; value=(uint) (unsigned char) (*str - '0');
578
while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
580
if (field_length-- > 0)
581
value= value*10 + (uint) (unsigned char) (*str - '0');
583
if (field_length > 0)
584
value*= (long) log_10_int[field_length];
585
else if (field_length < 0)
586
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
587
date[4]= (uint32_t) value;
592
/* Check for exponent part: E<gigit> | E<sign><digit> */
593
/* (may occur as result of %g formatting of time value) */
594
if ((end - str) > 1 &&
595
(*str == 'e' || *str == 'E') &&
596
(my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
597
((str[1] == '-' || str[1] == '+') &&
599
my_isdigit(&my_charset_utf8_general_ci, str[2]))))
602
if (internal_format_positions[7] != 255)
604
/* Read a possible AM/PM */
605
while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
607
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
609
if (str[0] == 'p' || str[0] == 'P')
612
date[1]= date[1]%12 + 12;
614
else if (str[0] == 'a' || str[0] == 'A')
619
/* Integer overflow checks */
620
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
621
date[2] > UINT_MAX || date[3] > UINT_MAX ||
625
l_time->year= 0; /* For protocol::store_time */
627
l_time->day= date[0];
628
l_time->hour= date[1];
629
l_time->minute= date[2];
630
l_time->second= date[3];
631
l_time->second_part= date[4];
632
l_time->time_type= DRIZZLE_TIMESTAMP_TIME;
634
/* Check if the value is valid and fits into DRIZZLE_TIME range */
635
if (check_time_range(l_time, warning))
638
/* Check if there is garbage at end of the DRIZZLE_TIME specification */
643
if (!my_isspace(&my_charset_utf8_general_ci,*str))
645
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
648
} while (++str != end);
655
Check 'time' value to lie in the DRIZZLE_TIME range
659
time pointer to DRIZZLE_TIME value
660
warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
663
If the time value lies outside of the range [-838:59:59, 838:59:59],
664
set it to the closest endpoint of the range and set
665
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
668
0 time value is valid, but was possibly truncated
669
1 time value is invalid
672
int check_time_range(DRIZZLE_TIME *my_time, int *warning)
676
if (my_time->minute >= 60 || my_time->second >= 60)
679
hour= my_time->hour + (24*my_time->day);
680
if (hour <= TIME_MAX_HOUR &&
681
(hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
682
my_time->second != TIME_MAX_SECOND || !my_time->second_part))
686
my_time->hour= TIME_MAX_HOUR;
687
my_time->minute= TIME_MAX_MINUTE;
688
my_time->second= TIME_MAX_SECOND;
689
my_time->second_part= 0;
690
*warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
696
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
704
struct tm *l_time,tm_tmp;
705
DRIZZLE_TIME my_time;
708
seconds= (time_t) time((time_t*) 0);
709
localtime_r(&seconds,&tm_tmp);
711
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
712
my_time.year= (uint) l_time->tm_year+1900;
713
my_time.month= (uint) l_time->tm_mon+1;
714
my_time.day= (uint) l_time->tm_mday;
715
my_time.hour= (uint) l_time->tm_hour;
716
my_time.minute= (uint) l_time->tm_min;
717
my_time.second= (uint) l_time->tm_sec;
718
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
723
Handle 2 digit year conversions
730
Year between 1970-2069
733
uint32_t year_2000_handling(uint32_t year)
735
if ((year=year+1900) < 1900+YY_PART_YEAR)
742
Calculate nr of day since year 0 in new date-system (from 1615)
746
year Year (exact 4 digit year, no year conversions)
750
NOTES: 0000-00-00 is a valid date, and will return 0
753
Days since 0000-00-00
756
long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
761
if (year == 0 && month == 0 && day == 0)
762
return(0); /* Skip errors */
763
delsum= (long) (365L * year+ 31*(month-1) +day);
767
delsum-= (long) (month*4+23)/10;
768
temp=(int) ((year/100+1)*3)/4;
769
return(delsum+(int) year/4-temp);
774
Convert time in DRIZZLE_TIME representation in system time zone to its
775
my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
779
t - time value to be converted
780
my_timezone - pointer to long where offset of system time zone
781
from UTC will be stored for caching
782
in_dst_time_gap - set to true if time falls into spring time-gap
785
The idea is to cache the time zone offset from UTC (including daylight
786
saving time) for the next call to make things faster. But currently we
787
just calculate this offset during startup (by calling init_time()
788
function) and use it all the time.
789
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
793
Time in UTC seconds since Unix Epoch representation.
796
my_system_gmt_sec(const DRIZZLE_TIME *t_src, long *my_timezone,
797
bool *in_dst_time_gap)
802
DRIZZLE_TIME tmp_time;
803
DRIZZLE_TIME *t= &tmp_time;
804
struct tm *l_time,tm_tmp;
805
long diff, current_timezone;
808
Use temp variable to avoid trashing input data, which could happen in
809
case of shift required for boundary dates processing.
811
memcpy(&tmp_time, t_src, sizeof(DRIZZLE_TIME));
813
if (!validate_timestamp_range(t))
817
Calculate the gmt time based on current time and timezone
818
The -1 on the end is to ensure that if have a date that exists twice
819
(like 2002-10-27 02:00:0 MET), we will find the initial date.
821
By doing -3600 we will have to call localtime_r() several times, but
822
I couldn't come up with a better way to get a repeatable result :(
824
We can't use mktime() as it's buggy on many platforms and not thread safe.
826
Note: this code assumes that our time_t estimation is not too far away
827
from real value (we assume that localtime_r(tmp) will return something
828
within 24 hrs from t) which is probably true for all current time zones.
830
Note2: For the dates, which have time_t representation close to
831
MAX_INT32 (efficient time_t limit for supported platforms), we should
832
do a small trick to avoid overflow. That is, convert the date, which is
833
two days earlier, and then add these days to the final value.
835
The same trick is done for the values close to 0 in time_t
836
representation for platfroms with unsigned time_t (QNX).
838
To be more verbose, here is a sample (extracted from the code below):
839
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
840
would return -2147480896 because of the long type overflow. In result
841
we would get 1901 year in localtime_r(), which is an obvious error.
843
Alike problem raises with the dates close to Epoch. E.g.
844
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
847
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
848
wil give us a date around 2106 year. Which is no good.
850
Theoreticaly, there could be problems with the latter conversion:
851
there are at least two timezones, which had time switches near 1 Jan
852
of 1970 (because of political reasons). These are America/Hermosillo and
853
America/Mazatlan time zones. They changed their offset on
854
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
855
the code below will give incorrect results for dates close to
856
1970-01-01, in the case OS takes into account these historical switches.
857
Luckily, it seems that we support only one platform with unsigned
858
time_t. It's QNX. And QNX does not support historical timezone data at all.
859
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
860
historical information for localtime_r() etc. That is, the problem is not
863
We are safe with shifts close to MAX_INT32, as there are no known
864
time switches on Jan 2038 yet :)
866
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
869
Below we will pass (uint) (t->day - shift) to calc_daynr.
870
As we don't want to get an overflow here, we will shift
871
only safe dates. That's why we have (t->day > 4) above.
876
#ifdef TIME_T_UNSIGNED
880
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
881
1970, 1 of Jan. For both dates we use shift, which is added
882
to t->day in order to step out a bit from the border.
883
This is required for platforms, where time_t is unsigned.
884
As far as I know, among the platforms we support it's only QNX.
885
Note: the order of below if-statements is significant.
888
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
895
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
906
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
907
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
908
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
911
current_timezone= my_time_zone;
912
localtime_r(&tmp,&tm_tmp);
916
(t->hour != (uint) l_time->tm_hour ||
917
t->minute != (uint) l_time->tm_min ||
918
t->second != (uint) l_time->tm_sec);
920
{ /* One check should be enough ? */
921
/* Get difference in days */
922
int days= t->day - l_time->tm_mday;
924
days= 1; /* Month has wrapped */
927
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
928
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
929
(long) ((int) t->second - (int) l_time->tm_sec));
930
current_timezone+= diff+3600; /* Compensate for -3600 above */
932
localtime_r(&tmp,&tm_tmp);
936
Fix that if we are in the non existing daylight saving time hour
937
we move the start of the next real hour.
939
This code doesn't handle such exotical thing as time-gaps whose length
940
is more than one hour or non-integer (latter can theoretically happen
941
if one of seconds will be removed due leap correction, or because of
942
general time correction like it happened for Africa/Monrovia time zone
945
if (loop == 2 && t->hour != (uint) l_time->tm_hour)
947
int days= t->day - l_time->tm_mday;
949
days=1; /* Month has wrapped */
952
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
953
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
954
(long) ((int) t->second - (int) l_time->tm_sec));
956
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
957
else if (diff == -3600)
958
tmp-=t->minute*60 + t->second; /* Move to previous hour */
960
*in_dst_time_gap= true;
962
*my_timezone= current_timezone;
965
/* shift back, if we were dealing with boundary dates */
969
This is possible for dates, which slightly exceed boundaries.
970
Conversion will pass ok for them, but we don't allow them.
971
First check will pass for platforms with signed time_t.
972
instruction above (tmp+= shift*86400L) could exceed
973
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
974
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
975
with unsigned time_t tmp+= shift*86400L might result in a number,
976
larger then TIMESTAMP_MAX_VALUE, so another check will work.
978
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
981
return (my_time_t) tmp;
982
} /* my_system_gmt_sec */
985
/* Set DRIZZLE_TIME structure to 0000-00-00 00:00:00.000000 */
987
void set_zero_time(DRIZZLE_TIME *tm, enum enum_drizzle_timestamp_type time_type)
989
memset(tm, 0, sizeof(*tm));
990
tm->time_type= time_type;
995
Functions to convert time/date/datetime value to a string,
996
using default format.
997
This functions don't check that given DRIZZLE_TIME structure members are
998
in valid range. If they are not, return value won't reflect any
999
valid date either. Additionally, make_time doesn't take into
1000
account time->day member: it's assumed that days have been converted
1004
number of characters written to 'to'
1007
int my_time_to_str(const DRIZZLE_TIME *l_time, char *to)
1009
uint32_t extra_hours= 0;
1010
return sprintf(to, "%s%02u:%02u:%02u",
1011
(l_time->neg ? "-" : ""),
1012
extra_hours+ l_time->hour,
1017
int my_date_to_str(const DRIZZLE_TIME *l_time, char *to)
1019
return sprintf(to, "%04u-%02u-%02u",
1025
int my_datetime_to_str(const DRIZZLE_TIME *l_time, char *to)
1027
return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u",
1038
Convert struct DATE/TIME/DATETIME value to string using built-in
1039
MySQL time conversion formats.
1045
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1048
int my_TIME_to_str(const DRIZZLE_TIME *l_time, char *to)
1050
switch (l_time->time_type) {
1051
case DRIZZLE_TIMESTAMP_DATETIME:
1052
return my_datetime_to_str(l_time, to);
1053
case DRIZZLE_TIMESTAMP_DATE:
1054
return my_date_to_str(l_time, to);
1055
case DRIZZLE_TIMESTAMP_TIME:
1056
return my_time_to_str(l_time, to);
1057
case DRIZZLE_TIMESTAMP_NONE:
1058
case DRIZZLE_TIMESTAMP_ERROR:
1069
Convert datetime value specified as number to broken-down TIME
1070
representation and form value of DATETIME type as side-effect.
1073
number_to_datetime()
1074
nr - datetime value as number
1075
time_res - pointer for structure for broken-down representation
1076
flags - flags to use in validating date, as in str_to_datetime()
1078
1 If value was cut during conversion
1079
2 check_date(date,flags) considers date invalid
1082
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1083
YYYYMMDDHHMMSS to broken-down DRIZZLE_TIME representation. Return value in
1084
YYYYMMDDHHMMSS format as side-effect.
1086
This function also checks if datetime value fits in DATETIME range.
1089
-1 Timestamp with wrong values
1090
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1091
Datetime value in YYYYMMDDHHMMSS format.
1094
int64_t number_to_datetime(int64_t nr, DRIZZLE_TIME *time_res,
1095
uint32_t flags, int *was_cut)
1100
memset(time_res, 0, sizeof(*time_res));
1101
time_res->time_type=DRIZZLE_TIMESTAMP_DATE;
1103
if (nr == 0LL || nr >= 10000101000000LL)
1105
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1110
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1112
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1115
if (nr < (YY_PART_YEAR)*10000L+101L)
1119
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1124
if (nr <= 99991231L)
1129
if (nr < 101000000L)
1132
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1134
if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1136
nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1139
if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1141
if (nr <= 991231235959LL)
1142
nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1145
part1=(long) (nr / 1000000LL);
1146
part2=(long) (nr - (int64_t) part1 * 1000000LL);
1147
time_res->year= (int) (part1/10000L); part1%=10000L;
1148
time_res->month= (int) part1 / 100;
1149
time_res->day= (int) part1 % 100;
1150
time_res->hour= (int) (part2/10000L); part2%=10000L;
1151
time_res->minute=(int) part2 / 100;
1152
time_res->second=(int) part2 % 100;
1154
if (time_res->year <= 9999 && time_res->month <= 12 &&
1155
time_res->day <= 31 && time_res->hour <= 23 &&
1156
time_res->minute <= 59 && time_res->second <= 59 &&
1157
!check_date(time_res, (nr != 0), flags, was_cut))
1160
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1161
if (!nr && (flags & TIME_NO_ZERO_DATE))
1170
/* Convert time value to integer in YYYYMMDDHHMMSS format */
1172
uint64_t TIME_to_uint64_t_datetime(const DRIZZLE_TIME *my_time)
1174
return ((uint64_t) (my_time->year * 10000UL +
1175
my_time->month * 100UL +
1176
my_time->day) * 1000000ULL +
1177
(uint64_t) (my_time->hour * 10000UL +
1178
my_time->minute * 100UL +
1183
/* Convert DRIZZLE_TIME value to integer in YYYYMMDD format */
1185
uint64_t TIME_to_uint64_t_date(const DRIZZLE_TIME *my_time)
1187
return (uint64_t) (my_time->year * 10000UL + my_time->month * 100UL +
1193
Convert DRIZZLE_TIME value to integer in HHMMSS format.
1194
This function doesn't take into account time->day member:
1195
it's assumed that days have been converted to hours already.
1198
uint64_t TIME_to_uint64_t_time(const DRIZZLE_TIME *my_time)
1200
return (uint64_t) (my_time->hour * 10000UL +
1201
my_time->minute * 100UL +
1207
Convert struct DRIZZLE_TIME (date and time split into year/month/day/hour/...
1208
to a number in format YYYYMMDDHHMMSS (DATETIME),
1209
YYYYMMDD (DATE) or HHMMSS (TIME).
1215
The function is used when we need to convert value of time item
1216
to a number if it's used in numeric context, i. e.:
1217
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1221
This function doesn't check that given DRIZZLE_TIME structure members are
1222
in valid range. If they are not, return value won't reflect any
1226
uint64_t TIME_to_uint64_t(const DRIZZLE_TIME *my_time)
1228
switch (my_time->time_type) {
1229
case DRIZZLE_TIMESTAMP_DATETIME:
1230
return TIME_to_uint64_t_datetime(my_time);
1231
case DRIZZLE_TIMESTAMP_DATE:
1232
return TIME_to_uint64_t_date(my_time);
1233
case DRIZZLE_TIMESTAMP_TIME:
1234
return TIME_to_uint64_t_time(my_time);
1235
case DRIZZLE_TIMESTAMP_NONE:
1236
case DRIZZLE_TIMESTAMP_ERROR: