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 "mysys_priv.h"
20
#include <mystrings/m_string.h>
21
#include <mystrings/m_ctype.h>
22
#include <drizzled/util/test.h>
25
/* Windows version of localtime_r() is declared in my_ptrhead.h */
27
uint64_t log_10_int[20]=
29
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
30
100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
31
1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
32
1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
33
1000000000000000000ULL, 10000000000000000000ULL
37
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
39
static unsigned char internal_format_positions[]=
40
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
42
static char time_separator=':';
44
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
45
unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
48
Offset of system time zone from UTC in seconds used to speed up
49
work of my_system_gmt_sec() function.
51
static long my_time_zone=0;
54
/* Calc days in one year. works with 0 <= year <= 99 */
56
uint32_t calc_days_in_year(uint32_t year)
58
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
63
@brief Check datetime value for validity according to flags.
65
@param[in] ltime Date to check.
66
@param[in] not_zero_date ltime is not the zero date
67
@param[in] flags flags to check
68
(see str_to_datetime() flags in my_time.h)
69
@param[out] was_cut set to 2 if value was invalid according to flags.
70
(Feb 29 in non-leap etc.) This remains unchanged
71
if value is not invalid.
73
@details Here we assume that year and month is ok!
74
If month is 0 we allow any date. (This only happens if we allow zero
75
date parts in str_to_datetime())
76
Disallow dates with zero year and non-zero month and/or day.
83
bool check_date(const DRIZZLE_TIME *ltime, bool not_zero_date,
84
uint32_t flags, int *was_cut)
88
if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
89
(ltime->month == 0 || ltime->day == 0)) ||
90
(!(flags & TIME_INVALID_DATES) &&
91
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
92
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
99
else if (flags & TIME_NO_ZERO_DATE)
102
We don't set *was_cut here to signal that the problem was a zero date
103
and not an invalid date
112
Convert a timestamp string to a DRIZZLE_TIME value.
117
length Length of string
118
l_time Date is stored here
119
flags Bitmap of following items
120
TIME_FUZZY_DATE Set if we should allow partial dates
121
TIME_DATETIME_ONLY Set if we only allow full datetimes.
122
TIME_NO_ZERO_IN_DATE Don't allow partial dates
123
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
124
TIME_INVALID_DATES Allow 2000-02-31
126
1 If value was cut during conversion
127
2 check_date(date,flags) considers date invalid
130
At least the following formats are recogniced (based on number of digits)
131
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
132
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
133
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
134
Also dates where all parts are zero are allowed
136
The second part may have an optional .###### fraction part.
139
This function should work with a format position vector as long as the
140
following things holds:
141
- All date are kept together and all time parts are kept together
142
- Date and time parts must be separated by blank
143
- Second fractions must come after second part and be separated
144
by a '.'. (The second fractions are optional)
145
- AM/PM must come after second fractions (or after seconds if no fractions)
146
- Year must always been specified.
147
- If time is before date, then we will use datetime format only if
148
the argument consist of two parts, separated by space.
149
Otherwise we will assume the argument is a date.
150
- The hour part must be specified in hour-minute-second order.
153
DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
154
[DD [HH:[MM:[SS]]]].fraction.
155
l_time is not changed.
156
DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
157
DRIZZLE_TIMESTAMP_DATETIME Full timestamp
158
DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
159
All elements in l_time is set to 0
162
#define MAX_DATE_PARTS 8
164
enum enum_drizzle_timestamp_type
165
str_to_datetime(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
166
uint32_t flags, int *was_cut)
168
uint32_t field_length, year_length=4, digits, i, number_of_fields;
169
uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
170
uint32_t add_hours= 0, start_loop;
171
uint32_t not_zero_date, allow_space;
172
bool is_internal_format;
173
const char *pos, *last_field_pos=NULL;
174
const char *end=str+length;
175
const unsigned char *format_position;
176
bool found_delimitier= 0, found_space= 0;
177
uint32_t frac_pos, frac_len;
181
/* Skip space at start */
182
for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
184
if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
187
return(DRIZZLE_TIMESTAMP_NONE);
190
is_internal_format= 0;
191
/* This has to be changed if want to activate different timestamp formats */
192
format_position= internal_format_positions;
195
Calculate number of digits in first part.
196
If length= 8 or >= 14 then year is of format YYYY.
197
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
200
pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
204
digits= (uint) (pos-str);
205
start_loop= 0; /* Start of scan loop */
206
date_len[format_position[0]]= 0; /* Length of year field */
207
if (pos == end || *pos == '.')
209
/* Found date in internal format (only numbers like YYYYMMDD) */
210
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
211
field_length= year_length;
212
is_internal_format= 1;
213
format_position= internal_format_positions;
217
if (format_position[0] >= 3) /* If year is after HHMMDD */
220
If year is not in first part then we have to determinate if we got
221
a date field or a datetime field.
222
We do this by checking if there is two numbers separated by
225
while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
227
while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
231
if (flags & TIME_DATETIME_ONLY)
234
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
236
/* Date field. Set hour, minutes and seconds to 0 */
237
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
238
start_loop= 5; /* Start with first date part */
242
field_length= format_position[0] == 0 ? 4 : 2;
246
Only allow space in the first "part" of the datetime field and:
247
- after days, part seconds
248
- before and after AM/PM (handled by code later)
250
2003-03-03 20:00:20 AM
251
20:00:20.000000 AM 03-03-2000
253
i= cmax((uint) format_position[0], (uint) format_position[1]);
254
set_if_bigger(i, (uint) format_position[2]);
255
allow_space= ((1 << i) | (1 << format_position[6]));
256
allow_space&= (1 | 2 | 4 | 8);
260
i < MAX_DATE_PARTS-1 && str != end &&
261
my_isdigit(&my_charset_utf8_general_ci,*str);
264
const char *start= str;
265
uint32_t tmp_value= (uint) (unsigned char) (*str++ - '0');
266
while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
267
(!is_internal_format || --field_length))
269
tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
272
date_len[i]= (uint) (str - start);
273
if (tmp_value > 999999) /* Impossible date part */
276
return(DRIZZLE_TIMESTAMP_NONE);
279
not_zero_date|= tmp_value;
281
/* Length of next field */
282
field_length= format_position[i+1] == 0 ? 4 : 2;
284
if ((last_field_pos= str) == end)
286
i++; /* Register last found part */
289
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
290
if (i == format_position[2] && *str == 'T')
292
str++; /* ISO8601: CCYYMMDDThhmmss */
295
if (i == format_position[5]) /* Seconds */
297
if (*str == '.') /* Followed by part seconds */
300
field_length= 6; /* 6 digits */
305
(my_ispunct(&my_charset_utf8_general_ci,*str) ||
306
my_isspace(&my_charset_utf8_general_ci,*str)))
308
if (my_isspace(&my_charset_utf8_general_ci,*str))
310
if (!(allow_space & (1 << i)))
313
return(DRIZZLE_TIMESTAMP_NONE);
318
found_delimitier= 1; /* Should be a 'normal' date */
320
/* Check if next position is AM/PM */
321
if (i == format_position[6]) /* Seconds, time for AM/PM */
323
i++; /* Skip AM/PM part */
324
if (format_position[7] != 255) /* If using AM/PM */
326
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
328
if (str[0] == 'p' || str[0] == 'P')
330
else if (str[0] != 'a' || str[0] != 'A')
331
continue; /* Not AM/PM */
332
str+= 2; /* Skip AM/PM */
333
/* Skip space after AM/PM */
334
while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
341
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
344
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
349
number_of_fields= i - start_loop;
350
while (i < MAX_DATE_PARTS)
356
if (!is_internal_format)
358
year_length= date_len[(uint) format_position[0]];
359
if (!year_length) /* Year must be specified */
362
return(DRIZZLE_TIMESTAMP_NONE);
365
l_time->year= date[(uint) format_position[0]];
366
l_time->month= date[(uint) format_position[1]];
367
l_time->day= date[(uint) format_position[2]];
368
l_time->hour= date[(uint) format_position[3]];
369
l_time->minute= date[(uint) format_position[4]];
370
l_time->second= date[(uint) format_position[5]];
372
frac_pos= (uint) format_position[6];
373
frac_len= date_len[frac_pos];
375
date[frac_pos]*= (uint) log_10_int[6 - frac_len];
376
l_time->second_part= date[frac_pos];
378
if (format_position[7] != (unsigned char) 255)
380
if (l_time->hour > 12)
385
l_time->hour= l_time->hour%12 + add_hours;
390
l_time->year= date[0];
391
l_time->month= date[1];
392
l_time->day= date[2];
393
l_time->hour= date[3];
394
l_time->minute= date[4];
395
l_time->second= date[5];
397
date[6]*= (uint) log_10_int[6 - date_len[6]];
398
l_time->second_part=date[6];
402
if (year_length == 2 && not_zero_date)
403
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
405
if (number_of_fields < 3 ||
406
l_time->year > 9999 || l_time->month > 12 ||
407
l_time->day > 31 || l_time->hour > 23 ||
408
l_time->minute > 59 || l_time->second > 59)
410
/* Only give warning for a zero date if there is some garbage after */
411
if (!not_zero_date) /* If zero date */
413
for (; str != end ; str++)
415
if (!my_isspace(&my_charset_utf8_general_ci, *str))
417
not_zero_date= 1; /* Give warning */
422
*was_cut= test(not_zero_date);
426
if (check_date(l_time, not_zero_date != 0, flags, was_cut))
429
l_time->time_type= (number_of_fields <= 3 ?
430
DRIZZLE_TIMESTAMP_DATE : DRIZZLE_TIMESTAMP_DATETIME);
432
for (; str != end ; str++)
434
if (!my_isspace(&my_charset_utf8_general_ci,*str))
441
return(l_time->time_type=
442
(number_of_fields <= 3 ? DRIZZLE_TIMESTAMP_DATE :
443
DRIZZLE_TIMESTAMP_DATETIME));
446
memset(l_time, 0, sizeof(*l_time));
447
return(DRIZZLE_TIMESTAMP_ERROR);
452
Convert a time string to a DRIZZLE_TIME struct.
456
str A string in full TIMESTAMP format or
457
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
459
There may be an optional [.second_part] after seconds
461
l_time Store result here
462
warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
463
was cut during conversion, and/or
464
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
468
Because of the extra days argument, this function can only
469
work with times where the time arguments are in the above order.
476
bool str_to_time(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
481
const char *end=str+length, *end_of_days;
482
bool found_days,found_hours;
487
for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
489
if (str != end && *str == '-')
498
/* Check first if this is a full TIMESTAMP */
500
{ /* Probably full timestamp */
502
enum enum_drizzle_timestamp_type
503
res= str_to_datetime(str, length, l_time,
504
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
505
if ((int) res >= (int) DRIZZLE_TIMESTAMP_ERROR)
508
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
509
return res == DRIZZLE_TIMESTAMP_ERROR;
513
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
514
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
515
value=value*10L + (long) (*str - '0');
517
/* Skip all space after 'days' */
519
for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
522
found_days=found_hours=0;
523
if ((uint) (end-str) > 1 && str != end_of_days &&
524
my_isdigit(&my_charset_utf8_general_ci, *str))
525
{ /* Found days part */
526
date[0]= (uint32_t) value;
527
state= 1; /* Assume next is hours */
530
else if ((end-str) > 1 && *str == time_separator &&
531
my_isdigit(&my_charset_utf8_general_ci, str[1]))
533
date[0]= 0; /* Assume we found hours */
534
date[1]= (uint32_t) value;
537
str++; /* skip ':' */
541
/* String given as one number; assume HHMMSS format */
543
date[1]= (uint32_t) (value/10000);
544
date[2]= (uint32_t) (value/100 % 100);
545
date[3]= (uint32_t) (value % 100);
550
/* Read hours, minutes and seconds */
553
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
554
value=value*10L + (long) (*str - '0');
555
date[state++]= (uint32_t) value;
556
if (state == 4 || (end-str) < 2 || *str != time_separator ||
557
!my_isdigit(&my_charset_utf8_general_ci,str[1]))
559
str++; /* Skip time_separator (':') */
564
/* Fix the date to assume that seconds was given */
565
if (!found_hours && !found_days)
567
bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
568
sizeof(long)*(state-1));
569
memset(date, 0, sizeof(long)*(4-state));
572
memset(date+state, 0, sizeof(long)*(4-state));
576
/* Get fractional second part */
577
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
580
str++; value=(uint) (unsigned char) (*str - '0');
581
while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
583
if (field_length-- > 0)
584
value= value*10 + (uint) (unsigned char) (*str - '0');
586
if (field_length > 0)
587
value*= (long) log_10_int[field_length];
588
else if (field_length < 0)
589
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
590
date[4]= (uint32_t) value;
595
/* Check for exponent part: E<gigit> | E<sign><digit> */
596
/* (may occur as result of %g formatting of time value) */
597
if ((end - str) > 1 &&
598
(*str == 'e' || *str == 'E') &&
599
(my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
600
((str[1] == '-' || str[1] == '+') &&
602
my_isdigit(&my_charset_utf8_general_ci, str[2]))))
605
if (internal_format_positions[7] != 255)
607
/* Read a possible AM/PM */
608
while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
610
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
612
if (str[0] == 'p' || str[0] == 'P')
615
date[1]= date[1]%12 + 12;
617
else if (str[0] == 'a' || str[0] == 'A')
622
/* Integer overflow checks */
623
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
624
date[2] > UINT_MAX || date[3] > UINT_MAX ||
628
l_time->year= 0; /* For protocol::store_time */
630
l_time->day= date[0];
631
l_time->hour= date[1];
632
l_time->minute= date[2];
633
l_time->second= date[3];
634
l_time->second_part= date[4];
635
l_time->time_type= DRIZZLE_TIMESTAMP_TIME;
637
/* Check if the value is valid and fits into DRIZZLE_TIME range */
638
if (check_time_range(l_time, warning))
641
/* Check if there is garbage at end of the DRIZZLE_TIME specification */
646
if (!my_isspace(&my_charset_utf8_general_ci,*str))
648
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
651
} while (++str != end);
658
Check 'time' value to lie in the DRIZZLE_TIME range
662
time pointer to DRIZZLE_TIME value
663
warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
666
If the time value lies outside of the range [-838:59:59, 838:59:59],
667
set it to the closest endpoint of the range and set
668
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
671
0 time value is valid, but was possibly truncated
672
1 time value is invalid
675
int check_time_range(DRIZZLE_TIME *my_time, int *warning)
679
if (my_time->minute >= 60 || my_time->second >= 60)
682
hour= my_time->hour + (24*my_time->day);
683
if (hour <= TIME_MAX_HOUR &&
684
(hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
685
my_time->second != TIME_MAX_SECOND || !my_time->second_part))
689
my_time->hour= TIME_MAX_HOUR;
690
my_time->minute= TIME_MAX_MINUTE;
691
my_time->second= TIME_MAX_SECOND;
692
my_time->second_part= 0;
693
*warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
699
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
707
struct tm *l_time,tm_tmp;
708
DRIZZLE_TIME my_time;
711
seconds= (time_t) time((time_t*) 0);
712
localtime_r(&seconds,&tm_tmp);
714
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
715
my_time.year= (uint) l_time->tm_year+1900;
716
my_time.month= (uint) l_time->tm_mon+1;
717
my_time.day= (uint) l_time->tm_mday;
718
my_time.hour= (uint) l_time->tm_hour;
719
my_time.minute= (uint) l_time->tm_min;
720
my_time.second= (uint) l_time->tm_sec;
721
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
726
Handle 2 digit year conversions
733
Year between 1970-2069
736
uint32_t year_2000_handling(uint32_t year)
738
if ((year=year+1900) < 1900+YY_PART_YEAR)
745
Calculate nr of day since year 0 in new date-system (from 1615)
749
year Year (exact 4 digit year, no year conversions)
753
NOTES: 0000-00-00 is a valid date, and will return 0
756
Days since 0000-00-00
759
long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
764
if (year == 0 && month == 0 && day == 0)
765
return(0); /* Skip errors */
766
delsum= (long) (365L * year+ 31*(month-1) +day);
770
delsum-= (long) (month*4+23)/10;
771
temp=(int) ((year/100+1)*3)/4;
772
return(delsum+(int) year/4-temp);
777
Convert time in DRIZZLE_TIME representation in system time zone to its
778
time_t form (number of seconds in UTC since begginning of Unix Epoch).
782
t - time value to be converted
783
my_timezone - pointer to long where offset of system time zone
784
from UTC will be stored for caching
785
in_dst_time_gap - set to true if time falls into spring time-gap
788
The idea is to cache the time zone offset from UTC (including daylight
789
saving time) for the next call to make things faster. But currently we
790
just calculate this offset during startup (by calling init_time()
791
function) and use it all the time.
792
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
796
Time in UTC seconds since Unix Epoch representation.
799
my_system_gmt_sec(const DRIZZLE_TIME *t_src, long *my_timezone,
800
bool *in_dst_time_gap)
805
DRIZZLE_TIME tmp_time;
806
DRIZZLE_TIME *t= &tmp_time;
807
struct tm *l_time,tm_tmp;
808
long diff, current_timezone;
811
Use temp variable to avoid trashing input data, which could happen in
812
case of shift required for boundary dates processing.
814
memcpy(&tmp_time, t_src, sizeof(DRIZZLE_TIME));
816
if (!validate_timestamp_range(t))
820
Calculate the gmt time based on current time and timezone
821
The -1 on the end is to ensure that if have a date that exists twice
822
(like 2002-10-27 02:00:0 MET), we will find the initial date.
824
By doing -3600 we will have to call localtime_r() several times, but
825
I couldn't come up with a better way to get a repeatable result :(
827
We can't use mktime() as it's buggy on many platforms and not thread safe.
829
Note: this code assumes that our time_t estimation is not too far away
830
from real value (we assume that localtime_r(tmp) will return something
831
within 24 hrs from t) which is probably true for all current time zones.
833
Note2: For the dates, which have time_t representation close to
834
MAX_INT32 (efficient time_t limit for supported platforms), we should
835
do a small trick to avoid overflow. That is, convert the date, which is
836
two days earlier, and then add these days to the final value.
838
The same trick is done for the values close to 0 in time_t
839
representation for platfroms with unsigned time_t (QNX).
841
To be more verbose, here is a sample (extracted from the code below):
842
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
843
would return -2147480896 because of the long type overflow. In result
844
we would get 1901 year in localtime_r(), which is an obvious error.
846
Alike problem raises with the dates close to Epoch. E.g.
847
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
850
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
851
wil give us a date around 2106 year. Which is no good.
853
Theoreticaly, there could be problems with the latter conversion:
854
there are at least two timezones, which had time switches near 1 Jan
855
of 1970 (because of political reasons). These are America/Hermosillo and
856
America/Mazatlan time zones. They changed their offset on
857
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
858
the code below will give incorrect results for dates close to
859
1970-01-01, in the case OS takes into account these historical switches.
860
Luckily, it seems that we support only one platform with unsigned
861
time_t. It's QNX. And QNX does not support historical timezone data at all.
862
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
863
historical information for localtime_r() etc. That is, the problem is not
866
We are safe with shifts close to MAX_INT32, as there are no known
867
time switches on Jan 2038 yet :)
869
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
872
Below we will pass (uint) (t->day - shift) to calc_daynr.
873
As we don't want to get an overflow here, we will shift
874
only safe dates. That's why we have (t->day > 4) above.
879
#ifdef TIME_T_UNSIGNED
883
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
884
1970, 1 of Jan. For both dates we use shift, which is added
885
to t->day in order to step out a bit from the border.
886
This is required for platforms, where time_t is unsigned.
887
As far as I know, among the platforms we support it's only QNX.
888
Note: the order of below if-statements is significant.
891
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
898
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
909
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
910
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
911
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
914
current_timezone= my_time_zone;
915
localtime_r(&tmp,&tm_tmp);
919
(t->hour != (uint) l_time->tm_hour ||
920
t->minute != (uint) l_time->tm_min ||
921
t->second != (uint) l_time->tm_sec);
923
{ /* One check should be enough ? */
924
/* Get difference in days */
925
int days= t->day - l_time->tm_mday;
927
days= 1; /* Month has wrapped */
930
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
931
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
932
(long) ((int) t->second - (int) l_time->tm_sec));
933
current_timezone+= diff+3600; /* Compensate for -3600 above */
935
localtime_r(&tmp,&tm_tmp);
939
Fix that if we are in the non existing daylight saving time hour
940
we move the start of the next real hour.
942
This code doesn't handle such exotical thing as time-gaps whose length
943
is more than one hour or non-integer (latter can theoretically happen
944
if one of seconds will be removed due leap correction, or because of
945
general time correction like it happened for Africa/Monrovia time zone
948
if (loop == 2 && t->hour != (uint) l_time->tm_hour)
950
int days= t->day - l_time->tm_mday;
952
days=1; /* Month has wrapped */
955
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
956
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
957
(long) ((int) t->second - (int) l_time->tm_sec));
959
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
960
else if (diff == -3600)
961
tmp-=t->minute*60 + t->second; /* Move to previous hour */
963
*in_dst_time_gap= true;
965
*my_timezone= current_timezone;
968
/* shift back, if we were dealing with boundary dates */
972
This is possible for dates, which slightly exceed boundaries.
973
Conversion will pass ok for them, but we don't allow them.
974
First check will pass for platforms with signed time_t.
975
instruction above (tmp+= shift*86400L) could exceed
976
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
977
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
978
with unsigned time_t tmp+= shift*86400L might result in a number,
979
larger then TIMESTAMP_MAX_VALUE, so another check will work.
981
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
985
} /* my_system_gmt_sec */
988
/* Set DRIZZLE_TIME structure to 0000-00-00 00:00:00.000000 */
990
void set_zero_time(DRIZZLE_TIME *tm, enum enum_drizzle_timestamp_type time_type)
992
memset(tm, 0, sizeof(*tm));
993
tm->time_type= time_type;
998
Functions to convert time/date/datetime value to a string,
999
using default format.
1000
This functions don't check that given DRIZZLE_TIME structure members are
1001
in valid range. If they are not, return value won't reflect any
1002
valid date either. Additionally, make_time doesn't take into
1003
account time->day member: it's assumed that days have been converted
1007
number of characters written to 'to'
1010
int my_time_to_str(const DRIZZLE_TIME *l_time, char *to)
1012
uint32_t extra_hours= 0;
1013
return sprintf(to, "%s%02u:%02u:%02u",
1014
(l_time->neg ? "-" : ""),
1015
extra_hours+ l_time->hour,
1020
int my_date_to_str(const DRIZZLE_TIME *l_time, char *to)
1022
return sprintf(to, "%04u-%02u-%02u",
1028
int my_datetime_to_str(const DRIZZLE_TIME *l_time, char *to)
1030
return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u",
1041
Convert struct DATE/TIME/DATETIME value to string using built-in
1042
MySQL time conversion formats.
1048
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1051
int my_TIME_to_str(const DRIZZLE_TIME *l_time, char *to)
1053
switch (l_time->time_type) {
1054
case DRIZZLE_TIMESTAMP_DATETIME:
1055
return my_datetime_to_str(l_time, to);
1056
case DRIZZLE_TIMESTAMP_DATE:
1057
return my_date_to_str(l_time, to);
1058
case DRIZZLE_TIMESTAMP_TIME:
1059
return my_time_to_str(l_time, to);
1060
case DRIZZLE_TIMESTAMP_NONE:
1061
case DRIZZLE_TIMESTAMP_ERROR:
1072
Convert datetime value specified as number to broken-down TIME
1073
representation and form value of DATETIME type as side-effect.
1076
number_to_datetime()
1077
nr - datetime value as number
1078
time_res - pointer for structure for broken-down representation
1079
flags - flags to use in validating date, as in str_to_datetime()
1081
1 If value was cut during conversion
1082
2 check_date(date,flags) considers date invalid
1085
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1086
YYYYMMDDHHMMSS to broken-down DRIZZLE_TIME representation. Return value in
1087
YYYYMMDDHHMMSS format as side-effect.
1089
This function also checks if datetime value fits in DATETIME range.
1092
-1 Timestamp with wrong values
1093
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1094
Datetime value in YYYYMMDDHHMMSS format.
1097
int64_t number_to_datetime(int64_t nr, DRIZZLE_TIME *time_res,
1098
uint32_t flags, int *was_cut)
1103
memset(time_res, 0, sizeof(*time_res));
1104
time_res->time_type=DRIZZLE_TIMESTAMP_DATE;
1106
if (nr == 0LL || nr >= 10000101000000LL)
1108
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1113
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1115
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1118
if (nr < (YY_PART_YEAR)*10000L+101L)
1122
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1127
if (nr <= 99991231L)
1132
if (nr < 101000000L)
1135
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1137
if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1139
nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1142
if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1144
if (nr <= 991231235959LL)
1145
nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1148
part1=(long) (nr / 1000000LL);
1149
part2=(long) (nr - (int64_t) part1 * 1000000LL);
1150
time_res->year= (int) (part1/10000L); part1%=10000L;
1151
time_res->month= (int) part1 / 100;
1152
time_res->day= (int) part1 % 100;
1153
time_res->hour= (int) (part2/10000L); part2%=10000L;
1154
time_res->minute=(int) part2 / 100;
1155
time_res->second=(int) part2 % 100;
1157
if (time_res->year <= 9999 && time_res->month <= 12 &&
1158
time_res->day <= 31 && time_res->hour <= 23 &&
1159
time_res->minute <= 59 && time_res->second <= 59 &&
1160
!check_date(time_res, (nr != 0), flags, was_cut))
1163
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1164
if (!nr && (flags & TIME_NO_ZERO_DATE))
1173
/* Convert time value to integer in YYYYMMDDHHMMSS format */
1175
uint64_t TIME_to_uint64_t_datetime(const DRIZZLE_TIME *my_time)
1177
return ((uint64_t) (my_time->year * 10000UL +
1178
my_time->month * 100UL +
1179
my_time->day) * 1000000ULL +
1180
(uint64_t) (my_time->hour * 10000UL +
1181
my_time->minute * 100UL +
1186
/* Convert DRIZZLE_TIME value to integer in YYYYMMDD format */
1188
uint64_t TIME_to_uint64_t_date(const DRIZZLE_TIME *my_time)
1190
return (uint64_t) (my_time->year * 10000UL + my_time->month * 100UL +
1196
Convert DRIZZLE_TIME value to integer in HHMMSS format.
1197
This function doesn't take into account time->day member:
1198
it's assumed that days have been converted to hours already.
1201
uint64_t TIME_to_uint64_t_time(const DRIZZLE_TIME *my_time)
1203
return (uint64_t) (my_time->hour * 10000UL +
1204
my_time->minute * 100UL +
1210
Convert struct DRIZZLE_TIME (date and time split into year/month/day/hour/...
1211
to a number in format YYYYMMDDHHMMSS (DATETIME),
1212
YYYYMMDD (DATE) or HHMMSS (TIME).
1218
The function is used when we need to convert value of time item
1219
to a number if it's used in numeric context, i. e.:
1220
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1224
This function doesn't check that given DRIZZLE_TIME structure members are
1225
in valid range. If they are not, return value won't reflect any
1229
uint64_t TIME_to_uint64_t(const DRIZZLE_TIME *my_time)
1231
switch (my_time->time_type) {
1232
case DRIZZLE_TIMESTAMP_DATETIME:
1233
return TIME_to_uint64_t_datetime(my_time);
1234
case DRIZZLE_TIMESTAMP_DATE:
1235
return TIME_to_uint64_t_date(my_time);
1236
case DRIZZLE_TIMESTAMP_TIME:
1237
return TIME_to_uint64_t_time(my_time);
1238
case DRIZZLE_TIMESTAMP_NONE:
1239
case DRIZZLE_TIMESTAMP_ERROR: