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 */
18
#include "drizzled/drizzle_time.h"
20
#include "drizzled/internal/m_string.h"
21
#include "drizzled/charset_info.h"
22
#include <drizzled/util/test.h>
23
#include "drizzled/definitions.h"
33
static int check_time_range(DRIZZLE_TIME *my_time, int *warning);
35
/* Windows version of localtime_r() is declared in my_ptrhead.h */
37
uint64_t log_10_int[20]=
39
1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
40
100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
41
1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
42
1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
43
1000000000000000000ULL, 10000000000000000000ULL
47
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
49
static unsigned char internal_format_positions[]=
50
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
52
static char time_separator=':';
54
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
55
unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
58
Offset of system time zone from UTC in seconds used to speed up
59
work of my_system_gmt_sec() function.
61
static long my_time_zone=0;
64
/* Calc days in one year. works with 0 <= year <= 99 */
66
uint32_t calc_days_in_year(uint32_t year)
68
return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
73
@brief Check datetime value for validity according to flags.
75
@param[in] ltime Date to check.
76
@param[in] not_zero_date ltime is not the zero date
77
@param[in] flags flags to check
78
(see str_to_datetime() flags in my_time.h)
79
@param[out] was_cut set to 2 if value was invalid according to flags.
80
(Feb 29 in non-leap etc.) This remains unchanged
81
if value is not invalid.
83
@details Here we assume that year and month is ok!
84
If month is 0 we allow any date. (This only happens if we allow zero
85
date parts in str_to_datetime())
86
Disallow dates with zero year and non-zero month and/or day.
93
bool check_date(const DRIZZLE_TIME *ltime, bool not_zero_date,
94
uint32_t flags, int *was_cut)
98
if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
99
(ltime->month == 0 || ltime->day == 0)) ||
100
(!(flags & TIME_INVALID_DATES) &&
101
ltime->month && ltime->day > days_in_month[ltime->month-1] &&
102
(ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
109
else if (flags & TIME_NO_ZERO_DATE)
112
We don't set *was_cut here to signal that the problem was a zero date
113
and not an invalid date
122
Convert a timestamp string to a DRIZZLE_TIME value.
127
length Length of string
128
l_time Date is stored here
129
flags Bitmap of following items
130
TIME_FUZZY_DATE Set if we should allow partial dates
131
TIME_DATETIME_ONLY Set if we only allow full datetimes.
132
TIME_NO_ZERO_IN_DATE Don't allow partial dates
133
TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
134
TIME_INVALID_DATES Allow 2000-02-31
136
1 If value was cut during conversion
137
2 check_date(date,flags) considers date invalid
140
At least the following formats are recogniced (based on number of digits)
141
YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
142
YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
143
YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
144
Also dates where all parts are zero are allowed
146
The second part may have an optional .###### fraction part.
149
This function should work with a format position vector as long as the
150
following things holds:
151
- All date are kept together and all time parts are kept together
152
- Date and time parts must be separated by blank
153
- Second fractions must come after second part and be separated
154
by a '.'. (The second fractions are optional)
155
- AM/PM must come after second fractions (or after seconds if no fractions)
156
- Year must always been specified.
157
- If time is before date, then we will use datetime format only if
158
the argument consist of two parts, separated by space.
159
Otherwise we will assume the argument is a date.
160
- The hour part must be specified in hour-minute-second order.
163
DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
164
[DD [HH:[MM:[SS]]]].fraction.
165
l_time is not changed.
166
DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
167
DRIZZLE_TIMESTAMP_DATETIME Full timestamp
168
DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
169
All elements in l_time is set to 0
172
#define MAX_DATE_PARTS 8
174
enum enum_drizzle_timestamp_type
175
str_to_datetime(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
176
uint32_t flags, int *was_cut)
178
uint32_t field_length, year_length=4, digits, i, number_of_fields;
179
uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
180
uint32_t add_hours= 0, start_loop;
181
uint32_t not_zero_date, allow_space;
182
bool is_internal_format;
183
const char *pos, *last_field_pos=NULL;
184
const char *end=str+length;
185
const unsigned char *format_position;
186
bool found_delimitier= 0, found_space= 0;
187
uint32_t frac_pos, frac_len;
191
/* Skip space at start */
192
for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
194
if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *str))
197
return(DRIZZLE_TIMESTAMP_NONE);
200
is_internal_format= 0;
201
/* This has to be changed if want to activate different timestamp formats */
202
format_position= internal_format_positions;
205
Calculate number of digits in first part.
206
If length= 8 or >= 14 then year is of format YYYY.
207
(YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
210
pos != end && (my_isdigit(&my_charset_utf8_general_ci,*pos) || *pos == 'T');
214
digits= (uint32_t) (pos-str);
215
start_loop= 0; /* Start of scan loop */
216
date_len[format_position[0]]= 0; /* Length of year field */
217
if (pos == end || *pos == '.')
219
/* Found date in internal format (only numbers like YYYYMMDD) */
220
year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
221
field_length= year_length;
222
is_internal_format= 1;
223
format_position= internal_format_positions;
227
if (format_position[0] >= 3) /* If year is after HHMMDD */
230
If year is not in first part then we have to determinate if we got
231
a date field or a datetime field.
232
We do this by checking if there is two numbers separated by
235
while (pos < end && !my_isspace(&my_charset_utf8_general_ci, *pos))
237
while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *pos))
241
if (flags & TIME_DATETIME_ONLY)
244
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
246
/* Date field. Set hour, minutes and seconds to 0 */
247
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
248
start_loop= 5; /* Start with first date part */
252
field_length= format_position[0] == 0 ? 4 : 2;
256
Only allow space in the first "part" of the datetime field and:
257
- after days, part seconds
258
- before and after AM/PM (handled by code later)
260
2003-03-03 20:00:20 AM
261
20:00:20.000000 AM 03-03-2000
263
i= max((uint32_t) format_position[0], (uint32_t) format_position[1]);
264
set_if_bigger(i, (uint32_t) format_position[2]);
265
allow_space= ((1 << i) | (1 << format_position[6]));
266
allow_space&= (1 | 2 | 4 | 8);
270
i < MAX_DATE_PARTS-1 && str != end &&
271
my_isdigit(&my_charset_utf8_general_ci,*str);
274
const char *start= str;
275
uint32_t tmp_value= (uint32_t) (unsigned char) (*str++ - '0');
276
while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
277
(!is_internal_format || --field_length))
279
tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
282
date_len[i]= (uint32_t) (str - start);
283
if (tmp_value > 999999) /* Impossible date part */
286
return(DRIZZLE_TIMESTAMP_NONE);
289
not_zero_date|= tmp_value;
291
/* Length of next field */
292
field_length= format_position[i+1] == 0 ? 4 : 2;
294
if ((last_field_pos= str) == end)
296
i++; /* Register last found part */
299
/* Allow a 'T' after day to allow CCYYMMDDT type of fields */
300
if (i == format_position[2] && *str == 'T')
302
str++; /* ISO8601: CCYYMMDDThhmmss */
305
if (i == format_position[5]) /* Seconds */
307
if (*str == '.') /* Followed by part seconds */
310
field_length= 6; /* 6 digits */
315
(my_ispunct(&my_charset_utf8_general_ci,*str) ||
316
my_isspace(&my_charset_utf8_general_ci,*str)))
318
if (my_isspace(&my_charset_utf8_general_ci,*str))
320
if (!(allow_space & (1 << i)))
323
return(DRIZZLE_TIMESTAMP_NONE);
328
found_delimitier= 1; /* Should be a 'normal' date */
330
/* Check if next position is AM/PM */
331
if (i == format_position[6]) /* Seconds, time for AM/PM */
333
i++; /* Skip AM/PM part */
334
if (format_position[7] != 255) /* If using AM/PM */
336
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
338
if (str[0] == 'p' || str[0] == 'P')
340
else if (str[0] != 'a' || str[0] != 'A')
341
continue; /* Not AM/PM */
342
str+= 2; /* Skip AM/PM */
343
/* Skip space after AM/PM */
344
while (str != end && my_isspace(&my_charset_utf8_general_ci,*str))
351
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
354
return(DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
359
number_of_fields= i - start_loop;
360
while (i < MAX_DATE_PARTS)
366
if (!is_internal_format)
368
year_length= date_len[(uint32_t) format_position[0]];
369
if (!year_length) /* Year must be specified */
372
return(DRIZZLE_TIMESTAMP_NONE);
375
l_time->year= date[(uint32_t) format_position[0]];
376
l_time->month= date[(uint32_t) format_position[1]];
377
l_time->day= date[(uint32_t) format_position[2]];
378
l_time->hour= date[(uint32_t) format_position[3]];
379
l_time->minute= date[(uint32_t) format_position[4]];
380
l_time->second= date[(uint32_t) format_position[5]];
382
frac_pos= (uint32_t) format_position[6];
383
frac_len= date_len[frac_pos];
385
date[frac_pos]*= (uint32_t) log_10_int[6 - frac_len];
386
l_time->second_part= date[frac_pos];
388
if (format_position[7] != (unsigned char) 255)
390
if (l_time->hour > 12)
395
l_time->hour= l_time->hour%12 + add_hours;
400
l_time->year= date[0];
401
l_time->month= date[1];
402
l_time->day= date[2];
403
l_time->hour= date[3];
404
l_time->minute= date[4];
405
l_time->second= date[5];
407
date[6]*= (uint32_t) log_10_int[6 - date_len[6]];
408
l_time->second_part=date[6];
412
if (year_length == 2 && not_zero_date)
413
l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
415
if (number_of_fields < 3 ||
416
l_time->year > 9999 || l_time->month > 12 ||
417
l_time->day > 31 || l_time->hour > 23 ||
418
l_time->minute > 59 || l_time->second > 59)
420
/* Only give warning for a zero date if there is some garbage after */
421
if (!not_zero_date) /* If zero date */
423
for (; str != end ; str++)
425
if (!my_isspace(&my_charset_utf8_general_ci, *str))
427
not_zero_date= 1; /* Give warning */
432
*was_cut= test(not_zero_date);
436
if (check_date(l_time, not_zero_date != 0, flags, was_cut))
439
l_time->time_type= (number_of_fields <= 3 ?
440
DRIZZLE_TIMESTAMP_DATE : DRIZZLE_TIMESTAMP_DATETIME);
442
for (; str != end ; str++)
444
if (!my_isspace(&my_charset_utf8_general_ci,*str))
451
return(l_time->time_type=
452
(number_of_fields <= 3 ? DRIZZLE_TIMESTAMP_DATE :
453
DRIZZLE_TIMESTAMP_DATETIME));
456
memset(l_time, 0, sizeof(*l_time));
457
return(DRIZZLE_TIMESTAMP_ERROR);
462
Convert a time string to a DRIZZLE_TIME struct.
466
str A string in full TIMESTAMP format or
467
[-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
469
There may be an optional [.second_part] after seconds
471
l_time Store result here
472
warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
473
was cut during conversion, and/or
474
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
478
Because of the extra days argument, this function can only
479
work with times where the time arguments are in the above order.
486
bool str_to_time(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
491
const char *end=str+length, *end_of_days;
492
bool found_days,found_hours;
497
for (; str != end && my_isspace(&my_charset_utf8_general_ci,*str) ; str++)
499
if (str != end && *str == '-')
508
/* Check first if this is a full TIMESTAMP */
510
{ /* Probably full timestamp */
512
enum enum_drizzle_timestamp_type
513
res= str_to_datetime(str, length, l_time,
514
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
515
if ((int) res >= (int) DRIZZLE_TIMESTAMP_ERROR)
518
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
519
return res == DRIZZLE_TIMESTAMP_ERROR;
523
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
524
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
525
value=value*10L + (long) (*str - '0');
527
/* Skip all space after 'days' */
529
for (; str != end && my_isspace(&my_charset_utf8_general_ci, str[0]) ; str++)
532
found_days=found_hours=0;
533
if ((uint32_t) (end-str) > 1 && str != end_of_days &&
534
my_isdigit(&my_charset_utf8_general_ci, *str))
535
{ /* Found days part */
536
date[0]= (uint32_t) value;
537
state= 1; /* Assume next is hours */
540
else if ((end-str) > 1 && *str == time_separator &&
541
my_isdigit(&my_charset_utf8_general_ci, str[1]))
543
date[0]= 0; /* Assume we found hours */
544
date[1]= (uint32_t) value;
547
str++; /* skip ':' */
551
/* String given as one number; assume HHMMSS format */
553
date[1]= (uint32_t) (value/10000);
554
date[2]= (uint32_t) (value/100 % 100);
555
date[3]= (uint32_t) (value % 100);
560
/* Read hours, minutes and seconds */
563
for (value=0; str != end && my_isdigit(&my_charset_utf8_general_ci,*str) ; str++)
564
value=value*10L + (long) (*str - '0');
565
date[state++]= (uint32_t) value;
566
if (state == 4 || (end-str) < 2 || *str != time_separator ||
567
!my_isdigit(&my_charset_utf8_general_ci,str[1]))
569
str++; /* Skip time_separator (':') */
574
/* Fix the date to assume that seconds was given */
575
if (!found_hours && !found_days)
577
internal::bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
578
sizeof(long)*(state-1));
579
memset(date, 0, sizeof(long)*(4-state));
582
memset(date+state, 0, sizeof(long)*(4-state));
586
/* Get fractional second part */
587
if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_utf8_general_ci,str[1]))
590
str++; value=(uint32_t) (unsigned char) (*str - '0');
591
while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
593
if (field_length-- > 0)
594
value= value*10 + (uint32_t) (unsigned char) (*str - '0');
596
if (field_length > 0)
597
value*= (long) log_10_int[field_length];
598
else if (field_length < 0)
599
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
600
date[4]= (uint32_t) value;
605
/* Check for exponent part: E<gigit> | E<sign><digit> */
606
/* (may occur as result of %g formatting of time value) */
607
if ((end - str) > 1 &&
608
(*str == 'e' || *str == 'E') &&
609
(my_isdigit(&my_charset_utf8_general_ci, str[1]) ||
610
((str[1] == '-' || str[1] == '+') &&
612
my_isdigit(&my_charset_utf8_general_ci, str[2]))))
615
if (internal_format_positions[7] != 255)
617
/* Read a possible AM/PM */
618
while (str != end && my_isspace(&my_charset_utf8_general_ci, *str))
620
if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
622
if (str[0] == 'p' || str[0] == 'P')
625
date[1]= date[1]%12 + 12;
627
else if (str[0] == 'a' || str[0] == 'A')
632
/* Integer overflow checks */
633
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
634
date[2] > UINT_MAX || date[3] > UINT_MAX ||
638
l_time->year= 0; /* For protocol::store_time */
640
l_time->day= date[0];
641
l_time->hour= date[1];
642
l_time->minute= date[2];
643
l_time->second= date[3];
644
l_time->second_part= date[4];
645
l_time->time_type= DRIZZLE_TIMESTAMP_TIME;
647
/* Check if the value is valid and fits into DRIZZLE_TIME range */
648
if (check_time_range(l_time, warning))
651
/* Check if there is garbage at end of the DRIZZLE_TIME specification */
656
if (!my_isspace(&my_charset_utf8_general_ci,*str))
658
*warning|= DRIZZLE_TIME_WARN_TRUNCATED;
661
} while (++str != end);
668
Check 'time' value to lie in the DRIZZLE_TIME range
672
time pointer to DRIZZLE_TIME value
673
warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
676
If the time value lies outside of the range [-838:59:59, 838:59:59],
677
set it to the closest endpoint of the range and set
678
DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
681
0 time value is valid, but was possibly truncated
682
1 time value is invalid
685
static int check_time_range(DRIZZLE_TIME *my_time, int *warning)
689
if (my_time->minute >= 60 || my_time->second >= 60)
692
hour= my_time->hour + (24*my_time->day);
693
if (hour <= TIME_MAX_HOUR &&
694
(hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
695
my_time->second != TIME_MAX_SECOND || !my_time->second_part))
699
my_time->hour= TIME_MAX_HOUR;
700
my_time->minute= TIME_MAX_MINUTE;
701
my_time->second= TIME_MAX_SECOND;
702
my_time->second_part= 0;
703
*warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
709
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
717
struct tm *l_time,tm_tmp;
718
DRIZZLE_TIME my_time;
721
seconds= (time_t) time((time_t*) 0);
722
localtime_r(&seconds,&tm_tmp);
724
my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
725
my_time.year= (uint32_t) l_time->tm_year+1900;
726
my_time.month= (uint32_t) l_time->tm_mon+1;
727
my_time.day= (uint32_t) l_time->tm_mday;
728
my_time.hour= (uint32_t) l_time->tm_hour;
729
my_time.minute= (uint32_t) l_time->tm_min;
730
my_time.second= (uint32_t) l_time->tm_sec;
731
my_time.time_type= DRIZZLE_TIMESTAMP_NONE;
732
my_time.second_part= 0;
734
my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
739
Handle 2 digit year conversions
746
Year between 1970-2069
749
uint32_t year_2000_handling(uint32_t year)
751
if ((year=year+1900) < 1900+YY_PART_YEAR)
758
Calculate nr of day since year 0 in new date-system (from 1615)
762
year Year (exact 4 digit year, no year conversions)
766
NOTES: 0000-00-00 is a valid date, and will return 0
769
Days since 0000-00-00
772
long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
777
if (year == 0 && month == 0 && day == 0)
778
return(0); /* Skip errors */
779
delsum= (long) (365L * year+ 31*(month-1) +day);
783
delsum-= (long) (month*4+23)/10;
784
temp=(int) ((year/100+1)*3)/4;
785
return(delsum+(int) year/4-temp);
790
Convert time in DRIZZLE_TIME representation in system time zone to its
791
time_t form (number of seconds in UTC since begginning of Unix Epoch).
795
t - time value to be converted
796
my_timezone - pointer to long where offset of system time zone
797
from UTC will be stored for caching
798
in_dst_time_gap - set to true if time falls into spring time-gap
801
The idea is to cache the time zone offset from UTC (including daylight
802
saving time) for the next call to make things faster. But currently we
803
just calculate this offset during startup (by calling init_time()
804
function) and use it all the time.
805
Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
809
Time in UTC seconds since Unix Epoch representation.
812
my_system_gmt_sec(const DRIZZLE_TIME *t_src, long *my_timezone,
813
bool *in_dst_time_gap)
818
DRIZZLE_TIME tmp_time;
819
DRIZZLE_TIME *t= &tmp_time;
820
struct tm *l_time,tm_tmp;
821
long diff, current_timezone;
824
Use temp variable to avoid trashing input data, which could happen in
825
case of shift required for boundary dates processing.
827
memcpy(&tmp_time, t_src, sizeof(DRIZZLE_TIME));
829
if (!validate_timestamp_range(t))
833
Calculate the gmt time based on current time and timezone
834
The -1 on the end is to ensure that if have a date that exists twice
835
(like 2002-10-27 02:00:0 MET), we will find the initial date.
837
By doing -3600 we will have to call localtime_r() several times, but
838
I couldn't come up with a better way to get a repeatable result :(
840
We can't use mktime() as it's buggy on many platforms and not thread safe.
842
Note: this code assumes that our time_t estimation is not too far away
843
from real value (we assume that localtime_r(tmp) will return something
844
within 24 hrs from t) which is probably true for all current time zones.
846
Note2: For the dates, which have time_t representation close to
847
MAX_INT32 (efficient time_t limit for supported platforms), we should
848
do a small trick to avoid overflow. That is, convert the date, which is
849
two days earlier, and then add these days to the final value.
851
The same trick is done for the values close to 0 in time_t
852
representation for platfroms with unsigned time_t (QNX).
854
To be more verbose, here is a sample (extracted from the code below):
855
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
856
would return -2147480896 because of the long type overflow. In result
857
we would get 1901 year in localtime_r(), which is an obvious error.
859
Alike problem raises with the dates close to Epoch. E.g.
860
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
863
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
864
wil give us a date around 2106 year. Which is no good.
866
Theoreticaly, there could be problems with the latter conversion:
867
there are at least two timezones, which had time switches near 1 Jan
868
of 1970 (because of political reasons). These are America/Hermosillo and
869
America/Mazatlan time zones. They changed their offset on
870
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
871
the code below will give incorrect results for dates close to
872
1970-01-01, in the case OS takes into account these historical switches.
873
Luckily, it seems that we support only one platform with unsigned
874
time_t. It's QNX. And QNX does not support historical timezone data at all.
875
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
876
historical information for localtime_r() etc. That is, the problem is not
879
We are safe with shifts close to MAX_INT32, as there are no known
880
time switches on Jan 2038 yet :)
882
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
885
Below we will pass (uint32_t) (t->day - shift) to calc_daynr.
886
As we don't want to get an overflow here, we will shift
887
only safe dates. That's why we have (t->day > 4) above.
892
#ifdef TIME_T_UNSIGNED
896
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
897
1970, 1 of Jan. For both dates we use shift, which is added
898
to t->day in order to step out a bit from the border.
899
This is required for platforms, where time_t is unsigned.
900
As far as I know, among the platforms we support it's only QNX.
901
Note: the order of below if-statements is significant.
904
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
911
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
922
tmp= (time_t) (((calc_daynr((uint32_t) t->year, (uint32_t) t->month, (uint32_t) t->day) -
923
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
924
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
927
current_timezone= my_time_zone;
928
localtime_r(&tmp,&tm_tmp);
932
(t->hour != (uint32_t) l_time->tm_hour ||
933
t->minute != (uint32_t) l_time->tm_min ||
934
t->second != (uint32_t) l_time->tm_sec);
936
{ /* One check should be enough ? */
937
/* Get difference in days */
938
int days= t->day - l_time->tm_mday;
940
days= 1; /* Month has wrapped */
943
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
944
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
945
(long) ((int) t->second - (int) l_time->tm_sec));
946
current_timezone+= diff+3600; /* Compensate for -3600 above */
948
localtime_r(&tmp,&tm_tmp);
952
Fix that if we are in the non existing daylight saving time hour
953
we move the start of the next real hour.
955
This code doesn't handle such exotical thing as time-gaps whose length
956
is more than one hour or non-integer (latter can theoretically happen
957
if one of seconds will be removed due leap correction, or because of
958
general time correction like it happened for Africa/Monrovia time zone
961
if (loop == 2 && t->hour != (uint32_t) l_time->tm_hour)
963
int days= t->day - l_time->tm_mday;
965
days=1; /* Month has wrapped */
968
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
969
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
970
(long) ((int) t->second - (int) l_time->tm_sec));
972
tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
973
else if (diff == -3600)
974
tmp-=t->minute*60 + t->second; /* Move to previous hour */
976
*in_dst_time_gap= true;
978
*my_timezone= current_timezone;
981
/* shift back, if we were dealing with boundary dates */
985
This is possible for dates, which slightly exceed boundaries.
986
Conversion will pass ok for them, but we don't allow them.
987
First check will pass for platforms with signed time_t.
988
instruction above (tmp+= shift*86400L) could exceed
989
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
990
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
991
with unsigned time_t tmp+= shift*86400L might result in a number,
992
larger then TIMESTAMP_MAX_VALUE, so another check will work.
994
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
998
} /* my_system_gmt_sec */
1001
/* Set DRIZZLE_TIME structure to 0000-00-00 00:00:00.000000 */
1003
void set_zero_time(DRIZZLE_TIME *tm, enum enum_drizzle_timestamp_type time_type)
1005
memset(tm, 0, sizeof(*tm));
1006
tm->time_type= time_type;
1011
Functions to convert time/date/datetime value to a string,
1012
using default format.
1013
This functions don't check that given DRIZZLE_TIME structure members are
1014
in valid range. If they are not, return value won't reflect any
1015
valid date either. Additionally, make_time doesn't take into
1016
account time->day member: it's assumed that days have been converted
1020
number of characters written to 'to'
1023
static int my_time_to_str(const DRIZZLE_TIME *l_time, char *to)
1025
uint32_t extra_hours= 0;
1026
return sprintf(to, "%s%02u:%02u:%02u",
1027
(l_time->neg ? "-" : ""),
1028
extra_hours+ l_time->hour,
1033
int my_date_to_str(const DRIZZLE_TIME *l_time, char *to)
1035
return sprintf(to, "%04u-%02u-%02u",
1041
int my_datetime_to_str(const DRIZZLE_TIME *l_time, char *to)
1043
return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u",
1054
Convert struct DATE/TIME/DATETIME value to string using built-in
1055
MySQL time conversion formats.
1061
The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1064
int my_TIME_to_str(const DRIZZLE_TIME *l_time, char *to)
1066
switch (l_time->time_type) {
1067
case DRIZZLE_TIMESTAMP_DATETIME:
1068
return my_datetime_to_str(l_time, to);
1069
case DRIZZLE_TIMESTAMP_DATE:
1070
return my_date_to_str(l_time, to);
1071
case DRIZZLE_TIMESTAMP_TIME:
1072
return my_time_to_str(l_time, to);
1073
case DRIZZLE_TIMESTAMP_NONE:
1074
case DRIZZLE_TIMESTAMP_ERROR:
1085
Convert datetime value specified as number to broken-down TIME
1086
representation and form value of DATETIME type as side-effect.
1089
number_to_datetime()
1090
nr - datetime value as number
1091
time_res - pointer for structure for broken-down representation
1092
flags - flags to use in validating date, as in str_to_datetime()
1094
1 If value was cut during conversion
1095
2 check_date(date,flags) considers date invalid
1098
Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1099
YYYYMMDDHHMMSS to broken-down DRIZZLE_TIME representation. Return value in
1100
YYYYMMDDHHMMSS format as side-effect.
1102
This function also checks if datetime value fits in DATETIME range.
1105
-1 Timestamp with wrong values
1106
anything else DATETIME as integer in YYYYMMDDHHMMSS format
1107
Datetime value in YYYYMMDDHHMMSS format.
1110
int64_t number_to_datetime(int64_t nr, DRIZZLE_TIME *time_res,
1111
uint32_t flags, int *was_cut)
1116
memset(time_res, 0, sizeof(*time_res));
1117
time_res->time_type=DRIZZLE_TIMESTAMP_DATE;
1119
if (nr == 0LL || nr >= 10000101000000LL)
1121
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1126
if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1128
nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1131
if (nr < (YY_PART_YEAR)*10000L+101L)
1135
nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1140
if (nr <= 99991231L)
1145
if (nr < 101000000L)
1148
time_res->time_type=DRIZZLE_TIMESTAMP_DATETIME;
1150
if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1152
nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1155
if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1157
if (nr <= 991231235959LL)
1158
nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1161
part1=(long) (nr / 1000000LL);
1162
part2=(long) (nr - (int64_t) part1 * 1000000LL);
1163
time_res->year= (int) (part1/10000L); part1%=10000L;
1164
time_res->month= (int) part1 / 100;
1165
time_res->day= (int) part1 % 100;
1166
time_res->hour= (int) (part2/10000L); part2%=10000L;
1167
time_res->minute=(int) part2 / 100;
1168
time_res->second=(int) part2 % 100;
1170
if (time_res->year <= 9999 && time_res->month <= 12 &&
1171
time_res->day <= 31 && time_res->hour <= 23 &&
1172
time_res->minute <= 59 && time_res->second <= 59 &&
1173
!check_date(time_res, (nr != 0), flags, was_cut))
1176
/* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1177
if (!nr && (flags & TIME_NO_ZERO_DATE))
1186
/* Convert time value to integer in YYYYMMDDHHMMSS format */
1188
uint64_t TIME_to_uint64_t_datetime(const DRIZZLE_TIME *my_time)
1190
return ((uint64_t) (my_time->year * 10000UL +
1191
my_time->month * 100UL +
1192
my_time->day) * 1000000ULL +
1193
(uint64_t) (my_time->hour * 10000UL +
1194
my_time->minute * 100UL +
1199
/* Convert DRIZZLE_TIME value to integer in YYYYMMDD format */
1201
static uint64_t TIME_to_uint64_t_date(const DRIZZLE_TIME *my_time)
1203
return (uint64_t) (my_time->year * 10000UL + my_time->month * 100UL +
1209
Convert DRIZZLE_TIME value to integer in HHMMSS format.
1210
This function doesn't take into account time->day member:
1211
it's assumed that days have been converted to hours already.
1214
static uint64_t TIME_to_uint64_t_time(const DRIZZLE_TIME *my_time)
1216
return (uint64_t) (my_time->hour * 10000UL +
1217
my_time->minute * 100UL +
1223
Convert struct DRIZZLE_TIME (date and time split into year/month/day/hour/...
1224
to a number in format YYYYMMDDHHMMSS (DATETIME),
1225
YYYYMMDD (DATE) or HHMMSS (TIME).
1231
The function is used when we need to convert value of time item
1232
to a number if it's used in numeric context, i. e.:
1233
SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1237
This function doesn't check that given DRIZZLE_TIME structure members are
1238
in valid range. If they are not, return value won't reflect any
1242
uint64_t TIME_to_uint64_t(const DRIZZLE_TIME *my_time)
1244
switch (my_time->time_type) {
1245
case DRIZZLE_TIMESTAMP_DATETIME:
1246
return TIME_to_uint64_t_datetime(my_time);
1247
case DRIZZLE_TIMESTAMP_DATE:
1248
return TIME_to_uint64_t_date(my_time);
1249
case DRIZZLE_TIMESTAMP_TIME:
1250
return TIME_to_uint64_t_time(my_time);
1251
case DRIZZLE_TIMESTAMP_NONE:
1252
case DRIZZLE_TIMESTAMP_ERROR:
1260
} /* namespace drizzled */