mySQL 5.0.11 sources for tomato
[tomato.git] / release / src / router / mysql / sql / tztime.cc
blob6f340f26d0eef4b7af53f6f964a956d38f8c7c59
1 /*
2 Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 Most of the following code and structures were derived from
20 public domain code from ftp://elsie.nci.nih.gov/pub
21 (We will refer to this code as to elsie-code further.)
25 We should not include mysql_priv.h in mysql_tzinfo_to_sql utility since
26 it creates unsolved link dependencies on some platforms.
29 #ifdef USE_PRAGMA_IMPLEMENTATION
30 #pragma implementation // gcc: Class implementation
31 #endif
33 #include <my_global.h>
34 #if !defined(TZINFO2SQL) && !defined(TESTTIME)
35 #include "mysql_priv.h"
36 #else
37 #include <my_time.h>
38 #include "tztime.h"
39 #include <my_sys.h>
40 #endif
42 #include "tzfile.h"
43 #include <m_string.h>
44 #include <my_dir.h>
47 Now we don't use abbreviations in server but we will do this in future.
49 #if defined(TZINFO2SQL) || defined(TESTTIME)
50 #define ABBR_ARE_USED
51 #else
52 #if !defined(DBUG_OFF)
53 /* Let use abbreviations for debug purposes */
54 #undef ABBR_ARE_USED
55 #define ABBR_ARE_USED
56 #endif /* !defined(DBUG_OFF) */
57 #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
59 /* Structure describing local time type (e.g. Moscow summer time (MSD)) */
60 typedef struct ttinfo
62 long tt_gmtoff; // Offset from UTC in seconds
63 uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst
64 #ifdef ABBR_ARE_USED
65 uint tt_abbrind; // Index of start of abbreviation for this time type.
66 #endif
68 We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
69 struct since we don't support POSIX-style TZ descriptions in variables.
71 } TRAN_TYPE_INFO;
73 /* Structure describing leap-second corrections. */
74 typedef struct lsinfo
76 my_time_t ls_trans; // Transition time
77 long ls_corr; // Correction to apply
78 } LS_INFO;
81 Structure with information describing ranges of my_time_t shifted to local
82 time (my_time_t + offset). Used for local MYSQL_TIME -> my_time_t conversion.
83 See comments for TIME_to_gmt_sec() for more info.
85 typedef struct revtinfo
87 long rt_offset; // Offset of local time from UTC in seconds
88 uint rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap
89 } REVT_INFO;
91 #ifdef TZNAME_MAX
92 #define MY_TZNAME_MAX TZNAME_MAX
93 #endif
94 #ifndef TZNAME_MAX
95 #define MY_TZNAME_MAX 255
96 #endif
99 Structure which fully describes time zone which is
100 described in our db or in zoneinfo files.
102 typedef struct st_time_zone_info
104 uint leapcnt; // Number of leap-second corrections
105 uint timecnt; // Number of transitions between time types
106 uint typecnt; // Number of local time types
107 uint charcnt; // Number of characters used for abbreviations
108 uint revcnt; // Number of transition descr. for TIME->my_time_t conversion
109 /* The following are dynamical arrays are allocated in MEM_ROOT */
110 my_time_t *ats; // Times of transitions between time types
111 uchar *types; // Local time types for transitions
112 TRAN_TYPE_INFO *ttis; // Local time types descriptions
113 #ifdef ABBR_ARE_USED
114 /* Storage for local time types abbreviations. They are stored as ASCIIZ */
115 char *chars;
116 #endif
118 Leap seconds corrections descriptions, this array is shared by
119 all time zones who use leap seconds.
121 LS_INFO *lsis;
123 Starting points and descriptions of shifted my_time_t (my_time_t + offset)
124 ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
125 Used for tm -> my_time_t conversion.
127 my_time_t *revts;
128 REVT_INFO *revtis;
130 Time type which is used for times smaller than first transition or if
131 there are no transitions at all.
133 TRAN_TYPE_INFO *fallback_tti;
135 } TIME_ZONE_INFO;
138 static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);
141 #if defined(TZINFO2SQL) || defined(TESTTIME)
144 Load time zone description from zoneinfo (TZinfo) file.
146 SYNOPSIS
147 tz_load()
148 name - path to zoneinfo file
149 sp - TIME_ZONE_INFO structure to fill
151 RETURN VALUES
152 0 - Ok
153 1 - Error
155 static my_bool
156 tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
158 uchar *p;
159 int read_from_file;
160 uint i;
161 FILE *file;
163 if (!(file= my_fopen(name, O_RDONLY|O_BINARY, MYF(MY_WME))))
164 return 1;
166 union
168 struct tzhead tzhead;
169 uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
170 TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
171 #ifdef ABBR_ARE_USED
172 max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
173 #endif
174 sizeof(LS_INFO) * TZ_MAX_LEAPS];
175 } u;
176 uint ttisstdcnt;
177 uint ttisgmtcnt;
178 char *tzinfo_buf;
180 read_from_file= my_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
182 if (my_fclose(file, MYF(MY_WME)) != 0)
183 return 1;
185 if (read_from_file < (int)sizeof(struct tzhead))
186 return 1;
188 ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt);
189 ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt);
190 sp->leapcnt= int4net(u.tzhead.tzh_leapcnt);
191 sp->timecnt= int4net(u.tzhead.tzh_timecnt);
192 sp->typecnt= int4net(u.tzhead.tzh_typecnt);
193 sp->charcnt= int4net(u.tzhead.tzh_charcnt);
194 p= u.tzhead.tzh_charcnt + sizeof(u.tzhead.tzh_charcnt);
195 if (sp->leapcnt > TZ_MAX_LEAPS ||
196 sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES ||
197 sp->timecnt > TZ_MAX_TIMES ||
198 sp->charcnt > TZ_MAX_CHARS ||
199 (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
200 (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
201 return 1;
202 if ((uint)(read_from_file - (p - u.buf)) <
203 sp->timecnt * 4 + /* ats */
204 sp->timecnt + /* types */
205 sp->typecnt * (4 + 2) + /* ttinfos */
206 sp->charcnt + /* chars */
207 sp->leapcnt * (4 + 4) + /* lsinfos */
208 ttisstdcnt + /* ttisstds */
209 ttisgmtcnt) /* ttisgmts */
210 return 1;
212 if (!(tzinfo_buf= (char *)alloc_root(storage,
213 ALIGN_SIZE(sp->timecnt *
214 sizeof(my_time_t)) +
215 ALIGN_SIZE(sp->timecnt) +
216 ALIGN_SIZE(sp->typecnt *
217 sizeof(TRAN_TYPE_INFO)) +
218 #ifdef ABBR_ARE_USED
219 ALIGN_SIZE(sp->charcnt) +
220 #endif
221 sp->leapcnt * sizeof(LS_INFO))))
222 return 1;
224 sp->ats= (my_time_t *)tzinfo_buf;
225 tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
226 sp->types= (uchar *)tzinfo_buf;
227 tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
228 sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
229 tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
230 #ifdef ABBR_ARE_USED
231 sp->chars= tzinfo_buf;
232 tzinfo_buf+= ALIGN_SIZE(sp->charcnt);
233 #endif
234 sp->lsis= (LS_INFO *)tzinfo_buf;
236 for (i= 0; i < sp->timecnt; i++, p+= 4)
237 sp->ats[i]= int4net(p);
239 for (i= 0; i < sp->timecnt; i++)
241 sp->types[i]= (uchar) *p++;
242 if (sp->types[i] >= sp->typecnt)
243 return 1;
245 for (i= 0; i < sp->typecnt; i++)
247 TRAN_TYPE_INFO * ttisp;
249 ttisp= &sp->ttis[i];
250 ttisp->tt_gmtoff= int4net(p);
251 p+= 4;
252 ttisp->tt_isdst= (uchar) *p++;
253 if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
254 return 1;
255 ttisp->tt_abbrind= (uchar) *p++;
256 if (ttisp->tt_abbrind > sp->charcnt)
257 return 1;
259 for (i= 0; i < sp->charcnt; i++)
260 sp->chars[i]= *p++;
261 sp->chars[i]= '\0'; /* ensure '\0' at end */
262 for (i= 0; i < sp->leapcnt; i++)
264 LS_INFO *lsisp;
266 lsisp= &sp->lsis[i];
267 lsisp->ls_trans= int4net(p);
268 p+= 4;
269 lsisp->ls_corr= int4net(p);
270 p+= 4;
273 Since we don't support POSIX style TZ definitions in variables we
274 don't read further like glibc or elsie code.
278 return prepare_tz_info(sp, storage);
280 #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
284 Finish preparation of time zone description for use in TIME_to_gmt_sec()
285 and gmt_sec_to_TIME() functions.
287 SYNOPSIS
288 prepare_tz_info()
289 sp - pointer to time zone description
290 storage - pointer to MEM_ROOT where arrays for map allocated
292 DESCRIPTION
293 First task of this function is to find fallback time type which will
294 be used if there are no transitions or we have moment in time before
295 any transitions.
296 Second task is to build "shifted my_time_t" -> my_time_t map used in
297 MYSQL_TIME -> my_time_t conversion.
298 Note: See description of TIME_to_gmt_sec() function first.
299 In order to perform MYSQL_TIME -> my_time_t conversion we need to build table
300 which defines "shifted by tz offset and leap seconds my_time_t" ->
301 my_time_t function wich is almost the same (except ranges of ambiguity)
302 as reverse function to piecewise linear function used for my_time_t ->
303 "shifted my_time_t" conversion and which is also specified as table in
304 zoneinfo file or in our db (It is specified as start of time type ranges
305 and time type offsets). So basic idea is very simple - let us iterate
306 through my_time_t space from one point of discontinuity of my_time_t ->
307 "shifted my_time_t" function to another and build our approximation of
308 reverse function. (Actually we iterate through ranges on which
309 my_time_t -> "shifted my_time_t" is linear function).
311 RETURN VALUES
312 0 Ok
313 1 Error
315 static my_bool
316 prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
318 my_time_t cur_t= MY_TIME_T_MIN;
319 my_time_t cur_l, end_t, end_l;
320 my_time_t cur_max_seen_l= MY_TIME_T_MIN;
321 long cur_offset, cur_corr, cur_off_and_corr;
322 uint next_trans_idx, next_leap_idx;
323 uint i;
325 Temporary arrays where we will store tables. Needed because
326 we don't know table sizes ahead. (Well we can estimate their
327 upper bound but this will take extra space.)
329 my_time_t revts[TZ_MAX_REV_RANGES];
330 REVT_INFO revtis[TZ_MAX_REV_RANGES];
332 LINT_INIT(end_l);
335 Let us setup fallback time type which will be used if we have not any
336 transitions or if we have moment of time before first transition.
337 We will find first non-DST local time type and use it (or use first
338 local time type if all of them are DST types).
340 for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
341 /* no-op */ ;
342 if (i == sp->typecnt)
343 i= 0;
344 sp->fallback_tti= &(sp->ttis[i]);
348 Let us build shifted my_time_t -> my_time_t map.
350 sp->revcnt= 0;
352 /* Let us find initial offset */
353 if (sp->timecnt == 0 || cur_t < sp->ats[0])
356 If we have not any transitions or t is before first transition we are using
357 already found fallback time type which index is already in i.
359 next_trans_idx= 0;
361 else
363 /* cur_t == sp->ats[0] so we found transition */
364 i= sp->types[0];
365 next_trans_idx= 1;
368 cur_offset= sp->ttis[i].tt_gmtoff;
371 /* let us find leap correction... unprobable, but... */
372 for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
373 cur_t >= sp->lsis[next_leap_idx].ls_trans;
374 ++next_leap_idx)
375 continue;
377 if (next_leap_idx > 0)
378 cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
379 else
380 cur_corr= 0;
382 /* Iterate trough t space */
383 while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
385 cur_off_and_corr= cur_offset - cur_corr;
388 We assuming that cur_t could be only overflowed downwards,
389 we also assume that end_t won't be overflowed in this case.
391 if (cur_off_and_corr < 0 &&
392 cur_t < MY_TIME_T_MIN - cur_off_and_corr)
393 cur_t= MY_TIME_T_MIN - cur_off_and_corr;
395 cur_l= cur_t + cur_off_and_corr;
398 Let us choose end_t as point before next time type change or leap
399 second correction.
401 end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
402 MY_TIME_T_MAX,
403 (next_leap_idx < sp->leapcnt) ?
404 sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
406 again assuming that end_t can be overlowed only in positive side
407 we also assume that end_t won't be overflowed in this case.
409 if (cur_off_and_corr > 0 &&
410 end_t > MY_TIME_T_MAX - cur_off_and_corr)
411 end_t= MY_TIME_T_MAX - cur_off_and_corr;
413 end_l= end_t + cur_off_and_corr;
416 if (end_l > cur_max_seen_l)
418 /* We want special handling in the case of first range */
419 if (cur_max_seen_l == MY_TIME_T_MIN)
421 revts[sp->revcnt]= cur_l;
422 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
423 revtis[sp->revcnt].rt_type= 0;
424 sp->revcnt++;
425 cur_max_seen_l= end_l;
427 else
429 if (cur_l > cur_max_seen_l + 1)
431 /* We have a spring time-gap and we are not at the first range */
432 revts[sp->revcnt]= cur_max_seen_l + 1;
433 revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
434 revtis[sp->revcnt].rt_type= 1;
435 sp->revcnt++;
436 if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
437 break; /* That was too much */
438 cur_max_seen_l= cur_l - 1;
441 /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */
443 revts[sp->revcnt]= cur_max_seen_l + 1;
444 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
445 revtis[sp->revcnt].rt_type= 0;
446 sp->revcnt++;
447 cur_max_seen_l= end_l;
451 if (end_t == MY_TIME_T_MAX ||
452 ((cur_off_and_corr > 0) &&
453 (end_t >= MY_TIME_T_MAX - cur_off_and_corr)))
454 /* end of t space */
455 break;
457 cur_t= end_t + 1;
460 Let us find new offset and correction. Because of our choice of end_t
461 cur_t can only be point where new time type starts or/and leap
462 correction is performed.
464 if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
465 if (next_trans_idx < sp->timecnt &&
466 cur_t == sp->ats[next_trans_idx])
468 /* We are at offset point */
469 cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
470 ++next_trans_idx;
473 if (next_leap_idx < sp->leapcnt &&
474 cur_t == sp->lsis[next_leap_idx].ls_trans)
476 /* we are at leap point */
477 cur_corr= sp->lsis[next_leap_idx].ls_corr;
478 ++next_leap_idx;
482 /* check if we have had enough space */
483 if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
484 return 1;
486 /* set maximum end_l as finisher */
487 revts[sp->revcnt]= end_l;
489 /* Allocate arrays of proper size in sp and copy result there */
490 if (!(sp->revts= (my_time_t *)alloc_root(storage,
491 sizeof(my_time_t) * (sp->revcnt + 1))) ||
492 !(sp->revtis= (REVT_INFO *)alloc_root(storage,
493 sizeof(REVT_INFO) * sp->revcnt)))
494 return 1;
496 memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
497 memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
499 return 0;
503 #if !defined(TZINFO2SQL)
505 static const uint mon_lengths[2][MONS_PER_YEAR]=
507 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
508 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
511 static const uint mon_starts[2][MONS_PER_YEAR]=
513 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
514 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
517 static const uint year_lengths[2]=
519 DAYS_PER_NYEAR, DAYS_PER_LYEAR
522 #define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
526 Converts time from my_time_t representation (seconds in UTC since Epoch)
527 to broken down representation using given local time zone offset.
529 SYNOPSIS
530 sec_to_TIME()
531 tmp - pointer to structure for broken down representation
532 t - my_time_t value to be converted
533 offset - local time zone offset
535 DESCRIPTION
536 Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
537 (from elsie code) because doesn't contain any leap correction and
538 TM_GMTOFF and is_dst setting and contains some MySQL specific
539 initialization. Funny but with removing of these we almost have
540 glibc's offtime function.
542 static void
543 sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
545 long days;
546 long rem;
547 int y;
548 int yleap;
549 const uint *ip;
551 days= (long) (t / SECS_PER_DAY);
552 rem= (long) (t % SECS_PER_DAY);
555 We do this as separate step after dividing t, because this
556 allows us handle times near my_time_t bounds without overflows.
558 rem+= offset;
559 while (rem < 0)
561 rem+= SECS_PER_DAY;
562 days--;
564 while (rem >= SECS_PER_DAY)
566 rem -= SECS_PER_DAY;
567 days++;
569 tmp->hour= (uint)(rem / SECS_PER_HOUR);
570 rem= rem % SECS_PER_HOUR;
571 tmp->minute= (uint)(rem / SECS_PER_MIN);
573 A positive leap second requires a special
574 representation. This uses "... ??:59:60" et seq.
576 tmp->second= (uint)(rem % SECS_PER_MIN);
578 y= EPOCH_YEAR;
579 while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
581 int newy;
583 newy= y + days / DAYS_PER_NYEAR;
584 if (days < 0)
585 newy--;
586 days-= (newy - y) * DAYS_PER_NYEAR +
587 LEAPS_THRU_END_OF(newy - 1) -
588 LEAPS_THRU_END_OF(y - 1);
589 y= newy;
591 tmp->year= y;
593 ip= mon_lengths[yleap];
594 for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
595 days= days - (long) ip[tmp->month];
596 tmp->month++;
597 tmp->day= (uint)(days + 1);
599 /* filling MySQL specific MYSQL_TIME members */
600 tmp->neg= 0; tmp->second_part= 0;
601 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
606 Find time range wich contains given my_time_t value
608 SYNOPSIS
609 find_time_range()
610 t - my_time_t value for which we looking for range
611 range_boundaries - sorted array of range starts.
612 higher_bound - number of ranges
614 DESCRIPTION
615 Performs binary search for range which contains given my_time_t value.
616 It has sense if number of ranges is greater than zero and my_time_t value
617 is greater or equal than beginning of first range. It also assumes that
618 t belongs to some range specified or end of last is MY_TIME_T_MAX.
620 With this localtime_r on real data may takes less time than with linear
621 search (I've seen 30% speed up).
623 RETURN VALUE
624 Index of range to which t belongs
626 static uint
627 find_time_range(my_time_t t, const my_time_t *range_boundaries,
628 uint higher_bound)
630 uint i, lower_bound= 0;
633 Function will work without this assertion but result would be meaningless.
635 DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
638 Do binary search for minimal interval which contain t. We preserve:
639 range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
640 invariant and decrease this higher_bound - lower_bound gap twice
641 times on each step.
644 while (higher_bound - lower_bound > 1)
646 i= (lower_bound + higher_bound) >> 1;
647 if (range_boundaries[i] <= t)
648 lower_bound= i;
649 else
650 higher_bound= i;
652 return lower_bound;
656 Find local time transition for given my_time_t.
658 SYNOPSIS
659 find_transition_type()
660 t - my_time_t value to be converted
661 sp - pointer to struct with time zone description
663 RETURN VALUE
664 Pointer to structure in time zone description describing
665 local time type for given my_time_t.
667 static
668 const TRAN_TYPE_INFO *
669 find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
671 if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
674 If we have not any transitions or t is before first transition let
675 us use fallback time type.
677 return sp->fallback_tti;
681 Do binary search for minimal interval between transitions which
682 contain t. With this localtime_r on real data may takes less
683 time than with linear search (I've seen 30% speed up).
685 return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
690 Converts time in my_time_t representation (seconds in UTC since Epoch) to
691 broken down MYSQL_TIME representation in local time zone.
693 SYNOPSIS
694 gmt_sec_to_TIME()
695 tmp - pointer to structure for broken down represenatation
696 sec_in_utc - my_time_t value to be converted
697 sp - pointer to struct with time zone description
699 TODO
700 We can improve this function by creating joined array of transitions and
701 leap corrections. This will require adding extra field to TRAN_TYPE_INFO
702 for storing number of "extra" seconds to minute occured due to correction
703 (60th and 61st second, look how we calculate them as "hit" in this
704 function).
705 Under realistic assumptions about frequency of transitions the same array
706 can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
707 implement tweaked binary search which will take into account that some
708 MYSQL_TIME has two matching my_time_t ranges and some of them have none.
710 static void
711 gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
713 const TRAN_TYPE_INFO *ttisp;
714 const LS_INFO *lp;
715 long corr= 0;
716 int hit= 0;
717 int i;
720 Find proper transition (and its local time type) for our sec_in_utc value.
721 Funny but again by separating this step in function we receive code
722 which very close to glibc's code. No wonder since they obviously use
723 the same base and all steps are sensible.
725 ttisp= find_transition_type(sec_in_utc, sp);
728 Let us find leap correction for our sec_in_utc value and number of extra
729 secs to add to this minute.
730 This loop is rarely used because most users will use time zones without
731 leap seconds, and even in case when we have such time zone there won't
732 be many iterations (we have about 22 corrections at this moment (2004)).
734 for ( i= sp->leapcnt; i-- > 0; )
736 lp= &sp->lsis[i];
737 if (sec_in_utc >= lp->ls_trans)
739 if (sec_in_utc == lp->ls_trans)
741 hit= ((i == 0 && lp->ls_corr > 0) ||
742 lp->ls_corr > sp->lsis[i - 1].ls_corr);
743 if (hit)
745 while (i > 0 &&
746 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
747 sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
749 hit++;
750 i--;
754 corr= lp->ls_corr;
755 break;
759 sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
761 tmp->second+= hit;
766 Converts local time in broken down representation to local
767 time zone analog of my_time_t represenation.
769 SYNOPSIS
770 sec_since_epoch()
771 year, mon, mday, hour, min, sec - broken down representation.
773 DESCRIPTION
774 Converts time in broken down representation to my_time_t representation
775 ignoring time zone. Note that we cannot convert back some valid _local_
776 times near ends of my_time_t range because of my_time_t overflow. But we
777 ignore this fact now since MySQL will never pass such argument.
779 RETURN VALUE
780 Seconds since epoch time representation.
782 static my_time_t
783 sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
785 /* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
786 DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
787 #ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
789 It turns out that only whenever month is normalized or unnormalized
790 plays role.
792 DBUG_ASSERT(mon > 0 && mon < 13);
793 long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
794 LEAPS_THRU_END_OF(year - 1) -
795 LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
796 days+= mon_starts[isleap(year)][mon - 1];
797 #else
798 long norm_month= (mon - 1) % MONS_PER_YEAR;
799 long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
800 long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
801 LEAPS_THRU_END_OF(a_year - 1) -
802 LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
803 days+= mon_starts[isleap(a_year)]
804 [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
805 #endif
806 days+= mday - 1;
808 return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
809 SECS_PER_MIN + sec;
813 Converts local time in broken down MYSQL_TIME representation to my_time_t
814 representation.
816 SYNOPSIS
817 TIME_to_gmt_sec()
818 t - pointer to structure for broken down represenatation
819 sp - pointer to struct with time zone description
820 in_dst_time_gap - pointer to bool which is set to true if datetime
821 value passed doesn't really exist (i.e. falls into
822 spring time-gap) and is not touched otherwise.
824 DESCRIPTION
825 This is mktime analog for MySQL. It is essentially different
826 from mktime (or hypotetical my_mktime) because:
827 - It has no idea about tm_isdst member so if it
828 has two answers it will give the smaller one
829 - If we are in spring time gap then it will return
830 beginning of the gap
831 - It can give wrong results near the ends of my_time_t due to
832 overflows, but we are safe since in MySQL we will never
833 call this function for such dates (its restriction for year
834 between 1970 and 2038 gives us several days of reserve).
835 - By default it doesn't support un-normalized input. But if
836 sec_since_epoch() function supports un-normalized dates
837 then this function should handle un-normalized input right,
838 altough it won't normalize structure TIME.
840 Traditional approach to problem of conversion from broken down
841 representation to time_t is iterative. Both elsie's and glibc
842 implementation try to guess what time_t value should correspond to
843 this broken-down value. They perform localtime_r function on their
844 guessed value and then calculate the difference and try to improve
845 their guess. Elsie's code guesses time_t value in bit by bit manner,
846 Glibc's code tries to add difference between broken-down value
847 corresponding to guess and target broken-down value to current guess.
848 It also uses caching of last found correction... So Glibc's approach
849 is essentially faster but introduces some undetermenism (in case if
850 is_dst member of broken-down representation (tm struct) is not known
851 and we have two possible answers).
853 We use completely different approach. It is better since it is both
854 faster than iterative implementations and fully determenistic. If you
855 look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
856 of two steps:
857 The first is calculating shifted my_time_t value and the second - TIME
858 calculation from shifted my_time_t value (well it is a bit simplified
859 picture). The part in which we are interested in is my_time_t -> shifted
860 my_time_t conversion. It is piecewise linear function which is defined
861 by combination of transition times as break points and times offset
862 as changing function parameter. The possible inverse function for this
863 converison would be ambiguos but with MySQL's restrictions we can use
864 some function which is the same as inverse function on unambigiuos
865 ranges and coincides with one of branches of inverse function in
866 other ranges. Thus we just need to build table which will determine
867 this shifted my_time_t -> my_time_t conversion similar to existing
868 (my_time_t -> shifted my_time_t table). We do this in
869 prepare_tz_info function.
871 TODO
872 If we can even more improve this function. For doing this we will need to
873 build joined map of transitions and leap corrections for gmt_sec_to_TIME()
874 function (similar to revts/revtis). Under realistic assumptions about
875 frequency of transitions we can use the same array for TIME_to_gmt_sec().
876 We need to implement special version of binary search for this. Such step
877 will be beneficial to CPU cache since we will decrease data-set used for
878 conversion twice.
880 RETURN VALUE
881 Seconds in UTC since Epoch.
882 0 in case of error.
884 static my_time_t
885 TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp,
886 my_bool *in_dst_time_gap)
888 my_time_t local_t;
889 uint saved_seconds;
890 uint i;
891 int shift= 0;
893 DBUG_ENTER("TIME_to_gmt_sec");
895 if (!validate_timestamp_range(t))
896 DBUG_RETURN(0);
899 /* We need this for correct leap seconds handling */
900 if (t->second < SECS_PER_MIN)
901 saved_seconds= 0;
902 else
903 saved_seconds= t->second;
906 NOTE: to convert full my_time_t range we do a shift of the
907 boundary dates here to avoid overflow of my_time_t.
908 We use alike approach in my_system_gmt_sec().
910 However in that function we also have to take into account
911 overflow near 0 on some platforms. That's because my_system_gmt_sec
912 uses localtime_r(), which doesn't work with negative values correctly
913 on platforms with unsigned time_t (QNX). Here we don't use localtime()
914 => we negative values of local_t are ok.
917 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
920 We will pass (t->day - shift) to sec_since_epoch(), and
921 want this value to be a positive number, so we shift
922 only dates > 4.01.2038 (to avoid owerflow).
924 shift= 2;
928 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
929 t->hour, t->minute,
930 saved_seconds ? 0 : t->second);
932 /* We have at least one range */
933 DBUG_ASSERT(sp->revcnt >= 1);
935 if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
938 This means that source time can't be represented as my_time_t due to
939 limited my_time_t range.
941 DBUG_RETURN(0);
944 /* binary search for our range */
945 i= find_time_range(local_t, sp->revts, sp->revcnt);
948 As there are no offset switches at the end of TIMESTAMP range,
949 we could simply check for overflow here (and don't need to bother
950 about DST gaps etc)
952 if (shift)
954 if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
955 sp->revtis[i].rt_offset - saved_seconds))
957 DBUG_RETURN(0); /* my_time_t overflow */
959 local_t+= shift * SECS_PER_DAY;
962 if (sp->revtis[i].rt_type)
965 Oops! We are in spring time gap.
966 May be we should return error here?
967 Now we are returning my_time_t value corresponding to the
968 beginning of the gap.
970 *in_dst_time_gap= 1;
971 local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
973 else
974 local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
976 /* check for TIMESTAMP_MAX_VALUE was already done above */
977 if (local_t < TIMESTAMP_MIN_VALUE)
978 local_t= 0;
980 DBUG_RETURN(local_t);
985 End of elsie derived code.
987 #endif /* !defined(TZINFO2SQL) */
990 #if !defined(TESTTIME) && !defined(TZINFO2SQL)
993 String with names of SYSTEM time zone.
995 static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
999 Instance of this class represents local time zone used on this system
1000 (specified by TZ environment variable or via any other system mechanism).
1001 It uses system functions (localtime_r, my_system_gmt_sec) for conversion
1002 and is always available. Because of this it is used by default - if there
1003 were no explicit time zone specified. On the other hand because of this
1004 conversion methods provided by this class is significantly slower and
1005 possibly less multi-threaded-friendly than corresponding Time_zone_db
1006 methods so the latter should be preffered there it is possible.
1008 class Time_zone_system : public Time_zone
1010 public:
1011 Time_zone_system() {} /* Remove gcc warning */
1012 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1013 my_bool *in_dst_time_gap) const;
1014 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1015 virtual const String * get_name() const;
1020 Converts local time in system time zone in MYSQL_TIME representation
1021 to its my_time_t representation.
1023 SYNOPSIS
1024 TIME_to_gmt_sec()
1025 t - pointer to MYSQL_TIME structure with local time in
1026 broken-down representation.
1027 in_dst_time_gap - pointer to bool which is set to true if datetime
1028 value passed doesn't really exist (i.e. falls into
1029 spring time-gap) and is not touched otherwise.
1031 DESCRIPTION
1032 This method uses system function (localtime_r()) for conversion
1033 local time in system time zone in MYSQL_TIME structure to its my_time_t
1034 representation. Unlike the same function for Time_zone_db class
1035 it it won't handle unnormalized input properly. Still it will
1036 return lowest possible my_time_t in case of ambiguity or if we
1037 provide time corresponding to the time-gap.
1039 You should call my_init_time() function before using this function.
1041 RETURN VALUE
1042 Corresponding my_time_t value or 0 in case of error
1044 my_time_t
1045 Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1047 long not_used;
1048 return my_system_gmt_sec(t, &not_used, in_dst_time_gap);
1053 Converts time from UTC seconds since Epoch (my_time_t) representation
1054 to system local time zone broken-down representation.
1056 SYNOPSIS
1057 gmt_sec_to_TIME()
1058 tmp - pointer to MYSQL_TIME structure to fill-in
1059 t - my_time_t value to be converted
1061 NOTE
1062 We assume that value passed to this function will fit into time_t range
1063 supported by localtime_r. This conversion is putting restriction on
1064 TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
1065 for interaction with client then we can extend TIMESTAMP range down to
1066 the 1902 easily.
1068 void
1069 Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1071 struct tm tmp_tm;
1072 time_t tmp_t= (time_t)t;
1074 localtime_r(&tmp_t, &tmp_tm);
1075 localtime_to_TIME(tmp, &tmp_tm);
1076 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1077 adjust_leap_second(tmp);
1082 Get name of time zone
1084 SYNOPSIS
1085 get_name()
1087 RETURN VALUE
1088 Name of time zone as String
1090 const String *
1091 Time_zone_system::get_name() const
1093 return &tz_SYSTEM_name;
1098 Instance of this class represents UTC time zone. It uses system gmtime_r
1099 function for conversions and is always available. It is used only for
1100 my_time_t -> MYSQL_TIME conversions in various UTC_... functions, it is not
1101 intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
1103 class Time_zone_utc : public Time_zone
1105 public:
1106 Time_zone_utc() {} /* Remove gcc warning */
1107 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1108 my_bool *in_dst_time_gap) const;
1109 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1110 virtual const String * get_name() const;
1115 Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
1117 SYNOPSIS
1118 TIME_to_gmt_sec()
1119 t - pointer to MYSQL_TIME structure with local time
1120 in broken-down representation.
1121 in_dst_time_gap - pointer to bool which is set to true if datetime
1122 value passed doesn't really exist (i.e. falls into
1123 spring time-gap) and is not touched otherwise.
1125 DESCRIPTION
1126 Since Time_zone_utc is used only internally for my_time_t -> TIME
1127 conversions, this function of Time_zone interface is not implemented for
1128 this class and should not be called.
1130 RETURN VALUE
1133 my_time_t
1134 Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1136 /* Should be never called */
1137 DBUG_ASSERT(0);
1138 return 0;
1143 Converts time from UTC seconds since Epoch (my_time_t) representation
1144 to broken-down representation (also in UTC).
1146 SYNOPSIS
1147 gmt_sec_to_TIME()
1148 tmp - pointer to MYSQL_TIME structure to fill-in
1149 t - my_time_t value to be converted
1151 NOTE
1152 See note for apropriate Time_zone_system method.
1154 void
1155 Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1157 struct tm tmp_tm;
1158 time_t tmp_t= (time_t)t;
1159 gmtime_r(&tmp_t, &tmp_tm);
1160 localtime_to_TIME(tmp, &tmp_tm);
1161 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1162 adjust_leap_second(tmp);
1167 Get name of time zone
1169 SYNOPSIS
1170 get_name()
1172 DESCRIPTION
1173 Since Time_zone_utc is used only internally by SQL's UTC_* functions it
1174 is not accessible directly, and hence this function of Time_zone
1175 interface is not implemented for this class and should not be called.
1177 RETURN VALUE
1180 const String *
1181 Time_zone_utc::get_name() const
1183 /* Should be never called */
1184 DBUG_ASSERT(0);
1185 return 0;
1190 Instance of this class represents some time zone which is
1191 described in mysql.time_zone family of tables.
1193 class Time_zone_db : public Time_zone
1195 public:
1196 Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
1197 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1198 my_bool *in_dst_time_gap) const;
1199 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1200 virtual const String * get_name() const;
1201 private:
1202 TIME_ZONE_INFO *tz_info;
1203 const String *tz_name;
1208 Initializes object representing time zone described by mysql.time_zone
1209 tables.
1211 SYNOPSIS
1212 Time_zone_db()
1213 tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
1214 according to db or other time zone description
1215 (for example by my_tz_init()).
1216 Several Time_zone_db instances can share one
1217 TIME_ZONE_INFO structure.
1218 tz_name_arg - name of time zone.
1220 Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
1221 const String *tz_name_arg):
1222 tz_info(tz_info_arg), tz_name(tz_name_arg)
1228 Converts local time in time zone described from TIME
1229 representation to its my_time_t representation.
1231 SYNOPSIS
1232 TIME_to_gmt_sec()
1233 t - pointer to MYSQL_TIME structure with local time
1234 in broken-down representation.
1235 in_dst_time_gap - pointer to bool which is set to true if datetime
1236 value passed doesn't really exist (i.e. falls into
1237 spring time-gap) and is not touched otherwise.
1239 DESCRIPTION
1240 Please see ::TIME_to_gmt_sec for function description and
1241 parameter restrictions.
1243 RETURN VALUE
1244 Corresponding my_time_t value or 0 in case of error
1246 my_time_t
1247 Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1249 return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
1254 Converts time from UTC seconds since Epoch (my_time_t) representation
1255 to local time zone described in broken-down representation.
1257 SYNOPSIS
1258 gmt_sec_to_TIME()
1259 tmp - pointer to MYSQL_TIME structure to fill-in
1260 t - my_time_t value to be converted
1262 void
1263 Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1265 ::gmt_sec_to_TIME(tmp, t, tz_info);
1266 adjust_leap_second(tmp);
1271 Get name of time zone
1273 SYNOPSIS
1274 get_name()
1276 RETURN VALUE
1277 Name of time zone as ASCIIZ-string
1279 const String *
1280 Time_zone_db::get_name() const
1282 return tz_name;
1287 Instance of this class represents time zone which
1288 was specified as offset from UTC.
1290 class Time_zone_offset : public Time_zone
1292 public:
1293 Time_zone_offset(long tz_offset_arg);
1294 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1295 my_bool *in_dst_time_gap) const;
1296 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1297 virtual const String * get_name() const;
1299 This have to be public because we want to be able to access it from
1300 my_offset_tzs_get_key() function
1302 long offset;
1303 private:
1304 /* Extra reserve because of snprintf */
1305 char name_buff[7+16];
1306 String name;
1311 Initializes object representing time zone described by its offset from UTC.
1313 SYNOPSIS
1314 Time_zone_offset()
1315 tz_offset_arg - offset from UTC in seconds.
1316 Positive for direction to east.
1318 Time_zone_offset::Time_zone_offset(long tz_offset_arg):
1319 offset(tz_offset_arg)
1321 uint hours= abs((int)(offset / SECS_PER_HOUR));
1322 uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
1323 ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
1324 (offset>=0) ? "+" : "-", hours, minutes);
1325 name.set(name_buff, length, &my_charset_latin1);
1330 Converts local time in time zone described as offset from UTC
1331 from MYSQL_TIME representation to its my_time_t representation.
1333 SYNOPSIS
1334 TIME_to_gmt_sec()
1335 t - pointer to MYSQL_TIME structure with local time
1336 in broken-down representation.
1337 in_dst_time_gap - pointer to bool which should be set to true if
1338 datetime value passed doesn't really exist
1339 (i.e. falls into spring time-gap) and is not
1340 touched otherwise.
1341 It is not really used in this class.
1343 RETURN VALUE
1344 Corresponding my_time_t value or 0 in case of error
1346 my_time_t
1347 Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1349 my_time_t local_t;
1350 int shift= 0;
1353 Check timestamp range.we have to do this as calling function relies on
1354 us to make all validation checks here.
1356 if (!validate_timestamp_range(t))
1357 return 0;
1360 Do a temporary shift of the boundary dates to avoid
1361 overflow of my_time_t if the time value is near it's
1362 maximum range
1364 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
1365 shift= 2;
1367 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
1368 t->hour, t->minute, t->second) -
1369 offset;
1371 if (shift)
1373 /* Add back the shifted time */
1374 local_t+= shift * SECS_PER_DAY;
1377 if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
1378 return local_t;
1380 /* range error*/
1381 return 0;
1386 Converts time from UTC seconds since Epoch (my_time_t) representation
1387 to local time zone described as offset from UTC and in broken-down
1388 representation.
1390 SYNOPSIS
1391 gmt_sec_to_TIME()
1392 tmp - pointer to MYSQL_TIME structure to fill-in
1393 t - my_time_t value to be converted
1395 void
1396 Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1398 sec_to_TIME(tmp, t, offset);
1403 Get name of time zone
1405 SYNOPSIS
1406 get_name()
1408 RETURN VALUE
1409 Name of time zone as pointer to String object
1411 const String *
1412 Time_zone_offset::get_name() const
1414 return &name;
1418 static Time_zone_utc tz_UTC;
1419 static Time_zone_system tz_SYSTEM;
1420 static Time_zone_offset tz_OFFSET0(0);
1422 Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1423 Time_zone *my_tz_UTC= &tz_UTC;
1424 Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1426 static HASH tz_names;
1427 static HASH offset_tzs;
1428 static MEM_ROOT tz_storage;
1431 These mutex protects offset_tzs and tz_storage.
1432 These protection needed only when we are trying to set
1433 time zone which is specified as offset, and searching for existing
1434 time zone in offset_tzs or creating if it didn't existed before in
1435 tz_storage. So contention is low.
1437 static pthread_mutex_t tz_LOCK;
1438 static bool tz_inited= 0;
1441 This two static variables are inteded for holding info about leap seconds
1442 shared by all time zones.
1444 static uint tz_leapcnt= 0;
1445 static LS_INFO *tz_lsis= 0;
1448 Shows whenever we have found time zone tables during start-up.
1449 Used for avoiding of putting those tables to global table list
1450 for queries that use time zone info.
1452 static bool time_zone_tables_exist= 1;
1456 Names of tables (with their lengths) that are needed
1457 for dynamical loading of time zone descriptions.
1460 static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
1462 { C_STRING_WITH_LEN("time_zone_name")},
1463 { C_STRING_WITH_LEN("time_zone")},
1464 { C_STRING_WITH_LEN("time_zone_transition_type")},
1465 { C_STRING_WITH_LEN("time_zone_transition")}
1468 /* Name of database to which those tables belong. */
1470 static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
1473 class Tz_names_entry: public Sql_alloc
1475 public:
1476 String name;
1477 Time_zone *tz;
1482 We are going to call both of these functions from C code so
1483 they should obey C calling conventions.
1486 extern "C" uchar *
1487 my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
1488 my_bool not_used __attribute__((unused)))
1490 *length= entry->name.length();
1491 return (uchar*) entry->name.ptr();
1494 extern "C" uchar *
1495 my_offset_tzs_get_key(Time_zone_offset *entry,
1496 size_t *length,
1497 my_bool not_used __attribute__((unused)))
1499 *length= sizeof(long);
1500 return (uchar*) &entry->offset;
1505 Prepare table list with time zone related tables from preallocated array.
1507 SYNOPSIS
1508 tz_init_table_list()
1509 tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT
1510 TABLE_LIST objects
1512 DESCRIPTION
1513 This function prepares list of TABLE_LIST objects which can be used
1514 for opening of time zone tables from preallocated array.
1517 static void
1518 tz_init_table_list(TABLE_LIST *tz_tabs)
1520 bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT);
1522 for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
1524 tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str;
1525 tz_tabs[i].table_name_length= tz_tables_names[i].length;
1526 tz_tabs[i].db= tz_tables_db_name.str;
1527 tz_tabs[i].db_length= tz_tables_db_name.length;
1528 tz_tabs[i].lock_type= TL_READ;
1530 if (i != MY_TZ_TABLES_COUNT - 1)
1531 tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
1532 if (i != 0)
1533 tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
1539 Initialize time zone support infrastructure.
1541 SYNOPSIS
1542 my_tz_init()
1543 thd - current thread object
1544 default_tzname - default time zone or 0 if none.
1545 bootstrap - indicates whenever we are in bootstrap mode
1547 DESCRIPTION
1548 This function will init memory structures needed for time zone support,
1549 it will register mandatory SYSTEM time zone in them. It will try to open
1550 mysql.time_zone* tables and load information about default time zone and
1551 information which further will be shared among all time zones loaded.
1552 If system tables with time zone descriptions don't exist it won't fail
1553 (unless default_tzname is time zone from tables). If bootstrap parameter
1554 is true then this routine assumes that we are in bootstrap mode and won't
1555 load time zone descriptions unless someone specifies default time zone
1556 which is supposedly stored in those tables.
1557 It'll also set default time zone if it is specified.
1559 RETURN VALUES
1560 0 - ok
1561 1 - Error
1563 my_bool
1564 my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
1566 THD *thd;
1567 TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
1568 Open_tables_state open_tables_state_backup;
1569 TABLE *table;
1570 Tz_names_entry *tmp_tzname;
1571 my_bool return_val= 1;
1572 char db[]= "mysql";
1573 int res;
1574 DBUG_ENTER("my_tz_init");
1577 To be able to run this from boot, we allocate a temporary THD
1579 if (!(thd= new THD))
1580 DBUG_RETURN(1);
1581 thd->thread_stack= (char*) &thd;
1582 thd->store_globals();
1583 lex_start(thd);
1585 /* Init all memory structures that require explicit destruction */
1586 if (hash_init(&tz_names, &my_charset_latin1, 20,
1587 0, 0, (hash_get_key) my_tz_names_get_key, 0, 0))
1589 sql_print_error("Fatal error: OOM while initializing time zones");
1590 goto end;
1592 if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
1593 (hash_get_key)my_offset_tzs_get_key, 0, 0))
1595 sql_print_error("Fatal error: OOM while initializing time zones");
1596 hash_free(&tz_names);
1597 goto end;
1599 init_alloc_root(&tz_storage, 32 * 1024, 0);
1600 VOID(pthread_mutex_init(&tz_LOCK, MY_MUTEX_INIT_FAST));
1601 tz_inited= 1;
1603 /* Add 'SYSTEM' time zone to tz_names hash */
1604 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()))
1606 sql_print_error("Fatal error: OOM while initializing time zones");
1607 goto end_with_cleanup;
1609 tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
1610 tmp_tzname->tz= my_tz_SYSTEM;
1611 if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
1613 sql_print_error("Fatal error: OOM while initializing time zones");
1614 goto end_with_cleanup;
1617 if (bootstrap)
1619 /* If we are in bootstrap mode we should not load time zone tables */
1620 return_val= time_zone_tables_exist= 0;
1621 goto end_with_setting_default_tz;
1625 After this point all memory structures are inited and we even can live
1626 without time zone description tables. Now try to load information about
1627 leap seconds shared by all time zones.
1630 thd->set_db(db, sizeof(db)-1);
1631 bzero((char*) &tz_tables[0], sizeof(TABLE_LIST));
1632 tz_tables[0].alias= tz_tables[0].table_name=
1633 (char*)"time_zone_leap_second";
1634 tz_tables[0].table_name_length= 21;
1635 tz_tables[0].db= db;
1636 tz_tables[0].db_length= sizeof(db)-1;
1637 tz_tables[0].lock_type= TL_READ;
1639 tz_init_table_list(tz_tables+1);
1640 tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
1641 tz_tables[1].prev_global= &tz_tables[0].next_global;
1644 We need to open only mysql.time_zone_leap_second, but we try to
1645 open all time zone tables to see if they exist.
1647 if (open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup))
1649 sql_print_warning("Can't open and lock time zone table: %s "
1650 "trying to live without them", thd->main_da.message());
1651 /* We will try emulate that everything is ok */
1652 return_val= time_zone_tables_exist= 0;
1653 goto end_with_setting_default_tz;
1657 Now we are going to load leap seconds descriptions that are shared
1658 between all time zones that use them. We are using index for getting
1659 records in proper order. Since we share the same MEM_ROOT between
1660 all time zones we just allocate enough memory for it first.
1662 if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
1663 sizeof(LS_INFO) * TZ_MAX_LEAPS)))
1665 sql_print_error("Fatal error: Out of memory while loading "
1666 "mysql.time_zone_leap_second table");
1667 goto end_with_close;
1670 table= tz_tables[0].table;
1672 It is OK to ignore ha_index_init()/ha_index_end() return values since
1673 mysql.time_zone* tables are MyISAM and these operations always succeed
1674 for MyISAM.
1676 (void)table->file->ha_index_init(0, 1);
1677 table->use_all_columns();
1679 tz_leapcnt= 0;
1681 res= table->file->index_first(table->record[0]);
1683 while (!res)
1685 if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
1687 sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
1688 " table: too much leaps");
1689 table->file->ha_index_end();
1690 goto end_with_close;
1693 tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
1694 tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();
1696 tz_leapcnt++;
1698 DBUG_PRINT("info",
1699 ("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld",
1700 tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
1701 tz_lsis[tz_leapcnt-1].ls_corr));
1703 res= table->file->index_next(table->record[0]);
1706 (void)table->file->ha_index_end();
1708 if (res != HA_ERR_END_OF_FILE)
1710 sql_print_error("Fatal error: Error while loading "
1711 "mysql.time_zone_leap_second table");
1712 goto end_with_close;
1716 Loading of info about leap seconds succeeded
1719 return_val= 0;
1722 end_with_setting_default_tz:
1723 /* If we have default time zone try to load it */
1724 if (default_tzname)
1726 String tmp_tzname2(default_tzname, &my_charset_latin1);
1728 Time zone tables may be open here, and my_tz_find() may open
1729 most of them once more, but this is OK for system tables open
1730 for READ.
1732 if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
1734 sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
1735 default_tzname);
1736 return_val= 1;
1740 end_with_close:
1741 if (time_zone_tables_exist)
1743 thd->version--; /* Force close to free memory */
1744 close_system_tables(thd, &open_tables_state_backup);
1747 end_with_cleanup:
1749 /* if there were error free time zone describing structs */
1750 if (return_val)
1751 my_tz_free();
1752 end:
1753 delete thd;
1754 if (org_thd)
1755 org_thd->store_globals(); /* purecov: inspected */
1756 else
1758 /* Remember that we don't have a THD */
1759 my_pthread_setspecific_ptr(THR_THD, 0);
1760 my_pthread_setspecific_ptr(THR_MALLOC, 0);
1762 DBUG_RETURN(return_val);
1767 Free resources used by time zone support infrastructure.
1769 SYNOPSIS
1770 my_tz_free()
1773 void my_tz_free()
1775 if (tz_inited)
1777 tz_inited= 0;
1778 VOID(pthread_mutex_destroy(&tz_LOCK));
1779 hash_free(&offset_tzs);
1780 hash_free(&tz_names);
1781 free_root(&tz_storage, MYF(0));
1787 Load time zone description from system tables.
1789 SYNOPSIS
1790 tz_load_from_open_tables()
1791 tz_name - name of time zone that should be loaded.
1792 tz_tables - list of tables from which time zone description
1793 should be loaded
1795 DESCRIPTION
1796 This function will try to load information about time zone specified
1797 from the list of the already opened and locked tables (first table in
1798 tz_tables should be time_zone_name, next time_zone, then
1799 time_zone_transition_type and time_zone_transition should be last).
1800 It will also update information in hash used for time zones lookup.
1802 RETURN VALUES
1803 Returns pointer to newly created Time_zone object or 0 in case of error.
1807 static Time_zone*
1808 tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
1810 TABLE *table= 0;
1811 TIME_ZONE_INFO *tz_info= NULL;
1812 Tz_names_entry *tmp_tzname;
1813 Time_zone *return_val= 0;
1814 int res;
1815 uint tzid, ttid;
1816 my_time_t ttime;
1817 char buff[MAX_FIELD_WIDTH];
1818 String abbr(buff, sizeof(buff), &my_charset_latin1);
1819 char *alloc_buff= NULL;
1820 char *tz_name_buff= NULL;
1822 Temporary arrays that are used for loading of data for filling
1823 TIME_ZONE_INFO structure
1825 my_time_t ats[TZ_MAX_TIMES];
1826 uchar types[TZ_MAX_TIMES];
1827 TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
1828 #ifdef ABBR_ARE_USED
1829 char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
1830 #endif
1832 Used as a temporary tz_info until we decide that we actually want to
1833 allocate and keep the tz info and tz name in tz_storage.
1835 TIME_ZONE_INFO tmp_tz_info;
1836 memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
1838 DBUG_ENTER("tz_load_from_open_tables");
1841 Let us find out time zone id by its name (there is only one index
1842 and it is specifically for this purpose).
1844 table= tz_tables->table;
1845 tz_tables= tz_tables->next_local;
1846 table->field[0]->store(tz_name->ptr(), tz_name->length(),
1847 &my_charset_latin1);
1849 It is OK to ignore ha_index_init()/ha_index_end() return values since
1850 mysql.time_zone* tables are MyISAM and these operations always succeed
1851 for MyISAM.
1853 (void)table->file->ha_index_init(0, 1);
1855 if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
1856 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1858 #ifdef EXTRA_DEBUG
1860 Most probably user has mistyped time zone name, so no need to bark here
1861 unless we need it for debugging.
1863 sql_print_error("Can't find description of time zone '%.*s'",
1864 tz_name->length(), tz_name->ptr());
1865 #endif
1866 goto end;
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);
1887 goto end;
1890 /* If Uses_leap_seconds == 'Y' */
1891 if (table->field[1]->val_int() == 1)
1893 tmp_tz_info.leapcnt= tz_leapcnt;
1894 tmp_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);
1912 while (!res)
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");
1921 goto end;
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 (tmp_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");
1935 goto end;
1937 ttis[ttid].tt_abbrind= tmp_tz_info.charcnt;
1938 memcpy(chars + tmp_tz_info.charcnt, abbr.ptr(), abbr.length());
1939 tmp_tz_info.charcnt+= abbr.length();
1940 chars[tmp_tz_info.charcnt]= 0;
1941 tmp_tz_info.charcnt++;
1943 DBUG_PRINT("info",
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));
1947 #else
1948 DBUG_PRINT("info",
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));
1951 #endif
1953 /* ttid is increasing because we are reading using index */
1954 DBUG_ASSERT(ttid >= tmp_tz_info.typecnt);
1956 tmp_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");
1966 goto end;
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);
1983 while (!res)
1985 ttime= (my_time_t)table->field[1]->val_int();
1986 ttid= (uint)table->field[2]->val_int();
1988 if (tmp_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");
1993 goto end;
1995 if (ttid + 1 > tmp_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");
2000 goto end;
2003 ats[tmp_tz_info.timecnt]= ttime;
2004 types[tmp_tz_info.timecnt]= ttid;
2005 tmp_tz_info.timecnt++;
2007 DBUG_PRINT("info",
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");
2023 goto end;
2026 (void)table->file->ha_index_end();
2027 table= 0;
2030 Let us check how correct our time zone description is. We don't check for
2031 tz->timecnt < 1 since it is ok for GMT.
2033 if (tmp_tz_info.typecnt < 1)
2035 sql_print_error("loading time zone without transition types");
2036 goto end;
2039 /* Allocate memory for the timezone info and timezone name in tz_storage. */
2040 if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
2041 tz_name->length() + 1)))
2043 sql_print_error("Out of memory while loading time zone description");
2044 return 0;
2047 /* Move the temporary tz_info into the allocated area */
2048 tz_info= (TIME_ZONE_INFO *)alloc_buff;
2049 memcpy(tz_info, &tmp_tz_info, sizeof(TIME_ZONE_INFO));
2050 tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
2052 By writing zero to the end we guarantee that we can call ptr()
2053 instead of c_ptr() for time zone name.
2055 strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
2058 Now we will allocate memory and init TIME_ZONE_INFO structure.
2060 if (!(alloc_buff= (char*) alloc_root(&tz_storage,
2061 ALIGN_SIZE(sizeof(my_time_t) *
2062 tz_info->timecnt) +
2063 ALIGN_SIZE(tz_info->timecnt) +
2064 #ifdef ABBR_ARE_USED
2065 ALIGN_SIZE(tz_info->charcnt) +
2066 #endif
2067 sizeof(TRAN_TYPE_INFO) *
2068 tz_info->typecnt)))
2070 sql_print_error("Out of memory while loading time zone description");
2071 goto end;
2074 tz_info->ats= (my_time_t *) alloc_buff;
2075 memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
2076 alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
2077 tz_info->types= (uchar *)alloc_buff;
2078 memcpy(tz_info->types, types, tz_info->timecnt);
2079 alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
2080 #ifdef ABBR_ARE_USED
2081 tz_info->chars= alloc_buff;
2082 memcpy(tz_info->chars, chars, tz_info->charcnt);
2083 alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
2084 #endif
2085 tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
2086 memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
2088 /* Build reversed map. */
2089 if (prepare_tz_info(tz_info, &tz_storage))
2091 sql_print_error("Unable to build mktime map for time zone");
2092 goto end;
2096 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) ||
2097 !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
2098 &(tmp_tzname->name))) ||
2099 (tmp_tzname->name.set(tz_name_buff, tz_name->length(),
2100 &my_charset_latin1),
2101 my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
2103 sql_print_error("Out of memory while loading time zone");
2104 goto end;
2108 Loading of time zone succeeded
2110 return_val= tmp_tzname->tz;
2112 end:
2114 if (table)
2115 (void)table->file->ha_index_end();
2117 DBUG_RETURN(return_val);
2122 Parse string that specifies time zone as offset from UTC.
2124 SYNOPSIS
2125 str_to_offset()
2126 str - pointer to string which contains offset
2127 length - length of string
2128 offset - out parameter for storing found offset in seconds.
2130 DESCRIPTION
2131 This function parses string which contains time zone offset
2132 in form similar to '+10:00' and converts found value to
2133 seconds from UTC form (east is positive).
2135 RETURN VALUE
2136 0 - Ok
2137 1 - String doesn't contain valid time zone offset
2139 my_bool
2140 str_to_offset(const char *str, uint length, long *offset)
2142 const char *end= str + length;
2143 my_bool negative;
2144 ulong number_tmp;
2145 long offset_tmp;
2147 if (length < 4)
2148 return 1;
2150 if (*str == '+')
2151 negative= 0;
2152 else if (*str == '-')
2153 negative= 1;
2154 else
2155 return 1;
2156 str++;
2158 number_tmp= 0;
2160 while (str < end && my_isdigit(&my_charset_latin1, *str))
2162 number_tmp= number_tmp*10 + *str - '0';
2163 str++;
2166 if (str + 1 >= end || *str != ':')
2167 return 1;
2168 str++;
2170 offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
2172 while (str < end && my_isdigit(&my_charset_latin1, *str))
2174 number_tmp= number_tmp * 10 + *str - '0';
2175 str++;
2178 if (str != end)
2179 return 1;
2181 offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
2183 if (negative)
2184 offset_tmp= -offset_tmp;
2187 Check if offset is in range prescribed by standard
2188 (from -12:59 to 13:00).
2191 if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
2192 offset_tmp > 13 * SECS_PER_HOUR)
2193 return 1;
2195 *offset= offset_tmp;
2197 return 0;
2202 Get Time_zone object for specified time zone.
2204 SYNOPSIS
2205 my_tz_find()
2206 thd - pointer to thread THD structure
2207 name - time zone specification
2209 DESCRIPTION
2210 This function checks if name is one of time zones described in db,
2211 predefined SYSTEM time zone or valid time zone specification as
2212 offset from UTC (In last case it will create proper Time_zone_offset
2213 object if there were not any.). If name is ok it returns corresponding
2214 Time_zone object.
2216 Clients of this function are not responsible for releasing resources
2217 occupied by returned Time_zone object so they can just forget pointers
2218 to Time_zone object if they are not needed longer.
2220 Other important property of this function: if some Time_zone found once
2221 it will be for sure found later, so this function can also be used for
2222 checking if proper Time_zone object exists (and if there will be error
2223 it will be reported during first call).
2225 If name pointer is 0 then this function returns 0 (this allows to pass 0
2226 values as parameter without additional external check and this property
2227 is used by @@time_zone variable handling code).
2229 It will perform lookup in system tables (mysql.time_zone*),
2230 opening and locking them, and closing afterwards. It won't perform
2231 such lookup if no time zone describing tables were found during
2232 server start up.
2234 RETURN VALUE
2235 Pointer to corresponding Time_zone object. 0 - in case of bad time zone
2236 specification or other error.
2239 Time_zone *
2240 my_tz_find(THD *thd, const String *name)
2242 Tz_names_entry *tmp_tzname;
2243 Time_zone *result_tz= 0;
2244 long offset;
2245 DBUG_ENTER("my_tz_find");
2246 DBUG_PRINT("enter", ("time zone name='%s'",
2247 name ? ((String *)name)->c_ptr_safe() : "NULL"));
2249 if (!name || name->is_empty())
2250 DBUG_RETURN(0);
2252 VOID(pthread_mutex_lock(&tz_LOCK));
2254 if (!str_to_offset(name->ptr(), name->length(), &offset))
2257 if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs,
2258 (const uchar *)&offset,
2259 sizeof(long))))
2261 DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
2263 if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
2264 my_hash_insert(&offset_tzs, (const uchar *) result_tz))
2266 result_tz= 0;
2267 sql_print_error("Fatal error: Out of memory "
2268 "while setting new time zone");
2272 else
2274 result_tz= 0;
2275 if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names,
2276 (const uchar *)name->ptr(),
2277 name->length())))
2278 result_tz= tmp_tzname->tz;
2279 else if (time_zone_tables_exist)
2281 TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
2282 Open_tables_state open_tables_state_backup;
2284 tz_init_table_list(tz_tables);
2285 if (!open_system_tables_for_read(thd, tz_tables,
2286 &open_tables_state_backup))
2288 result_tz= tz_load_from_open_tables(name, tz_tables);
2289 close_system_tables(thd, &open_tables_state_backup);
2294 VOID(pthread_mutex_unlock(&tz_LOCK));
2296 DBUG_RETURN(result_tz);
2301 Convert leap seconds into non-leap
2303 This function will convert the leap seconds added by the OS to
2304 non-leap seconds, e.g. 23:59:59, 23:59:60 -> 23:59:59, 00:00:01 ...
2305 This check is not checking for years on purpose : although it's not a
2306 complete check this way it doesn't require looking (and having installed)
2307 the leap seconds table.
2309 @param[in,out] broken down time structure as filled in by the OS
2312 void Time_zone::adjust_leap_second(MYSQL_TIME *t)
2314 if (t->second == 60 || t->second == 61)
2315 t->second= 59;
2318 #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
2321 #ifdef TZINFO2SQL
2323 This code belongs to mysql_tzinfo_to_sql converter command line utility.
2324 This utility should be used by db admin for populating mysql.time_zone
2325 tables.
2330 Print info about time zone described by TIME_ZONE_INFO struct as
2331 SQL statements populating mysql.time_zone* tables.
2333 SYNOPSIS
2334 print_tz_as_sql()
2335 tz_name - name of time zone
2336 sp - structure describing time zone
2338 void
2339 print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
2341 uint i;
2343 /* Here we assume that all time zones have same leap correction tables */
2344 printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
2345 sp->leapcnt ? "Y" : "N");
2346 printf("SET @time_zone_id= LAST_INSERT_ID();\n");
2347 printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
2348 ('%s', @time_zone_id);\n", tz_name);
2350 if (sp->timecnt)
2352 printf("INSERT INTO time_zone_transition \
2353 (Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
2354 for (i= 0; i < sp->timecnt; i++)
2355 printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
2356 (uint)sp->types[i]);
2357 printf(";\n");
2360 printf("INSERT INTO time_zone_transition_type \
2361 (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
2363 for (i= 0; i < sp->typecnt; i++)
2364 printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i,
2365 sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
2366 sp->chars + sp->ttis[i].tt_abbrind);
2367 printf(";\n");
2372 Print info about leap seconds in time zone as SQL statements
2373 populating mysql.time_zone_leap_second table.
2375 SYNOPSIS
2376 print_tz_leaps_as_sql()
2377 sp - structure describing time zone
2379 void
2380 print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
2382 uint i;
2385 We are assuming that there are only one list of leap seconds
2386 For all timezones.
2388 printf("TRUNCATE TABLE time_zone_leap_second;\n");
2390 if (sp->leapcnt)
2392 printf("INSERT INTO time_zone_leap_second \
2393 (Transition_time, Correction) VALUES\n");
2394 for (i= 0; i < sp->leapcnt; i++)
2395 printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
2396 sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
2397 printf(";\n");
2400 printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n");
2405 Some variables used as temporary or as parameters
2406 in recursive scan_tz_dir() code.
2408 TIME_ZONE_INFO tz_info;
2409 MEM_ROOT tz_storage;
2410 char fullname[FN_REFLEN + 1];
2411 char *root_name_end;
2415 Recursively scan zoneinfo directory and print all found time zone
2416 descriptions as SQL.
2418 SYNOPSIS
2419 scan_tz_dir()
2420 name_end - pointer to end of path to directory to be searched.
2422 DESCRIPTION
2423 This auxiliary recursive function also uses several global
2424 variables as in parameters and for storing temporary values.
2426 fullname - path to directory that should be scanned.
2427 root_name_end - pointer to place in fullname where part with
2428 path to initial directory ends.
2429 current_tz_id - last used time zone id
2431 RETURN VALUE
2432 0 - Ok, 1 - Fatal error
2435 my_bool
2436 scan_tz_dir(char * name_end)
2438 MY_DIR *cur_dir;
2439 char *name_end_tmp;
2440 uint i;
2442 if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
2443 return 1;
2445 name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
2447 for (i= 0; i < cur_dir->number_off_files; i++)
2449 if (cur_dir->dir_entry[i].name[0] != '.')
2451 name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
2452 FN_REFLEN - (name_end - fullname));
2454 if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
2456 if (scan_tz_dir(name_end_tmp))
2458 my_dirend(cur_dir);
2459 return 1;
2462 else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
2464 init_alloc_root(&tz_storage, 32768, 0);
2465 if (!tz_load(fullname, &tz_info, &tz_storage))
2466 print_tz_as_sql(root_name_end + 1, &tz_info);
2467 else
2468 fprintf(stderr,
2469 "Warning: Unable to load '%s' as time zone. Skipping it.\n",
2470 fullname);
2471 free_root(&tz_storage, MYF(0));
2473 else
2474 fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
2475 fullname);
2479 my_dirend(cur_dir);
2481 return 0;
2486 main(int argc, char **argv)
2488 #ifndef __NETWARE__
2489 MY_INIT(argv[0]);
2491 if (argc != 2 && argc != 3)
2493 fprintf(stderr, "Usage:\n");
2494 fprintf(stderr, " %s timezonedir\n", argv[0]);
2495 fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
2496 fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
2497 return 1;
2500 if (argc == 2)
2502 root_name_end= strmake(fullname, argv[1], FN_REFLEN);
2504 printf("TRUNCATE TABLE time_zone;\n");
2505 printf("TRUNCATE TABLE time_zone_name;\n");
2506 printf("TRUNCATE TABLE time_zone_transition;\n");
2507 printf("TRUNCATE TABLE time_zone_transition_type;\n");
2509 if (scan_tz_dir(root_name_end))
2511 fprintf(stderr, "There were fatal errors during processing "
2512 "of zoneinfo directory\n");
2513 return 1;
2516 printf("ALTER TABLE time_zone_transition "
2517 "ORDER BY Time_zone_id, Transition_time;\n");
2518 printf("ALTER TABLE time_zone_transition_type "
2519 "ORDER BY Time_zone_id, Transition_type_id;\n");
2521 else
2523 init_alloc_root(&tz_storage, 32768, 0);
2525 if (strcmp(argv[1], "--leap") == 0)
2527 if (tz_load(argv[2], &tz_info, &tz_storage))
2529 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2530 return 1;
2532 print_tz_leaps_as_sql(&tz_info);
2534 else
2536 if (tz_load(argv[1], &tz_info, &tz_storage))
2538 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2539 return 1;
2541 print_tz_as_sql(argv[2], &tz_info);
2544 free_root(&tz_storage, MYF(0));
2547 #else
2548 fprintf(stderr, "This tool has not been ported to NetWare\n");
2549 #endif /* __NETWARE__ */
2551 return 0;
2554 #endif /* defined(TZINFO2SQL) */
2557 #ifdef TESTTIME
2560 Some simple brute-force test wich allowed to catch a pair of bugs.
2561 Also can provide interesting facts about system's time zone support
2562 implementation.
2565 #ifndef CHAR_BIT
2566 #define CHAR_BIT 8
2567 #endif
2569 #ifndef TYPE_BIT
2570 #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
2571 #endif
2573 #ifndef TYPE_SIGNED
2574 #define TYPE_SIGNED(type) (((type) -1) < 0)
2575 #endif
2577 my_bool
2578 is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg)
2580 return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) &&
2581 (time_arg->month == (uint)tm_arg->tm_mon+1) &&
2582 (time_arg->day == (uint)tm_arg->tm_mday) &&
2583 (time_arg->hour == (uint)tm_arg->tm_hour) &&
2584 (time_arg->minute == (uint)tm_arg->tm_min) &&
2585 (time_arg->second == (uint)tm_arg->tm_sec) &&
2586 time_arg->second_part == 0;
2591 main(int argc, char **argv)
2593 my_bool localtime_negative;
2594 TIME_ZONE_INFO tz_info;
2595 struct tm tmp;
2596 MYSQL_TIME time_tmp;
2597 time_t t, t1, t2;
2598 char fullname[FN_REFLEN+1];
2599 char *str_end;
2600 MEM_ROOT tz_storage;
2602 MY_INIT(argv[0]);
2604 init_alloc_root(&tz_storage, 32768, 0);
2606 /* let us set some well known timezone */
2607 setenv("TZ", "MET", 1);
2608 tzset();
2610 /* Some initial time zone related system info */
2611 printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned",
2612 (uint)TYPE_BIT(time_t));
2613 if (TYPE_SIGNED(time_t))
2615 t= -100;
2616 localtime_negative= test(localtime_r(&t, &tmp) != 0);
2617 printf("localtime_r %s negative params \
2618 (time_t=%d is %d-%d-%d %d:%d:%d)\n",
2619 (localtime_negative ? "supports" : "doesn't support"), (int)t,
2620 TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday,
2621 tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
2623 printf("mktime %s negative results (%d)\n",
2624 (t == mktime(&tmp) ? "doesn't support" : "supports"),
2625 (int)mktime(&tmp));
2628 tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30;
2629 tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1;
2630 t= mktime(&tmp);
2631 printf("mktime returns %s for spring time gap (%d)\n",
2632 (t != (time_t)-1 ? "something" : "error"), (int)t);
2634 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2635 tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0;
2636 t= mktime(&tmp);
2637 printf("mktime returns %s for non existing date (%d)\n",
2638 (t != (time_t)-1 ? "something" : "error"), (int)t);
2640 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2641 tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1;
2642 t= mktime(&tmp);
2643 printf("mktime %s unnormalized input (%d)\n",
2644 (t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t);
2646 tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26;
2647 tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1;
2648 mktime(&tmp);
2649 tmp.tm_hour= 2; tmp.tm_isdst= -1;
2650 t= mktime(&tmp);
2651 tmp.tm_hour= 4; tmp.tm_isdst= 0;
2652 mktime(&tmp);
2653 tmp.tm_hour= 2; tmp.tm_isdst= -1;
2654 t1= mktime(&tmp);
2655 printf("mktime is %s (%d %d)\n",
2656 (t == t1 ? "determenistic" : "is non-determenistic"),
2657 (int)t, (int)t1);
2659 /* Let us load time zone description */
2660 str_end= strmake(fullname, TZDIR, FN_REFLEN);
2661 strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
2663 if (tz_load(fullname, &tz_info, &tz_storage))
2665 printf("Unable to load time zone info from '%s'\n", fullname);
2666 free_root(&tz_storage, MYF(0));
2667 return 1;
2670 printf("Testing our implementation\n");
2672 if (TYPE_SIGNED(time_t) && localtime_negative)
2674 for (t= -40000; t < 20000; t++)
2676 localtime_r(&t, &tmp);
2677 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2678 if (!is_equal_TIME_tm(&time_tmp, &tmp))
2680 printf("Problem with negative time_t = %d\n", (int)t);
2681 free_root(&tz_storage, MYF(0));
2682 return 1;
2685 printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n");
2688 for (t= 1000000000; t < 1100000000; t+= 13)
2690 localtime_r(&t,&tmp);
2691 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2693 if (!is_equal_TIME_tm(&time_tmp, &tmp))
2695 printf("Problem with time_t = %d\n", (int)t);
2696 free_root(&tz_storage, MYF(0));
2697 return 1;
2700 printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n");
2702 my_init_time();
2705 Be careful here! my_system_gmt_sec doesn't fully handle unnormalized
2706 dates.
2708 for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++)
2710 for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++)
2712 for (time_tmp.day= 1;
2713 time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1];
2714 time_tmp.day++)
2716 for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++)
2718 for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5)
2720 for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25)
2722 long not_used;
2723 my_bool not_used_2;
2724 t= (time_t)my_system_gmt_sec(&time_tmp, &not_used, &not_used_2);
2725 t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, &not_used_2);
2726 if (t != t1)
2729 We need special handling during autumn since my_system_gmt_sec
2730 prefers greater time_t values (in MET) for ambiguity.
2731 And BTW that is a bug which should be fixed !!!
2733 tmp.tm_year= time_tmp.year - TM_YEAR_BASE;
2734 tmp.tm_mon= time_tmp.month - 1;
2735 tmp.tm_mday= time_tmp.day;
2736 tmp.tm_hour= time_tmp.hour;
2737 tmp.tm_min= time_tmp.minute;
2738 tmp.tm_sec= time_tmp.second;
2739 tmp.tm_isdst= 1;
2741 t2= mktime(&tmp);
2743 if (t1 == t2)
2744 continue;
2746 printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n",
2747 time_tmp.year, time_tmp.month, time_tmp.day,
2748 time_tmp.hour, time_tmp.minute, time_tmp.second,
2749 (int)t,(int)t1);
2751 free_root(&tz_storage, MYF(0));
2752 return 1;
2761 printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n");
2763 free_root(&tz_storage, MYF(0));
2764 return 0;
2767 #endif /* defined(TESTTIME) */