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/mysys_priv.h"
20
#include <mystrings/m_string.h>
21
#include <mystrings/m_ctype.h>
22
#include <drizzled/util/test.h>
29
/* Windows version of localtime_r() is declared in my_ptrhead.h */
31
uint64_t log_10_int[20]=
33
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
34
100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
35
1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
36
1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
37
1000000000000000000ULL, 10000000000000000000ULL
41
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
43
static unsigned char internal_format_positions[]=
44
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
46
static char time_separator=':';
48
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
49
unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
52
Offset of system time zone from UTC in seconds used to speed up
53
work of my_system_gmt_sec() function.
55
static long my_time_zone=0;
58
/* Calc days in one year. works with 0 <= year <= 99 */
60
uint32_t calc_days_in_year(uint32_t year)
62
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
67
@brief Check datetime value for validity according to flags.
69
@param[in] ltime Date to check.
70
@param[in] not_zero_date ltime is not the zero date
71
@param[in] flags flags to check
72
(see str_to_datetime() flags in my_time.h)
73
@param[out] was_cut set to 2 if value was invalid according to flags.
74
(Feb 29 in non-leap etc.) This remains unchanged
75
if value is not invalid.
77
@details Here we assume that year and month is ok!
78
If month is 0 we allow any date. (This only happens if we allow zero
79
date parts in str_to_datetime())
80
Disallow dates with zero year and non-zero month and/or day.
87
bool check_date(const DRIZZLE_TIME *ltime, bool not_zero_date,
88
uint32_t flags, int *was_cut)
92
if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
93
(ltime->month == 0 || ltime->day == 0)) ||
94
(!(flags & TIME_INVALID_DATES) &&
95
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
96
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
103
else if (flags & TIME_NO_ZERO_DATE)
106
We don't set *was_cut here to signal that the problem was a zero date
107
and not an invalid date
116
Convert a timestamp string to a DRIZZLE_TIME value.
121
length Length of string
122
l_time Date is stored here
123
flags Bitmap of following items
124
TIME_FUZZY_DATE Set if we should allow partial dates
125
TIME_DATETIME_ONLY Set if we only allow full datetimes.
126
TIME_NO_ZERO_IN_DATE Don't allow partial dates
127
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
128
TIME_INVALID_DATES Allow 2000-02-31
130
1 If value was cut during conversion
131
2 check_date(date,flags) considers date invalid
134
At least the following formats are recogniced (based on number of digits)
135
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
136
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
137
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
138
Also dates where all parts are zero are allowed
140
The second part may have an optional .###### fraction part.
143
This function should work with a format position vector as long as the
144
following things holds:
145
- All date are kept together and all time parts are kept together
146
- Date and time parts must be separated by blank
147
- Second fractions must come after second part and be separated
148
by a '.'. (The second fractions are optional)
149
- AM/PM must come after second fractions (or after seconds if no fractions)
150
- Year must always been specified.
151
- If time is before date, then we will use datetime format only if
152
the argument consist of two parts, separated by space.
153
Otherwise we will assume the argument is a date.
154
- The hour part must be specified in hour-minute-second order.
157
DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
158
[DD [HH:[MM:[SS]]]].fraction.
159
l_time is not changed.
160
DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
161
DRIZZLE_TIMESTAMP_DATETIME Full timestamp
162
DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
163
All elements in l_time is set to 0
166
#define MAX_DATE_PARTS 8
168
enum enum_drizzle_timestamp_type
169
str_to_datetime(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
170
uint32_t flags, int *was_cut)
172
uint32_t field_length, year_length=4, digits, i, number_of_fields;
173
uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
174
uint32_t add_hours= 0, start_loop;
175
uint32_t not_zero_date, allow_space;
176
bool is_internal_format;
177
const char *pos, *last_field_pos=NULL;
178
const char *end=str+length;
179
const unsigned char *format_position;
180
bool found_delimitier= 0, found_space= 0;
181
uint32_t frac_pos, frac_len;
185
/* Skip space at start */
186
for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
188
if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
191
return(DRIZZLE_TIMESTAMP_NONE);
194
is_internal_format= 0;
195
/* This has to be changed if want to activate different timestamp formats */
196
format_position= internal_format_positions;
199
Calculate number of digits in first part.
200
If length= 8 or >= 14 then year is of format YYYY.
201
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
204
pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
208
digits= (uint32_t) (pos-str);
209
start_loop= 0; /* Start of scan loop */
210
date_len[format_position[0]]= 0; /* Length of year field */
211
if (pos == end || *pos == '.')
213
/* Found date in internal format (only numbers like YYYYMMDD) */
214
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
215
field_length= year_length;
216
is_internal_format= 1;
217
format_position= internal_format_positions;
221
if (format_position[0] >= 3) /* If year is after HHMMDD */
224
If year is not in first part then we have to determinate if we got
225
a date field or a datetime field.
226
We do this by checking if there is two numbers separated by
229
while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
231
while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
235
if (flags & TIME_DATETIME_ONLY)
238
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
240
/* Date field. Set hour, minutes and seconds to 0 */
241
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
242
start_loop= 5; /* Start with first date part */
246
field_length= format_position[0] == 0 ? 4 : 2;
250
Only allow space in the first "part" of the datetime field and:
251
- after days, part seconds
252
- before and after AM/PM (handled by code later)
254
2003-03-03 20:00:20 AM
255
20:00:20.000000 AM 03-03-2000
257
i= max((uint32_t) format_position[0], (uint32_t) format_position[1]);
258
set_if_bigger(i, (uint32_t) format_position[2]);
259
allow_space= ((1 << i) | (1 << format_position[6]));
260
allow_space&= (1 | 2 | 4 | 8);
264
i < MAX_DATE_PARTS-1 && str != end &&
265
my_isdigit(&my_charset_utf8_general_ci,*str);
268
const char *start= str;
269
uint32_t tmp_value= (uint32_t) (unsigned char) (*str++ - '0');
270
while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
271
(!is_internal_format || --field_length))
273
tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
276
date_len[i]= (uint32_t) (str - start);
277
if (tmp_value > 999999) /* Impossible date part */
280
return(DRIZZLE_TIMESTAMP_NONE);
283
not_zero_date|= tmp_value;
285
/* Length of next field */
286
field_length= format_position[i+1] == 0 ? 4 : 2;
288
if ((last_field_pos= str) == end)
290
i++; /* Register last found part */
293
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
294
if (i == format_position[2] && *str == 'T')
296
str++; /* ISO8601: CCYYMMDDThhmmss */
299
if (i == format_position[5]) /* Seconds */
301
if (*str == '.') /* Followed by part seconds */
304
field_length= 6; /* 6 digits */
309
(my_ispunct(&my_charset_utf8_general_ci,*str) ||
310
my_isspace(&my_charset_utf8_general_ci,*str)))
312
if (my_isspace(&my_charset_utf8_general_ci,*str))
314
if (!(allow_space & (1 << i)))
317
return(DRIZZLE_TIMESTAMP_NONE);
322
found_delimitier= 1; /* Should be a 'normal' date */
324
/* Check if next position is AM/PM */
325
if (i == format_position[6]) /* Seconds, time for AM/PM */
327
i++; /* Skip AM/PM part */
328
if (format_position[7] != 255) /* If using AM/PM */
330
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
332
if (str[0] == 'p' || str[0] == 'P')
334
else if (str[0] != 'a' || str[0] != 'A')
335
continue; /* Not AM/PM */
336
str+= 2; /* Skip AM/PM */
337
/* Skip space after AM/PM */
338
while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
345
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
348
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
353
number_of_fields= i - start_loop;
354
while (i < MAX_DATE_PARTS)
360
if (!is_internal_format)
362
year_length= date_len[(uint32_t) format_position[0]];
363
if (!year_length) /* Year must be specified */
366
return(DRIZZLE_TIMESTAMP_NONE);
369
l_time->year= date[(uint32_t) format_position[0]];
370
l_time->month= date[(uint32_t) format_position[1]];
371
l_time->day= date[(uint32_t) format_position[2]];
372
l_time->hour= date[(uint32_t) format_position[3]];
373
l_time->minute= date[(uint32_t) format_position[4]];
374
l_time->second= date[(uint32_t) format_position[5]];
376
frac_pos= (uint32_t) format_position[6];
377
frac_len= date_len[frac_pos];
379
date[frac_pos]*= (uint32_t) log_10_int[6 - frac_len];
380
l_time->second_part= date[frac_pos];
382
if (format_position[7] != (unsigned char) 255)
384
if (l_time->hour > 12)
389
l_time->hour= l_time->hour%12 + add_hours;
394
l_time->year= date[0];
395
l_time->month= date[1];
396
l_time->day= date[2];
397
l_time->hour= date[3];
398
l_time->minute= date[4];
399
l_time->second= date[5];
401
date[6]*= (uint32_t) log_10_int[6 - date_len[6]];
402
l_time->second_part=date[6];
406
if (year_length == 2 && not_zero_date)
407
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
409
if (number_of_fields < 3 ||
410
l_time->year > 9999 || l_time->month > 12 ||
411
l_time->day > 31 || l_time->hour > 23 ||
412
l_time->minute > 59 || l_time->second > 59)
414
/* Only give warning for a zero date if there is some garbage after */
415
if (!not_zero_date) /* If zero date */
417
for (; str != end ; str++)
419
if (!my_isspace(&my_charset_utf8_general_ci, *str))
421
not_zero_date= 1; /* Give warning */
426
*was_cut= test(not_zero_date);
430
if (check_date(l_time, not_zero_date != 0, flags, was_cut))
433
l_time->time_type= (number_of_fields <= 3 ?
434
DRIZZLE_TIMESTAMP_DATE : DRIZZLE_TIMESTAMP_DATETIME);
436
for (; str != end ; str++)
438
if (!my_isspace(&my_charset_utf8_general_ci,*str))
445
return(l_time->time_type=
446
(number_of_fields <= 3 ? DRIZZLE_TIMESTAMP_DATE :
447
DRIZZLE_TIMESTAMP_DATETIME));
450
memset(l_time, 0, sizeof(*l_time));
451
return(DRIZZLE_TIMESTAMP_ERROR);
456
Convert a time string to a DRIZZLE_TIME struct.
460
str A string in full TIMESTAMP format or
461
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
463
There may be an optional [.second_part] after seconds
465
l_time Store result here
466
warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
467
was cut during conversion, and/or
468
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
472
Because of the extra days argument, this function can only
473
work with times where the time arguments are in the above order.
480
bool str_to_time(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
485
const char *end=str+length, *end_of_days;
486
bool found_days,found_hours;
491
for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
493
if (str != end && *str == '-')
502
/* Check first if this is a full TIMESTAMP */
504
{ /* Probably full timestamp */
506
enum enum_drizzle_timestamp_type
507
res= str_to_datetime(str, length, l_time,
508
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
509
if ((int) res >= (int) DRIZZLE_TIMESTAMP_ERROR)
512
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
513
return res == DRIZZLE_TIMESTAMP_ERROR;
517
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
518
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
519
value=value*10L + (long) (*str - '0');
521
/* Skip all space after 'days' */
523
for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
526
found_days=found_hours=0;
527
if ((uint32_t) (end-str) > 1 && str != end_of_days &&
528
my_isdigit(&my_charset_utf8_general_ci, *str))
529
{ /* Found days part */
530
date[0]= (uint32_t) value;
531
state= 1; /* Assume next is hours */
534
else if ((end-str) > 1 && *str == time_separator &&
535
my_isdigit(&my_charset_utf8_general_ci, str[1]))
537
date[0]= 0; /* Assume we found hours */
538
date[1]= (uint32_t) value;
541
str++; /* skip ':' */
545
/* String given as one number; assume HHMMSS format */
547
date[1]= (uint32_t) (value/10000);
548
date[2]= (uint32_t) (value/100 % 100);
549
date[3]= (uint32_t) (value % 100);
554
/* Read hours, minutes and seconds */
557
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
558
value=value*10L + (long) (*str - '0');
559
date[state++]= (uint32_t) value;
560
if (state == 4 || (end-str) < 2 || *str != time_separator ||
561
!my_isdigit(&my_charset_utf8_general_ci,str[1]))
563
str++; /* Skip time_separator (':') */
568
/* Fix the date to assume that seconds was given */
569
if (!found_hours && !found_days)
571
bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
572
sizeof(long)*(state-1));
573
memset(date, 0, sizeof(long)*(4-state));
576
memset(date+state, 0, sizeof(long)*(4-state));
580
/* Get fractional second part */
581
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
584
str++; value=(uint32_t) (unsigned char) (*str - '0');
585
while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
587
if (field_length-- > 0)
588
value= value*10 + (uint32_t) (unsigned char) (*str - '0');
590
if (field_length > 0)
591
value*= (long) log_10_int[field_length];
592
else if (field_length < 0)
593
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
594
date[4]= (uint32_t) value;
599
/* Check for exponent part: E<gigit> | E<sign><digit> */
600
/* (may occur as result of %g formatting of time value) */
601
if ((end - str) > 1 &&
602
(*str == 'e' || *str == 'E') &&
603
(my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
604
((str[1] == '-' || str[1] == '+') &&
606
my_isdigit(&my_charset_utf8_general_ci, str[2]))))
609
if (internal_format_positions[7] != 255)
611
/* Read a possible AM/PM */
612
while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
614
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
616
if (str[0] == 'p' || str[0] == 'P')
619
date[1]= date[1]%12 + 12;
621
else if (str[0] == 'a' || str[0] == 'A')
626
/* Integer overflow checks */
627
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
628
date[2] > UINT_MAX || date[3] > UINT_MAX ||
632
l_time->year= 0; /* For protocol::store_time */
634
l_time->day= date[0];
635
l_time->hour= date[1];
636
l_time->minute= date[2];
637
l_time->second= date[3];
638
l_time->second_part= date[4];
639
l_time->time_type= DRIZZLE_TIMESTAMP_TIME;
641
/* Check if the value is valid and fits into DRIZZLE_TIME range */
642
if (check_time_range(l_time, warning))
645
/* Check if there is garbage at end of the DRIZZLE_TIME specification */
650
if (!my_isspace(&my_charset_utf8_general_ci,*str))
652
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
655
} while (++str != end);
662
Check 'time' value to lie in the DRIZZLE_TIME range
666
time pointer to DRIZZLE_TIME value
667
warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
670
If the time value lies outside of the range [-838:59:59, 838:59:59],
671
set it to the closest endpoint of the range and set
672
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
675
0 time value is valid, but was possibly truncated
676
1 time value is invalid
679
int check_time_range(DRIZZLE_TIME *my_time, int *warning)
683
if (my_time->minute >= 60 || my_time->second >= 60)
686
hour= my_time->hour + (24*my_time->day);
687
if (hour <= TIME_MAX_HOUR &&
688
(hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
689
my_time->second != TIME_MAX_SECOND || !my_time->second_part))
693
my_time->hour= TIME_MAX_HOUR;
694
my_time->minute= TIME_MAX_MINUTE;
695
my_time->second= TIME_MAX_SECOND;
696
my_time->second_part= 0;
697
*warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
703
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
711
struct tm *l_time,tm_tmp;
712
DRIZZLE_TIME my_time;
715
seconds= (time_t) time((time_t*) 0);
716
localtime_r(&seconds,&tm_tmp);
718
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
719
my_time.year= (uint32_t) l_time->tm_year+1900;
720
my_time.month= (uint32_t) l_time->tm_mon+1;
721
my_time.day= (uint32_t) l_time->tm_mday;
722
my_time.hour= (uint32_t) l_time->tm_hour;
723
my_time.minute= (uint32_t) l_time->tm_min;
724
my_time.second= (uint32_t) l_time->tm_sec;
725
my_time.time_type= DRIZZLE_TIMESTAMP_NONE;
726
my_time.second_part= 0;
728
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
733
Handle 2 digit year conversions
740
Year between 1970-2069
743
uint32_t year_2000_handling(uint32_t year)
745
if ((year=year+1900) < 1900+YY_PART_YEAR)
752
Calculate nr of day since year 0 in new date-system (from 1615)
756
year Year (exact 4 digit year, no year conversions)
760
NOTES: 0000-00-00 is a valid date, and will return 0
763
Days since 0000-00-00
766
long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
771
if (year == 0 && month == 0 && day == 0)
772
return(0); /* Skip errors */
773
delsum= (long) (365L * year+ 31*(month-1) +day);
777
delsum-= (long) (month*4+23)/10;
778
temp=(int) ((year/100+1)*3)/4;
779
return(delsum+(int) year/4-temp);
784
Convert time in DRIZZLE_TIME representation in system time zone to its
785
time_t form (number of seconds in UTC since begginning of Unix Epoch).
789
t - time value to be converted
790
my_timezone - pointer to long where offset of system time zone
791
from UTC will be stored for caching
792
in_dst_time_gap - set to true if time falls into spring time-gap
795
The idea is to cache the time zone offset from UTC (including daylight
796
saving time) for the next call to make things faster. But currently we
797
just calculate this offset during startup (by calling init_time()
798
function) and use it all the time.
799
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
803
Time in UTC seconds since Unix Epoch representation.
806
my_system_gmt_sec(const DRIZZLE_TIME *t_src, long *my_timezone,
807
bool *in_dst_time_gap)
812
DRIZZLE_TIME tmp_time;
813
DRIZZLE_TIME *t= &tmp_time;
814
struct tm *l_time,tm_tmp;
815
long diff, current_timezone;
818
Use temp variable to avoid trashing input data, which could happen in
819
case of shift required for boundary dates processing.
821
memcpy(&tmp_time, t_src, sizeof(DRIZZLE_TIME));
823
if (!validate_timestamp_range(t))
827
Calculate the gmt time based on current time and timezone
828
The -1 on the end is to ensure that if have a date that exists twice
829
(like 2002-10-27 02:00:0 MET), we will find the initial date.
831
By doing -3600 we will have to call localtime_r() several times, but
832
I couldn't come up with a better way to get a repeatable result :(
834
We can't use mktime() as it's buggy on many platforms and not thread safe.
836
Note: this code assumes that our time_t estimation is not too far away
837
from real value (we assume that localtime_r(tmp) will return something
838
within 24 hrs from t) which is probably true for all current time zones.
840
Note2: For the dates, which have time_t representation close to
841
MAX_INT32 (efficient time_t limit for supported platforms), we should
842
do a small trick to avoid overflow. That is, convert the date, which is
843
two days earlier, and then add these days to the final value.
845
The same trick is done for the values close to 0 in time_t
846
representation for platfroms with unsigned time_t (QNX).
848
To be more verbose, here is a sample (extracted from the code below):
849
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
850
would return -2147480896 because of the long type overflow. In result
851
we would get 1901 year in localtime_r(), which is an obvious error.
853
Alike problem raises with the dates close to Epoch. E.g.
854
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
857
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
858
wil give us a date around 2106 year. Which is no good.
860
Theoreticaly, there could be problems with the latter conversion:
861
there are at least two timezones, which had time switches near 1 Jan
862
of 1970 (because of political reasons). These are America/Hermosillo and
863
America/Mazatlan time zones. They changed their offset on
864
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
865
the code below will give incorrect results for dates close to
866
1970-01-01, in the case OS takes into account these historical switches.
867
Luckily, it seems that we support only one platform with unsigned
868
time_t. It's QNX. And QNX does not support historical timezone data at all.
869
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
870
historical information for localtime_r() etc. That is, the problem is not
873
We are safe with shifts close to MAX_INT32, as there are no known
874
time switches on Jan 2038 yet :)
876
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
879
Below we will pass (uint32_t) (t->day - shift) to calc_daynr.
880
As we don't want to get an overflow here, we will shift
881
only safe dates. That's why we have (t->day > 4) above.
886
#ifdef TIME_T_UNSIGNED
890
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
891
1970, 1 of Jan. For both dates we use shift, which is added
892
to t->day in order to step out a bit from the border.
893
This is required for platforms, where time_t is unsigned.
894
As far as I know, among the platforms we support it's only QNX.
895
Note: the order of below if-statements is significant.
898
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
905
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
916
tmp= (time_t) (((calc_daynr((uint32_t) t->year, (uint32_t) t->month, (uint32_t) t->day) -
917
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
918
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
921
current_timezone= my_time_zone;
922
localtime_r(&tmp,&tm_tmp);
926
(t->hour != (uint32_t) l_time->tm_hour ||
927
t->minute != (uint32_t) l_time->tm_min ||
928
t->second != (uint32_t) l_time->tm_sec);
930
{ /* One check should be enough ? */
931
/* Get difference in days */
932
int days= t->day - l_time->tm_mday;
934
days= 1; /* Month has wrapped */
937
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
938
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
939
(long) ((int) t->second - (int) l_time->tm_sec));
940
current_timezone+= diff+3600; /* Compensate for -3600 above */
942
localtime_r(&tmp,&tm_tmp);
946
Fix that if we are in the non existing daylight saving time hour
947
we move the start of the next real hour.
949
This code doesn't handle such exotical thing as time-gaps whose length
950
is more than one hour or non-integer (latter can theoretically happen
951
if one of seconds will be removed due leap correction, or because of
952
general time correction like it happened for Africa/Monrovia time zone
955
if (loop == 2 && t->hour != (uint32_t) l_time->tm_hour)
957
int days= t->day - l_time->tm_mday;
959
days=1; /* Month has wrapped */
962
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
963
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
964
(long) ((int) t->second - (int) l_time->tm_sec));
966
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
967
else if (diff == -3600)
968
tmp-=t->minute*60 + t->second; /* Move to previous hour */
970
*in_dst_time_gap= true;
972
*my_timezone= current_timezone;
975
/* shift back, if we were dealing with boundary dates */
979
This is possible for dates, which slightly exceed boundaries.
980
Conversion will pass ok for them, but we don't allow them.
981
First check will pass for platforms with signed time_t.
982
instruction above (tmp+= shift*86400L) could exceed
983
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
984
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
985
with unsigned time_t tmp+= shift*86400L might result in a number,
986
larger then TIMESTAMP_MAX_VALUE, so another check will work.
988
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
992
} /* my_system_gmt_sec */
995
/* Set DRIZZLE_TIME structure to 0000-00-00 00:00:00.000000 */
997
void set_zero_time(DRIZZLE_TIME *tm, enum enum_drizzle_timestamp_type time_type)
999
memset(tm, 0, sizeof(*tm));
1000
tm->time_type= time_type;
1005
Functions to convert time/date/datetime value to a string,
1006
using default format.
1007
This functions don't check that given DRIZZLE_TIME structure members are
1008
in valid range. If they are not, return value won't reflect any
1009
valid date either. Additionally, make_time doesn't take into
1010
account time->day member: it's assumed that days have been converted
1014
number of characters written to 'to'
1017
int my_time_to_str(const DRIZZLE_TIME *l_time, char *to)
1019
uint32_t extra_hours= 0;
1020
return sprintf(to, "%s%02u:%02u:%02u",
1021
(l_time->neg ? "-" : ""),
1022
extra_hours+ l_time->hour,
1027
int my_date_to_str(const DRIZZLE_TIME *l_time, char *to)
1029
return sprintf(to, "%04u-%02u-%02u",
1035
int my_datetime_to_str(const DRIZZLE_TIME *l_time, char *to)
1037
return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u",
1048
Convert struct DATE/TIME/DATETIME value to string using built-in
1049
MySQL time conversion formats.
1055
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1058
int my_TIME_to_str(const DRIZZLE_TIME *l_time, char *to)
1060
switch (l_time->time_type) {
1061
case DRIZZLE_TIMESTAMP_DATETIME:
1062
return my_datetime_to_str(l_time, to);
1063
case DRIZZLE_TIMESTAMP_DATE:
1064
return my_date_to_str(l_time, to);
1065
case DRIZZLE_TIMESTAMP_TIME:
1066
return my_time_to_str(l_time, to);
1067
case DRIZZLE_TIMESTAMP_NONE:
1068
case DRIZZLE_TIMESTAMP_ERROR:
1079
Convert datetime value specified as number to broken-down TIME
1080
representation and form value of DATETIME type as side-effect.
1083
number_to_datetime()
1084
nr - datetime value as number
1085
time_res - pointer for structure for broken-down representation
1086
flags - flags to use in validating date, as in str_to_datetime()
1088
1 If value was cut during conversion
1089
2 check_date(date,flags) considers date invalid
1092
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1093
YYYYMMDDHHMMSS to broken-down DRIZZLE_TIME representation. Return value in
1094
YYYYMMDDHHMMSS format as side-effect.
1096
This function also checks if datetime value fits in DATETIME range.
1099
-1 Timestamp with wrong values
1100
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1101
Datetime value in YYYYMMDDHHMMSS format.
1104
int64_t number_to_datetime(int64_t nr, DRIZZLE_TIME *time_res,
1105
uint32_t flags, int *was_cut)
1110
memset(time_res, 0, sizeof(*time_res));
1111
time_res->time_type=DRIZZLE_TIMESTAMP_DATE;
1113
if (nr == 0LL || nr >= 10000101000000LL)
1115
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1120
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1122
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1125
if (nr < (YY_PART_YEAR)*10000L+101L)
1129
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1134
if (nr <= 99991231L)
1139
if (nr < 101000000L)
1142
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1144
if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1146
nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1149
if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1151
if (nr <= 991231235959LL)
1152
nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1155
part1=(long) (nr / 1000000LL);
1156
part2=(long) (nr - (int64_t) part1 * 1000000LL);
1157
time_res->year= (int) (part1/10000L); part1%=10000L;
1158
time_res->month= (int) part1 / 100;
1159
time_res->day= (int) part1 % 100;
1160
time_res->hour= (int) (part2/10000L); part2%=10000L;
1161
time_res->minute=(int) part2 / 100;
1162
time_res->second=(int) part2 % 100;
1164
if (time_res->year <= 9999 && time_res->month <= 12 &&
1165
time_res->day <= 31 && time_res->hour <= 23 &&
1166
time_res->minute <= 59 && time_res->second <= 59 &&
1167
!check_date(time_res, (nr != 0), flags, was_cut))
1170
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1171
if (!nr && (flags & TIME_NO_ZERO_DATE))
1180
/* Convert time value to integer in YYYYMMDDHHMMSS format */
1182
uint64_t TIME_to_uint64_t_datetime(const DRIZZLE_TIME *my_time)
1184
return ((uint64_t) (my_time->year * 10000UL +
1185
my_time->month * 100UL +
1186
my_time->day) * 1000000ULL +
1187
(uint64_t) (my_time->hour * 10000UL +
1188
my_time->minute * 100UL +
1193
/* Convert DRIZZLE_TIME value to integer in YYYYMMDD format */
1195
uint64_t TIME_to_uint64_t_date(const DRIZZLE_TIME *my_time)
1197
return (uint64_t) (my_time->year * 10000UL + my_time->month * 100UL +
1203
Convert DRIZZLE_TIME value to integer in HHMMSS format.
1204
This function doesn't take into account time->day member:
1205
it's assumed that days have been converted to hours already.
1208
uint64_t TIME_to_uint64_t_time(const DRIZZLE_TIME *my_time)
1210
return (uint64_t) (my_time->hour * 10000UL +
1211
my_time->minute * 100UL +
1217
Convert struct DRIZZLE_TIME (date and time split into year/month/day/hour/...
1218
to a number in format YYYYMMDDHHMMSS (DATETIME),
1219
YYYYMMDD (DATE) or HHMMSS (TIME).
1225
The function is used when we need to convert value of time item
1226
to a number if it's used in numeric context, i. e.:
1227
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1231
This function doesn't check that given DRIZZLE_TIME structure members are
1232
in valid range. If they are not, return value won't reflect any
1236
uint64_t TIME_to_uint64_t(const DRIZZLE_TIME *my_time)
1238
switch (my_time->time_type) {
1239
case DRIZZLE_TIMESTAMP_DATETIME:
1240
return TIME_to_uint64_t_datetime(my_time);
1241
case DRIZZLE_TIMESTAMP_DATE:
1242
return TIME_to_uint64_t_date(my_time);
1243
case DRIZZLE_TIMESTAMP_TIME:
1244
return TIME_to_uint64_t_time(my_time);
1245
case DRIZZLE_TIMESTAMP_NONE:
1246
case DRIZZLE_TIMESTAMP_ERROR: