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];
333
Let us setup fallback time type which will be used if we have not any
334
transitions or if we have moment of time before first transition.
335
We will find first non-DST local time type and use it (or use first
336
local time type if all of them are DST types).
338
for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
340
if (i == sp->typecnt)
342
sp->fallback_tti= &(sp->ttis[i]);
346
Let us build shifted my_time_t -> my_time_t map.
350
/* Let us find initial offset */
351
if (sp->timecnt == 0 || cur_t < sp->ats[0])
354
If we have not any transitions or t is before first transition we are using
355
already found fallback time type which index is already in i.
361
/* cur_t == sp->ats[0] so we found transition */
366
cur_offset= sp->ttis[i].tt_gmtoff;
369
/* let us find leap correction... unprobable, but... */
370
for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
371
cur_t >= sp->lsis[next_leap_idx].ls_trans;
375
if (next_leap_idx > 0)
376
cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
380
/* Iterate trough t space */
381
while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
383
cur_off_and_corr= cur_offset - cur_corr;
386
We assuming that cur_t could be only overflowed downwards,
387
we also assume that end_t won't be overflowed in this case.
389
if (cur_off_and_corr < 0 &&
390
cur_t < MY_TIME_T_MIN - cur_off_and_corr)
391
cur_t= MY_TIME_T_MIN - cur_off_and_corr;
393
cur_l= cur_t + cur_off_and_corr;
396
Let us choose end_t as point before next time type change or leap
399
end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
401
(next_leap_idx < sp->leapcnt) ?
402
sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
404
again assuming that end_t can be overlowed only in positive side
405
we also assume that end_t won't be overflowed in this case.
407
if (cur_off_and_corr > 0 &&
408
end_t > MY_TIME_T_MAX - cur_off_and_corr)
409
end_t= MY_TIME_T_MAX - cur_off_and_corr;
411
end_l= end_t + cur_off_and_corr;
414
if (end_l > cur_max_seen_l)
416
/* We want special handling in the case of first range */
417
if (cur_max_seen_l == MY_TIME_T_MIN)
419
revts[sp->revcnt]= cur_l;
420
revtis[sp->revcnt].rt_offset= cur_off_and_corr;
421
revtis[sp->revcnt].rt_type= 0;
423
cur_max_seen_l= end_l;
427
if (cur_l > cur_max_seen_l + 1)
429
/* We have a spring time-gap and we are not at the first range */
430
revts[sp->revcnt]= cur_max_seen_l + 1;
431
revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
432
revtis[sp->revcnt].rt_type= 1;
434
if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
435
break; /* That was too much */
436
cur_max_seen_l= cur_l - 1;
439
/* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */
441
revts[sp->revcnt]= cur_max_seen_l + 1;
442
revtis[sp->revcnt].rt_offset= cur_off_and_corr;
443
revtis[sp->revcnt].rt_type= 0;
445
cur_max_seen_l= end_l;
449
if (end_t == MY_TIME_T_MAX ||
450
((cur_off_and_corr > 0) &&
451
(end_t >= MY_TIME_T_MAX - cur_off_and_corr)))
458
Let us find new offset and correction. Because of our choice of end_t
459
cur_t can only be point where new time type starts or/and leap
460
correction is performed.
462
if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
463
if (next_trans_idx < sp->timecnt &&
464
cur_t == sp->ats[next_trans_idx])
466
/* We are at offset point */
467
cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
471
if (next_leap_idx < sp->leapcnt &&
472
cur_t == sp->lsis[next_leap_idx].ls_trans)
474
/* we are at leap point */
475
cur_corr= sp->lsis[next_leap_idx].ls_corr;
480
/* check if we have had enough space */
481
if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
484
/* set maximum end_l as finisher */
485
revts[sp->revcnt]= end_l;
487
/* Allocate arrays of proper size in sp and copy result there */
488
if (!(sp->revts= (my_time_t *)alloc_root(storage,
489
sizeof(my_time_t) * (sp->revcnt + 1))) ||
490
!(sp->revtis= (REVT_INFO *)alloc_root(storage,
491
sizeof(REVT_INFO) * sp->revcnt)))
494
memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
495
memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
501
#if !defined(TZINFO2SQL)
503
static const uint mon_lengths[2][MONS_PER_YEAR]=
505
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
506
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
509
static const uint mon_starts[2][MONS_PER_YEAR]=
511
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
512
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
515
static const uint year_lengths[2]=
517
DAYS_PER_NYEAR, DAYS_PER_LYEAR
520
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
524
Converts time from my_time_t representation (seconds in UTC since Epoch)
525
to broken down representation using given local time zone offset.
529
tmp - pointer to structure for broken down representation
530
t - my_time_t value to be converted
531
offset - local time zone offset
534
Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
535
(from elsie code) because doesn't contain any leap correction and
536
TM_GMTOFF and is_dst setting and contains some MySQL specific
537
initialization. Funny but with removing of these we almost have
538
glibc's offtime function.
541
sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
549
days= (long) (t / SECS_PER_DAY);
550
rem= (long) (t % SECS_PER_DAY);
553
We do this as separate step after dividing t, because this
554
allows us handle times near my_time_t bounds without overflows.
562
while (rem >= SECS_PER_DAY)
567
tmp->hour= (uint)(rem / SECS_PER_HOUR);
568
rem= rem % SECS_PER_HOUR;
569
tmp->minute= (uint)(rem / SECS_PER_MIN);
571
A positive leap second requires a special
572
representation. This uses "... ??:59:60" et seq.
574
tmp->second= (uint)(rem % SECS_PER_MIN);
577
while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
581
newy= y + days / DAYS_PER_NYEAR;
584
days-= (newy - y) * DAYS_PER_NYEAR +
585
LEAPS_THRU_END_OF(newy - 1) -
586
LEAPS_THRU_END_OF(y - 1);
591
ip= mon_lengths[yleap];
592
for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
593
days= days - (long) ip[tmp->month];
595
tmp->day= (uint)(days + 1);
597
/* filling MySQL specific MYSQL_TIME members */
598
tmp->neg= 0; tmp->second_part= 0;
599
tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
604
Find time range wich contains given my_time_t value
608
t - my_time_t value for which we looking for range
609
range_boundaries - sorted array of range starts.
610
higher_bound - number of ranges
613
Performs binary search for range which contains given my_time_t value.
614
It has sense if number of ranges is greater than zero and my_time_t value
615
is greater or equal than beginning of first range. It also assumes that
616
t belongs to some range specified or end of last is MY_TIME_T_MAX.
618
With this localtime_r on real data may takes less time than with linear
619
search (I've seen 30% speed up).
622
Index of range to which t belongs
625
find_time_range(my_time_t t, const my_time_t *range_boundaries,
628
uint i, lower_bound= 0;
631
Function will work without this assertion but result would be meaningless.
633
DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
636
Do binary search for minimal interval which contain t. We preserve:
637
range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
638
invariant and decrease this higher_bound - lower_bound gap twice
642
while (higher_bound - lower_bound > 1)
644
i= (lower_bound + higher_bound) >> 1;
645
if (range_boundaries[i] <= t)
654
Find local time transition for given my_time_t.
657
find_transition_type()
658
t - my_time_t value to be converted
659
sp - pointer to struct with time zone description
662
Pointer to structure in time zone description describing
663
local time type for given my_time_t.
666
const TRAN_TYPE_INFO *
667
find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
669
if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
672
If we have not any transitions or t is before first transition let
673
us use fallback time type.
675
return sp->fallback_tti;
679
Do binary search for minimal interval between transitions which
680
contain t. With this localtime_r on real data may takes less
681
time than with linear search (I've seen 30% speed up).
683
return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
688
Converts time in my_time_t representation (seconds in UTC since Epoch) to
689
broken down MYSQL_TIME representation in local time zone.
693
tmp - pointer to structure for broken down represenatation
694
sec_in_utc - my_time_t value to be converted
695
sp - pointer to struct with time zone description
698
We can improve this function by creating joined array of transitions and
699
leap corrections. This will require adding extra field to TRAN_TYPE_INFO
700
for storing number of "extra" seconds to minute occured due to correction
701
(60th and 61st second, look how we calculate them as "hit" in this
703
Under realistic assumptions about frequency of transitions the same array
704
can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
705
implement tweaked binary search which will take into account that some
706
MYSQL_TIME has two matching my_time_t ranges and some of them have none.
709
gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
711
const TRAN_TYPE_INFO *ttisp;
718
Find proper transition (and its local time type) for our sec_in_utc value.
719
Funny but again by separating this step in function we receive code
720
which very close to glibc's code. No wonder since they obviously use
721
the same base and all steps are sensible.
723
ttisp= find_transition_type(sec_in_utc, sp);
726
Let us find leap correction for our sec_in_utc value and number of extra
727
secs to add to this minute.
728
This loop is rarely used because most users will use time zones without
729
leap seconds, and even in case when we have such time zone there won't
730
be many iterations (we have about 22 corrections at this moment (2004)).
732
for ( i= sp->leapcnt; i-- > 0; )
735
if (sec_in_utc >= lp->ls_trans)
737
if (sec_in_utc == lp->ls_trans)
739
hit= ((i == 0 && lp->ls_corr > 0) ||
740
lp->ls_corr > sp->lsis[i - 1].ls_corr);
744
sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
745
sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
757
sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
764
Converts local time in broken down representation to local
765
time zone analog of my_time_t represenation.
769
year, mon, mday, hour, min, sec - broken down representation.
772
Converts time in broken down representation to my_time_t representation
773
ignoring time zone. Note that we cannot convert back some valid _local_
774
times near ends of my_time_t range because of my_time_t overflow. But we
775
ignore this fact now since MySQL will never pass such argument.
778
Seconds since epoch time representation.
781
sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
783
/* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
784
DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
785
#ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
787
It turns out that only whenever month is normalized or unnormalized
790
DBUG_ASSERT(mon > 0 && mon < 13);
791
long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
792
LEAPS_THRU_END_OF(year - 1) -
793
LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
794
days+= mon_starts[isleap(year)][mon - 1];
796
long norm_month= (mon - 1) % MONS_PER_YEAR;
797
long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
798
long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
799
LEAPS_THRU_END_OF(a_year - 1) -
800
LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
801
days+= mon_starts[isleap(a_year)]
802
[norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
806
return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
811
Converts local time in broken down MYSQL_TIME representation to my_time_t
816
t - pointer to structure for broken down represenatation
817
sp - pointer to struct with time zone description
818
in_dst_time_gap - pointer to bool which is set to true if datetime
819
value passed doesn't really exist (i.e. falls into
820
spring time-gap) and is not touched otherwise.
823
This is mktime analog for MySQL. It is essentially different
824
from mktime (or hypotetical my_mktime) because:
825
- It has no idea about tm_isdst member so if it
826
has two answers it will give the smaller one
827
- If we are in spring time gap then it will return
829
- It can give wrong results near the ends of my_time_t due to
830
overflows, but we are safe since in MySQL we will never
831
call this function for such dates (its restriction for year
832
between 1970 and 2038 gives us several days of reserve).
833
- By default it doesn't support un-normalized input. But if
834
sec_since_epoch() function supports un-normalized dates
835
then this function should handle un-normalized input right,
836
altough it won't normalize structure TIME.
838
Traditional approach to problem of conversion from broken down
839
representation to time_t is iterative. Both elsie's and glibc
840
implementation try to guess what time_t value should correspond to
841
this broken-down value. They perform localtime_r function on their
842
guessed value and then calculate the difference and try to improve
843
their guess. Elsie's code guesses time_t value in bit by bit manner,
844
Glibc's code tries to add difference between broken-down value
845
corresponding to guess and target broken-down value to current guess.
846
It also uses caching of last found correction... So Glibc's approach
847
is essentially faster but introduces some undetermenism (in case if
848
is_dst member of broken-down representation (tm struct) is not known
849
and we have two possible answers).
851
We use completely different approach. It is better since it is both
852
faster than iterative implementations and fully determenistic. If you
853
look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
855
The first is calculating shifted my_time_t value and the second - TIME
856
calculation from shifted my_time_t value (well it is a bit simplified
857
picture). The part in which we are interested in is my_time_t -> shifted
858
my_time_t conversion. It is piecewise linear function which is defined
859
by combination of transition times as break points and times offset
860
as changing function parameter. The possible inverse function for this
861
converison would be ambiguos but with MySQL's restrictions we can use
862
some function which is the same as inverse function on unambigiuos
863
ranges and coincides with one of branches of inverse function in
864
other ranges. Thus we just need to build table which will determine
865
this shifted my_time_t -> my_time_t conversion similar to existing
866
(my_time_t -> shifted my_time_t table). We do this in
867
prepare_tz_info function.
870
If we can even more improve this function. For doing this we will need to
871
build joined map of transitions and leap corrections for gmt_sec_to_TIME()
872
function (similar to revts/revtis). Under realistic assumptions about
873
frequency of transitions we can use the same array for TIME_to_gmt_sec().
874
We need to implement special version of binary search for this. Such step
875
will be beneficial to CPU cache since we will decrease data-set used for
879
Seconds in UTC since Epoch.
883
TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp,
884
my_bool *in_dst_time_gap)
891
DBUG_ENTER("TIME_to_gmt_sec");
893
if (!validate_timestamp_range(t))
897
/* We need this for correct leap seconds handling */
898
if (t->second < SECS_PER_MIN)
901
saved_seconds= t->second;
904
NOTE: to convert full my_time_t range we do a shift of the
905
boundary dates here to avoid overflow of my_time_t.
906
We use alike approach in my_system_gmt_sec().
908
However in that function we also have to take into account
909
overflow near 0 on some platforms. That's because my_system_gmt_sec
910
uses localtime_r(), which doesn't work with negative values correctly
911
on platforms with unsigned time_t (QNX). Here we don't use localtime()
912
=> we negative values of local_t are ok.
915
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
918
We will pass (t->day - shift) to sec_since_epoch(), and
919
want this value to be a positive number, so we shift
920
only dates > 4.01.2038 (to avoid owerflow).
926
local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
928
saved_seconds ? 0 : t->second);
930
/* We have at least one range */
931
DBUG_ASSERT(sp->revcnt >= 1);
933
if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
936
This means that source time can't be represented as my_time_t due to
937
limited my_time_t range.
942
/* binary search for our range */
943
i= find_time_range(local_t, sp->revts, sp->revcnt);
946
As there are no offset switches at the end of TIMESTAMP range,
947
we could simply check for overflow here (and don't need to bother
952
if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
953
sp->revtis[i].rt_offset - saved_seconds))
955
DBUG_RETURN(0); /* my_time_t overflow */
957
local_t+= shift * SECS_PER_DAY;
960
if (sp->revtis[i].rt_type)
963
Oops! We are in spring time gap.
964
May be we should return error here?
965
Now we are returning my_time_t value corresponding to the
966
beginning of the gap.
969
local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
972
local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
974
/* check for TIMESTAMP_MAX_VALUE was already done above */
975
if (local_t < TIMESTAMP_MIN_VALUE)
978
DBUG_RETURN(local_t);
983
End of elsie derived code.
985
#endif /* !defined(TZINFO2SQL) */
988
#if !defined(TESTTIME) && !defined(TZINFO2SQL)
991
String with names of SYSTEM time zone.
993
static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
997
Instance of this class represents local time zone used on this system
998
(specified by TZ environment variable or via any other system mechanism).
999
It uses system functions (localtime_r, my_system_gmt_sec) for conversion
1000
and is always available. Because of this it is used by default - if there
1001
were no explicit time zone specified. On the other hand because of this
1002
conversion methods provided by this class is significantly slower and
1003
possibly less multi-threaded-friendly than corresponding Time_zone_db
1004
methods so the latter should be preffered there it is possible.
1006
class Time_zone_system : public Time_zone
1009
Time_zone_system() {} /* Remove gcc warning */
1010
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1011
my_bool *in_dst_time_gap) const;
1012
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1013
virtual const String * get_name() const;
1018
Converts local time in system time zone in MYSQL_TIME representation
1019
to its my_time_t representation.
1023
t - pointer to MYSQL_TIME structure with local time in
1024
broken-down representation.
1025
in_dst_time_gap - pointer to bool which is set to true if datetime
1026
value passed doesn't really exist (i.e. falls into
1027
spring time-gap) and is not touched otherwise.
1030
This method uses system function (localtime_r()) for conversion
1031
local time in system time zone in MYSQL_TIME structure to its my_time_t
1032
representation. Unlike the same function for Time_zone_db class
1033
it it won't handle unnormalized input properly. Still it will
1034
return lowest possible my_time_t in case of ambiguity or if we
1035
provide time corresponding to the time-gap.
1037
You should call my_init_time() function before using this function.
1040
Corresponding my_time_t value or 0 in case of error
1043
Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1046
return my_system_gmt_sec(t, ¬_used, in_dst_time_gap);
1051
Converts time from UTC seconds since Epoch (my_time_t) representation
1052
to system local time zone broken-down representation.
1056
tmp - pointer to MYSQL_TIME structure to fill-in
1057
t - my_time_t value to be converted
1060
We assume that value passed to this function will fit into time_t range
1061
supported by localtime_r. This conversion is putting restriction on
1062
TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
1063
for interaction with client then we can extend TIMESTAMP range down to
1067
Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1070
time_t tmp_t= (time_t)t;
1072
localtime_r(&tmp_t, &tmp_tm);
1073
localtime_to_TIME(tmp, &tmp_tm);
1074
tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1075
adjust_leap_second(tmp);
1080
Get name of time zone
1086
Name of time zone as String
1089
Time_zone_system::get_name() const
1091
return &tz_SYSTEM_name;
1096
Instance of this class represents UTC time zone. It uses system gmtime_r
1097
function for conversions and is always available. It is used only for
1098
my_time_t -> MYSQL_TIME conversions in various UTC_... functions, it is not
1099
intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
1101
class Time_zone_utc : public Time_zone
1104
Time_zone_utc() {} /* Remove gcc warning */
1105
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1106
my_bool *in_dst_time_gap) const;
1107
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1108
virtual const String * get_name() const;
1113
Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
1117
t - pointer to MYSQL_TIME structure with local time
1118
in broken-down representation.
1119
in_dst_time_gap - pointer to bool which is set to true if datetime
1120
value passed doesn't really exist (i.e. falls into
1121
spring time-gap) and is not touched otherwise.
1124
Since Time_zone_utc is used only internally for my_time_t -> TIME
1125
conversions, this function of Time_zone interface is not implemented for
1126
this class and should not be called.
1132
Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1134
/* Should be never called */
1141
Converts time from UTC seconds since Epoch (my_time_t) representation
1142
to broken-down representation (also in UTC).
1146
tmp - pointer to MYSQL_TIME structure to fill-in
1147
t - my_time_t value to be converted
1150
See note for apropriate Time_zone_system method.
1153
Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1156
time_t tmp_t= (time_t)t;
1157
gmtime_r(&tmp_t, &tmp_tm);
1158
localtime_to_TIME(tmp, &tmp_tm);
1159
tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1160
adjust_leap_second(tmp);
1165
Get name of time zone
1171
Since Time_zone_utc is used only internally by SQL's UTC_* functions it
1172
is not accessible directly, and hence this function of Time_zone
1173
interface is not implemented for this class and should not be called.
1179
Time_zone_utc::get_name() const
1181
/* Should be never called */
1188
Instance of this class represents some time zone which is
1189
described in mysql.time_zone family of tables.
1191
class Time_zone_db : public Time_zone
1194
Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
1195
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1196
my_bool *in_dst_time_gap) const;
1197
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1198
virtual const String * get_name() const;
1200
TIME_ZONE_INFO *tz_info;
1201
const String *tz_name;
1206
Initializes object representing time zone described by mysql.time_zone
1211
tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
1212
according to db or other time zone description
1213
(for example by my_tz_init()).
1214
Several Time_zone_db instances can share one
1215
TIME_ZONE_INFO structure.
1216
tz_name_arg - name of time zone.
1218
Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
1219
const String *tz_name_arg):
1220
tz_info(tz_info_arg), tz_name(tz_name_arg)
1226
Converts local time in time zone described from TIME
1227
representation to its my_time_t representation.
1231
t - pointer to MYSQL_TIME structure with local time
1232
in broken-down representation.
1233
in_dst_time_gap - pointer to bool which is set to true if datetime
1234
value passed doesn't really exist (i.e. falls into
1235
spring time-gap) and is not touched otherwise.
1238
Please see ::TIME_to_gmt_sec for function description and
1239
parameter restrictions.
1242
Corresponding my_time_t value or 0 in case of error
1245
Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1247
return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
1252
Converts time from UTC seconds since Epoch (my_time_t) representation
1253
to local time zone described in broken-down representation.
1257
tmp - pointer to MYSQL_TIME structure to fill-in
1258
t - my_time_t value to be converted
1261
Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1263
::gmt_sec_to_TIME(tmp, t, tz_info);
1264
adjust_leap_second(tmp);
1269
Get name of time zone
1275
Name of time zone as ASCIIZ-string
1278
Time_zone_db::get_name() const
1285
Instance of this class represents time zone which
1286
was specified as offset from UTC.
1288
class Time_zone_offset : public Time_zone
1291
Time_zone_offset(long tz_offset_arg);
1292
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1293
my_bool *in_dst_time_gap) const;
1294
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1295
virtual const String * get_name() const;
1297
This have to be public because we want to be able to access it from
1298
my_offset_tzs_get_key() function
1302
/* Extra reserve because of snprintf */
1303
char name_buff[7+16];
1309
Initializes object representing time zone described by its offset from UTC.
1313
tz_offset_arg - offset from UTC in seconds.
1314
Positive for direction to east.
1316
Time_zone_offset::Time_zone_offset(long tz_offset_arg):
1317
offset(tz_offset_arg)
1319
uint hours= abs((int)(offset / SECS_PER_HOUR));
1320
uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
1321
ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
1322
(offset>=0) ? "+" : "-", hours, minutes);
1323
name.set(name_buff, length, &my_charset_latin1);
1328
Converts local time in time zone described as offset from UTC
1329
from MYSQL_TIME representation to its my_time_t representation.
1333
t - pointer to MYSQL_TIME structure with local time
1334
in broken-down representation.
1335
in_dst_time_gap - pointer to bool which should be set to true if
1336
datetime value passed doesn't really exist
1337
(i.e. falls into spring time-gap) and is not
1339
It is not really used in this class.
1342
Corresponding my_time_t value or 0 in case of error
1345
Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1351
Check timestamp range.we have to do this as calling function relies on
1352
us to make all validation checks here.
1354
if (!validate_timestamp_range(t))
1358
Do a temporary shift of the boundary dates to avoid
1359
overflow of my_time_t if the time value is near it's
1362
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
1365
local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
1366
t->hour, t->minute, t->second) -
1371
/* Add back the shifted time */
1372
local_t+= shift * SECS_PER_DAY;
1375
if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
1384
Converts time from UTC seconds since Epoch (my_time_t) representation
1385
to local time zone described as offset from UTC and in broken-down
1390
tmp - pointer to MYSQL_TIME structure to fill-in
1391
t - my_time_t value to be converted
1394
Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1396
sec_to_TIME(tmp, t, offset);
1401
Get name of time zone
1407
Name of time zone as pointer to String object
1410
Time_zone_offset::get_name() const
1416
static Time_zone_utc tz_UTC;
1417
static Time_zone_system tz_SYSTEM;
1418
static Time_zone_offset tz_OFFSET0(0);
1420
Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1421
Time_zone *my_tz_UTC= &tz_UTC;
1422
Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1424
static HASH tz_names;
1425
static HASH offset_tzs;
1426
static MEM_ROOT tz_storage;
1429
These mutex protects offset_tzs and tz_storage.
1430
These protection needed only when we are trying to set
1431
time zone which is specified as offset, and searching for existing
1432
time zone in offset_tzs or creating if it didn't existed before in
1433
tz_storage. So contention is low.
1435
static pthread_mutex_t tz_LOCK;
1436
static bool tz_inited= 0;
1439
This two static variables are inteded for holding info about leap seconds
1440
shared by all time zones.
1442
static uint tz_leapcnt= 0;
1443
static LS_INFO *tz_lsis= 0;
1446
Shows whenever we have found time zone tables during start-up.
1447
Used for avoiding of putting those tables to global table list
1448
for queries that use time zone info.
1450
static bool time_zone_tables_exist= 1;
1454
Names of tables (with their lengths) that are needed
1455
for dynamical loading of time zone descriptions.
1458
static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
1460
{ C_STRING_WITH_LEN("time_zone_name")},
1461
{ C_STRING_WITH_LEN("time_zone")},
1462
{ C_STRING_WITH_LEN("time_zone_transition_type")},
1463
{ C_STRING_WITH_LEN("time_zone_transition")}
1466
/* Name of database to which those tables belong. */
1468
static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
1471
class Tz_names_entry: public Sql_alloc
1480
We are going to call both of these functions from C code so
1481
they should obey C calling conventions.
1485
my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
1486
my_bool not_used __attribute__((unused)))
1488
*length= entry->name.length();
1489
return (uchar*) entry->name.ptr();
1493
my_offset_tzs_get_key(Time_zone_offset *entry,
1495
my_bool not_used __attribute__((unused)))
1497
*length= sizeof(long);
1498
return (uchar*) &entry->offset;
1503
Prepare table list with time zone related tables from preallocated array.
1506
tz_init_table_list()
1507
tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT
1511
This function prepares list of TABLE_LIST objects which can be used
1512
for opening of time zone tables from preallocated array.
1516
tz_init_table_list(TABLE_LIST *tz_tabs)
1518
bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT);
1520
for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
1522
tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str;
1523
tz_tabs[i].table_name_length= tz_tables_names[i].length;
1524
tz_tabs[i].db= tz_tables_db_name.str;
1525
tz_tabs[i].db_length= tz_tables_db_name.length;
1526
tz_tabs[i].lock_type= TL_READ;
1528
if (i != MY_TZ_TABLES_COUNT - 1)
1529
tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
1531
tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
1537
Initialize time zone support infrastructure.
1541
thd - current thread object
1542
default_tzname - default time zone or 0 if none.
1543
bootstrap - indicates whenever we are in bootstrap mode
1546
This function will init memory structures needed for time zone support,
1547
it will register mandatory SYSTEM time zone in them. It will try to open
1548
mysql.time_zone* tables and load information about default time zone and
1549
information which further will be shared among all time zones loaded.
1550
If system tables with time zone descriptions don't exist it won't fail
1551
(unless default_tzname is time zone from tables). If bootstrap parameter
1552
is true then this routine assumes that we are in bootstrap mode and won't
1553
load time zone descriptions unless someone specifies default time zone
1554
which is supposedly stored in those tables.
1555
It'll also set default time zone if it is specified.
1562
my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
1565
TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
1566
Open_tables_state open_tables_state_backup;
1568
Tz_names_entry *tmp_tzname;
1569
my_bool return_val= 1;
1572
DBUG_ENTER("my_tz_init");
1575
To be able to run this from boot, we allocate a temporary THD
1577
if (!(thd= new THD))
1579
thd->thread_stack= (char*) &thd;
1580
thd->store_globals();
1583
/* Init all memory structures that require explicit destruction */
1584
if (hash_init(&tz_names, &my_charset_latin1, 20,
1585
0, 0, (hash_get_key) my_tz_names_get_key, 0, 0))
1587
sql_print_error("Fatal error: OOM while initializing time zones");
1590
if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
1591
(hash_get_key)my_offset_tzs_get_key, 0, 0))
1593
sql_print_error("Fatal error: OOM while initializing time zones");
1594
hash_free(&tz_names);
1597
init_alloc_root(&tz_storage, 32 * 1024, 0);
1598
VOID(pthread_mutex_init(&tz_LOCK, MY_MUTEX_INIT_FAST));
1601
/* Add 'SYSTEM' time zone to tz_names hash */
1602
if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()))
1604
sql_print_error("Fatal error: OOM while initializing time zones");
1605
goto end_with_cleanup;
1607
tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
1608
tmp_tzname->tz= my_tz_SYSTEM;
1609
if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
1611
sql_print_error("Fatal error: OOM while initializing time zones");
1612
goto end_with_cleanup;
1617
/* If we are in bootstrap mode we should not load time zone tables */
1618
return_val= time_zone_tables_exist= 0;
1619
goto end_with_setting_default_tz;
1623
After this point all memory structures are inited and we even can live
1624
without time zone description tables. Now try to load information about
1625
leap seconds shared by all time zones.
1628
thd->set_db(db, sizeof(db)-1);
1629
bzero((char*) &tz_tables[0], sizeof(TABLE_LIST));
1630
tz_tables[0].alias= tz_tables[0].table_name=
1631
(char*)"time_zone_leap_second";
1632
tz_tables[0].table_name_length= 21;
1633
tz_tables[0].db= db;
1634
tz_tables[0].db_length= sizeof(db)-1;
1635
tz_tables[0].lock_type= TL_READ;
1637
tz_init_table_list(tz_tables+1);
1638
tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
1639
tz_tables[1].prev_global= &tz_tables[0].next_global;
1642
We need to open only mysql.time_zone_leap_second, but we try to
1643
open all time zone tables to see if they exist.
1645
if (open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup))
1647
sql_print_warning("Can't open and lock time zone table: %s "
1648
"trying to live without them", thd->main_da.message());
1649
/* We will try emulate that everything is ok */
1650
return_val= time_zone_tables_exist= 0;
1651
goto end_with_setting_default_tz;
1655
Now we are going to load leap seconds descriptions that are shared
1656
between all time zones that use them. We are using index for getting
1657
records in proper order. Since we share the same MEM_ROOT between
1658
all time zones we just allocate enough memory for it first.
1660
if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
1661
sizeof(LS_INFO) * TZ_MAX_LEAPS)))
1663
sql_print_error("Fatal error: Out of memory while loading "
1664
"mysql.time_zone_leap_second table");
1665
goto end_with_close;
1668
table= tz_tables[0].table;
1670
It is OK to ignore ha_index_init()/ha_index_end() return values since
1671
mysql.time_zone* tables are MyISAM and these operations always succeed
1674
(void)table->file->ha_index_init(0, 1);
1675
table->use_all_columns();
1679
res= table->file->index_first(table->record[0]);
1683
if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
1685
sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
1686
" table: too much leaps");
1687
table->file->ha_index_end();
1688
goto end_with_close;
1691
tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
1692
tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();
1697
("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld",
1698
tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
1699
tz_lsis[tz_leapcnt-1].ls_corr));
1701
res= table->file->index_next(table->record[0]);
1704
(void)table->file->ha_index_end();
1706
if (res != HA_ERR_END_OF_FILE)
1708
sql_print_error("Fatal error: Error while loading "
1709
"mysql.time_zone_leap_second table");
1710
goto end_with_close;
1714
Loading of info about leap seconds succeeded
1720
end_with_setting_default_tz:
1721
/* If we have default time zone try to load it */
1724
String tmp_tzname2(default_tzname, &my_charset_latin1);
1726
Time zone tables may be open here, and my_tz_find() may open
1727
most of them once more, but this is OK for system tables open
1730
if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
1732
sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
1739
if (time_zone_tables_exist)
1741
thd->version--; /* Force close to free memory */
1742
close_system_tables(thd, &open_tables_state_backup);
1747
/* if there were error free time zone describing structs */
1753
org_thd->store_globals(); /* purecov: inspected */
1756
/* Remember that we don't have a THD */
1757
my_pthread_setspecific_ptr(THR_THD, 0);
1758
my_pthread_setspecific_ptr(THR_MALLOC, 0);
1760
DBUG_RETURN(return_val);
1765
Free resources used by time zone support infrastructure.
1776
VOID(pthread_mutex_destroy(&tz_LOCK));
1777
hash_free(&offset_tzs);
1778
hash_free(&tz_names);
1779
free_root(&tz_storage, MYF(0));
1785
Load time zone description from system tables.
1788
tz_load_from_open_tables()
1789
tz_name - name of time zone that should be loaded.
1790
tz_tables - list of tables from which time zone description
1794
This function will try to load information about time zone specified
1795
from the list of the already opened and locked tables (first table in
1796
tz_tables should be time_zone_name, next time_zone, then
1797
time_zone_transition_type and time_zone_transition should be last).
1798
It will also update information in hash used for time zones lookup.
1801
Returns pointer to newly created Time_zone object or 0 in case of error.
1806
tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
1809
TIME_ZONE_INFO *tz_info;
1810
Tz_names_entry *tmp_tzname;
1811
Time_zone *return_val= 0;
1815
char buff[MAX_FIELD_WIDTH];
1816
String abbr(buff, sizeof(buff), &my_charset_latin1);
1817
char *alloc_buff, *tz_name_buff;
1819
Temporary arrays that are used for loading of data for filling
1820
TIME_ZONE_INFO structure
1822
my_time_t ats[TZ_MAX_TIMES];
1823
uchar types[TZ_MAX_TIMES];
1824
TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
1825
#ifdef ABBR_ARE_USED
1826
char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
1829
Used as a temporary tz_info until we decide that we actually want to
1830
allocate and keep the tz info and tz name in tz_storage.
1832
TIME_ZONE_INFO tmp_tz_info;
1833
memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
1835
DBUG_ENTER("tz_load_from_open_tables");
1837
/* Prepare tz_info for loading also let us make copy of time zone name */
1838
if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
1839
tz_name->length() + 1)))
1841
sql_print_error("Out of memory while loading time zone description");
1844
tz_info= (TIME_ZONE_INFO *)alloc_buff;
1845
bzero(tz_info, sizeof(TIME_ZONE_INFO));
1846
tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
1848
By writing zero to the end we guarantee that we can call ptr()
1849
instead of c_ptr() for time zone name.
1851
strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
1854
Let us find out time zone id by its name (there is only one index
1855
and it is specifically for this purpose).
1857
table= tz_tables->table;
1858
tz_tables= tz_tables->next_local;
1859
table->field[0]->store(tz_name->ptr(), tz_name->length(),
1860
&my_charset_latin1);
1862
It is OK to ignore ha_index_init()/ha_index_end() return values since
1863
mysql.time_zone* tables are MyISAM and these operations always succeed
1866
(void)table->file->ha_index_init(0, 1);
1868
if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
1869
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1873
Most probably user has mistyped time zone name, so no need to bark here
1874
unless we need it for debugging.
1876
sql_print_error("Can't find description of time zone '%.*s'",
1877
tz_name->length(), tz_name->ptr());
1882
tzid= (uint)table->field[1]->val_int();
1884
(void)table->file->ha_index_end();
1887
Now we need to lookup record in mysql.time_zone table in order to
1888
understand whenever this timezone uses leap seconds (again we are
1889
using the only index in this table).
1891
table= tz_tables->table;
1892
tz_tables= tz_tables->next_local;
1893
table->field[0]->store((longlong) tzid, TRUE);
1894
(void)table->file->ha_index_init(0, 1);
1896
if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
1897
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1899
sql_print_error("Can't find description of time zone '%u'", tzid);
1903
/* If Uses_leap_seconds == 'Y' */
1904
if (table->field[1]->val_int() == 1)
1906
tmp_tz_info.leapcnt= tz_leapcnt;
1907
tmp_tz_info.lsis= tz_lsis;
1910
(void)table->file->ha_index_end();
1913
Now we will iterate through records for out time zone in
1914
mysql.time_zone_transition_type table. Because we want records
1915
only for our time zone guess what are we doing?
1916
Right - using special index.
1918
table= tz_tables->table;
1919
tz_tables= tz_tables->next_local;
1920
table->field[0]->store((longlong) tzid, TRUE);
1921
(void)table->file->ha_index_init(0, 1);
1923
res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
1924
(key_part_map)1, HA_READ_KEY_EXACT);
1927
ttid= (uint)table->field[1]->val_int();
1929
if (ttid >= TZ_MAX_TYPES)
1931
sql_print_error("Error while loading time zone description from "
1932
"mysql.time_zone_transition_type table: too big "
1933
"transition type id");
1937
ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int();
1938
ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0);
1940
#ifdef ABBR_ARE_USED
1941
// FIXME should we do something with duplicates here ?
1942
table->field[4]->val_str(&abbr, &abbr);
1943
if (tmp_tz_info.charcnt + abbr.length() + 1 > sizeof(chars))
1945
sql_print_error("Error while loading time zone description from "
1946
"mysql.time_zone_transition_type table: not enough "
1947
"room for abbreviations");
1950
ttis[ttid].tt_abbrind= tmp_tz_info.charcnt;
1951
memcpy(chars + tmp_tz_info.charcnt, abbr.ptr(), abbr.length());
1952
tmp_tz_info.charcnt+= abbr.length();
1953
chars[tmp_tz_info.charcnt]= 0;
1954
tmp_tz_info.charcnt++;
1957
("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1958
"abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff,
1959
chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst));
1962
("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1963
"tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst));
1966
/* ttid is increasing because we are reading using index */
1967
DBUG_ASSERT(ttid >= tmp_tz_info.typecnt);
1969
tmp_tz_info.typecnt= ttid + 1;
1971
res= table->file->index_next_same(table->record[0],
1972
table->field[0]->ptr, 4);
1975
if (res != HA_ERR_END_OF_FILE)
1977
sql_print_error("Error while loading time zone description from "
1978
"mysql.time_zone_transition_type table");
1982
(void)table->file->ha_index_end();
1986
At last we are doing the same thing for records in
1987
mysql.time_zone_transition table. Here we additionaly need records
1988
in ascending order by index scan also satisfies us.
1990
table= tz_tables->table;
1991
table->field[0]->store((longlong) tzid, TRUE);
1992
(void)table->file->ha_index_init(0, 1);
1994
res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
1995
(key_part_map)1, HA_READ_KEY_EXACT);
1998
ttime= (my_time_t)table->field[1]->val_int();
1999
ttid= (uint)table->field[2]->val_int();
2001
if (tmp_tz_info.timecnt + 1 > TZ_MAX_TIMES)
2003
sql_print_error("Error while loading time zone description from "
2004
"mysql.time_zone_transition table: "
2005
"too much transitions");
2008
if (ttid + 1 > tmp_tz_info.typecnt)
2010
sql_print_error("Error while loading time zone description from "
2011
"mysql.time_zone_transition table: "
2012
"bad transition type id");
2016
ats[tmp_tz_info.timecnt]= ttime;
2017
types[tmp_tz_info.timecnt]= ttid;
2018
tmp_tz_info.timecnt++;
2021
("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
2022
tzid, (ulong) ttime, ttid));
2024
res= table->file->index_next_same(table->record[0],
2025
table->field[0]->ptr, 4);
2029
We have to allow HA_ERR_KEY_NOT_FOUND because some time zones
2030
for example UTC have no transitons.
2032
if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
2034
sql_print_error("Error while loading time zone description from "
2035
"mysql.time_zone_transition table");
2039
(void)table->file->ha_index_end();
2043
Let us check how correct our time zone description is. We don't check for
2044
tz->timecnt < 1 since it is ok for GMT.
2046
if (tmp_tz_info.typecnt < 1)
2048
sql_print_error("loading time zone without transition types");
2052
/* Allocate memory for the timezone info and timezone name in tz_storage. */
2053
if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
2054
tz_name->length() + 1)))
2056
sql_print_error("Out of memory while loading time zone description");
2060
/* Move the temporary tz_info into the allocated area */
2061
tz_info= (TIME_ZONE_INFO *)alloc_buff;
2062
memcpy(tz_info, &tmp_tz_info, sizeof(TIME_ZONE_INFO));
2063
tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
2065
By writing zero to the end we guarantee that we can call ptr()
2066
instead of c_ptr() for time zone name.
2068
strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
2071
Now we will allocate memory and init TIME_ZONE_INFO structure.
2073
if (!(alloc_buff= (char*) alloc_root(&tz_storage,
2074
ALIGN_SIZE(sizeof(my_time_t) *
2076
ALIGN_SIZE(tz_info->timecnt) +
2077
#ifdef ABBR_ARE_USED
2078
ALIGN_SIZE(tz_info->charcnt) +
2080
sizeof(TRAN_TYPE_INFO) *
2083
sql_print_error("Out of memory while loading time zone description");
2087
tz_info->ats= (my_time_t *) alloc_buff;
2088
memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
2089
alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
2090
tz_info->types= (uchar *)alloc_buff;
2091
memcpy(tz_info->types, types, tz_info->timecnt);
2092
alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
2093
#ifdef ABBR_ARE_USED
2094
tz_info->chars= alloc_buff;
2095
memcpy(tz_info->chars, chars, tz_info->charcnt);
2096
alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
2098
tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
2099
memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
2101
/* Build reversed map. */
2102
if (prepare_tz_info(tz_info, &tz_storage))
2104
sql_print_error("Unable to build mktime map for time zone");
2109
if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) ||
2110
!(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
2111
&(tmp_tzname->name))) ||
2112
(tmp_tzname->name.set(tz_name_buff, tz_name->length(),
2113
&my_charset_latin1),
2114
my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
2116
sql_print_error("Out of memory while loading time zone");
2121
Loading of time zone succeeded
2123
return_val= tmp_tzname->tz;
2128
(void)table->file->ha_index_end();
2130
DBUG_RETURN(return_val);
2135
Parse string that specifies time zone as offset from UTC.
2139
str - pointer to string which contains offset
2140
length - length of string
2141
offset - out parameter for storing found offset in seconds.
2144
This function parses string which contains time zone offset
2145
in form similar to '+10:00' and converts found value to
2146
seconds from UTC form (east is positive).
2150
1 - String doesn't contain valid time zone offset
2153
str_to_offset(const char *str, uint length, long *offset)
2155
const char *end= str + length;
2165
else if (*str == '-')
2173
while (str < end && my_isdigit(&my_charset_latin1, *str))
2175
number_tmp= number_tmp*10 + *str - '0';
2179
if (str + 1 >= end || *str != ':')
2183
offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
2185
while (str < end && my_isdigit(&my_charset_latin1, *str))
2187
number_tmp= number_tmp * 10 + *str - '0';
2194
offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
2197
offset_tmp= -offset_tmp;
2200
Check if offset is in range prescribed by standard
2201
(from -12:59 to 13:00).
2204
if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
2205
offset_tmp > 13 * SECS_PER_HOUR)
2208
*offset= offset_tmp;
2215
Get Time_zone object for specified time zone.
2219
thd - pointer to thread THD structure
2220
name - time zone specification
2223
This function checks if name is one of time zones described in db,
2224
predefined SYSTEM time zone or valid time zone specification as
2225
offset from UTC (In last case it will create proper Time_zone_offset
2226
object if there were not any.). If name is ok it returns corresponding
2229
Clients of this function are not responsible for releasing resources
2230
occupied by returned Time_zone object so they can just forget pointers
2231
to Time_zone object if they are not needed longer.
2233
Other important property of this function: if some Time_zone found once
2234
it will be for sure found later, so this function can also be used for
2235
checking if proper Time_zone object exists (and if there will be error
2236
it will be reported during first call).
2238
If name pointer is 0 then this function returns 0 (this allows to pass 0
2239
values as parameter without additional external check and this property
2240
is used by @@time_zone variable handling code).
2242
It will perform lookup in system tables (mysql.time_zone*),
2243
opening and locking them, and closing afterwards. It won't perform
2244
such lookup if no time zone describing tables were found during
2248
Pointer to corresponding Time_zone object. 0 - in case of bad time zone
2249
specification or other error.
2253
my_tz_find(THD *thd, const String *name)
2255
Tz_names_entry *tmp_tzname;
2256
Time_zone *result_tz= 0;
2258
DBUG_ENTER("my_tz_find");
2259
DBUG_PRINT("enter", ("time zone name='%s'",
2260
name ? ((String *)name)->c_ptr_safe() : "NULL"));
2265
VOID(pthread_mutex_lock(&tz_LOCK));
2267
if (!str_to_offset(name->ptr(), name->length(), &offset))
2270
if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs,
2271
(const uchar *)&offset,
2274
DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
2276
if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
2277
my_hash_insert(&offset_tzs, (const uchar *) result_tz))
2280
sql_print_error("Fatal error: Out of memory "
2281
"while setting new time zone");
2288
if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names,
2289
(const uchar *)name->ptr(),
2291
result_tz= tmp_tzname->tz;
2292
else if (time_zone_tables_exist)
2294
TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
2295
Open_tables_state open_tables_state_backup;
2297
tz_init_table_list(tz_tables);
2298
if (!open_system_tables_for_read(thd, tz_tables,
2299
&open_tables_state_backup))
2301
result_tz= tz_load_from_open_tables(name, tz_tables);
2302
close_system_tables(thd, &open_tables_state_backup);
2307
VOID(pthread_mutex_unlock(&tz_LOCK));
2309
DBUG_RETURN(result_tz);
2314
Convert leap seconds into non-leap
2316
This function will convert the leap seconds added by the OS to
2317
non-leap seconds, e.g. 23:59:59, 23:59:60 -> 23:59:59, 00:00:01 ...
2318
This check is not checking for years on purpose : although it's not a
2319
complete check this way it doesn't require looking (and having installed)
2320
the leap seconds table.
2322
@param[in,out] broken down time structure as filled in by the OS
2325
void Time_zone::adjust_leap_second(MYSQL_TIME *t)
2327
if (t->second == 60 || t->second == 61)
2331
#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
2336
This code belongs to mysql_tzinfo_to_sql converter command line utility.
2337
This utility should be used by db admin for populating mysql.time_zone
2343
Print info about time zone described by TIME_ZONE_INFO struct as
2344
SQL statements populating mysql.time_zone* tables.
2348
tz_name - name of time zone
2349
sp - structure describing time zone
2352
print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
2356
/* Here we assume that all time zones have same leap correction tables */
2357
printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
2358
sp->leapcnt ? "Y" : "N");
2359
printf("SET @time_zone_id= LAST_INSERT_ID();\n");
2360
printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
2361
('%s', @time_zone_id);\n", tz_name);
2365
printf("INSERT INTO time_zone_transition \
2366
(Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
2367
for (i= 0; i < sp->timecnt; i++)
2368
printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
2369
(uint)sp->types[i]);
2373
printf("INSERT INTO time_zone_transition_type \
2374
(Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
2376
for (i= 0; i < sp->typecnt; i++)
2377
printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i,
2378
sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
2379
sp->chars + sp->ttis[i].tt_abbrind);
2385
Print info about leap seconds in time zone as SQL statements
2386
populating mysql.time_zone_leap_second table.
2389
print_tz_leaps_as_sql()
2390
sp - structure describing time zone
2393
print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
2398
We are assuming that there are only one list of leap seconds
2401
printf("TRUNCATE TABLE time_zone_leap_second;\n");
2405
printf("INSERT INTO time_zone_leap_second \
2406
(Transition_time, Correction) VALUES\n");
2407
for (i= 0; i < sp->leapcnt; i++)
2408
printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
2409
sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
2413
printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n");
2418
Some variables used as temporary or as parameters
2419
in recursive scan_tz_dir() code.
2421
TIME_ZONE_INFO tz_info;
2422
MEM_ROOT tz_storage;
2423
char fullname[FN_REFLEN + 1];
2424
char *root_name_end;
2428
Recursively scan zoneinfo directory and print all found time zone
2429
descriptions as SQL.
2433
name_end - pointer to end of path to directory to be searched.
2436
This auxiliary recursive function also uses several global
2437
variables as in parameters and for storing temporary values.
2439
fullname - path to directory that should be scanned.
2440
root_name_end - pointer to place in fullname where part with
2441
path to initial directory ends.
2442
current_tz_id - last used time zone id
2445
0 - Ok, 1 - Fatal error
2449
scan_tz_dir(char * name_end)
2455
if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
2458
name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
2460
for (i= 0; i < cur_dir->number_off_files; i++)
2462
if (cur_dir->dir_entry[i].name[0] != '.')
2464
name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
2465
FN_REFLEN - (name_end - fullname));
2467
if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
2469
if (scan_tz_dir(name_end_tmp))
2475
else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
2477
init_alloc_root(&tz_storage, 32768, 0);
2478
if (!tz_load(fullname, &tz_info, &tz_storage))
2479
print_tz_as_sql(root_name_end + 1, &tz_info);
2482
"Warning: Unable to load '%s' as time zone. Skipping it.\n",
2484
free_root(&tz_storage, MYF(0));
2487
fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
2499
main(int argc, char **argv)
2504
if (argc != 2 && argc != 3)
2506
fprintf(stderr, "Usage:\n");
2507
fprintf(stderr, " %s timezonedir\n", argv[0]);
2508
fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
2509
fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
2515
root_name_end= strmake(fullname, argv[1], FN_REFLEN);
2517
printf("TRUNCATE TABLE time_zone;\n");
2518
printf("TRUNCATE TABLE time_zone_name;\n");
2519
printf("TRUNCATE TABLE time_zone_transition;\n");
2520
printf("TRUNCATE TABLE time_zone_transition_type;\n");
2522
if (scan_tz_dir(root_name_end))
2524
fprintf(stderr, "There were fatal errors during processing "
2525
"of zoneinfo directory\n");
2529
printf("ALTER TABLE time_zone_transition "
2530
"ORDER BY Time_zone_id, Transition_time;\n");
2531
printf("ALTER TABLE time_zone_transition_type "
2532
"ORDER BY Time_zone_id, Transition_type_id;\n");
2536
init_alloc_root(&tz_storage, 32768, 0);
2538
if (strcmp(argv[1], "--leap") == 0)
2540
if (tz_load(argv[2], &tz_info, &tz_storage))
2542
fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2545
print_tz_leaps_as_sql(&tz_info);
2549
if (tz_load(argv[1], &tz_info, &tz_storage))
2551
fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2554
print_tz_as_sql(argv[2], &tz_info);
2557
free_root(&tz_storage, MYF(0));
2561
fprintf(stderr, "This tool has not been ported to NetWare\n");
2562
#endif /* __NETWARE__ */
2567
#endif /* defined(TZINFO2SQL) */
2573
Some simple brute-force test wich allowed to catch a pair of bugs.
2574
Also can provide interesting facts about system's time zone support
2583
#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
2587
#define TYPE_SIGNED(type) (((type) -1) < 0)
2591
is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg)
2593
return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) &&
2594
(time_arg->month == (uint)tm_arg->tm_mon+1) &&
2595
(time_arg->day == (uint)tm_arg->tm_mday) &&
2596
(time_arg->hour == (uint)tm_arg->tm_hour) &&
2597
(time_arg->minute == (uint)tm_arg->tm_min) &&
2598
(time_arg->second == (uint)tm_arg->tm_sec) &&
2599
time_arg->second_part == 0;
2604
main(int argc, char **argv)
2606
my_bool localtime_negative;
2607
TIME_ZONE_INFO tz_info;
2609
MYSQL_TIME time_tmp;
2611
char fullname[FN_REFLEN+1];
2613
MEM_ROOT tz_storage;
2617
init_alloc_root(&tz_storage, 32768, 0);
2619
/* let us set some well known timezone */
2620
setenv("TZ", "MET", 1);
2623
/* Some initial time zone related system info */
2624
printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned",
2625
(uint)TYPE_BIT(time_t));
2626
if (TYPE_SIGNED(time_t))
2629
localtime_negative= test(localtime_r(&t, &tmp) != 0);
2630
printf("localtime_r %s negative params \
2631
(time_t=%d is %d-%d-%d %d:%d:%d)\n",
2632
(localtime_negative ? "supports" : "doesn't support"), (int)t,
2633
TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday,
2634
tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
2636
printf("mktime %s negative results (%d)\n",
2637
(t == mktime(&tmp) ? "doesn't support" : "supports"),
2641
tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30;
2642
tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1;
2644
printf("mktime returns %s for spring time gap (%d)\n",
2645
(t != (time_t)-1 ? "something" : "error"), (int)t);
2647
tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2648
tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0;
2650
printf("mktime returns %s for non existing date (%d)\n",
2651
(t != (time_t)-1 ? "something" : "error"), (int)t);
2653
tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2654
tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1;
2656
printf("mktime %s unnormalized input (%d)\n",
2657
(t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t);
2659
tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26;
2660
tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1;
2662
tmp.tm_hour= 2; tmp.tm_isdst= -1;
2664
tmp.tm_hour= 4; tmp.tm_isdst= 0;
2666
tmp.tm_hour= 2; tmp.tm_isdst= -1;
2668
printf("mktime is %s (%d %d)\n",
2669
(t == t1 ? "determenistic" : "is non-determenistic"),
2672
/* Let us load time zone description */
2673
str_end= strmake(fullname, TZDIR, FN_REFLEN);
2674
strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
2676
if (tz_load(fullname, &tz_info, &tz_storage))
2678
printf("Unable to load time zone info from '%s'\n", fullname);
2679
free_root(&tz_storage, MYF(0));
2683
printf("Testing our implementation\n");
2685
if (TYPE_SIGNED(time_t) && localtime_negative)
2687
for (t= -40000; t < 20000; t++)
2689
localtime_r(&t, &tmp);
2690
gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2691
if (!is_equal_TIME_tm(&time_tmp, &tmp))
2693
printf("Problem with negative time_t = %d\n", (int)t);
2694
free_root(&tz_storage, MYF(0));
2698
printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n");
2701
for (t= 1000000000; t < 1100000000; t+= 13)
2703
localtime_r(&t,&tmp);
2704
gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2706
if (!is_equal_TIME_tm(&time_tmp, &tmp))
2708
printf("Problem with time_t = %d\n", (int)t);
2709
free_root(&tz_storage, MYF(0));
2713
printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n");
2718
Be careful here! my_system_gmt_sec doesn't fully handle unnormalized
2721
for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++)
2723
for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++)
2725
for (time_tmp.day= 1;
2726
time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1];
2729
for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++)
2731
for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5)
2733
for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25)
2737
t= (time_t)my_system_gmt_sec(&time_tmp, ¬_used, ¬_used_2);
2738
t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, ¬_used_2);
2742
We need special handling during autumn since my_system_gmt_sec
2743
prefers greater time_t values (in MET) for ambiguity.
2744
And BTW that is a bug which should be fixed !!!
2746
tmp.tm_year= time_tmp.year - TM_YEAR_BASE;
2747
tmp.tm_mon= time_tmp.month - 1;
2748
tmp.tm_mday= time_tmp.day;
2749
tmp.tm_hour= time_tmp.hour;
2750
tmp.tm_min= time_tmp.minute;
2751
tmp.tm_sec= time_tmp.second;
2759
printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n",
2760
time_tmp.year, time_tmp.month, time_tmp.day,
2761
time_tmp.hour, time_tmp.minute, time_tmp.second,
2764
free_root(&tz_storage, MYF(0));
2774
printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n");
2776
free_root(&tz_storage, MYF(0));
2780
#endif /* defined(TESTTIME) */