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
static int check_time_range(DRIZZLE_TIME *my_time, int *warning);
31
/* Windows version of localtime_r() is declared in my_ptrhead.h */
33
uint64_t log_10_int[20]=
35
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
36
100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
37
1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
38
1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
39
1000000000000000000ULL, 10000000000000000000ULL
43
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
45
static unsigned char internal_format_positions[]=
46
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
48
static char time_separator=':';
50
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
51
unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
54
Offset of system time zone from UTC in seconds used to speed up
55
work of my_system_gmt_sec() function.
57
static long my_time_zone=0;
60
/* Calc days in one year. works with 0 <= year <= 99 */
62
uint32_t calc_days_in_year(uint32_t year)
64
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
69
@brief Check datetime value for validity according to flags.
71
@param[in] ltime Date to check.
72
@param[in] not_zero_date ltime is not the zero date
73
@param[in] flags flags to check
74
(see str_to_datetime() flags in my_time.h)
75
@param[out] was_cut set to 2 if value was invalid according to flags.
76
(Feb 29 in non-leap etc.) This remains unchanged
77
if value is not invalid.
79
@details Here we assume that year and month is ok!
80
If month is 0 we allow any date. (This only happens if we allow zero
81
date parts in str_to_datetime())
82
Disallow dates with zero year and non-zero month and/or day.
89
bool check_date(const DRIZZLE_TIME *ltime, bool not_zero_date,
90
uint32_t flags, int *was_cut)
94
if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
95
(ltime->month == 0 || ltime->day == 0)) ||
96
(!(flags & TIME_INVALID_DATES) &&
97
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
98
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
105
else if (flags & TIME_NO_ZERO_DATE)
108
We don't set *was_cut here to signal that the problem was a zero date
109
and not an invalid date
118
Convert a timestamp string to a DRIZZLE_TIME value.
123
length Length of string
124
l_time Date is stored here
125
flags Bitmap of following items
126
TIME_FUZZY_DATE Set if we should allow partial dates
127
TIME_DATETIME_ONLY Set if we only allow full datetimes.
128
TIME_NO_ZERO_IN_DATE Don't allow partial dates
129
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
130
TIME_INVALID_DATES Allow 2000-02-31
132
1 If value was cut during conversion
133
2 check_date(date,flags) considers date invalid
136
At least the following formats are recogniced (based on number of digits)
137
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
138
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
139
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
140
Also dates where all parts are zero are allowed
142
The second part may have an optional .###### fraction part.
145
This function should work with a format position vector as long as the
146
following things holds:
147
- All date are kept together and all time parts are kept together
148
- Date and time parts must be separated by blank
149
- Second fractions must come after second part and be separated
150
by a '.'. (The second fractions are optional)
151
- AM/PM must come after second fractions (or after seconds if no fractions)
152
- Year must always been specified.
153
- If time is before date, then we will use datetime format only if
154
the argument consist of two parts, separated by space.
155
Otherwise we will assume the argument is a date.
156
- The hour part must be specified in hour-minute-second order.
159
DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
160
[DD [HH:[MM:[SS]]]].fraction.
161
l_time is not changed.
162
DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
163
DRIZZLE_TIMESTAMP_DATETIME Full timestamp
164
DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
165
All elements in l_time is set to 0
168
#define MAX_DATE_PARTS 8
170
enum enum_drizzle_timestamp_type
171
str_to_datetime(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
172
uint32_t flags, int *was_cut)
174
uint32_t field_length, year_length=4, digits, i, number_of_fields;
175
uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
176
uint32_t add_hours= 0, start_loop;
177
uint32_t not_zero_date, allow_space;
178
bool is_internal_format;
179
const char *pos, *last_field_pos=NULL;
180
const char *end=str+length;
181
const unsigned char *format_position;
182
bool found_delimitier= 0, found_space= 0;
183
uint32_t frac_pos, frac_len;
187
/* Skip space at start */
188
for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
190
if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
193
return(DRIZZLE_TIMESTAMP_NONE);
196
is_internal_format= 0;
197
/* This has to be changed if want to activate different timestamp formats */
198
format_position= internal_format_positions;
201
Calculate number of digits in first part.
202
If length= 8 or >= 14 then year is of format YYYY.
203
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
206
pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
210
digits= (uint32_t) (pos-str);
211
start_loop= 0; /* Start of scan loop */
212
date_len[format_position[0]]= 0; /* Length of year field */
213
if (pos == end || *pos == '.')
215
/* Found date in internal format (only numbers like YYYYMMDD) */
216
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
217
field_length= year_length;
218
is_internal_format= 1;
219
format_position= internal_format_positions;
223
if (format_position[0] >= 3) /* If year is after HHMMDD */
226
If year is not in first part then we have to determinate if we got
227
a date field or a datetime field.
228
We do this by checking if there is two numbers separated by
231
while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
233
while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
237
if (flags & TIME_DATETIME_ONLY)
240
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
242
/* Date field. Set hour, minutes and seconds to 0 */
243
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
244
start_loop= 5; /* Start with first date part */
248
field_length= format_position[0] == 0 ? 4 : 2;
252
Only allow space in the first "part" of the datetime field and:
253
- after days, part seconds
254
- before and after AM/PM (handled by code later)
256
2003-03-03 20:00:20 AM
257
20:00:20.000000 AM 03-03-2000
259
i= max((uint32_t) format_position[0], (uint32_t) format_position[1]);
260
set_if_bigger(i, (uint32_t) format_position[2]);
261
allow_space= ((1 << i) | (1 << format_position[6]));
262
allow_space&= (1 | 2 | 4 | 8);
266
i < MAX_DATE_PARTS-1 && str != end &&
267
my_isdigit(&my_charset_utf8_general_ci,*str);
270
const char *start= str;
271
uint32_t tmp_value= (uint32_t) (unsigned char) (*str++ - '0');
272
while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
273
(!is_internal_format || --field_length))
275
tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
278
date_len[i]= (uint32_t) (str - start);
279
if (tmp_value > 999999) /* Impossible date part */
282
return(DRIZZLE_TIMESTAMP_NONE);
285
not_zero_date|= tmp_value;
287
/* Length of next field */
288
field_length= format_position[i+1] == 0 ? 4 : 2;
290
if ((last_field_pos= str) == end)
292
i++; /* Register last found part */
295
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
296
if (i == format_position[2] && *str == 'T')
298
str++; /* ISO8601: CCYYMMDDThhmmss */
301
if (i == format_position[5]) /* Seconds */
303
if (*str == '.') /* Followed by part seconds */
306
field_length= 6; /* 6 digits */
311
(my_ispunct(&my_charset_utf8_general_ci,*str) ||
312
my_isspace(&my_charset_utf8_general_ci,*str)))
314
if (my_isspace(&my_charset_utf8_general_ci,*str))
316
if (!(allow_space & (1 << i)))
319
return(DRIZZLE_TIMESTAMP_NONE);
324
found_delimitier= 1; /* Should be a 'normal' date */
326
/* Check if next position is AM/PM */
327
if (i == format_position[6]) /* Seconds, time for AM/PM */
329
i++; /* Skip AM/PM part */
330
if (format_position[7] != 255) /* If using AM/PM */
332
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
334
if (str[0] == 'p' || str[0] == 'P')
336
else if (str[0] != 'a' || str[0] != 'A')
337
continue; /* Not AM/PM */
338
str+= 2; /* Skip AM/PM */
339
/* Skip space after AM/PM */
340
while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
347
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
350
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
355
number_of_fields= i - start_loop;
356
while (i < MAX_DATE_PARTS)
362
if (!is_internal_format)
364
year_length= date_len[(uint32_t) format_position[0]];
365
if (!year_length) /* Year must be specified */
368
return(DRIZZLE_TIMESTAMP_NONE);
371
l_time->year= date[(uint32_t) format_position[0]];
372
l_time->month= date[(uint32_t) format_position[1]];
373
l_time->day= date[(uint32_t) format_position[2]];
374
l_time->hour= date[(uint32_t) format_position[3]];
375
l_time->minute= date[(uint32_t) format_position[4]];
376
l_time->second= date[(uint32_t) format_position[5]];
378
frac_pos= (uint32_t) format_position[6];
379
frac_len= date_len[frac_pos];
381
date[frac_pos]*= (uint32_t) log_10_int[6 - frac_len];
382
l_time->second_part= date[frac_pos];
384
if (format_position[7] != (unsigned char) 255)
386
if (l_time->hour > 12)
391
l_time->hour= l_time->hour%12 + add_hours;
396
l_time->year= date[0];
397
l_time->month= date[1];
398
l_time->day= date[2];
399
l_time->hour= date[3];
400
l_time->minute= date[4];
401
l_time->second= date[5];
403
date[6]*= (uint32_t) log_10_int[6 - date_len[6]];
404
l_time->second_part=date[6];
408
if (year_length == 2 && not_zero_date)
409
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
411
if (number_of_fields < 3 ||
412
l_time->year > 9999 || l_time->month > 12 ||
413
l_time->day > 31 || l_time->hour > 23 ||
414
l_time->minute > 59 || l_time->second > 59)
416
/* Only give warning for a zero date if there is some garbage after */
417
if (!not_zero_date) /* If zero date */
419
for (; str != end ; str++)
421
if (!my_isspace(&my_charset_utf8_general_ci, *str))
423
not_zero_date= 1; /* Give warning */
428
*was_cut= test(not_zero_date);
432
if (check_date(l_time, not_zero_date != 0, flags, was_cut))
435
l_time->time_type= (number_of_fields <= 3 ?
436
DRIZZLE_TIMESTAMP_DATE : DRIZZLE_TIMESTAMP_DATETIME);
438
for (; str != end ; str++)
440
if (!my_isspace(&my_charset_utf8_general_ci,*str))
447
return(l_time->time_type=
448
(number_of_fields <= 3 ? DRIZZLE_TIMESTAMP_DATE :
449
DRIZZLE_TIMESTAMP_DATETIME));
452
memset(l_time, 0, sizeof(*l_time));
453
return(DRIZZLE_TIMESTAMP_ERROR);
458
Convert a time string to a DRIZZLE_TIME struct.
462
str A string in full TIMESTAMP format or
463
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
465
There may be an optional [.second_part] after seconds
467
l_time Store result here
468
warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
469
was cut during conversion, and/or
470
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
474
Because of the extra days argument, this function can only
475
work with times where the time arguments are in the above order.
482
bool str_to_time(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
487
const char *end=str+length, *end_of_days;
488
bool found_days,found_hours;
493
for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
495
if (str != end && *str == '-')
504
/* Check first if this is a full TIMESTAMP */
506
{ /* Probably full timestamp */
508
enum enum_drizzle_timestamp_type
509
res= str_to_datetime(str, length, l_time,
510
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
511
if ((int) res >= (int) DRIZZLE_TIMESTAMP_ERROR)
514
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
515
return res == DRIZZLE_TIMESTAMP_ERROR;
519
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
520
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
521
value=value*10L + (long) (*str - '0');
523
/* Skip all space after 'days' */
525
for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
528
found_days=found_hours=0;
529
if ((uint32_t) (end-str) > 1 && str != end_of_days &&
530
my_isdigit(&my_charset_utf8_general_ci, *str))
531
{ /* Found days part */
532
date[0]= (uint32_t) value;
533
state= 1; /* Assume next is hours */
536
else if ((end-str) > 1 && *str == time_separator &&
537
my_isdigit(&my_charset_utf8_general_ci, str[1]))
539
date[0]= 0; /* Assume we found hours */
540
date[1]= (uint32_t) value;
543
str++; /* skip ':' */
547
/* String given as one number; assume HHMMSS format */
549
date[1]= (uint32_t) (value/10000);
550
date[2]= (uint32_t) (value/100 % 100);
551
date[3]= (uint32_t) (value % 100);
556
/* Read hours, minutes and seconds */
559
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
560
value=value*10L + (long) (*str - '0');
561
date[state++]= (uint32_t) value;
562
if (state == 4 || (end-str) < 2 || *str != time_separator ||
563
!my_isdigit(&my_charset_utf8_general_ci,str[1]))
565
str++; /* Skip time_separator (':') */
570
/* Fix the date to assume that seconds was given */
571
if (!found_hours && !found_days)
573
bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
574
sizeof(long)*(state-1));
575
memset(date, 0, sizeof(long)*(4-state));
578
memset(date+state, 0, sizeof(long)*(4-state));
582
/* Get fractional second part */
583
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
586
str++; value=(uint32_t) (unsigned char) (*str - '0');
587
while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
589
if (field_length-- > 0)
590
value= value*10 + (uint32_t) (unsigned char) (*str - '0');
592
if (field_length > 0)
593
value*= (long) log_10_int[field_length];
594
else if (field_length < 0)
595
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
596
date[4]= (uint32_t) value;
601
/* Check for exponent part: E<gigit> | E<sign><digit> */
602
/* (may occur as result of %g formatting of time value) */
603
if ((end - str) > 1 &&
604
(*str == 'e' || *str == 'E') &&
605
(my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
606
((str[1] == '-' || str[1] == '+') &&
608
my_isdigit(&my_charset_utf8_general_ci, str[2]))))
611
if (internal_format_positions[7] != 255)
613
/* Read a possible AM/PM */
614
while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
616
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
618
if (str[0] == 'p' || str[0] == 'P')
621
date[1]= date[1]%12 + 12;
623
else if (str[0] == 'a' || str[0] == 'A')
628
/* Integer overflow checks */
629
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
630
date[2] > UINT_MAX || date[3] > UINT_MAX ||
634
l_time->year= 0; /* For protocol::store_time */
636
l_time->day= date[0];
637
l_time->hour= date[1];
638
l_time->minute= date[2];
639
l_time->second= date[3];
640
l_time->second_part= date[4];
641
l_time->time_type= DRIZZLE_TIMESTAMP_TIME;
643
/* Check if the value is valid and fits into DRIZZLE_TIME range */
644
if (check_time_range(l_time, warning))
647
/* Check if there is garbage at end of the DRIZZLE_TIME specification */
652
if (!my_isspace(&my_charset_utf8_general_ci,*str))
654
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
657
} while (++str != end);
664
Check 'time' value to lie in the DRIZZLE_TIME range
668
time pointer to DRIZZLE_TIME value
669
warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
672
If the time value lies outside of the range [-838:59:59, 838:59:59],
673
set it to the closest endpoint of the range and set
674
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
677
0 time value is valid, but was possibly truncated
678
1 time value is invalid
681
static int check_time_range(DRIZZLE_TIME *my_time, int *warning)
685
if (my_time->minute >= 60 || my_time->second >= 60)
688
hour= my_time->hour + (24*my_time->day);
689
if (hour <= TIME_MAX_HOUR &&
690
(hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
691
my_time->second != TIME_MAX_SECOND || !my_time->second_part))
695
my_time->hour= TIME_MAX_HOUR;
696
my_time->minute= TIME_MAX_MINUTE;
697
my_time->second= TIME_MAX_SECOND;
698
my_time->second_part= 0;
699
*warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
705
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
713
struct tm *l_time,tm_tmp;
714
DRIZZLE_TIME my_time;
717
seconds= (time_t) time((time_t*) 0);
718
localtime_r(&seconds,&tm_tmp);
720
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
721
my_time.year= (uint32_t) l_time->tm_year+1900;
722
my_time.month= (uint32_t) l_time->tm_mon+1;
723
my_time.day= (uint32_t) l_time->tm_mday;
724
my_time.hour= (uint32_t) l_time->tm_hour;
725
my_time.minute= (uint32_t) l_time->tm_min;
726
my_time.second= (uint32_t) l_time->tm_sec;
727
my_time.time_type= DRIZZLE_TIMESTAMP_NONE;
728
my_time.second_part= 0;
730
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
735
Handle 2 digit year conversions
742
Year between 1970-2069
745
uint32_t year_2000_handling(uint32_t year)
747
if ((year=year+1900) < 1900+YY_PART_YEAR)
754
Calculate nr of day since year 0 in new date-system (from 1615)
758
year Year (exact 4 digit year, no year conversions)
762
NOTES: 0000-00-00 is a valid date, and will return 0
765
Days since 0000-00-00
768
long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
773
if (year == 0 && month == 0 && day == 0)
774
return(0); /* Skip errors */
775
delsum= (long) (365L * year+ 31*(month-1) +day);
779
delsum-= (long) (month*4+23)/10;
780
temp=(int) ((year/100+1)*3)/4;
781
return(delsum+(int) year/4-temp);
786
Convert time in DRIZZLE_TIME representation in system time zone to its
787
time_t form (number of seconds in UTC since begginning of Unix Epoch).
791
t - time value to be converted
792
my_timezone - pointer to long where offset of system time zone
793
from UTC will be stored for caching
794
in_dst_time_gap - set to true if time falls into spring time-gap
797
The idea is to cache the time zone offset from UTC (including daylight
798
saving time) for the next call to make things faster. But currently we
799
just calculate this offset during startup (by calling init_time()
800
function) and use it all the time.
801
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
805
Time in UTC seconds since Unix Epoch representation.
808
my_system_gmt_sec(const DRIZZLE_TIME *t_src, long *my_timezone,
809
bool *in_dst_time_gap)
814
DRIZZLE_TIME tmp_time;
815
DRIZZLE_TIME *t= &tmp_time;
816
struct tm *l_time,tm_tmp;
817
long diff, current_timezone;
820
Use temp variable to avoid trashing input data, which could happen in
821
case of shift required for boundary dates processing.
823
memcpy(&tmp_time, t_src, sizeof(DRIZZLE_TIME));
825
if (!validate_timestamp_range(t))
829
Calculate the gmt time based on current time and timezone
830
The -1 on the end is to ensure that if have a date that exists twice
831
(like 2002-10-27 02:00:0 MET), we will find the initial date.
833
By doing -3600 we will have to call localtime_r() several times, but
834
I couldn't come up with a better way to get a repeatable result :(
836
We can't use mktime() as it's buggy on many platforms and not thread safe.
838
Note: this code assumes that our time_t estimation is not too far away
839
from real value (we assume that localtime_r(tmp) will return something
840
within 24 hrs from t) which is probably true for all current time zones.
842
Note2: For the dates, which have time_t representation close to
843
MAX_INT32 (efficient time_t limit for supported platforms), we should
844
do a small trick to avoid overflow. That is, convert the date, which is
845
two days earlier, and then add these days to the final value.
847
The same trick is done for the values close to 0 in time_t
848
representation for platfroms with unsigned time_t (QNX).
850
To be more verbose, here is a sample (extracted from the code below):
851
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
852
would return -2147480896 because of the long type overflow. In result
853
we would get 1901 year in localtime_r(), which is an obvious error.
855
Alike problem raises with the dates close to Epoch. E.g.
856
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
859
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
860
wil give us a date around 2106 year. Which is no good.
862
Theoreticaly, there could be problems with the latter conversion:
863
there are at least two timezones, which had time switches near 1 Jan
864
of 1970 (because of political reasons). These are America/Hermosillo and
865
America/Mazatlan time zones. They changed their offset on
866
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
867
the code below will give incorrect results for dates close to
868
1970-01-01, in the case OS takes into account these historical switches.
869
Luckily, it seems that we support only one platform with unsigned
870
time_t. It's QNX. And QNX does not support historical timezone data at all.
871
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
872
historical information for localtime_r() etc. That is, the problem is not
875
We are safe with shifts close to MAX_INT32, as there are no known
876
time switches on Jan 2038 yet :)
878
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
881
Below we will pass (uint32_t) (t->day - shift) to calc_daynr.
882
As we don't want to get an overflow here, we will shift
883
only safe dates. That's why we have (t->day > 4) above.
888
#ifdef TIME_T_UNSIGNED
892
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
893
1970, 1 of Jan. For both dates we use shift, which is added
894
to t->day in order to step out a bit from the border.
895
This is required for platforms, where time_t is unsigned.
896
As far as I know, among the platforms we support it's only QNX.
897
Note: the order of below if-statements is significant.
900
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
907
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
918
tmp= (time_t) (((calc_daynr((uint32_t) t->year, (uint32_t) t->month, (uint32_t) t->day) -
919
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
920
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
923
current_timezone= my_time_zone;
924
localtime_r(&tmp,&tm_tmp);
928
(t->hour != (uint32_t) l_time->tm_hour ||
929
t->minute != (uint32_t) l_time->tm_min ||
930
t->second != (uint32_t) l_time->tm_sec);
932
{ /* One check should be enough ? */
933
/* Get difference in days */
934
int days= t->day - l_time->tm_mday;
936
days= 1; /* Month has wrapped */
939
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
940
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
941
(long) ((int) t->second - (int) l_time->tm_sec));
942
current_timezone+= diff+3600; /* Compensate for -3600 above */
944
localtime_r(&tmp,&tm_tmp);
948
Fix that if we are in the non existing daylight saving time hour
949
we move the start of the next real hour.
951
This code doesn't handle such exotical thing as time-gaps whose length
952
is more than one hour or non-integer (latter can theoretically happen
953
if one of seconds will be removed due leap correction, or because of
954
general time correction like it happened for Africa/Monrovia time zone
957
if (loop == 2 && t->hour != (uint32_t) l_time->tm_hour)
959
int days= t->day - l_time->tm_mday;
961
days=1; /* Month has wrapped */
964
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
965
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
966
(long) ((int) t->second - (int) l_time->tm_sec));
968
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
969
else if (diff == -3600)
970
tmp-=t->minute*60 + t->second; /* Move to previous hour */
972
*in_dst_time_gap= true;
974
*my_timezone= current_timezone;
977
/* shift back, if we were dealing with boundary dates */
981
This is possible for dates, which slightly exceed boundaries.
982
Conversion will pass ok for them, but we don't allow them.
983
First check will pass for platforms with signed time_t.
984
instruction above (tmp+= shift*86400L) could exceed
985
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
986
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
987
with unsigned time_t tmp+= shift*86400L might result in a number,
988
larger then TIMESTAMP_MAX_VALUE, so another check will work.
990
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
994
} /* my_system_gmt_sec */
997
/* Set DRIZZLE_TIME structure to 0000-00-00 00:00:00.000000 */
999
void set_zero_time(DRIZZLE_TIME *tm, enum enum_drizzle_timestamp_type time_type)
1001
memset(tm, 0, sizeof(*tm));
1002
tm->time_type= time_type;
1007
Functions to convert time/date/datetime value to a string,
1008
using default format.
1009
This functions don't check that given DRIZZLE_TIME structure members are
1010
in valid range. If they are not, return value won't reflect any
1011
valid date either. Additionally, make_time doesn't take into
1012
account time->day member: it's assumed that days have been converted
1016
number of characters written to 'to'
1019
static int my_time_to_str(const DRIZZLE_TIME *l_time, char *to)
1021
uint32_t extra_hours= 0;
1022
return sprintf(to, "%s%02u:%02u:%02u",
1023
(l_time->neg ? "-" : ""),
1024
extra_hours+ l_time->hour,
1029
int my_date_to_str(const DRIZZLE_TIME *l_time, char *to)
1031
return sprintf(to, "%04u-%02u-%02u",
1037
int my_datetime_to_str(const DRIZZLE_TIME *l_time, char *to)
1039
return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u",
1050
Convert struct DATE/TIME/DATETIME value to string using built-in
1051
MySQL time conversion formats.
1057
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1060
int my_TIME_to_str(const DRIZZLE_TIME *l_time, char *to)
1062
switch (l_time->time_type) {
1063
case DRIZZLE_TIMESTAMP_DATETIME:
1064
return my_datetime_to_str(l_time, to);
1065
case DRIZZLE_TIMESTAMP_DATE:
1066
return my_date_to_str(l_time, to);
1067
case DRIZZLE_TIMESTAMP_TIME:
1068
return my_time_to_str(l_time, to);
1069
case DRIZZLE_TIMESTAMP_NONE:
1070
case DRIZZLE_TIMESTAMP_ERROR:
1081
Convert datetime value specified as number to broken-down TIME
1082
representation and form value of DATETIME type as side-effect.
1085
number_to_datetime()
1086
nr - datetime value as number
1087
time_res - pointer for structure for broken-down representation
1088
flags - flags to use in validating date, as in str_to_datetime()
1090
1 If value was cut during conversion
1091
2 check_date(date,flags) considers date invalid
1094
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1095
YYYYMMDDHHMMSS to broken-down DRIZZLE_TIME representation. Return value in
1096
YYYYMMDDHHMMSS format as side-effect.
1098
This function also checks if datetime value fits in DATETIME range.
1101
-1 Timestamp with wrong values
1102
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1103
Datetime value in YYYYMMDDHHMMSS format.
1106
int64_t number_to_datetime(int64_t nr, DRIZZLE_TIME *time_res,
1107
uint32_t flags, int *was_cut)
1112
memset(time_res, 0, sizeof(*time_res));
1113
time_res->time_type=DRIZZLE_TIMESTAMP_DATE;
1115
if (nr == 0LL || nr >= 10000101000000LL)
1117
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1122
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1124
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1127
if (nr < (YY_PART_YEAR)*10000L+101L)
1131
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1136
if (nr <= 99991231L)
1141
if (nr < 101000000L)
1144
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1146
if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1148
nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1151
if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1153
if (nr <= 991231235959LL)
1154
nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1157
part1=(long) (nr / 1000000LL);
1158
part2=(long) (nr - (int64_t) part1 * 1000000LL);
1159
time_res->year= (int) (part1/10000L); part1%=10000L;
1160
time_res->month= (int) part1 / 100;
1161
time_res->day= (int) part1 % 100;
1162
time_res->hour= (int) (part2/10000L); part2%=10000L;
1163
time_res->minute=(int) part2 / 100;
1164
time_res->second=(int) part2 % 100;
1166
if (time_res->year <= 9999 && time_res->month <= 12 &&
1167
time_res->day <= 31 && time_res->hour <= 23 &&
1168
time_res->minute <= 59 && time_res->second <= 59 &&
1169
!check_date(time_res, (nr != 0), flags, was_cut))
1172
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1173
if (!nr && (flags & TIME_NO_ZERO_DATE))
1182
/* Convert time value to integer in YYYYMMDDHHMMSS format */
1184
uint64_t TIME_to_uint64_t_datetime(const DRIZZLE_TIME *my_time)
1186
return ((uint64_t) (my_time->year * 10000UL +
1187
my_time->month * 100UL +
1188
my_time->day) * 1000000ULL +
1189
(uint64_t) (my_time->hour * 10000UL +
1190
my_time->minute * 100UL +
1195
/* Convert DRIZZLE_TIME value to integer in YYYYMMDD format */
1197
static uint64_t TIME_to_uint64_t_date(const DRIZZLE_TIME *my_time)
1199
return (uint64_t) (my_time->year * 10000UL + my_time->month * 100UL +
1205
Convert DRIZZLE_TIME value to integer in HHMMSS format.
1206
This function doesn't take into account time->day member:
1207
it's assumed that days have been converted to hours already.
1210
static uint64_t TIME_to_uint64_t_time(const DRIZZLE_TIME *my_time)
1212
return (uint64_t) (my_time->hour * 10000UL +
1213
my_time->minute * 100UL +
1219
Convert struct DRIZZLE_TIME (date and time split into year/month/day/hour/...
1220
to a number in format YYYYMMDDHHMMSS (DATETIME),
1221
YYYYMMDD (DATE) or HHMMSS (TIME).
1227
The function is used when we need to convert value of time item
1228
to a number if it's used in numeric context, i. e.:
1229
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1233
This function doesn't check that given DRIZZLE_TIME structure members are
1234
in valid range. If they are not, return value won't reflect any
1238
uint64_t TIME_to_uint64_t(const DRIZZLE_TIME *my_time)
1240
switch (my_time->time_type) {
1241
case DRIZZLE_TIMESTAMP_DATETIME:
1242
return TIME_to_uint64_t_datetime(my_time);
1243
case DRIZZLE_TIMESTAMP_DATE:
1244
return TIME_to_uint64_t_date(my_time);
1245
case DRIZZLE_TIMESTAMP_TIME:
1246
return TIME_to_uint64_t_time(my_time);
1247
case DRIZZLE_TIMESTAMP_NONE:
1248
case DRIZZLE_TIMESTAMP_ERROR: