1
/* Copyright (C) 2004 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 */
17
Most of the following code and structures were derived from
18
public domain code from ftp://elsie.nci.nih.gov/pub
19
(We will refer to this code as to elsie-code further.)
23
We should not include mysql_priv.h in mysql_tzinfo_to_sql utility since
24
it creates unsolved link dependencies on some platforms.
27
#ifdef USE_PRAGMA_IMPLEMENTATION
28
#pragma implementation // gcc: Class implementation
31
#include <my_global.h>
32
#if !defined(TZINFO2SQL) && !defined(TESTTIME)
33
#include "mysql_priv.h"
45
Now we don't use abbreviations in server but we will do this in future.
47
#if defined(TZINFO2SQL) || defined(TESTTIME)
50
#if !defined(DBUG_OFF)
51
/* Let use abbreviations for debug purposes */
54
#endif /* !defined(DBUG_OFF) */
55
#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
57
/* Structure describing local time type (e.g. Moscow summer time (MSD)) */
60
long tt_gmtoff; // Offset from UTC in seconds
61
uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst
63
uint tt_abbrind; // Index of start of abbreviation for this time type.
66
We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
67
struct since we don't support POSIX-style TZ descriptions in variables.
71
/* Structure describing leap-second corrections. */
74
my_time_t ls_trans; // Transition time
75
long ls_corr; // Correction to apply
79
Structure with information describing ranges of my_time_t shifted to local
80
time (my_time_t + offset). Used for local MYSQL_TIME -> my_time_t conversion.
81
See comments for TIME_to_gmt_sec() for more info.
83
typedef struct revtinfo
85
long rt_offset; // Offset of local time from UTC in seconds
86
uint rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap
90
#define MY_TZNAME_MAX TZNAME_MAX
93
#define MY_TZNAME_MAX 255
97
Structure which fully describes time zone which is
98
described in our db or in zoneinfo files.
100
typedef struct st_time_zone_info
102
uint leapcnt; // Number of leap-second corrections
103
uint timecnt; // Number of transitions between time types
104
uint typecnt; // Number of local time types
105
uint charcnt; // Number of characters used for abbreviations
106
uint revcnt; // Number of transition descr. for TIME->my_time_t conversion
107
/* The following are dynamical arrays are allocated in MEM_ROOT */
108
my_time_t *ats; // Times of transitions between time types
109
uchar *types; // Local time types for transitions
110
TRAN_TYPE_INFO *ttis; // Local time types descriptions
112
/* Storage for local time types abbreviations. They are stored as ASCIIZ */
116
Leap seconds corrections descriptions, this array is shared by
117
all time zones who use leap seconds.
121
Starting points and descriptions of shifted my_time_t (my_time_t + offset)
122
ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
123
Used for tm -> my_time_t conversion.
128
Time type which is used for times smaller than first transition or if
129
there are no transitions at all.
131
TRAN_TYPE_INFO *fallback_tti;
136
static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);
139
#if defined(TZINFO2SQL) || defined(TESTTIME)
142
Load time zone description from zoneinfo (TZinfo) file.
146
name - path to zoneinfo file
147
sp - TIME_ZONE_INFO structure to fill
154
tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
161
if (!(file= my_fopen(name, O_RDONLY|O_BINARY, MYF(MY_WME))))
166
struct tzhead tzhead;
167
uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
168
TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
170
max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
172
sizeof(LS_INFO) * TZ_MAX_LEAPS];
178
read_from_file= my_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
180
if (my_fclose(file, MYF(MY_WME)) != 0)
183
if (read_from_file < (int)sizeof(struct tzhead))
186
ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt);
187
ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt);
188
sp->leapcnt= int4net(u.tzhead.tzh_leapcnt);
189
sp->timecnt= int4net(u.tzhead.tzh_timecnt);
190
sp->typecnt= int4net(u.tzhead.tzh_typecnt);
191
sp->charcnt= int4net(u.tzhead.tzh_charcnt);
192
p= u.tzhead.tzh_charcnt + sizeof(u.tzhead.tzh_charcnt);
193
if (sp->leapcnt > TZ_MAX_LEAPS ||
194
sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES ||
195
sp->timecnt > TZ_MAX_TIMES ||
196
sp->charcnt > TZ_MAX_CHARS ||
197
(ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
198
(ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
200
if ((uint)(read_from_file - (p - u.buf)) <
201
sp->timecnt * 4 + /* ats */
202
sp->timecnt + /* types */
203
sp->typecnt * (4 + 2) + /* ttinfos */
204
sp->charcnt + /* chars */
205
sp->leapcnt * (4 + 4) + /* lsinfos */
206
ttisstdcnt + /* ttisstds */
207
ttisgmtcnt) /* ttisgmts */
210
if (!(tzinfo_buf= (char *)alloc_root(storage,
211
ALIGN_SIZE(sp->timecnt *
213
ALIGN_SIZE(sp->timecnt) +
214
ALIGN_SIZE(sp->typecnt *
215
sizeof(TRAN_TYPE_INFO)) +
217
ALIGN_SIZE(sp->charcnt) +
219
sp->leapcnt * sizeof(LS_INFO))))
222
sp->ats= (my_time_t *)tzinfo_buf;
223
tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
224
sp->types= (uchar *)tzinfo_buf;
225
tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
226
sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
227
tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
229
sp->chars= tzinfo_buf;
230
tzinfo_buf+= ALIGN_SIZE(sp->charcnt);
232
sp->lsis= (LS_INFO *)tzinfo_buf;
234
for (i= 0; i < sp->timecnt; i++, p+= 4)
235
sp->ats[i]= int4net(p);
237
for (i= 0; i < sp->timecnt; i++)
239
sp->types[i]= (uchar) *p++;
240
if (sp->types[i] >= sp->typecnt)
243
for (i= 0; i < sp->typecnt; i++)
245
TRAN_TYPE_INFO * ttisp;
248
ttisp->tt_gmtoff= int4net(p);
250
ttisp->tt_isdst= (uchar) *p++;
251
if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
253
ttisp->tt_abbrind= (uchar) *p++;
254
if (ttisp->tt_abbrind > sp->charcnt)
257
for (i= 0; i < sp->charcnt; i++)
259
sp->chars[i]= '\0'; /* ensure '\0' at end */
260
for (i= 0; i < sp->leapcnt; i++)
265
lsisp->ls_trans= int4net(p);
267
lsisp->ls_corr= int4net(p);
271
Since we don't support POSIX style TZ definitions in variables we
272
don't read further like glibc or elsie code.
276
return prepare_tz_info(sp, storage);
278
#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
282
Finish preparation of time zone description for use in TIME_to_gmt_sec()
283
and gmt_sec_to_TIME() functions.
287
sp - pointer to time zone description
288
storage - pointer to MEM_ROOT where arrays for map allocated
291
First task of this function is to find fallback time type which will
292
be used if there are no transitions or we have moment in time before
294
Second task is to build "shifted my_time_t" -> my_time_t map used in
295
MYSQL_TIME -> my_time_t conversion.
296
Note: See description of TIME_to_gmt_sec() function first.
297
In order to perform MYSQL_TIME -> my_time_t conversion we need to build table
298
which defines "shifted by tz offset and leap seconds my_time_t" ->
299
my_time_t function wich is almost the same (except ranges of ambiguity)
300
as reverse function to piecewise linear function used for my_time_t ->
301
"shifted my_time_t" conversion and which is also specified as table in
302
zoneinfo file or in our db (It is specified as start of time type ranges
303
and time type offsets). So basic idea is very simple - let us iterate
304
through my_time_t space from one point of discontinuity of my_time_t ->
305
"shifted my_time_t" function to another and build our approximation of
306
reverse function. (Actually we iterate through ranges on which
307
my_time_t -> "shifted my_time_t" is linear function).
314
prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
316
my_time_t cur_t= MY_TIME_T_MIN;
317
my_time_t cur_l, end_t, end_l;
318
my_time_t cur_max_seen_l= MY_TIME_T_MIN;
319
long cur_offset, cur_corr, cur_off_and_corr;
320
uint next_trans_idx, next_leap_idx;
323
Temporary arrays where we will store tables. Needed because
324
we don't know table sizes ahead. (Well we can estimate their
325
upper bound but this will take extra space.)
327
my_time_t revts[TZ_MAX_REV_RANGES];
328
REVT_INFO revtis[TZ_MAX_REV_RANGES];
331
Let us setup fallback time type which will be used if we have not any
332
transitions or if we have moment of time before first transition.
333
We will find first non-DST local time type and use it (or use first
334
local time type if all of them are DST types).
336
for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
338
if (i == sp->typecnt)
340
sp->fallback_tti= &(sp->ttis[i]);
344
Let us build shifted my_time_t -> my_time_t map.
348
/* Let us find initial offset */
349
if (sp->timecnt == 0 || cur_t < sp->ats[0])
352
If we have not any transitions or t is before first transition we are using
353
already found fallback time type which index is already in i.
359
/* cur_t == sp->ats[0] so we found transition */
364
cur_offset= sp->ttis[i].tt_gmtoff;
367
/* let us find leap correction... unprobable, but... */
368
for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
369
cur_t >= sp->lsis[next_leap_idx].ls_trans;
373
if (next_leap_idx > 0)
374
cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
378
/* Iterate trough t space */
379
while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
381
cur_off_and_corr= cur_offset - cur_corr;
384
We assuming that cur_t could be only overflowed downwards,
385
we also assume that end_t won't be overflowed in this case.
387
if (cur_off_and_corr < 0 &&
388
cur_t < MY_TIME_T_MIN - cur_off_and_corr)
389
cur_t= MY_TIME_T_MIN - cur_off_and_corr;
391
cur_l= cur_t + cur_off_and_corr;
394
Let us choose end_t as point before next time type change or leap
397
end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
399
(next_leap_idx < sp->leapcnt) ?
400
sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
402
again assuming that end_t can be overlowed only in positive side
403
we also assume that end_t won't be overflowed in this case.
405
if (cur_off_and_corr > 0 &&
406
end_t > MY_TIME_T_MAX - cur_off_and_corr)
407
end_t= MY_TIME_T_MAX - cur_off_and_corr;
409
end_l= end_t + cur_off_and_corr;
412
if (end_l > cur_max_seen_l)
414
/* We want special handling in the case of first range */
415
if (cur_max_seen_l == MY_TIME_T_MIN)
417
revts[sp->revcnt]= cur_l;
418
revtis[sp->revcnt].rt_offset= cur_off_and_corr;
419
revtis[sp->revcnt].rt_type= 0;
421
cur_max_seen_l= end_l;
425
if (cur_l > cur_max_seen_l + 1)
427
/* We have a spring time-gap and we are not at the first range */
428
revts[sp->revcnt]= cur_max_seen_l + 1;
429
revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
430
revtis[sp->revcnt].rt_type= 1;
432
if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
433
break; /* That was too much */
434
cur_max_seen_l= cur_l - 1;
437
/* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */
439
revts[sp->revcnt]= cur_max_seen_l + 1;
440
revtis[sp->revcnt].rt_offset= cur_off_and_corr;
441
revtis[sp->revcnt].rt_type= 0;
443
cur_max_seen_l= end_l;
447
if (end_t == MY_TIME_T_MAX ||
448
(cur_off_and_corr > 0) &&
449
(end_t >= MY_TIME_T_MAX - cur_off_and_corr))
456
Let us find new offset and correction. Because of our choice of end_t
457
cur_t can only be point where new time type starts or/and leap
458
correction is performed.
460
if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
461
if (next_trans_idx < sp->timecnt &&
462
cur_t == sp->ats[next_trans_idx])
464
/* We are at offset point */
465
cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
469
if (next_leap_idx < sp->leapcnt &&
470
cur_t == sp->lsis[next_leap_idx].ls_trans)
472
/* we are at leap point */
473
cur_corr= sp->lsis[next_leap_idx].ls_corr;
478
/* check if we have had enough space */
479
if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
482
/* set maximum end_l as finisher */
483
revts[sp->revcnt]= end_l;
485
/* Allocate arrays of proper size in sp and copy result there */
486
if (!(sp->revts= (my_time_t *)alloc_root(storage,
487
sizeof(my_time_t) * (sp->revcnt + 1))) ||
488
!(sp->revtis= (REVT_INFO *)alloc_root(storage,
489
sizeof(REVT_INFO) * sp->revcnt)))
492
memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
493
memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
499
#if !defined(TZINFO2SQL)
501
static const uint mon_lengths[2][MONS_PER_YEAR]=
503
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
504
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
507
static const uint mon_starts[2][MONS_PER_YEAR]=
509
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
510
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
513
static const uint year_lengths[2]=
515
DAYS_PER_NYEAR, DAYS_PER_LYEAR
518
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
522
Converts time from my_time_t representation (seconds in UTC since Epoch)
523
to broken down representation using given local time zone offset.
527
tmp - pointer to structure for broken down representation
528
t - my_time_t value to be converted
529
offset - local time zone offset
532
Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
533
(from elsie code) because doesn't contain any leap correction and
534
TM_GMTOFF and is_dst setting and contains some MySQL specific
535
initialization. Funny but with removing of these we almost have
536
glibc's offtime function.
539
sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
547
days= (long) (t / SECS_PER_DAY);
548
rem= (long) (t % SECS_PER_DAY);
551
We do this as separate step after dividing t, because this
552
allows us handle times near my_time_t bounds without overflows.
560
while (rem >= SECS_PER_DAY)
565
tmp->hour= (uint)(rem / SECS_PER_HOUR);
566
rem= rem % SECS_PER_HOUR;
567
tmp->minute= (uint)(rem / SECS_PER_MIN);
569
A positive leap second requires a special
570
representation. This uses "... ??:59:60" et seq.
572
tmp->second= (uint)(rem % SECS_PER_MIN);
575
while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
579
newy= y + days / DAYS_PER_NYEAR;
582
days-= (newy - y) * DAYS_PER_NYEAR +
583
LEAPS_THRU_END_OF(newy - 1) -
584
LEAPS_THRU_END_OF(y - 1);
589
ip= mon_lengths[yleap];
590
for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
591
days= days - (long) ip[tmp->month];
593
tmp->day= (uint)(days + 1);
595
/* filling MySQL specific MYSQL_TIME members */
596
tmp->neg= 0; tmp->second_part= 0;
597
tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
602
Find time range wich contains given my_time_t value
606
t - my_time_t value for which we looking for range
607
range_boundaries - sorted array of range starts.
608
higher_bound - number of ranges
611
Performs binary search for range which contains given my_time_t value.
612
It has sense if number of ranges is greater than zero and my_time_t value
613
is greater or equal than beginning of first range. It also assumes that
614
t belongs to some range specified or end of last is MY_TIME_T_MAX.
616
With this localtime_r on real data may takes less time than with linear
617
search (I've seen 30% speed up).
620
Index of range to which t belongs
623
find_time_range(my_time_t t, const my_time_t *range_boundaries,
626
uint i, lower_bound= 0;
629
Function will work without this assertion but result would be meaningless.
631
DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
634
Do binary search for minimal interval which contain t. We preserve:
635
range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
636
invariant and decrease this higher_bound - lower_bound gap twice
640
while (higher_bound - lower_bound > 1)
642
i= (lower_bound + higher_bound) >> 1;
643
if (range_boundaries[i] <= t)
652
Find local time transition for given my_time_t.
655
find_transition_type()
656
t - my_time_t value to be converted
657
sp - pointer to struct with time zone description
660
Pointer to structure in time zone description describing
661
local time type for given my_time_t.
664
const TRAN_TYPE_INFO *
665
find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
667
if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
670
If we have not any transitions or t is before first transition let
671
us use fallback time type.
673
return sp->fallback_tti;
677
Do binary search for minimal interval between transitions which
678
contain t. With this localtime_r on real data may takes less
679
time than with linear search (I've seen 30% speed up).
681
return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
686
Converts time in my_time_t representation (seconds in UTC since Epoch) to
687
broken down MYSQL_TIME representation in local time zone.
691
tmp - pointer to structure for broken down represenatation
692
sec_in_utc - my_time_t value to be converted
693
sp - pointer to struct with time zone description
696
We can improve this function by creating joined array of transitions and
697
leap corrections. This will require adding extra field to TRAN_TYPE_INFO
698
for storing number of "extra" seconds to minute occured due to correction
699
(60th and 61st second, look how we calculate them as "hit" in this
701
Under realistic assumptions about frequency of transitions the same array
702
can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
703
implement tweaked binary search which will take into account that some
704
MYSQL_TIME has two matching my_time_t ranges and some of them have none.
707
gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
709
const TRAN_TYPE_INFO *ttisp;
716
Find proper transition (and its local time type) for our sec_in_utc value.
717
Funny but again by separating this step in function we receive code
718
which very close to glibc's code. No wonder since they obviously use
719
the same base and all steps are sensible.
721
ttisp= find_transition_type(sec_in_utc, sp);
724
Let us find leap correction for our sec_in_utc value and number of extra
725
secs to add to this minute.
726
This loop is rarely used because most users will use time zones without
727
leap seconds, and even in case when we have such time zone there won't
728
be many iterations (we have about 22 corrections at this moment (2004)).
730
for ( i= sp->leapcnt; i-- > 0; )
733
if (sec_in_utc >= lp->ls_trans)
735
if (sec_in_utc == lp->ls_trans)
737
hit= ((i == 0 && lp->ls_corr > 0) ||
738
lp->ls_corr > sp->lsis[i - 1].ls_corr);
742
sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
743
sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
755
sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
762
Converts local time in broken down representation to local
763
time zone analog of my_time_t represenation.
767
year, mon, mday, hour, min, sec - broken down representation.
770
Converts time in broken down representation to my_time_t representation
771
ignoring time zone. Note that we cannot convert back some valid _local_
772
times near ends of my_time_t range because of my_time_t overflow. But we
773
ignore this fact now since MySQL will never pass such argument.
776
Seconds since epoch time representation.
779
sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
781
/* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
782
DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
783
#ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
785
It turns out that only whenever month is normalized or unnormalized
788
DBUG_ASSERT(mon > 0 && mon < 13);
789
long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
790
LEAPS_THRU_END_OF(year - 1) -
791
LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
792
days+= mon_starts[isleap(year)][mon - 1];
794
long norm_month= (mon - 1) % MONS_PER_YEAR;
795
long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
796
long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
797
LEAPS_THRU_END_OF(a_year - 1) -
798
LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
799
days+= mon_starts[isleap(a_year)]
800
[norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
804
return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
809
Converts local time in broken down MYSQL_TIME representation to my_time_t
814
t - pointer to structure for broken down represenatation
815
sp - pointer to struct with time zone description
816
in_dst_time_gap - pointer to bool which is set to true if datetime
817
value passed doesn't really exist (i.e. falls into
818
spring time-gap) and is not touched otherwise.
821
This is mktime analog for MySQL. It is essentially different
822
from mktime (or hypotetical my_mktime) because:
823
- It has no idea about tm_isdst member so if it
824
has two answers it will give the smaller one
825
- If we are in spring time gap then it will return
827
- It can give wrong results near the ends of my_time_t due to
828
overflows, but we are safe since in MySQL we will never
829
call this function for such dates (its restriction for year
830
between 1970 and 2038 gives us several days of reserve).
831
- By default it doesn't support un-normalized input. But if
832
sec_since_epoch() function supports un-normalized dates
833
then this function should handle un-normalized input right,
834
altough it won't normalize structure TIME.
836
Traditional approach to problem of conversion from broken down
837
representation to time_t is iterative. Both elsie's and glibc
838
implementation try to guess what time_t value should correspond to
839
this broken-down value. They perform localtime_r function on their
840
guessed value and then calculate the difference and try to improve
841
their guess. Elsie's code guesses time_t value in bit by bit manner,
842
Glibc's code tries to add difference between broken-down value
843
corresponding to guess and target broken-down value to current guess.
844
It also uses caching of last found correction... So Glibc's approach
845
is essentially faster but introduces some undetermenism (in case if
846
is_dst member of broken-down representation (tm struct) is not known
847
and we have two possible answers).
849
We use completely different approach. It is better since it is both
850
faster than iterative implementations and fully determenistic. If you
851
look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
853
The first is calculating shifted my_time_t value and the second - TIME
854
calculation from shifted my_time_t value (well it is a bit simplified
855
picture). The part in which we are interested in is my_time_t -> shifted
856
my_time_t conversion. It is piecewise linear function which is defined
857
by combination of transition times as break points and times offset
858
as changing function parameter. The possible inverse function for this
859
converison would be ambiguos but with MySQL's restrictions we can use
860
some function which is the same as inverse function on unambigiuos
861
ranges and coincides with one of branches of inverse function in
862
other ranges. Thus we just need to build table which will determine
863
this shifted my_time_t -> my_time_t conversion similar to existing
864
(my_time_t -> shifted my_time_t table). We do this in
865
prepare_tz_info function.
868
If we can even more improve this function. For doing this we will need to
869
build joined map of transitions and leap corrections for gmt_sec_to_TIME()
870
function (similar to revts/revtis). Under realistic assumptions about
871
frequency of transitions we can use the same array for TIME_to_gmt_sec().
872
We need to implement special version of binary search for this. Such step
873
will be beneficial to CPU cache since we will decrease data-set used for
877
Seconds in UTC since Epoch.
881
TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp,
882
my_bool *in_dst_time_gap)
889
DBUG_ENTER("TIME_to_gmt_sec");
891
if (!validate_timestamp_range(t))
895
/* We need this for correct leap seconds handling */
896
if (t->second < SECS_PER_MIN)
899
saved_seconds= t->second;
902
NOTE: to convert full my_time_t range we do a shift of the
903
boundary dates here to avoid overflow of my_time_t.
904
We use alike approach in my_system_gmt_sec().
906
However in that function we also have to take into account
907
overflow near 0 on some platforms. That's because my_system_gmt_sec
908
uses localtime_r(), which doesn't work with negative values correctly
909
on platforms with unsigned time_t (QNX). Here we don't use localtime()
910
=> we negative values of local_t are ok.
913
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
916
We will pass (t->day - shift) to sec_since_epoch(), and
917
want this value to be a positive number, so we shift
918
only dates > 4.01.2038 (to avoid owerflow).
924
local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
926
saved_seconds ? 0 : t->second);
928
/* We have at least one range */
929
DBUG_ASSERT(sp->revcnt >= 1);
931
if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
934
This means that source time can't be represented as my_time_t due to
935
limited my_time_t range.
940
/* binary search for our range */
941
i= find_time_range(local_t, sp->revts, sp->revcnt);
944
As there are no offset switches at the end of TIMESTAMP range,
945
we could simply check for overflow here (and don't need to bother
950
if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
951
sp->revtis[i].rt_offset - saved_seconds))
953
DBUG_RETURN(0); /* my_time_t overflow */
955
local_t+= shift * SECS_PER_DAY;
958
if (sp->revtis[i].rt_type)
961
Oops! We are in spring time gap.
962
May be we should return error here?
963
Now we are returning my_time_t value corresponding to the
964
beginning of the gap.
967
local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
970
local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
972
/* check for TIMESTAMP_MAX_VALUE was already done above */
973
if (local_t < TIMESTAMP_MIN_VALUE)
976
DBUG_RETURN(local_t);
981
End of elsie derived code.
983
#endif /* !defined(TZINFO2SQL) */
986
#if !defined(TESTTIME) && !defined(TZINFO2SQL)
989
String with names of SYSTEM time zone.
991
static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
995
Instance of this class represents local time zone used on this system
996
(specified by TZ environment variable or via any other system mechanism).
997
It uses system functions (localtime_r, my_system_gmt_sec) for conversion
998
and is always available. Because of this it is used by default - if there
999
were no explicit time zone specified. On the other hand because of this
1000
conversion methods provided by this class is significantly slower and
1001
possibly less multi-threaded-friendly than corresponding Time_zone_db
1002
methods so the latter should be preffered there it is possible.
1004
class Time_zone_system : public Time_zone
1007
Time_zone_system() {} /* Remove gcc warning */
1008
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1009
my_bool *in_dst_time_gap) const;
1010
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1011
virtual const String * get_name() const;
1016
Converts local time in system time zone in MYSQL_TIME representation
1017
to its my_time_t representation.
1021
t - pointer to MYSQL_TIME structure with local time in
1022
broken-down representation.
1023
in_dst_time_gap - pointer to bool which is set to true if datetime
1024
value passed doesn't really exist (i.e. falls into
1025
spring time-gap) and is not touched otherwise.
1028
This method uses system function (localtime_r()) for conversion
1029
local time in system time zone in MYSQL_TIME structure to its my_time_t
1030
representation. Unlike the same function for Time_zone_db class
1031
it it won't handle unnormalized input properly. Still it will
1032
return lowest possible my_time_t in case of ambiguity or if we
1033
provide time corresponding to the time-gap.
1035
You should call init_time() function before using this function.
1038
Corresponding my_time_t value or 0 in case of error
1041
Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1044
return my_system_gmt_sec(t, ¬_used, in_dst_time_gap);
1049
Converts time from UTC seconds since Epoch (my_time_t) representation
1050
to system local time zone broken-down representation.
1054
tmp - pointer to MYSQL_TIME structure to fill-in
1055
t - my_time_t value to be converted
1058
We assume that value passed to this function will fit into time_t range
1059
supported by localtime_r. This conversion is putting restriction on
1060
TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
1061
for interaction with client then we can extend TIMESTAMP range down to
1065
Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1068
time_t tmp_t= (time_t)t;
1070
localtime_r(&tmp_t, &tmp_tm);
1071
localtime_to_TIME(tmp, &tmp_tm);
1072
tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1077
Get name of time zone
1083
Name of time zone as String
1086
Time_zone_system::get_name() const
1088
return &tz_SYSTEM_name;
1093
Instance of this class represents UTC time zone. It uses system gmtime_r
1094
function for conversions and is always available. It is used only for
1095
my_time_t -> MYSQL_TIME conversions in various UTC_... functions, it is not
1096
intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
1098
class Time_zone_utc : public Time_zone
1101
Time_zone_utc() {} /* Remove gcc warning */
1102
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1103
my_bool *in_dst_time_gap) const;
1104
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1105
virtual const String * get_name() const;
1110
Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
1114
t - pointer to MYSQL_TIME structure with local time
1115
in broken-down representation.
1116
in_dst_time_gap - pointer to bool which is set to true if datetime
1117
value passed doesn't really exist (i.e. falls into
1118
spring time-gap) and is not touched otherwise.
1121
Since Time_zone_utc is used only internally for my_time_t -> TIME
1122
conversions, this function of Time_zone interface is not implemented for
1123
this class and should not be called.
1129
Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1131
/* Should be never called */
1138
Converts time from UTC seconds since Epoch (my_time_t) representation
1139
to broken-down representation (also in UTC).
1143
tmp - pointer to MYSQL_TIME structure to fill-in
1144
t - my_time_t value to be converted
1147
See note for apropriate Time_zone_system method.
1150
Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1153
time_t tmp_t= (time_t)t;
1154
gmtime_r(&tmp_t, &tmp_tm);
1155
localtime_to_TIME(tmp, &tmp_tm);
1156
tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1161
Get name of time zone
1167
Since Time_zone_utc is used only internally by SQL's UTC_* functions it
1168
is not accessible directly, and hence this function of Time_zone
1169
interface is not implemented for this class and should not be called.
1175
Time_zone_utc::get_name() const
1177
/* Should be never called */
1184
Instance of this class represents some time zone which is
1185
described in mysql.time_zone family of tables.
1187
class Time_zone_db : public Time_zone
1190
Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
1191
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1192
my_bool *in_dst_time_gap) const;
1193
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1194
virtual const String * get_name() const;
1196
TIME_ZONE_INFO *tz_info;
1197
const String *tz_name;
1202
Initializes object representing time zone described by mysql.time_zone
1207
tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
1208
according to db or other time zone description
1209
(for example by my_tz_init()).
1210
Several Time_zone_db instances can share one
1211
TIME_ZONE_INFO structure.
1212
tz_name_arg - name of time zone.
1214
Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
1215
const String *tz_name_arg):
1216
tz_info(tz_info_arg), tz_name(tz_name_arg)
1222
Converts local time in time zone described from TIME
1223
representation to its my_time_t representation.
1227
t - pointer to MYSQL_TIME structure with local time
1228
in broken-down representation.
1229
in_dst_time_gap - pointer to bool which is set to true if datetime
1230
value passed doesn't really exist (i.e. falls into
1231
spring time-gap) and is not touched otherwise.
1234
Please see ::TIME_to_gmt_sec for function description and
1235
parameter restrictions.
1238
Corresponding my_time_t value or 0 in case of error
1241
Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1243
return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
1248
Converts time from UTC seconds since Epoch (my_time_t) representation
1249
to local time zone described in broken-down representation.
1253
tmp - pointer to MYSQL_TIME structure to fill-in
1254
t - my_time_t value to be converted
1257
Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1259
::gmt_sec_to_TIME(tmp, t, tz_info);
1264
Get name of time zone
1270
Name of time zone as ASCIIZ-string
1273
Time_zone_db::get_name() const
1280
Instance of this class represents time zone which
1281
was specified as offset from UTC.
1283
class Time_zone_offset : public Time_zone
1286
Time_zone_offset(long tz_offset_arg);
1287
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1288
my_bool *in_dst_time_gap) const;
1289
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1290
virtual const String * get_name() const;
1292
This have to be public because we want to be able to access it from
1293
my_offset_tzs_get_key() function
1297
/* Extra reserve because of snprintf */
1298
char name_buff[7+16];
1304
Initializes object representing time zone described by its offset from UTC.
1308
tz_offset_arg - offset from UTC in seconds.
1309
Positive for direction to east.
1311
Time_zone_offset::Time_zone_offset(long tz_offset_arg):
1312
offset(tz_offset_arg)
1314
uint hours= abs((int)(offset / SECS_PER_HOUR));
1315
uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
1316
ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
1317
(offset>=0) ? "+" : "-", hours, minutes);
1318
name.set(name_buff, length, &my_charset_latin1);
1323
Converts local time in time zone described as offset from UTC
1324
from MYSQL_TIME representation to its my_time_t representation.
1328
t - pointer to MYSQL_TIME structure with local time
1329
in broken-down representation.
1330
in_dst_time_gap - pointer to bool which should be set to true if
1331
datetime value passed doesn't really exist
1332
(i.e. falls into spring time-gap) and is not
1334
It is not really used in this class.
1337
Corresponding my_time_t value or 0 in case of error
1340
Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1346
Check timestamp range.we have to do this as calling function relies on
1347
us to make all validation checks here.
1349
if (!validate_timestamp_range(t))
1353
Do a temporary shift of the boundary dates to avoid
1354
overflow of my_time_t if the time value is near it's
1357
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
1360
local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
1361
t->hour, t->minute, t->second) -
1366
/* Add back the shifted time */
1367
local_t+= shift * SECS_PER_DAY;
1370
if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
1379
Converts time from UTC seconds since Epoch (my_time_t) representation
1380
to local time zone described as offset from UTC and in broken-down
1385
tmp - pointer to MYSQL_TIME structure to fill-in
1386
t - my_time_t value to be converted
1389
Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1391
sec_to_TIME(tmp, t, offset);
1396
Get name of time zone
1402
Name of time zone as pointer to String object
1405
Time_zone_offset::get_name() const
1411
static Time_zone_utc tz_UTC;
1412
static Time_zone_system tz_SYSTEM;
1413
static Time_zone_offset tz_OFFSET0(0);
1415
Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1416
Time_zone *my_tz_UTC= &tz_UTC;
1417
Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1419
static HASH tz_names;
1420
static HASH offset_tzs;
1421
static MEM_ROOT tz_storage;
1424
These mutex protects offset_tzs and tz_storage.
1425
These protection needed only when we are trying to set
1426
time zone which is specified as offset, and searching for existing
1427
time zone in offset_tzs or creating if it didn't existed before in
1428
tz_storage. So contention is low.
1430
static pthread_mutex_t tz_LOCK;
1431
static bool tz_inited= 0;
1434
This two static variables are inteded for holding info about leap seconds
1435
shared by all time zones.
1437
static uint tz_leapcnt= 0;
1438
static LS_INFO *tz_lsis= 0;
1441
Shows whenever we have found time zone tables during start-up.
1442
Used for avoiding of putting those tables to global table list
1443
for queries that use time zone info.
1445
static bool time_zone_tables_exist= 1;
1449
Names of tables (with their lengths) that are needed
1450
for dynamical loading of time zone descriptions.
1453
static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
1455
{ C_STRING_WITH_LEN("time_zone_name")},
1456
{ C_STRING_WITH_LEN("time_zone")},
1457
{ C_STRING_WITH_LEN("time_zone_transition_type")},
1458
{ C_STRING_WITH_LEN("time_zone_transition")}
1461
/* Name of database to which those tables belong. */
1463
static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
1466
class Tz_names_entry: public Sql_alloc
1475
We are going to call both of these functions from C code so
1476
they should obey C calling conventions.
1480
my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
1481
my_bool not_used __attribute__((unused)))
1483
*length= entry->name.length();
1484
return (uchar*) entry->name.ptr();
1488
my_offset_tzs_get_key(Time_zone_offset *entry,
1490
my_bool not_used __attribute__((unused)))
1492
*length= sizeof(long);
1493
return (uchar*) &entry->offset;
1498
Prepare table list with time zone related tables from preallocated array.
1501
tz_init_table_list()
1502
tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT
1506
This function prepares list of TABLE_LIST objects which can be used
1507
for opening of time zone tables from preallocated array.
1511
tz_init_table_list(TABLE_LIST *tz_tabs)
1513
bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT);
1515
for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
1517
tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str;
1518
tz_tabs[i].table_name_length= tz_tables_names[i].length;
1519
tz_tabs[i].db= tz_tables_db_name.str;
1520
tz_tabs[i].db_length= tz_tables_db_name.length;
1521
tz_tabs[i].lock_type= TL_READ;
1523
if (i != MY_TZ_TABLES_COUNT - 1)
1524
tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
1526
tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
1532
Initialize time zone support infrastructure.
1536
thd - current thread object
1537
default_tzname - default time zone or 0 if none.
1538
bootstrap - indicates whenever we are in bootstrap mode
1541
This function will init memory structures needed for time zone support,
1542
it will register mandatory SYSTEM time zone in them. It will try to open
1543
mysql.time_zone* tables and load information about default time zone and
1544
information which further will be shared among all time zones loaded.
1545
If system tables with time zone descriptions don't exist it won't fail
1546
(unless default_tzname is time zone from tables). If bootstrap parameter
1547
is true then this routine assumes that we are in bootstrap mode and won't
1548
load time zone descriptions unless someone specifies default time zone
1549
which is supposedly stored in those tables.
1550
It'll also set default time zone if it is specified.
1557
my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
1560
TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
1561
Open_tables_state open_tables_state_backup;
1563
Tz_names_entry *tmp_tzname;
1564
my_bool return_val= 1;
1567
DBUG_ENTER("my_tz_init");
1570
To be able to run this from boot, we allocate a temporary THD
1572
if (!(thd= new THD))
1574
thd->thread_stack= (char*) &thd;
1575
thd->store_globals();
1578
/* Init all memory structures that require explicit destruction */
1579
if (hash_init(&tz_names, &my_charset_latin1, 20,
1580
0, 0, (hash_get_key) my_tz_names_get_key, 0, 0))
1582
sql_print_error("Fatal error: OOM while initializing time zones");
1585
if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
1586
(hash_get_key)my_offset_tzs_get_key, 0, 0))
1588
sql_print_error("Fatal error: OOM while initializing time zones");
1589
hash_free(&tz_names);
1592
init_sql_alloc(&tz_storage, 32 * 1024, 0);
1593
VOID(pthread_mutex_init(&tz_LOCK, MY_MUTEX_INIT_FAST));
1596
/* Add 'SYSTEM' time zone to tz_names hash */
1597
if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()))
1599
sql_print_error("Fatal error: OOM while initializing time zones");
1600
goto end_with_cleanup;
1602
tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
1603
tmp_tzname->tz= my_tz_SYSTEM;
1604
if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
1606
sql_print_error("Fatal error: OOM while initializing time zones");
1607
goto end_with_cleanup;
1612
/* If we are in bootstrap mode we should not load time zone tables */
1613
return_val= time_zone_tables_exist= 0;
1614
goto end_with_setting_default_tz;
1618
After this point all memory structures are inited and we even can live
1619
without time zone description tables. Now try to load information about
1620
leap seconds shared by all time zones.
1623
thd->set_db(db, sizeof(db)-1);
1624
bzero((char*) &tz_tables[0], sizeof(TABLE_LIST));
1625
tz_tables[0].alias= tz_tables[0].table_name=
1626
(char*)"time_zone_leap_second";
1627
tz_tables[0].table_name_length= 21;
1628
tz_tables[0].db= db;
1629
tz_tables[0].db_length= sizeof(db)-1;
1630
tz_tables[0].lock_type= TL_READ;
1632
tz_init_table_list(tz_tables+1);
1633
tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
1634
tz_tables[1].prev_global= &tz_tables[0].next_global;
1637
We need to open only mysql.time_zone_leap_second, but we try to
1638
open all time zone tables to see if they exist.
1640
if (open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup))
1642
sql_print_warning("Can't open and lock time zone table: %s "
1643
"trying to live without them", thd->main_da.message());
1644
/* We will try emulate that everything is ok */
1645
return_val= time_zone_tables_exist= 0;
1646
goto end_with_setting_default_tz;
1650
Now we are going to load leap seconds descriptions that are shared
1651
between all time zones that use them. We are using index for getting
1652
records in proper order. Since we share the same MEM_ROOT between
1653
all time zones we just allocate enough memory for it first.
1655
if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
1656
sizeof(LS_INFO) * TZ_MAX_LEAPS)))
1658
sql_print_error("Fatal error: Out of memory while loading "
1659
"mysql.time_zone_leap_second table");
1660
goto end_with_close;
1663
table= tz_tables[0].table;
1665
It is OK to ignore ha_index_init()/ha_index_end() return values since
1666
mysql.time_zone* tables are MyISAM and these operations always succeed
1669
(void)table->file->ha_index_init(0, 1);
1670
table->use_all_columns();
1674
res= table->file->index_first(table->record[0]);
1678
if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
1680
sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
1681
" table: too much leaps");
1682
table->file->ha_index_end();
1683
goto end_with_close;
1686
tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
1687
tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();
1692
("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld",
1693
tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
1694
tz_lsis[tz_leapcnt-1].ls_corr));
1696
res= table->file->index_next(table->record[0]);
1699
(void)table->file->ha_index_end();
1701
if (res != HA_ERR_END_OF_FILE)
1703
sql_print_error("Fatal error: Error while loading "
1704
"mysql.time_zone_leap_second table");
1705
goto end_with_close;
1709
Loading of info about leap seconds succeeded
1715
end_with_setting_default_tz:
1716
/* If we have default time zone try to load it */
1719
String tmp_tzname2(default_tzname, &my_charset_latin1);
1721
Time zone tables may be open here, and my_tz_find() may open
1722
most of them once more, but this is OK for system tables open
1725
if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
1727
sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
1734
if (time_zone_tables_exist)
1736
thd->version--; /* Force close to free memory */
1737
close_system_tables(thd, &open_tables_state_backup);
1742
/* if there were error free time zone describing structs */
1748
org_thd->store_globals(); /* purecov: inspected */
1751
/* Remember that we don't have a THD */
1752
my_pthread_setspecific_ptr(THR_THD, 0);
1753
my_pthread_setspecific_ptr(THR_MALLOC, 0);
1755
DBUG_RETURN(return_val);
1760
Free resources used by time zone support infrastructure.
1771
VOID(pthread_mutex_destroy(&tz_LOCK));
1772
hash_free(&offset_tzs);
1773
hash_free(&tz_names);
1774
free_root(&tz_storage, MYF(0));
1780
Load time zone description from system tables.
1783
tz_load_from_open_tables()
1784
tz_name - name of time zone that should be loaded.
1785
tz_tables - list of tables from which time zone description
1789
This function will try to load information about time zone specified
1790
from the list of the already opened and locked tables (first table in
1791
tz_tables should be time_zone_name, next time_zone, then
1792
time_zone_transition_type and time_zone_transition should be last).
1793
It will also update information in hash used for time zones lookup.
1796
Returns pointer to newly created Time_zone object or 0 in case of error.
1801
tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
1804
TIME_ZONE_INFO *tz_info;
1805
Tz_names_entry *tmp_tzname;
1806
Time_zone *return_val= 0;
1810
char buff[MAX_FIELD_WIDTH];
1811
String abbr(buff, sizeof(buff), &my_charset_latin1);
1812
char *alloc_buff, *tz_name_buff;
1814
Temporary arrays that are used for loading of data for filling
1815
TIME_ZONE_INFO structure
1817
my_time_t ats[TZ_MAX_TIMES];
1818
uchar types[TZ_MAX_TIMES];
1819
TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
1820
#ifdef ABBR_ARE_USED
1821
char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
1823
DBUG_ENTER("tz_load_from_open_tables");
1825
/* Prepare tz_info for loading also let us make copy of time zone name */
1826
if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
1827
tz_name->length() + 1)))
1829
sql_print_error("Out of memory while loading time zone description");
1832
tz_info= (TIME_ZONE_INFO *)alloc_buff;
1833
bzero(tz_info, sizeof(TIME_ZONE_INFO));
1834
tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
1836
By writing zero to the end we guarantee that we can call ptr()
1837
instead of c_ptr() for time zone name.
1839
strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
1842
Let us find out time zone id by its name (there is only one index
1843
and it is specifically for this purpose).
1845
table= tz_tables->table;
1846
tz_tables= tz_tables->next_local;
1847
table->field[0]->store(tz_name->ptr(), tz_name->length(),
1848
&my_charset_latin1);
1850
It is OK to ignore ha_index_init()/ha_index_end() return values since
1851
mysql.time_zone* tables are MyISAM and these operations always succeed
1854
(void)table->file->ha_index_init(0, 1);
1856
if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
1857
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1861
Most probably user has mistyped time zone name, so no need to bark here
1862
unless we need it for debugging.
1864
sql_print_error("Can't find description of time zone '%s'", tz_name_buff);
1869
tzid= (uint)table->field[1]->val_int();
1871
(void)table->file->ha_index_end();
1874
Now we need to lookup record in mysql.time_zone table in order to
1875
understand whenever this timezone uses leap seconds (again we are
1876
using the only index in this table).
1878
table= tz_tables->table;
1879
tz_tables= tz_tables->next_local;
1880
table->field[0]->store((longlong) tzid, TRUE);
1881
(void)table->file->ha_index_init(0, 1);
1883
if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
1884
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1886
sql_print_error("Can't find description of time zone '%u'", tzid);
1890
/* If Uses_leap_seconds == 'Y' */
1891
if (table->field[1]->val_int() == 1)
1893
tz_info->leapcnt= tz_leapcnt;
1894
tz_info->lsis= tz_lsis;
1897
(void)table->file->ha_index_end();
1900
Now we will iterate through records for out time zone in
1901
mysql.time_zone_transition_type table. Because we want records
1902
only for our time zone guess what are we doing?
1903
Right - using special index.
1905
table= tz_tables->table;
1906
tz_tables= tz_tables->next_local;
1907
table->field[0]->store((longlong) tzid, TRUE);
1908
(void)table->file->ha_index_init(0, 1);
1910
res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
1911
(key_part_map)1, HA_READ_KEY_EXACT);
1914
ttid= (uint)table->field[1]->val_int();
1916
if (ttid >= TZ_MAX_TYPES)
1918
sql_print_error("Error while loading time zone description from "
1919
"mysql.time_zone_transition_type table: too big "
1920
"transition type id");
1924
ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int();
1925
ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0);
1927
#ifdef ABBR_ARE_USED
1928
// FIXME should we do something with duplicates here ?
1929
table->field[4]->val_str(&abbr, &abbr);
1930
if (tz_info->charcnt + abbr.length() + 1 > sizeof(chars))
1932
sql_print_error("Error while loading time zone description from "
1933
"mysql.time_zone_transition_type table: not enough "
1934
"room for abbreviations");
1937
ttis[ttid].tt_abbrind= tz_info->charcnt;
1938
memcpy(chars + tz_info->charcnt, abbr.ptr(), abbr.length());
1939
tz_info->charcnt+= abbr.length();
1940
chars[tz_info->charcnt]= 0;
1944
("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1945
"abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff,
1946
chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst));
1949
("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1950
"tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst));
1953
/* ttid is increasing because we are reading using index */
1954
DBUG_ASSERT(ttid >= tz_info->typecnt);
1956
tz_info->typecnt= ttid + 1;
1958
res= table->file->index_next_same(table->record[0],
1959
table->field[0]->ptr, 4);
1962
if (res != HA_ERR_END_OF_FILE)
1964
sql_print_error("Error while loading time zone description from "
1965
"mysql.time_zone_transition_type table");
1969
(void)table->file->ha_index_end();
1973
At last we are doing the same thing for records in
1974
mysql.time_zone_transition table. Here we additionaly need records
1975
in ascending order by index scan also satisfies us.
1977
table= tz_tables->table;
1978
table->field[0]->store((longlong) tzid, TRUE);
1979
(void)table->file->ha_index_init(0, 1);
1981
res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
1982
(key_part_map)1, HA_READ_KEY_EXACT);
1985
ttime= (my_time_t)table->field[1]->val_int();
1986
ttid= (uint)table->field[2]->val_int();
1988
if (tz_info->timecnt + 1 > TZ_MAX_TIMES)
1990
sql_print_error("Error while loading time zone description from "
1991
"mysql.time_zone_transition table: "
1992
"too much transitions");
1995
if (ttid + 1 > tz_info->typecnt)
1997
sql_print_error("Error while loading time zone description from "
1998
"mysql.time_zone_transition table: "
1999
"bad transition type id");
2003
ats[tz_info->timecnt]= ttime;
2004
types[tz_info->timecnt]= ttid;
2008
("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
2009
tzid, (ulong) ttime, ttid));
2011
res= table->file->index_next_same(table->record[0],
2012
table->field[0]->ptr, 4);
2016
We have to allow HA_ERR_KEY_NOT_FOUND because some time zones
2017
for example UTC have no transitons.
2019
if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
2021
sql_print_error("Error while loading time zone description from "
2022
"mysql.time_zone_transition table");
2026
(void)table->file->ha_index_end();
2030
Now we will allocate memory and init TIME_ZONE_INFO structure.
2032
if (!(alloc_buff= (char*) alloc_root(&tz_storage,
2033
ALIGN_SIZE(sizeof(my_time_t) *
2035
ALIGN_SIZE(tz_info->timecnt) +
2036
#ifdef ABBR_ARE_USED
2037
ALIGN_SIZE(tz_info->charcnt) +
2039
sizeof(TRAN_TYPE_INFO) *
2042
sql_print_error("Out of memory while loading time zone description");
2046
tz_info->ats= (my_time_t *) alloc_buff;
2047
memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
2048
alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
2049
tz_info->types= (uchar *)alloc_buff;
2050
memcpy(tz_info->types, types, tz_info->timecnt);
2051
alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
2052
#ifdef ABBR_ARE_USED
2053
tz_info->chars= alloc_buff;
2054
memcpy(tz_info->chars, chars, tz_info->charcnt);
2055
alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
2057
tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
2058
memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
2061
Let us check how correct our time zone description and build
2062
reversed map. We don't check for tz->timecnt < 1 since it ok for GMT.
2064
if (tz_info->typecnt < 1)
2066
sql_print_error("loading time zone without transition types");
2069
if (prepare_tz_info(tz_info, &tz_storage))
2071
sql_print_error("Unable to build mktime map for time zone");
2076
if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) ||
2077
!(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
2078
&(tmp_tzname->name))) ||
2079
(tmp_tzname->name.set(tz_name_buff, tz_name->length(),
2080
&my_charset_latin1),
2081
my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
2083
sql_print_error("Out of memory while loading time zone");
2088
Loading of time zone succeeded
2090
return_val= tmp_tzname->tz;
2095
(void)table->file->ha_index_end();
2097
DBUG_RETURN(return_val);
2102
Parse string that specifies time zone as offset from UTC.
2106
str - pointer to string which contains offset
2107
length - length of string
2108
offset - out parameter for storing found offset in seconds.
2111
This function parses string which contains time zone offset
2112
in form similar to '+10:00' and converts found value to
2113
seconds from UTC form (east is positive).
2117
1 - String doesn't contain valid time zone offset
2120
str_to_offset(const char *str, uint length, long *offset)
2122
const char *end= str + length;
2132
else if (*str == '-')
2140
while (str < end && my_isdigit(&my_charset_latin1, *str))
2142
number_tmp= number_tmp*10 + *str - '0';
2146
if (str + 1 >= end || *str != ':')
2150
offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
2152
while (str < end && my_isdigit(&my_charset_latin1, *str))
2154
number_tmp= number_tmp * 10 + *str - '0';
2161
offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
2164
offset_tmp= -offset_tmp;
2167
Check if offset is in range prescribed by standard
2168
(from -12:59 to 13:00).
2171
if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
2172
offset_tmp > 13 * SECS_PER_HOUR)
2175
*offset= offset_tmp;
2182
Get Time_zone object for specified time zone.
2186
thd - pointer to thread THD structure
2187
name - time zone specification
2190
This function checks if name is one of time zones described in db,
2191
predefined SYSTEM time zone or valid time zone specification as
2192
offset from UTC (In last case it will create proper Time_zone_offset
2193
object if there were not any.). If name is ok it returns corresponding
2196
Clients of this function are not responsible for releasing resources
2197
occupied by returned Time_zone object so they can just forget pointers
2198
to Time_zone object if they are not needed longer.
2200
Other important property of this function: if some Time_zone found once
2201
it will be for sure found later, so this function can also be used for
2202
checking if proper Time_zone object exists (and if there will be error
2203
it will be reported during first call).
2205
If name pointer is 0 then this function returns 0 (this allows to pass 0
2206
values as parameter without additional external check and this property
2207
is used by @@time_zone variable handling code).
2209
It will perform lookup in system tables (mysql.time_zone*),
2210
opening and locking them, and closing afterwards. It won't perform
2211
such lookup if no time zone describing tables were found during
2215
Pointer to corresponding Time_zone object. 0 - in case of bad time zone
2216
specification or other error.
2220
my_tz_find(THD *thd, const String *name)
2222
Tz_names_entry *tmp_tzname;
2223
Time_zone *result_tz= 0;
2225
DBUG_ENTER("my_tz_find");
2226
DBUG_PRINT("enter", ("time zone name='%s'",
2227
name ? ((String *)name)->c_ptr_safe() : "NULL"));
2232
VOID(pthread_mutex_lock(&tz_LOCK));
2234
if (!str_to_offset(name->ptr(), name->length(), &offset))
2237
if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs,
2238
(const uchar *)&offset,
2241
DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
2243
if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
2244
my_hash_insert(&offset_tzs, (const uchar *) result_tz))
2247
sql_print_error("Fatal error: Out of memory "
2248
"while setting new time zone");
2255
if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names,
2256
(const uchar *)name->ptr(),
2258
result_tz= tmp_tzname->tz;
2259
else if (time_zone_tables_exist)
2261
TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
2262
Open_tables_state open_tables_state_backup;
2264
tz_init_table_list(tz_tables);
2265
if (!open_system_tables_for_read(thd, tz_tables,
2266
&open_tables_state_backup))
2268
result_tz= tz_load_from_open_tables(name, tz_tables);
2269
close_system_tables(thd, &open_tables_state_backup);
2274
VOID(pthread_mutex_unlock(&tz_LOCK));
2276
DBUG_RETURN(result_tz);
2280
#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
2285
This code belongs to mysql_tzinfo_to_sql converter command line utility.
2286
This utility should be used by db admin for populating mysql.time_zone
2292
Print info about time zone described by TIME_ZONE_INFO struct as
2293
SQL statements populating mysql.time_zone* tables.
2297
tz_name - name of time zone
2298
sp - structure describing time zone
2301
print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
2305
/* Here we assume that all time zones have same leap correction tables */
2306
printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
2307
sp->leapcnt ? "Y" : "N");
2308
printf("SET @time_zone_id= LAST_INSERT_ID();\n");
2309
printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
2310
('%s', @time_zone_id);\n", tz_name);
2314
printf("INSERT INTO time_zone_transition \
2315
(Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
2316
for (i= 0; i < sp->timecnt; i++)
2317
printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
2318
(uint)sp->types[i]);
2322
printf("INSERT INTO time_zone_transition_type \
2323
(Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
2325
for (i= 0; i < sp->typecnt; i++)
2326
printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i,
2327
sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
2328
sp->chars + sp->ttis[i].tt_abbrind);
2334
Print info about leap seconds in time zone as SQL statements
2335
populating mysql.time_zone_leap_second table.
2338
print_tz_leaps_as_sql()
2339
sp - structure describing time zone
2342
print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
2347
We are assuming that there are only one list of leap seconds
2350
printf("TRUNCATE TABLE time_zone_leap_second;\n");
2354
printf("INSERT INTO time_zone_leap_second \
2355
(Transition_time, Correction) VALUES\n");
2356
for (i= 0; i < sp->leapcnt; i++)
2357
printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
2358
sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
2362
printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n");
2367
Some variables used as temporary or as parameters
2368
in recursive scan_tz_dir() code.
2370
TIME_ZONE_INFO tz_info;
2371
MEM_ROOT tz_storage;
2372
char fullname[FN_REFLEN + 1];
2373
char *root_name_end;
2377
Recursively scan zoneinfo directory and print all found time zone
2378
descriptions as SQL.
2382
name_end - pointer to end of path to directory to be searched.
2385
This auxiliary recursive function also uses several global
2386
variables as in parameters and for storing temporary values.
2388
fullname - path to directory that should be scanned.
2389
root_name_end - pointer to place in fullname where part with
2390
path to initial directory ends.
2391
current_tz_id - last used time zone id
2394
0 - Ok, 1 - Fatal error
2398
scan_tz_dir(char * name_end)
2404
if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
2407
name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
2409
for (i= 0; i < cur_dir->number_off_files; i++)
2411
if (cur_dir->dir_entry[i].name[0] != '.')
2413
name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
2414
FN_REFLEN - (name_end - fullname));
2416
if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
2418
if (scan_tz_dir(name_end_tmp))
2424
else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
2426
init_alloc_root(&tz_storage, 32768, 0);
2427
if (!tz_load(fullname, &tz_info, &tz_storage))
2428
print_tz_as_sql(root_name_end + 1, &tz_info);
2431
"Warning: Unable to load '%s' as time zone. Skipping it.\n",
2433
free_root(&tz_storage, MYF(0));
2436
fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
2448
main(int argc, char **argv)
2453
if (argc != 2 && argc != 3)
2455
fprintf(stderr, "Usage:\n");
2456
fprintf(stderr, " %s timezonedir\n", argv[0]);
2457
fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
2458
fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
2464
root_name_end= strmake(fullname, argv[1], FN_REFLEN);
2466
printf("TRUNCATE TABLE time_zone;\n");
2467
printf("TRUNCATE TABLE time_zone_name;\n");
2468
printf("TRUNCATE TABLE time_zone_transition;\n");
2469
printf("TRUNCATE TABLE time_zone_transition_type;\n");
2471
if (scan_tz_dir(root_name_end))
2473
fprintf(stderr, "There were fatal errors during processing "
2474
"of zoneinfo directory\n");
2478
printf("ALTER TABLE time_zone_transition "
2479
"ORDER BY Time_zone_id, Transition_time;\n");
2480
printf("ALTER TABLE time_zone_transition_type "
2481
"ORDER BY Time_zone_id, Transition_type_id;\n");
2485
init_alloc_root(&tz_storage, 32768, 0);
2487
if (strcmp(argv[1], "--leap") == 0)
2489
if (tz_load(argv[2], &tz_info, &tz_storage))
2491
fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2494
print_tz_leaps_as_sql(&tz_info);
2498
if (tz_load(argv[1], &tz_info, &tz_storage))
2500
fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2503
print_tz_as_sql(argv[2], &tz_info);
2506
free_root(&tz_storage, MYF(0));
2510
fprintf(stderr, "This tool has not been ported to NetWare\n");
2511
#endif /* __NETWARE__ */
2516
#endif /* defined(TZINFO2SQL) */
2522
Some simple brute-force test wich allowed to catch a pair of bugs.
2523
Also can provide interesting facts about system's time zone support
2532
#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
2536
#define TYPE_SIGNED(type) (((type) -1) < 0)
2540
is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg)
2542
return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) &&
2543
(time_arg->month == (uint)tm_arg->tm_mon+1) &&
2544
(time_arg->day == (uint)tm_arg->tm_mday) &&
2545
(time_arg->hour == (uint)tm_arg->tm_hour) &&
2546
(time_arg->minute == (uint)tm_arg->tm_min) &&
2547
(time_arg->second == (uint)tm_arg->tm_sec) &&
2548
time_arg->second_part == 0;
2553
main(int argc, char **argv)
2555
my_bool localtime_negative;
2556
TIME_ZONE_INFO tz_info;
2558
MYSQL_TIME time_tmp;
2560
char fullname[FN_REFLEN+1];
2562
MEM_ROOT tz_storage;
2566
init_alloc_root(&tz_storage, 32768, 0);
2568
/* let us set some well known timezone */
2569
setenv("TZ", "MET", 1);
2572
/* Some initial time zone related system info */
2573
printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned",
2574
(uint)TYPE_BIT(time_t));
2575
if (TYPE_SIGNED(time_t))
2578
localtime_negative= test(localtime_r(&t, &tmp) != 0);
2579
printf("localtime_r %s negative params \
2580
(time_t=%d is %d-%d-%d %d:%d:%d)\n",
2581
(localtime_negative ? "supports" : "doesn't support"), (int)t,
2582
TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday,
2583
tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
2585
printf("mktime %s negative results (%d)\n",
2586
(t == mktime(&tmp) ? "doesn't support" : "supports"),
2590
tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30;
2591
tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1;
2593
printf("mktime returns %s for spring time gap (%d)\n",
2594
(t != (time_t)-1 ? "something" : "error"), (int)t);
2596
tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2597
tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0;
2599
printf("mktime returns %s for non existing date (%d)\n",
2600
(t != (time_t)-1 ? "something" : "error"), (int)t);
2602
tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2603
tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1;
2605
printf("mktime %s unnormalized input (%d)\n",
2606
(t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t);
2608
tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26;
2609
tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1;
2611
tmp.tm_hour= 2; tmp.tm_isdst= -1;
2613
tmp.tm_hour= 4; tmp.tm_isdst= 0;
2615
tmp.tm_hour= 2; tmp.tm_isdst= -1;
2617
printf("mktime is %s (%d %d)\n",
2618
(t == t1 ? "determenistic" : "is non-determenistic"),
2621
/* Let us load time zone description */
2622
str_end= strmake(fullname, TZDIR, FN_REFLEN);
2623
strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
2625
if (tz_load(fullname, &tz_info, &tz_storage))
2627
printf("Unable to load time zone info from '%s'\n", fullname);
2628
free_root(&tz_storage, MYF(0));
2632
printf("Testing our implementation\n");
2634
if (TYPE_SIGNED(time_t) && localtime_negative)
2636
for (t= -40000; t < 20000; t++)
2638
localtime_r(&t, &tmp);
2639
gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2640
if (!is_equal_TIME_tm(&time_tmp, &tmp))
2642
printf("Problem with negative time_t = %d\n", (int)t);
2643
free_root(&tz_storage, MYF(0));
2647
printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n");
2650
for (t= 1000000000; t < 1100000000; t+= 13)
2652
localtime_r(&t,&tmp);
2653
gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2655
if (!is_equal_TIME_tm(&time_tmp, &tmp))
2657
printf("Problem with time_t = %d\n", (int)t);
2658
free_root(&tz_storage, MYF(0));
2662
printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n");
2667
Be careful here! my_system_gmt_sec doesn't fully handle unnormalized
2670
for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++)
2672
for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++)
2674
for (time_tmp.day= 1;
2675
time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1];
2678
for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++)
2680
for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5)
2682
for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25)
2686
t= (time_t)my_system_gmt_sec(&time_tmp, ¬_used, ¬_used_2);
2687
t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, ¬_used_2);
2691
We need special handling during autumn since my_system_gmt_sec
2692
prefers greater time_t values (in MET) for ambiguity.
2693
And BTW that is a bug which should be fixed !!!
2695
tmp.tm_year= time_tmp.year - TM_YEAR_BASE;
2696
tmp.tm_mon= time_tmp.month - 1;
2697
tmp.tm_mday= time_tmp.day;
2698
tmp.tm_hour= time_tmp.hour;
2699
tmp.tm_min= time_tmp.minute;
2700
tmp.tm_sec= time_tmp.second;
2708
printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n",
2709
time_tmp.year, time_tmp.month, time_tmp.day,
2710
time_tmp.hour, time_tmp.minute, time_tmp.second,
2713
free_root(&tz_storage, MYF(0));
2723
printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n");
2725
free_root(&tz_storage, MYF(0));
2729
#endif /* defined(TESTTIME) */