1/*
2 Copyright (c) 2004, 2010, Oracle and/or its affiliates.
3
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.
7
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.
12
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 */
16
17/*
18 Most of the following code and structures were derived from
19 public domain code from ftp://elsie.nci.nih.gov/pub
20 (We will refer to this code as to elsie-code further.)
21*/
22
23/*
24 We should not include sql_priv.h in mysql_tzinfo_to_sql utility since
25 it creates unsolved link dependencies on some platforms.
26*/
27
28#ifdef USE_PRAGMA_IMPLEMENTATION
29#pragma implementation // gcc: Class implementation
30#endif
31
32#include "mariadb.h"
33#if !defined(TZINFO2SQL) && !defined(TESTTIME)
34#include "sql_priv.h"
35#include "unireg.h"
36#include "sql_time.h" // localtime_to_TIME
37#include "sql_base.h" // open_system_tables_for_read,
38 // close_system_tables
39#else
40#include <my_time.h>
41#include <my_sys.h>
42#include <mysql_version.h>
43#include <my_getopt.h>
44#endif
45
46#include "tztime.h"
47#include "tzfile.h"
48#include <m_string.h>
49#include <my_dir.h>
50#include <mysql/psi/mysql_file.h>
51#include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH,
52 // MYSQL_LOCK_IGNORE_TIMEOUT
53
54/*
55 Now we don't use abbreviations in server but we will do this in future.
56*/
57#if defined(TZINFO2SQL) || defined(TESTTIME)
58#define ABBR_ARE_USED
59#else
60#if !defined(DBUG_OFF)
61/* Let use abbreviations for debug purposes */
62#undef ABBR_ARE_USED
63#define ABBR_ARE_USED
64#endif /* !defined(DBUG_OFF) */
65#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
66
67#define PROGRAM_VERSION "1.1"
68
69/* Structure describing local time type (e.g. Moscow summer time (MSD)) */
70typedef struct ttinfo
71{
72 long tt_gmtoff; // Offset from UTC in seconds
73 uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst
74#ifdef ABBR_ARE_USED
75 uint tt_abbrind; // Index of start of abbreviation for this time type.
76#endif
77 /*
78 We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
79 struct since we don't support POSIX-style TZ descriptions in variables.
80 */
81} TRAN_TYPE_INFO;
82
83/* Structure describing leap-second corrections. */
84typedef struct lsinfo
85{
86 my_time_t ls_trans; // Transition time
87 long ls_corr; // Correction to apply
88} LS_INFO;
89
90/*
91 Structure with information describing ranges of my_time_t shifted to local
92 time (my_time_t + offset). Used for local MYSQL_TIME -> my_time_t conversion.
93 See comments for TIME_to_gmt_sec() for more info.
94*/
95typedef struct revtinfo
96{
97 long rt_offset; // Offset of local time from UTC in seconds
98 uint rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap
99} REVT_INFO;
100
101#ifdef TZNAME_MAX
102#define MY_TZNAME_MAX TZNAME_MAX
103#endif
104#ifndef TZNAME_MAX
105#define MY_TZNAME_MAX 255
106#endif
107
108/*
109 Structure which fully describes time zone which is
110 described in our db or in zoneinfo files.
111*/
112typedef struct st_time_zone_info
113{
114 uint leapcnt; // Number of leap-second corrections
115 uint timecnt; // Number of transitions between time types
116 uint typecnt; // Number of local time types
117 uint charcnt; // Number of characters used for abbreviations
118 uint revcnt; // Number of transition descr. for TIME->my_time_t conversion
119 /* The following are dynamical arrays are allocated in MEM_ROOT */
120 my_time_t *ats; // Times of transitions between time types
121 uchar *types; // Local time types for transitions
122 TRAN_TYPE_INFO *ttis; // Local time types descriptions
123#ifdef ABBR_ARE_USED
124 /* Storage for local time types abbreviations. They are stored as ASCIIZ */
125 char *chars;
126#endif
127 /*
128 Leap seconds corrections descriptions, this array is shared by
129 all time zones who use leap seconds.
130 */
131 LS_INFO *lsis;
132 /*
133 Starting points and descriptions of shifted my_time_t (my_time_t + offset)
134 ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
135 Used for tm -> my_time_t conversion.
136 */
137 my_time_t *revts;
138 REVT_INFO *revtis;
139 /*
140 Time type which is used for times smaller than first transition or if
141 there are no transitions at all.
142 */
143 TRAN_TYPE_INFO *fallback_tti;
144
145} TIME_ZONE_INFO;
146
147
148static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);
149
150
151#if defined(TZINFO2SQL) || defined(TESTTIME)
152
153/*
154 Load time zone description from zoneinfo (TZinfo) file.
155
156 SYNOPSIS
157 tz_load()
158 name - path to zoneinfo file
159 sp - TIME_ZONE_INFO structure to fill
160
161 RETURN VALUES
162 0 - Ok
163 1 - Error
164*/
165static my_bool
166tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
167{
168 uchar *p;
169 ssize_t read_from_file;
170 uint i;
171 MYSQL_FILE *file;
172
173 if (!(file= mysql_file_fopen(0, name, O_RDONLY|O_BINARY, MYF(MY_WME))))
174 return 1;
175 {
176 union
177 {
178 struct tzhead tzhead;
179 uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
180 TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
181#ifdef ABBR_ARE_USED
182 MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
183#endif
184 sizeof(LS_INFO) * TZ_MAX_LEAPS];
185 } u;
186 uint ttisstdcnt;
187 uint ttisgmtcnt;
188 char *tzinfo_buf;
189
190 read_from_file= (ssize_t)mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
191
192 if (mysql_file_fclose(file, MYF(MY_WME)) != 0)
193 return 1;
194
195 if (read_from_file < (int)sizeof(struct tzhead))
196 return 1;
197
198 ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt);
199 ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt);
200 sp->leapcnt= int4net(u.tzhead.tzh_leapcnt);
201 sp->timecnt= int4net(u.tzhead.tzh_timecnt);
202 sp->typecnt= int4net(u.tzhead.tzh_typecnt);
203 sp->charcnt= int4net(u.tzhead.tzh_charcnt);
204 p= u.tzhead.tzh_charcnt + sizeof(u.tzhead.tzh_charcnt);
205 if (sp->leapcnt > TZ_MAX_LEAPS ||
206 sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES ||
207 sp->timecnt > TZ_MAX_TIMES ||
208 sp->charcnt > TZ_MAX_CHARS ||
209 (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
210 (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
211 return 1;
212 if ((uint)(read_from_file - (p - u.buf)) <
213 sp->timecnt * 4 + /* ats */
214 sp->timecnt + /* types */
215 sp->typecnt * (4 + 2) + /* ttinfos */
216 sp->charcnt + /* chars */
217 sp->leapcnt * (4 + 4) + /* lsinfos */
218 ttisstdcnt + /* ttisstds */
219 ttisgmtcnt) /* ttisgmts */
220 return 1;
221
222 if (!(tzinfo_buf= (char *)alloc_root(storage,
223 ALIGN_SIZE(sp->timecnt *
224 sizeof(my_time_t)) +
225 ALIGN_SIZE(sp->timecnt) +
226 ALIGN_SIZE(sp->typecnt *
227 sizeof(TRAN_TYPE_INFO)) +
228#ifdef ABBR_ARE_USED
229 ALIGN_SIZE(sp->charcnt+1) +
230#endif
231 sp->leapcnt * sizeof(LS_INFO))))
232 return 1;
233
234 sp->ats= (my_time_t *)tzinfo_buf;
235 tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
236 sp->types= (uchar *)tzinfo_buf;
237 tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
238 sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
239 tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
240#ifdef ABBR_ARE_USED
241 sp->chars= tzinfo_buf;
242 tzinfo_buf+= ALIGN_SIZE(sp->charcnt+1);
243#endif
244 sp->lsis= (LS_INFO *)tzinfo_buf;
245
246 for (i= 0; i < sp->timecnt; i++, p+= 4)
247 sp->ats[i]= int4net(p);
248
249 for (i= 0; i < sp->timecnt; i++)
250 {
251 sp->types[i]= (uchar) *p++;
252 if (sp->types[i] >= sp->typecnt)
253 return 1;
254 }
255 for (i= 0; i < sp->typecnt; i++)
256 {
257 TRAN_TYPE_INFO * ttisp;
258
259 ttisp= &sp->ttis[i];
260 ttisp->tt_gmtoff= int4net(p);
261 p+= 4;
262 ttisp->tt_isdst= (uchar) *p++;
263 if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
264 return 1;
265 ttisp->tt_abbrind= (uchar) *p++;
266 if (ttisp->tt_abbrind > sp->charcnt)
267 return 1;
268 }
269 for (i= 0; i < sp->charcnt; i++)
270 sp->chars[i]= *p++;
271 sp->chars[i]= '\0'; /* ensure '\0' at end */
272 for (i= 0; i < sp->leapcnt; i++)
273 {
274 LS_INFO *lsisp;
275
276 lsisp= &sp->lsis[i];
277 lsisp->ls_trans= int4net(p);
278 p+= 4;
279 lsisp->ls_corr= int4net(p);
280 p+= 4;
281 }
282 /*
283 Since we don't support POSIX style TZ definitions in variables we
284 don't read further like glibc or elsie code.
285 */
286 }
287
288 return prepare_tz_info(sp, storage);
289}
290#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
291
292
293/*
294 Finish preparation of time zone description for use in TIME_to_gmt_sec()
295 and gmt_sec_to_TIME() functions.
296
297 SYNOPSIS
298 prepare_tz_info()
299 sp - pointer to time zone description
300 storage - pointer to MEM_ROOT where arrays for map allocated
301
302 DESCRIPTION
303 First task of this function is to find fallback time type which will
304 be used if there are no transitions or we have moment in time before
305 any transitions.
306 Second task is to build "shifted my_time_t" -> my_time_t map used in
307 MYSQL_TIME -> my_time_t conversion.
308 Note: See description of TIME_to_gmt_sec() function first.
309 In order to perform MYSQL_TIME -> my_time_t conversion we need to build table
310 which defines "shifted by tz offset and leap seconds my_time_t" ->
311 my_time_t function which is almost the same (except ranges of ambiguity)
312 as reverse function to piecewise linear function used for my_time_t ->
313 "shifted my_time_t" conversion and which is also specified as table in
314 zoneinfo file or in our db (It is specified as start of time type ranges
315 and time type offsets). So basic idea is very simple - let us iterate
316 through my_time_t space from one point of discontinuity of my_time_t ->
317 "shifted my_time_t" function to another and build our approximation of
318 reverse function. (Actually we iterate through ranges on which
319 my_time_t -> "shifted my_time_t" is linear function).
320
321 RETURN VALUES
322 0 Ok
323 1 Error
324*/
325static my_bool
326prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
327{
328 my_time_t cur_t= MY_TIME_T_MIN;
329 my_time_t cur_l, end_t, UNINIT_VAR(end_l);
330 my_time_t cur_max_seen_l= MY_TIME_T_MIN;
331 long cur_offset, cur_corr, cur_off_and_corr;
332 uint next_trans_idx, next_leap_idx;
333 uint i;
334 /*
335 Temporary arrays where we will store tables. Needed because
336 we don't know table sizes ahead. (Well we can estimate their
337 upper bound but this will take extra space.)
338 */
339 my_time_t revts[TZ_MAX_REV_RANGES];
340 REVT_INFO revtis[TZ_MAX_REV_RANGES];
341
342 /*
343 Let us setup fallback time type which will be used if we have not any
344 transitions or if we have moment of time before first transition.
345 We will find first non-DST local time type and use it (or use first
346 local time type if all of them are DST types).
347 */
348 for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
349 /* no-op */ ;
350 if (i == sp->typecnt)
351 i= 0;
352 sp->fallback_tti= &(sp->ttis[i]);
353
354
355 /*
356 Let us build shifted my_time_t -> my_time_t map.
357 */
358 sp->revcnt= 0;
359
360 /* Let us find initial offset */
361 if (sp->timecnt == 0 || cur_t < sp->ats[0])
362 {
363 /*
364 If we have not any transitions or t is before first transition we are using
365 already found fallback time type which index is already in i.
366 */
367 next_trans_idx= 0;
368 }
369 else
370 {
371 /* cur_t == sp->ats[0] so we found transition */
372 i= sp->types[0];
373 next_trans_idx= 1;
374 }
375
376 cur_offset= sp->ttis[i].tt_gmtoff;
377
378
379 /* let us find leap correction... unprobable, but... */
380 for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
381 cur_t >= sp->lsis[next_leap_idx].ls_trans;
382 ++next_leap_idx)
383 continue;
384
385 if (next_leap_idx > 0)
386 cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
387 else
388 cur_corr= 0;
389
390 /* Iterate trough t space */
391 while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
392 {
393 cur_off_and_corr= cur_offset - cur_corr;
394
395 /*
396 We assuming that cur_t could be only overflowed downwards,
397 we also assume that end_t won't be overflowed in this case.
398 */
399 if (cur_off_and_corr < 0 &&
400 cur_t < MY_TIME_T_MIN - cur_off_and_corr)
401 cur_t= MY_TIME_T_MIN - cur_off_and_corr;
402
403 cur_l= cur_t + cur_off_and_corr;
404
405 /*
406 Let us choose end_t as point before next time type change or leap
407 second correction.
408 */
409 end_t= MY_MIN((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
410 MY_TIME_T_MAX,
411 (next_leap_idx < sp->leapcnt) ?
412 sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
413 /*
414 again assuming that end_t can be overlowed only in positive side
415 we also assume that end_t won't be overflowed in this case.
416 */
417 if (cur_off_and_corr > 0 &&
418 end_t > MY_TIME_T_MAX - cur_off_and_corr)
419 end_t= MY_TIME_T_MAX - cur_off_and_corr;
420
421 end_l= end_t + cur_off_and_corr;
422
423
424 if (end_l > cur_max_seen_l)
425 {
426 /* We want special handling in the case of first range */
427 if (cur_max_seen_l == MY_TIME_T_MIN)
428 {
429 revts[sp->revcnt]= cur_l;
430 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
431 revtis[sp->revcnt].rt_type= 0;
432 sp->revcnt++;
433 cur_max_seen_l= end_l;
434 }
435 else
436 {
437 if (cur_l > cur_max_seen_l + 1)
438 {
439 /* We have a spring time-gap and we are not at the first range */
440 revts[sp->revcnt]= cur_max_seen_l + 1;
441 revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
442 revtis[sp->revcnt].rt_type= 1;
443 sp->revcnt++;
444 if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
445 break; /* That was too much */
446 cur_max_seen_l= cur_l - 1;
447 }
448
449 /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */
450
451 revts[sp->revcnt]= cur_max_seen_l + 1;
452 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
453 revtis[sp->revcnt].rt_type= 0;
454 sp->revcnt++;
455 cur_max_seen_l= end_l;
456 }
457 }
458
459 if (end_t == MY_TIME_T_MAX ||
460 ((cur_off_and_corr > 0) &&
461 (end_t >= MY_TIME_T_MAX - cur_off_and_corr)))
462 /* end of t space */
463 break;
464
465 cur_t= end_t + 1;
466
467 /*
468 Let us find new offset and correction. Because of our choice of end_t
469 cur_t can only be point where new time type starts or/and leap
470 correction is performed.
471 */
472 if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
473 if (next_trans_idx < sp->timecnt &&
474 cur_t == sp->ats[next_trans_idx])
475 {
476 /* We are at offset point */
477 cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
478 ++next_trans_idx;
479 }
480
481 if (next_leap_idx < sp->leapcnt &&
482 cur_t == sp->lsis[next_leap_idx].ls_trans)
483 {
484 /* we are at leap point */
485 cur_corr= sp->lsis[next_leap_idx].ls_corr;
486 ++next_leap_idx;
487 }
488 }
489
490 /* check if we have had enough space */
491 if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
492 return 1;
493
494 /* set maximum end_l as finisher */
495 revts[sp->revcnt]= end_l;
496
497 /* Allocate arrays of proper size in sp and copy result there */
498 if (!(sp->revts= (my_time_t *)alloc_root(storage,
499 sizeof(my_time_t) * (sp->revcnt + 1))) ||
500 !(sp->revtis= (REVT_INFO *)alloc_root(storage,
501 sizeof(REVT_INFO) * sp->revcnt)))
502 return 1;
503
504 memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
505 memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
506
507 return 0;
508}
509
510
511#if !defined(TZINFO2SQL)
512
513static const uint mon_lengths[2][MONS_PER_YEAR]=
514{
515 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
516 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
517};
518
519static const uint mon_starts[2][MONS_PER_YEAR]=
520{
521 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
522 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
523};
524
525static const uint year_lengths[2]=
526{
527 DAYS_PER_NYEAR, DAYS_PER_LYEAR
528};
529
530#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
531
532
533/*
534 Converts time from my_time_t representation (seconds in UTC since Epoch)
535 to broken down representation using given local time zone offset.
536
537 SYNOPSIS
538 sec_to_TIME()
539 tmp - pointer to structure for broken down representation
540 t - my_time_t value to be converted
541 offset - local time zone offset
542
543 DESCRIPTION
544 Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
545 (from elsie code) because doesn't contain any leap correction and
546 TM_GMTOFF and is_dst setting and contains some MySQL specific
547 initialization. Funny but with removing of these we almost have
548 glibc's offtime function.
549*/
550static void
551sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
552{
553 long days;
554 long rem;
555 int y;
556 int yleap;
557 const uint *ip;
558
559 days= (long) (t / SECS_PER_DAY);
560 rem= (long) (t % SECS_PER_DAY);
561
562 /*
563 We do this as separate step after dividing t, because this
564 allows us handle times near my_time_t bounds without overflows.
565 */
566 rem+= offset;
567 while (rem < 0)
568 {
569 rem+= SECS_PER_DAY;
570 days--;
571 }
572 while (rem >= SECS_PER_DAY)
573 {
574 rem -= SECS_PER_DAY;
575 days++;
576 }
577 tmp->hour= (uint)(rem / SECS_PER_HOUR);
578 rem= rem % SECS_PER_HOUR;
579 tmp->minute= (uint)(rem / SECS_PER_MIN);
580 /*
581 A positive leap second requires a special
582 representation. This uses "... ??:59:60" et seq.
583 */
584 tmp->second= (uint)(rem % SECS_PER_MIN);
585
586 y= EPOCH_YEAR;
587 while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
588 {
589 int newy;
590
591 newy= y + days / DAYS_PER_NYEAR;
592 if (days < 0)
593 newy--;
594 days-= (newy - y) * DAYS_PER_NYEAR +
595 LEAPS_THRU_END_OF(newy - 1) -
596 LEAPS_THRU_END_OF(y - 1);
597 y= newy;
598 }
599 tmp->year= y;
600
601 ip= mon_lengths[yleap];
602 for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
603 days= days - (long) ip[tmp->month];
604 tmp->month++;
605 tmp->day= (uint)(days + 1);
606
607 /* filling MySQL specific MYSQL_TIME members */
608 tmp->neg= 0; tmp->second_part= 0;
609 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
610}
611
612
613/*
614 Find time range which contains given my_time_t value
615
616 SYNOPSIS
617 find_time_range()
618 t - my_time_t value for which we looking for range
619 range_boundaries - sorted array of range starts.
620 higher_bound - number of ranges
621
622 DESCRIPTION
623 Performs binary search for range which contains given my_time_t value.
624 It has sense if number of ranges is greater than zero and my_time_t value
625 is greater or equal than beginning of first range. It also assumes that
626 t belongs to some range specified or end of last is MY_TIME_T_MAX.
627
628 With this localtime_r on real data may takes less time than with linear
629 search (I've seen 30% speed up).
630
631 RETURN VALUE
632 Index of range to which t belongs
633*/
634static uint
635find_time_range(my_time_t t, const my_time_t *range_boundaries,
636 uint higher_bound)
637{
638 uint i, lower_bound= 0;
639
640 /*
641 Function will work without this assertion but result would be meaningless.
642 */
643 DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
644
645 /*
646 Do binary search for minimal interval which contain t. We preserve:
647 range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
648 invariant and decrease this higher_bound - lower_bound gap twice
649 times on each step.
650 */
651
652 while (higher_bound - lower_bound > 1)
653 {
654 i= (lower_bound + higher_bound) >> 1;
655 if (range_boundaries[i] <= t)
656 lower_bound= i;
657 else
658 higher_bound= i;
659 }
660 return lower_bound;
661}
662
663/*
664 Find local time transition for given my_time_t.
665
666 SYNOPSIS
667 find_transition_type()
668 t - my_time_t value to be converted
669 sp - pointer to struct with time zone description
670
671 RETURN VALUE
672 Pointer to structure in time zone description describing
673 local time type for given my_time_t.
674*/
675static
676const TRAN_TYPE_INFO *
677find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
678{
679 if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
680 {
681 /*
682 If we have not any transitions or t is before first transition let
683 us use fallback time type.
684 */
685 return sp->fallback_tti;
686 }
687
688 /*
689 Do binary search for minimal interval between transitions which
690 contain t. With this localtime_r on real data may takes less
691 time than with linear search (I've seen 30% speed up).
692 */
693 return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
694}
695
696
697/*
698 Converts time in my_time_t representation (seconds in UTC since Epoch) to
699 broken down MYSQL_TIME representation in local time zone.
700
701 SYNOPSIS
702 gmt_sec_to_TIME()
703 tmp - pointer to structure for broken down represenatation
704 sec_in_utc - my_time_t value to be converted
705 sp - pointer to struct with time zone description
706
707 TODO
708 We can improve this function by creating joined array of transitions and
709 leap corrections. This will require adding extra field to TRAN_TYPE_INFO
710 for storing number of "extra" seconds to minute occurred due to correction
711 (60th and 61st second, look how we calculate them as "hit" in this
712 function).
713 Under realistic assumptions about frequency of transitions the same array
714 can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
715 implement tweaked binary search which will take into account that some
716 MYSQL_TIME has two matching my_time_t ranges and some of them have none.
717*/
718static void
719gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
720{
721 const TRAN_TYPE_INFO *ttisp;
722 const LS_INFO *lp;
723 long corr= 0;
724 int hit= 0;
725 int i;
726
727 /*
728 Find proper transition (and its local time type) for our sec_in_utc value.
729 Funny but again by separating this step in function we receive code
730 which very close to glibc's code. No wonder since they obviously use
731 the same base and all steps are sensible.
732 */
733 ttisp= find_transition_type(sec_in_utc, sp);
734
735 /*
736 Let us find leap correction for our sec_in_utc value and number of extra
737 secs to add to this minute.
738 This loop is rarely used because most users will use time zones without
739 leap seconds, and even in case when we have such time zone there won't
740 be many iterations (we have about 22 corrections at this moment (2004)).
741 */
742 for ( i= sp->leapcnt; i-- > 0; )
743 {
744 lp= &sp->lsis[i];
745 if (sec_in_utc >= lp->ls_trans)
746 {
747 if (sec_in_utc == lp->ls_trans)
748 {
749 hit= ((i == 0 && lp->ls_corr > 0) ||
750 lp->ls_corr > sp->lsis[i - 1].ls_corr);
751 if (hit)
752 {
753 while (i > 0 &&
754 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
755 sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
756 {
757 hit++;
758 i--;
759 }
760 }
761 }
762 corr= lp->ls_corr;
763 break;
764 }
765 }
766
767 sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
768
769 tmp->second+= hit;
770}
771
772
773/*
774 Converts local time in broken down representation to local
775 time zone analog of my_time_t represenation.
776
777 SYNOPSIS
778 sec_since_epoch()
779 year, mon, mday, hour, min, sec - broken down representation.
780
781 DESCRIPTION
782 Converts time in broken down representation to my_time_t representation
783 ignoring time zone. Note that we cannot convert back some valid _local_
784 times near ends of my_time_t range because of my_time_t overflow. But we
785 ignore this fact now since MySQL will never pass such argument.
786
787 RETURN VALUE
788 Seconds since epoch time representation.
789*/
790static my_time_t
791sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
792{
793 /* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
794 DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
795#ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
796 /*
797 It turns out that only whenever month is normalized or unnormalized
798 plays role.
799 */
800 DBUG_ASSERT(mon > 0 && mon < 13);
801 long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
802 LEAPS_THRU_END_OF(year - 1) -
803 LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
804 days+= mon_starts[isleap(year)][mon - 1];
805#else
806 long norm_month= (mon - 1) % MONS_PER_YEAR;
807 long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
808 long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
809 LEAPS_THRU_END_OF(a_year - 1) -
810 LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
811 days+= mon_starts[isleap(a_year)]
812 [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
813#endif
814 days+= mday - 1;
815
816 return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
817 SECS_PER_MIN + sec;
818}
819
820/*
821 Converts local time in broken down MYSQL_TIME representation to my_time_t
822 representation.
823
824 SYNOPSIS
825 TIME_to_gmt_sec()
826 t - pointer to structure for broken down represenatation
827 sp - pointer to struct with time zone description
828 error_code - 0, if the conversion was successful;
829 ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value
830 which is out of TIMESTAMP range;
831 ER_WARN_INVALID_TIMESTAMP, if t represents value which
832 doesn't exists (falls into the spring time-gap).
833
834 DESCRIPTION
835 This is mktime analog for MySQL. It is essentially different
836 from mktime (or hypotetical my_mktime) because:
837 - It has no idea about tm_isdst member so if it
838 has two answers it will give the smaller one
839 - If we are in spring time gap then it will return
840 beginning of the gap
841 - It can give wrong results near the ends of my_time_t due to
842 overflows, but we are safe since in MySQL we will never
843 call this function for such dates (its restriction for year
844 between 1970 and 2038 gives us several days of reserve).
845 - By default it doesn't support un-normalized input. But if
846 sec_since_epoch() function supports un-normalized dates
847 then this function should handle un-normalized input right,
848 altough it won't normalize structure TIME.
849
850 Traditional approach to problem of conversion from broken down
851 representation to time_t is iterative. Both elsie's and glibc
852 implementation try to guess what time_t value should correspond to
853 this broken-down value. They perform localtime_r function on their
854 guessed value and then calculate the difference and try to improve
855 their guess. Elsie's code guesses time_t value in bit by bit manner,
856 Glibc's code tries to add difference between broken-down value
857 corresponding to guess and target broken-down value to current guess.
858 It also uses caching of last found correction... So Glibc's approach
859 is essentially faster but introduces some undetermenism (in case if
860 is_dst member of broken-down representation (tm struct) is not known
861 and we have two possible answers).
862
863 We use completely different approach. It is better since it is both
864 faster than iterative implementations and fully determenistic. If you
865 look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
866 of two steps:
867 The first is calculating shifted my_time_t value and the second - TIME
868 calculation from shifted my_time_t value (well it is a bit simplified
869 picture). The part in which we are interested in is my_time_t -> shifted
870 my_time_t conversion. It is piecewise linear function which is defined
871 by combination of transition times as break points and times offset
872 as changing function parameter. The possible inverse function for this
873 converison would be ambiguos but with MySQL's restrictions we can use
874 some function which is the same as inverse function on unambigiuos
875 ranges and coincides with one of branches of inverse function in
876 other ranges. Thus we just need to build table which will determine
877 this shifted my_time_t -> my_time_t conversion similar to existing
878 (my_time_t -> shifted my_time_t table). We do this in
879 prepare_tz_info function.
880
881 TODO
882 If we can even more improve this function. For doing this we will need to
883 build joined map of transitions and leap corrections for gmt_sec_to_TIME()
884 function (similar to revts/revtis). Under realistic assumptions about
885 frequency of transitions we can use the same array for TIME_to_gmt_sec().
886 We need to implement special version of binary search for this. Such step
887 will be beneficial to CPU cache since we will decrease data-set used for
888 conversion twice.
889
890 RETURN VALUE
891 Seconds in UTC since Epoch.
892 0 in case of error.
893*/
894
895static my_time_t
896TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp, uint *error_code)
897{
898 my_time_t local_t;
899 uint saved_seconds;
900 uint i;
901 int shift= 0;
902 DBUG_ENTER("TIME_to_gmt_sec");
903
904 if (!validate_timestamp_range(t))
905 {
906 *error_code= ER_WARN_DATA_OUT_OF_RANGE;
907 DBUG_RETURN(0);
908 }
909
910 *error_code= 0;
911
912 /* We need this for correct leap seconds handling */
913 if (t->second < SECS_PER_MIN)
914 saved_seconds= 0;
915 else
916 saved_seconds= t->second;
917
918 /*
919 NOTE: to convert full my_time_t range we do a shift of the
920 boundary dates here to avoid overflow of my_time_t.
921 We use alike approach in my_system_gmt_sec().
922
923 However in that function we also have to take into account
924 overflow near 0 on some platforms. That's because my_system_gmt_sec
925 uses localtime_r(), which doesn't work with negative values correctly
926 on platforms with unsigned time_t (QNX). Here we don't use localtime()
927 => we negative values of local_t are ok.
928 */
929
930 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
931 {
932 /*
933 We will pass (t->day - shift) to sec_since_epoch(), and
934 want this value to be a positive number, so we shift
935 only dates > 4.01.2038 (to avoid owerflow).
936 */
937 shift= 2;
938 }
939
940
941 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
942 t->hour, t->minute,
943 saved_seconds ? 0 : t->second);
944
945 /* We have at least one range */
946 DBUG_ASSERT(sp->revcnt >= 1);
947
948 if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
949 {
950 /*
951 This means that source time can't be represented as my_time_t due to
952 limited my_time_t range.
953 */
954 *error_code= ER_WARN_DATA_OUT_OF_RANGE;
955 DBUG_RETURN(0);
956 }
957
958 /* binary search for our range */
959 i= find_time_range(local_t, sp->revts, sp->revcnt);
960
961 /*
962 As there are no offset switches at the end of TIMESTAMP range,
963 we could simply check for overflow here (and don't need to bother
964 about DST gaps etc)
965 */
966 if (shift)
967 {
968 if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
969 sp->revtis[i].rt_offset - saved_seconds))
970 {
971 *error_code= ER_WARN_DATA_OUT_OF_RANGE;
972 DBUG_RETURN(0); /* my_time_t overflow */
973 }
974 local_t+= shift * SECS_PER_DAY;
975 }
976
977 if (sp->revtis[i].rt_type)
978 {
979 /*
980 Oops! We are in spring time gap.
981 May be we should return error here?
982 Now we are returning my_time_t value corresponding to the
983 beginning of the gap.
984 */
985 *error_code= ER_WARN_INVALID_TIMESTAMP;
986 local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
987 }
988 else
989 local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
990
991 /* check for TIMESTAMP_MAX_VALUE was already done above */
992 if (local_t < TIMESTAMP_MIN_VALUE)
993 {
994 local_t= 0;
995 *error_code= ER_WARN_DATA_OUT_OF_RANGE;
996 }
997
998 DBUG_RETURN(local_t);
999}
1000
1001
1002/*
1003 End of elsie derived code.
1004*/
1005#endif /* !defined(TZINFO2SQL) */
1006
1007
1008#if !defined(TESTTIME) && !defined(TZINFO2SQL)
1009
1010/*
1011 String with names of SYSTEM time zone.
1012*/
1013static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
1014
1015
1016/*
1017 Instance of this class represents local time zone used on this system
1018 (specified by TZ environment variable or via any other system mechanism).
1019 It uses system functions (localtime_r, my_system_gmt_sec) for conversion
1020 and is always available. Because of this it is used by default - if there
1021 were no explicit time zone specified. On the other hand because of this
1022 conversion methods provided by this class is significantly slower and
1023 possibly less multi-threaded-friendly than corresponding Time_zone_db
1024 methods so the latter should be preffered there it is possible.
1025*/
1026class Time_zone_system : public Time_zone
1027{
1028public:
1029 Time_zone_system() {} /* Remove gcc warning */
1030 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const;
1031 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1032 virtual const String * get_name() const;
1033};
1034
1035
1036/*
1037 Converts local time in system time zone in MYSQL_TIME representation
1038 to its my_time_t representation.
1039
1040 SYNOPSIS
1041 TIME_to_gmt_sec()
1042 t - pointer to MYSQL_TIME structure with local time in
1043 broken-down representation.
1044 error_code - 0, if the conversion was successful;
1045 ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value
1046 which is out of TIMESTAMP range;
1047 ER_WARN_INVALID_TIMESTAMP, if t represents value which
1048 doesn't exists (falls into the spring time-gap).
1049
1050 DESCRIPTION
1051 This method uses system function (localtime_r()) for conversion
1052 local time in system time zone in MYSQL_TIME structure to its my_time_t
1053 representation. Unlike the same function for Time_zone_db class
1054 it it won't handle unnormalized input properly. Still it will
1055 return lowest possible my_time_t in case of ambiguity or if we
1056 provide time corresponding to the time-gap.
1057
1058 You should call my_init_time() function before using this function.
1059
1060 RETURN VALUE
1061 Corresponding my_time_t value or 0 in case of error
1062*/
1063my_time_t
1064Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const
1065{
1066 long not_used;
1067 return my_system_gmt_sec(t, &not_used, error_code);
1068}
1069
1070
1071/*
1072 Converts time from UTC seconds since Epoch (my_time_t) representation
1073 to system local time zone broken-down representation.
1074
1075 SYNOPSIS
1076 gmt_sec_to_TIME()
1077 tmp - pointer to MYSQL_TIME structure to fill-in
1078 t - my_time_t value to be converted
1079
1080 NOTE
1081 We assume that value passed to this function will fit into time_t range
1082 supported by localtime_r. This conversion is putting restriction on
1083 TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
1084 for interaction with client then we can extend TIMESTAMP range down to
1085 the 1902 easily.
1086*/
1087void
1088Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1089{
1090 struct tm tmp_tm;
1091 time_t tmp_t= (time_t)t;
1092
1093 localtime_r(&tmp_t, &tmp_tm);
1094 localtime_to_TIME(tmp, &tmp_tm);
1095 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1096 adjust_leap_second(tmp);
1097}
1098
1099
1100/*
1101 Get name of time zone
1102
1103 SYNOPSIS
1104 get_name()
1105
1106 RETURN VALUE
1107 Name of time zone as String
1108*/
1109const String *
1110Time_zone_system::get_name() const
1111{
1112 return &tz_SYSTEM_name;
1113}
1114
1115
1116/*
1117 Instance of this class represents UTC time zone. It uses system gmtime_r
1118 function for conversions and is always available. It is used only for
1119 my_time_t -> MYSQL_TIME conversions in various UTC_... functions, it is not
1120 intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
1121*/
1122class Time_zone_utc : public Time_zone
1123{
1124public:
1125 Time_zone_utc() {} /* Remove gcc warning */
1126 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1127 uint *error_code) const;
1128 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1129 virtual const String * get_name() const;
1130};
1131
1132
1133/*
1134 Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
1135
1136 DESCRIPTION
1137 Since Time_zone_utc is used only internally for my_time_t -> TIME
1138 conversions, this function of Time_zone interface is not implemented for
1139 this class and should not be called.
1140
1141 RETURN VALUE
1142 0
1143*/
1144my_time_t
1145Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const
1146{
1147 /* Should be never called */
1148 DBUG_ASSERT(0);
1149 *error_code= ER_WARN_DATA_OUT_OF_RANGE;
1150 return 0;
1151}
1152
1153
1154/*
1155 Converts time from UTC seconds since Epoch (my_time_t) representation
1156 to broken-down representation (also in UTC).
1157
1158 SYNOPSIS
1159 gmt_sec_to_TIME()
1160 tmp - pointer to MYSQL_TIME structure to fill-in
1161 t - my_time_t value to be converted
1162
1163 NOTE
1164 See note for apropriate Time_zone_system method.
1165*/
1166void
1167Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1168{
1169 struct tm tmp_tm;
1170 time_t tmp_t= (time_t)t;
1171 gmtime_r(&tmp_t, &tmp_tm);
1172 localtime_to_TIME(tmp, &tmp_tm);
1173 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1174 adjust_leap_second(tmp);
1175}
1176
1177
1178/*
1179 Get name of time zone
1180
1181 SYNOPSIS
1182 get_name()
1183
1184 DESCRIPTION
1185 Since Time_zone_utc is used only internally by SQL's UTC_* functions it
1186 is not accessible directly, and hence this function of Time_zone
1187 interface is not implemented for this class and should not be called.
1188
1189 RETURN VALUE
1190 0
1191*/
1192const String *
1193Time_zone_utc::get_name() const
1194{
1195 /* Should be never called */
1196 DBUG_ASSERT(0);
1197 return 0;
1198}
1199
1200
1201/*
1202 Instance of this class represents some time zone which is
1203 described in mysql.time_zone family of tables.
1204*/
1205class Time_zone_db : public Time_zone
1206{
1207public:
1208 Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
1209 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const;
1210 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1211 virtual const String * get_name() const;
1212private:
1213 TIME_ZONE_INFO *tz_info;
1214 const String *tz_name;
1215};
1216
1217
1218/*
1219 Initializes object representing time zone described by mysql.time_zone
1220 tables.
1221
1222 SYNOPSIS
1223 Time_zone_db()
1224 tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
1225 according to db or other time zone description
1226 (for example by my_tz_init()).
1227 Several Time_zone_db instances can share one
1228 TIME_ZONE_INFO structure.
1229 tz_name_arg - name of time zone.
1230*/
1231Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
1232 const String *tz_name_arg):
1233 tz_info(tz_info_arg), tz_name(tz_name_arg)
1234{
1235}
1236
1237
1238/*
1239 Converts local time in time zone described from TIME
1240 representation to its my_time_t representation.
1241
1242 SYNOPSIS
1243 TIME_to_gmt_sec()
1244 t - pointer to MYSQL_TIME structure with local time
1245 in broken-down representation.
1246 error_code - 0, if the conversion was successful;
1247 ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value
1248 which is out of TIMESTAMP range;
1249 ER_WARN_INVALID_TIMESTAMP, if t represents value which
1250 doesn't exists (falls into the spring time-gap).
1251
1252 DESCRIPTION
1253 Please see ::TIME_to_gmt_sec for function description and
1254 parameter restrictions.
1255
1256 RETURN VALUE
1257 Corresponding my_time_t value or 0 in case of error
1258*/
1259my_time_t
1260Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const
1261{
1262 return ::TIME_to_gmt_sec(t, tz_info, error_code);
1263}
1264
1265
1266/*
1267 Converts time from UTC seconds since Epoch (my_time_t) representation
1268 to local time zone described in broken-down representation.
1269
1270 SYNOPSIS
1271 gmt_sec_to_TIME()
1272 tmp - pointer to MYSQL_TIME structure to fill-in
1273 t - my_time_t value to be converted
1274*/
1275void
1276Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1277{
1278 ::gmt_sec_to_TIME(tmp, t, tz_info);
1279 adjust_leap_second(tmp);
1280}
1281
1282
1283/*
1284 Get name of time zone
1285
1286 SYNOPSIS
1287 get_name()
1288
1289 RETURN VALUE
1290 Name of time zone as ASCIIZ-string
1291*/
1292const String *
1293Time_zone_db::get_name() const
1294{
1295 return tz_name;
1296}
1297
1298
1299/*
1300 Instance of this class represents time zone which
1301 was specified as offset from UTC.
1302*/
1303class Time_zone_offset : public Time_zone
1304{
1305public:
1306 Time_zone_offset(long tz_offset_arg);
1307 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1308 uint *error_code) const;
1309 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1310 virtual const String * get_name() const;
1311 /*
1312 This have to be public because we want to be able to access it from
1313 my_offset_tzs_get_key() function
1314 */
1315 long offset;
1316private:
1317 /* Extra reserve because of snprintf */
1318 char name_buff[7+16];
1319 String name;
1320};
1321
1322
1323/*
1324 Initializes object representing time zone described by its offset from UTC.
1325
1326 SYNOPSIS
1327 Time_zone_offset()
1328 tz_offset_arg - offset from UTC in seconds.
1329 Positive for direction to east.
1330*/
1331Time_zone_offset::Time_zone_offset(long tz_offset_arg):
1332 offset(tz_offset_arg)
1333{
1334 uint hours= abs((int)(offset / SECS_PER_HOUR));
1335 uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
1336 size_t length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
1337 (offset>=0) ? "+" : "-", hours, minutes);
1338 name.set(name_buff, length, &my_charset_latin1);
1339}
1340
1341
1342/*
1343 Converts local time in time zone described as offset from UTC
1344 from MYSQL_TIME representation to its my_time_t representation.
1345
1346 SYNOPSIS
1347 TIME_to_gmt_sec()
1348 t - pointer to MYSQL_TIME structure with local time
1349 in broken-down representation.
1350 error_code - 0, if the conversion was successful;
1351 ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value
1352 which is out of TIMESTAMP range;
1353 ER_WARN_INVALID_TIMESTAMP, if t represents value which
1354 doesn't exists (falls into the spring time-gap).
1355
1356 RETURN VALUE
1357 Corresponding my_time_t value or 0 in case of error.
1358*/
1359
1360my_time_t
1361Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const
1362{
1363 my_time_t local_t;
1364 int shift= 0;
1365
1366 /*
1367 Check timestamp range.we have to do this as calling function relies on
1368 us to make all validation checks here.
1369 */
1370 if (!validate_timestamp_range(t))
1371 {
1372 *error_code= ER_WARN_DATA_OUT_OF_RANGE;
1373 return 0;
1374 }
1375 *error_code= 0;
1376
1377 /*
1378 Do a temporary shift of the boundary dates to avoid
1379 overflow of my_time_t if the time value is near it's
1380 maximum range
1381 */
1382 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
1383 shift= 2;
1384
1385 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
1386 t->hour, t->minute, t->second) -
1387 offset;
1388
1389 if (shift)
1390 {
1391 /* Add back the shifted time */
1392 local_t+= shift * SECS_PER_DAY;
1393 }
1394
1395 if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
1396 return local_t;
1397
1398 /* range error*/
1399 *error_code= ER_WARN_DATA_OUT_OF_RANGE;
1400 return 0;
1401}
1402
1403
1404/*
1405 Converts time from UTC seconds since Epoch (my_time_t) representation
1406 to local time zone described as offset from UTC and in broken-down
1407 representation.
1408
1409 SYNOPSIS
1410 gmt_sec_to_TIME()
1411 tmp - pointer to MYSQL_TIME structure to fill-in
1412 t - my_time_t value to be converted
1413*/
1414void
1415Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1416{
1417 sec_to_TIME(tmp, t, offset);
1418}
1419
1420
1421/*
1422 Get name of time zone
1423
1424 SYNOPSIS
1425 get_name()
1426
1427 RETURN VALUE
1428 Name of time zone as pointer to String object
1429*/
1430const String *
1431Time_zone_offset::get_name() const
1432{
1433 return &name;
1434}
1435
1436
1437static Time_zone_utc tz_UTC;
1438static Time_zone_system tz_SYSTEM;
1439static Time_zone_offset tz_OFFSET0(0);
1440
1441Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1442Time_zone *my_tz_UTC= &tz_UTC;
1443Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1444
1445static HASH tz_names;
1446static HASH offset_tzs;
1447static MEM_ROOT tz_storage;
1448
1449/*
1450 These mutex protects offset_tzs and tz_storage.
1451 These protection needed only when we are trying to set
1452 time zone which is specified as offset, and searching for existing
1453 time zone in offset_tzs or creating if it didn't existed before in
1454 tz_storage. So contention is low.
1455*/
1456static mysql_mutex_t tz_LOCK;
1457static bool tz_inited= 0;
1458
1459/*
1460 This two static variables are inteded for holding info about leap seconds
1461 shared by all time zones.
1462*/
1463static uint tz_leapcnt= 0;
1464static LS_INFO *tz_lsis= 0;
1465
1466/*
1467 Shows whenever we have found time zone tables during start-up.
1468 Used for avoiding of putting those tables to global table list
1469 for queries that use time zone info.
1470*/
1471static bool time_zone_tables_exist= 1;
1472
1473
1474/*
1475 Names of tables (with their lengths) that are needed
1476 for dynamical loading of time zone descriptions.
1477*/
1478
1479static const LEX_CSTRING tz_tables_names[MY_TZ_TABLES_COUNT]=
1480{
1481 { STRING_WITH_LEN("time_zone_name")},
1482 { STRING_WITH_LEN("time_zone")},
1483 { STRING_WITH_LEN("time_zone_transition_type")},
1484 { STRING_WITH_LEN("time_zone_transition")}
1485};
1486
1487class Tz_names_entry: public Sql_alloc
1488{
1489public:
1490 String name;
1491 Time_zone *tz;
1492};
1493
1494
1495/*
1496 We are going to call both of these functions from C code so
1497 they should obey C calling conventions.
1498*/
1499
1500extern "C" uchar *
1501my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
1502 my_bool not_used __attribute__((unused)))
1503{
1504 *length= entry->name.length();
1505 return (uchar*) entry->name.ptr();
1506}
1507
1508extern "C" uchar *
1509my_offset_tzs_get_key(Time_zone_offset *entry,
1510 size_t *length,
1511 my_bool not_used __attribute__((unused)))
1512{
1513 *length= sizeof(long);
1514 return (uchar*) &entry->offset;
1515}
1516
1517
1518/*
1519 Prepare table list with time zone related tables from preallocated array.
1520
1521 SYNOPSIS
1522 tz_init_table_list()
1523 tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT
1524 TABLE_LIST objects
1525
1526 DESCRIPTION
1527 This function prepares list of TABLE_LIST objects which can be used
1528 for opening of time zone tables from preallocated array.
1529*/
1530
1531static void
1532tz_init_table_list(TABLE_LIST *tz_tabs)
1533{
1534 bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT);
1535
1536 for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
1537 {
1538 tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i];
1539 tz_tabs[i].db= MYSQL_SCHEMA_NAME;
1540 tz_tabs[i].lock_type= TL_READ;
1541
1542 if (i != MY_TZ_TABLES_COUNT - 1)
1543 tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
1544 if (i != 0)
1545 tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
1546 }
1547}
1548
1549#ifdef HAVE_PSI_INTERFACE
1550static PSI_mutex_key key_tz_LOCK;
1551
1552static PSI_mutex_info all_tz_mutexes[]=
1553{
1554 { & key_tz_LOCK, "tz_LOCK", PSI_FLAG_GLOBAL}
1555};
1556
1557static void init_tz_psi_keys(void)
1558{
1559 const char* category= "sql";
1560 int count;
1561
1562 if (PSI_server == NULL)
1563 return;
1564
1565 count= array_elements(all_tz_mutexes);
1566 PSI_server->register_mutex(category, all_tz_mutexes, count);
1567}
1568#endif /* HAVE_PSI_INTERFACE */
1569
1570
1571/*
1572 Initialize time zone support infrastructure.
1573
1574 SYNOPSIS
1575 my_tz_init()
1576 thd - current thread object
1577 default_tzname - default time zone or 0 if none.
1578 bootstrap - indicates whenever we are in bootstrap mode
1579
1580 DESCRIPTION
1581 This function will init memory structures needed for time zone support,
1582 it will register mandatory SYSTEM time zone in them. It will try to open
1583 mysql.time_zone* tables and load information about default time zone and
1584 information which further will be shared among all time zones loaded.
1585 If system tables with time zone descriptions don't exist it won't fail
1586 (unless default_tzname is time zone from tables). If bootstrap parameter
1587 is true then this routine assumes that we are in bootstrap mode and won't
1588 load time zone descriptions unless someone specifies default time zone
1589 which is supposedly stored in those tables.
1590 It'll also set default time zone if it is specified.
1591
1592 RETURN VALUES
1593 0 - ok
1594 1 - Error
1595*/
1596my_bool
1597my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
1598{
1599 THD *thd;
1600 TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
1601 TABLE *table;
1602 const LEX_CSTRING tmp_table_name= { STRING_WITH_LEN("time_zone_leap_second") };
1603 Tz_names_entry *tmp_tzname;
1604 my_bool return_val= 1;
1605 int res;
1606 DBUG_ENTER("my_tz_init");
1607
1608#ifdef HAVE_PSI_INTERFACE
1609 init_tz_psi_keys();
1610#endif
1611
1612 /*
1613 To be able to run this from boot, we allocate a temporary THD
1614 */
1615 if (!(thd= new THD(0)))
1616 DBUG_RETURN(1);
1617 thd->thread_stack= (char*) &thd;
1618 thd->store_globals();
1619
1620 /* Init all memory structures that require explicit destruction */
1621 if (my_hash_init(&tz_names, &my_charset_latin1, 20,
1622 0, 0, (my_hash_get_key) my_tz_names_get_key, 0, 0))
1623 {
1624 sql_print_error("Fatal error: OOM while initializing time zones");
1625 goto end;
1626 }
1627 if (my_hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
1628 (my_hash_get_key)my_offset_tzs_get_key, 0, 0))
1629 {
1630 sql_print_error("Fatal error: OOM while initializing time zones");
1631 my_hash_free(&tz_names);
1632 goto end;
1633 }
1634 init_sql_alloc(&tz_storage, "timezone_storage", 32 * 1024, 0, MYF(0));
1635 mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
1636 tz_inited= 1;
1637
1638 /* Add 'SYSTEM' time zone to tz_names hash */
1639 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()))
1640 {
1641 sql_print_error("Fatal error: OOM while initializing time zones");
1642 goto end_with_cleanup;
1643 }
1644 tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
1645 tmp_tzname->tz= my_tz_SYSTEM;
1646 if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
1647 {
1648 sql_print_error("Fatal error: OOM while initializing time zones");
1649 goto end_with_cleanup;
1650 }
1651
1652 if (bootstrap)
1653 {
1654 /* If we are in bootstrap mode we should not load time zone tables */
1655 return_val= time_zone_tables_exist= 0;
1656 goto end_with_setting_default_tz;
1657 }
1658
1659 /*
1660 After this point all memory structures are inited and we even can live
1661 without time zone description tables. Now try to load information about
1662 leap seconds shared by all time zones.
1663 */
1664
1665 thd->set_db(&MYSQL_SCHEMA_NAME);
1666 bzero((char*) &tz_tables[0], sizeof(TABLE_LIST));
1667 tz_tables[0].alias= tz_tables[0].table_name= tmp_table_name;
1668 tz_tables[0].db= MYSQL_SCHEMA_NAME;
1669 tz_tables[0].lock_type= TL_READ;
1670
1671 tz_init_table_list(tz_tables+1);
1672 tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
1673 tz_tables[1].prev_global= &tz_tables[0].next_global;
1674 init_mdl_requests(tz_tables);
1675
1676 /*
1677 We need to open only mysql.time_zone_leap_second, but we try to
1678 open all time zone tables to see if they exist.
1679 */
1680 if (open_and_lock_tables(thd, tz_tables, FALSE,
1681 MYSQL_OPEN_IGNORE_FLUSH | MYSQL_LOCK_IGNORE_TIMEOUT))
1682 {
1683 sql_print_warning("Can't open and lock time zone table: %s "
1684 "trying to live without them",
1685 thd->get_stmt_da()->message());
1686 /* We will try emulate that everything is ok */
1687 return_val= time_zone_tables_exist= 0;
1688 goto end_with_setting_default_tz;
1689 }
1690
1691 for (TABLE_LIST *tl= tz_tables; tl; tl= tl->next_global)
1692 {
1693 tl->table->use_all_columns();
1694 /* Force close at the end of the function to free memory. */
1695 tl->table->m_needs_reopen= TRUE;
1696 }
1697
1698 /*
1699 Now we are going to load leap seconds descriptions that are shared
1700 between all time zones that use them. We are using index for getting
1701 records in proper order. Since we share the same MEM_ROOT between
1702 all time zones we just allocate enough memory for it first.
1703 */
1704 if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
1705 sizeof(LS_INFO) * TZ_MAX_LEAPS)))
1706 {
1707 sql_print_error("Fatal error: Out of memory while loading "
1708 "mysql.time_zone_leap_second table");
1709 goto end_with_close;
1710 }
1711
1712 table= tz_tables[0].table;
1713
1714 if (table->file->ha_index_init(0, 1))
1715 goto end_with_close;
1716
1717 table->use_all_columns();
1718 tz_leapcnt= 0;
1719
1720 res= table->file->ha_index_first(table->record[0]);
1721
1722 while (!res)
1723 {
1724 if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
1725 {
1726 sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
1727 " table: too much leaps");
1728 table->file->ha_index_end();
1729 goto end_with_close;
1730 }
1731
1732 tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
1733 tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();
1734
1735 tz_leapcnt++;
1736
1737 DBUG_PRINT("info",
1738 ("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld",
1739 tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
1740 tz_lsis[tz_leapcnt-1].ls_corr));
1741
1742 res= table->file->ha_index_next(table->record[0]);
1743 }
1744
1745 (void)table->file->ha_index_end();
1746
1747 if (res != HA_ERR_END_OF_FILE)
1748 {
1749 sql_print_error("Fatal error: Error while loading "
1750 "mysql.time_zone_leap_second table");
1751 goto end_with_close;
1752 }
1753
1754 /*
1755 Loading of info about leap seconds succeeded
1756 */
1757
1758 return_val= 0;
1759
1760
1761end_with_setting_default_tz:
1762 /* If we have default time zone try to load it */
1763 if (default_tzname)
1764 {
1765 String tmp_tzname2(default_tzname, &my_charset_latin1);
1766 /*
1767 Time zone tables may be open here, and my_tz_find() may open
1768 most of them once more, but this is OK for system tables open
1769 for READ.
1770 */
1771 if (unlikely(!(global_system_variables.time_zone=
1772 my_tz_find(thd, &tmp_tzname2))))
1773 {
1774 sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
1775 default_tzname);
1776 return_val= 1;
1777 }
1778 }
1779
1780end_with_close:
1781 if (time_zone_tables_exist)
1782 close_mysql_tables(thd);
1783
1784end_with_cleanup:
1785
1786 /* if there were error free time zone describing structs */
1787 if (unlikely(return_val))
1788 my_tz_free();
1789end:
1790 delete thd;
1791 if (org_thd)
1792 org_thd->store_globals(); /* purecov: inspected */
1793
1794 default_tz= default_tz_name ? global_system_variables.time_zone
1795 : my_tz_SYSTEM;
1796
1797 DBUG_RETURN(return_val);
1798}
1799
1800
1801/*
1802 Free resources used by time zone support infrastructure.
1803
1804 SYNOPSIS
1805 my_tz_free()
1806*/
1807
1808void my_tz_free()
1809{
1810 if (tz_inited)
1811 {
1812 tz_inited= 0;
1813 mysql_mutex_destroy(&tz_LOCK);
1814 my_hash_free(&offset_tzs);
1815 my_hash_free(&tz_names);
1816 free_root(&tz_storage, MYF(0));
1817 }
1818}
1819
1820
1821/*
1822 Load time zone description from system tables.
1823
1824 SYNOPSIS
1825 tz_load_from_open_tables()
1826 tz_name - name of time zone that should be loaded.
1827 tz_tables - list of tables from which time zone description
1828 should be loaded
1829
1830 DESCRIPTION
1831 This function will try to load information about time zone specified
1832 from the list of the already opened and locked tables (first table in
1833 tz_tables should be time_zone_name, next time_zone, then
1834 time_zone_transition_type and time_zone_transition should be last).
1835 It will also update information in hash used for time zones lookup.
1836
1837 RETURN VALUES
1838 Returns pointer to newly created Time_zone object or 0 in case of error.
1839
1840*/
1841
1842static Time_zone*
1843tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
1844{
1845 TABLE *table= 0;
1846 TIME_ZONE_INFO *tz_info= NULL;
1847 Tz_names_entry *tmp_tzname;
1848 Time_zone *return_val= 0;
1849 int res;
1850 uint tzid, ttid;
1851 my_time_t ttime;
1852 char buff[MAX_FIELD_WIDTH];
1853 uchar keybuff[32];
1854 Field *field;
1855 String abbr(buff, sizeof(buff), &my_charset_latin1);
1856 char *alloc_buff= NULL;
1857 char *tz_name_buff= NULL;
1858 /*
1859 Temporary arrays that are used for loading of data for filling
1860 TIME_ZONE_INFO structure
1861 */
1862 my_time_t ats[TZ_MAX_TIMES];
1863 uchar types[TZ_MAX_TIMES];
1864 TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
1865#ifdef ABBR_ARE_USED
1866 char chars[MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
1867#endif
1868 /*
1869 Used as a temporary tz_info until we decide that we actually want to
1870 allocate and keep the tz info and tz name in tz_storage.
1871 */
1872 TIME_ZONE_INFO tmp_tz_info;
1873 memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
1874
1875 DBUG_ENTER("tz_load_from_open_tables");
1876
1877 /*
1878 Let us find out time zone id by its name (there is only one index
1879 and it is specifically for this purpose).
1880 */
1881 table= tz_tables->table;
1882 tz_tables= tz_tables->next_local;
1883 table->field[0]->store(tz_name->ptr(), tz_name->length(),
1884 &my_charset_latin1);
1885 if (table->file->ha_index_init(0, 1))
1886 goto end;
1887
1888 if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
1889 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1890 {
1891#ifdef EXTRA_DEBUG
1892 /*
1893 Most probably user has mistyped time zone name, so no need to bark here
1894 unless we need it for debugging.
1895 */
1896 sql_print_error("Can't find description of time zone '%.*s'",
1897 tz_name->length(), tz_name->ptr());
1898#endif
1899 goto end;
1900 }
1901
1902 tzid= (uint)table->field[1]->val_int();
1903
1904 (void)table->file->ha_index_end();
1905
1906 /*
1907 Now we need to lookup record in mysql.time_zone table in order to
1908 understand whenever this timezone uses leap seconds (again we are
1909 using the only index in this table).
1910 */
1911 table= tz_tables->table;
1912 tz_tables= tz_tables->next_local;
1913 field= table->field[0];
1914 field->store((longlong) tzid, TRUE);
1915 DBUG_ASSERT(field->key_length() <= sizeof(keybuff));
1916 field->get_key_image(keybuff,
1917 MY_MIN(field->key_length(), sizeof(keybuff)),
1918 Field::itRAW);
1919 if (table->file->ha_index_init(0, 1))
1920 goto end;
1921
1922 if (table->file->ha_index_read_map(table->record[0], keybuff,
1923 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1924 {
1925 sql_print_error("Can't find description of time zone '%u'", tzid);
1926 goto end;
1927 }
1928
1929 /* If Uses_leap_seconds == 'Y' */
1930 if (table->field[1]->val_int() == 1)
1931 {
1932 tmp_tz_info.leapcnt= tz_leapcnt;
1933 tmp_tz_info.lsis= tz_lsis;
1934 }
1935
1936 (void)table->file->ha_index_end();
1937
1938 /*
1939 Now we will iterate through records for out time zone in
1940 mysql.time_zone_transition_type table. Because we want records
1941 only for our time zone guess what are we doing?
1942 Right - using special index.
1943 */
1944 table= tz_tables->table;
1945 tz_tables= tz_tables->next_local;
1946 field= table->field[0];
1947 field->store((longlong) tzid, TRUE);
1948 DBUG_ASSERT(field->key_length() <= sizeof(keybuff));
1949 field->get_key_image(keybuff,
1950 MY_MIN(field->key_length(), sizeof(keybuff)),
1951 Field::itRAW);
1952 if (table->file->ha_index_init(0, 1))
1953 goto end;
1954
1955 res= table->file->ha_index_read_map(table->record[0], keybuff,
1956 (key_part_map)1, HA_READ_KEY_EXACT);
1957 while (!res)
1958 {
1959 ttid= (uint)table->field[1]->val_int();
1960
1961 if (ttid >= TZ_MAX_TYPES)
1962 {
1963 sql_print_error("Error while loading time zone description from "
1964 "mysql.time_zone_transition_type table: too big "
1965 "transition type id");
1966 goto end;
1967 }
1968
1969 ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int();
1970 ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0);
1971
1972#ifdef ABBR_ARE_USED
1973 // FIXME should we do something with duplicates here ?
1974 table->field[4]->val_str(&abbr, &abbr);
1975 if (tmp_tz_info.charcnt + abbr.length() + 1 > sizeof(chars))
1976 {
1977 sql_print_error("Error while loading time zone description from "
1978 "mysql.time_zone_transition_type table: not enough "
1979 "room for abbreviations");
1980 goto end;
1981 }
1982 ttis[ttid].tt_abbrind= tmp_tz_info.charcnt;
1983 memcpy(chars + tmp_tz_info.charcnt, abbr.ptr(), abbr.length());
1984 tmp_tz_info.charcnt+= abbr.length();
1985 chars[tmp_tz_info.charcnt]= 0;
1986 tmp_tz_info.charcnt++;
1987
1988 DBUG_PRINT("info",
1989 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1990 "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff,
1991 chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst));
1992#else
1993 DBUG_PRINT("info",
1994 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1995 "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst));
1996#endif
1997
1998 /* ttid is increasing because we are reading using index */
1999 DBUG_ASSERT(ttid >= tmp_tz_info.typecnt);
2000
2001 tmp_tz_info.typecnt= ttid + 1;
2002
2003 res= table->file->ha_index_next_same(table->record[0], keybuff, 4);
2004 }
2005
2006 if (res != HA_ERR_END_OF_FILE)
2007 {
2008 sql_print_error("Error while loading time zone description from "
2009 "mysql.time_zone_transition_type table");
2010 goto end;
2011 }
2012
2013 (void)table->file->ha_index_end();
2014
2015
2016 /*
2017 At last we are doing the same thing for records in
2018 mysql.time_zone_transition table. Here we additionally need records
2019 in ascending order by index scan also satisfies us.
2020 */
2021 table= tz_tables->table;
2022 table->field[0]->store((longlong) tzid, TRUE);
2023 if (table->file->ha_index_init(0, 1))
2024 goto end;
2025
2026 res= table->file->ha_index_read_map(table->record[0], keybuff,
2027 (key_part_map)1, HA_READ_KEY_EXACT);
2028 while (!res)
2029 {
2030 ttime= (my_time_t)table->field[1]->val_int();
2031 ttid= (uint)table->field[2]->val_int();
2032
2033 if (tmp_tz_info.timecnt + 1 > TZ_MAX_TIMES)
2034 {
2035 sql_print_error("Error while loading time zone description from "
2036 "mysql.time_zone_transition table: "
2037 "too much transitions");
2038 goto end;
2039 }
2040 if (ttid + 1 > tmp_tz_info.typecnt)
2041 {
2042 sql_print_error("Error while loading time zone description from "
2043 "mysql.time_zone_transition table: "
2044 "bad transition type id");
2045 goto end;
2046 }
2047
2048 ats[tmp_tz_info.timecnt]= ttime;
2049 types[tmp_tz_info.timecnt]= ttid;
2050 tmp_tz_info.timecnt++;
2051
2052 DBUG_PRINT("info",
2053 ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
2054 tzid, (ulong) ttime, ttid));
2055
2056 res= table->file->ha_index_next_same(table->record[0], keybuff, 4);
2057 }
2058
2059 /*
2060 We have to allow HA_ERR_KEY_NOT_FOUND because some time zones
2061 for example UTC have no transitons.
2062 */
2063 if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
2064 {
2065 sql_print_error("Error while loading time zone description from "
2066 "mysql.time_zone_transition table");
2067 goto end;
2068 }
2069
2070 (void)table->file->ha_index_end();
2071 table= 0;
2072
2073 /*
2074 Let us check how correct our time zone description is. We don't check for
2075 tz->timecnt < 1 since it is ok for GMT.
2076 */
2077 if (tmp_tz_info.typecnt < 1)
2078 {
2079 sql_print_error("loading time zone without transition types");
2080 goto end;
2081 }
2082
2083 /* Allocate memory for the timezone info and timezone name in tz_storage. */
2084 if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
2085 tz_name->length() + 1)))
2086 {
2087 sql_print_error("Out of memory while loading time zone description");
2088 return 0;
2089 }
2090
2091 /* Move the temporary tz_info into the allocated area */
2092 tz_info= (TIME_ZONE_INFO *)alloc_buff;
2093 memcpy(tz_info, &tmp_tz_info, sizeof(TIME_ZONE_INFO));
2094 tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
2095 /*
2096 By writing zero to the end we guarantee that we can call ptr()
2097 instead of c_ptr() for time zone name.
2098 */
2099 strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
2100
2101 /*
2102 Now we will allocate memory and init TIME_ZONE_INFO structure.
2103 */
2104 if (!(alloc_buff= (char*) alloc_root(&tz_storage,
2105 ALIGN_SIZE(sizeof(my_time_t) *
2106 tz_info->timecnt) +
2107 ALIGN_SIZE(tz_info->timecnt) +
2108#ifdef ABBR_ARE_USED
2109 ALIGN_SIZE(tz_info->charcnt) +
2110#endif
2111 sizeof(TRAN_TYPE_INFO) *
2112 tz_info->typecnt)))
2113 {
2114 sql_print_error("Out of memory while loading time zone description");
2115 goto end;
2116 }
2117
2118 tz_info->ats= (my_time_t *) alloc_buff;
2119 memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
2120 alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
2121 tz_info->types= (uchar *)alloc_buff;
2122 memcpy(tz_info->types, types, tz_info->timecnt);
2123 alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
2124#ifdef ABBR_ARE_USED
2125 tz_info->chars= alloc_buff;
2126 memcpy(tz_info->chars, chars, tz_info->charcnt);
2127 alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
2128#endif
2129 tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
2130 memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
2131
2132 /* Build reversed map. */
2133 if (prepare_tz_info(tz_info, &tz_storage))
2134 {
2135 sql_print_error("Unable to build mktime map for time zone");
2136 goto end;
2137 }
2138
2139
2140 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) ||
2141 !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
2142 &(tmp_tzname->name))) ||
2143 (tmp_tzname->name.set(tz_name_buff, tz_name->length(),
2144 &my_charset_latin1),
2145 my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
2146 {
2147 sql_print_error("Out of memory while loading time zone");
2148 goto end;
2149 }
2150
2151 /*
2152 Loading of time zone succeeded
2153 */
2154 return_val= tmp_tzname->tz;
2155
2156end:
2157
2158 if (table && table->file->inited)
2159 (void) table->file->ha_index_end();
2160
2161 DBUG_RETURN(return_val);
2162}
2163
2164
2165/*
2166 Parse string that specifies time zone as offset from UTC.
2167
2168 SYNOPSIS
2169 str_to_offset()
2170 str - pointer to string which contains offset
2171 length - length of string
2172 offset - out parameter for storing found offset in seconds.
2173
2174 DESCRIPTION
2175 This function parses string which contains time zone offset
2176 in form similar to '+10:00' and converts found value to
2177 seconds from UTC form (east is positive).
2178
2179 RETURN VALUE
2180 0 - Ok
2181 1 - String doesn't contain valid time zone offset
2182*/
2183my_bool
2184str_to_offset(const char *str, uint length, long *offset)
2185{
2186 const char *end= str + length;
2187 my_bool negative;
2188 ulong number_tmp;
2189 long offset_tmp;
2190
2191 if (length < 4)
2192 return 1;
2193
2194 if (*str == '+')
2195 negative= 0;
2196 else if (*str == '-')
2197 negative= 1;
2198 else
2199 return 1;
2200 str++;
2201
2202 number_tmp= 0;
2203
2204 while (str < end && my_isdigit(&my_charset_latin1, *str))
2205 {
2206 number_tmp= number_tmp*10 + *str - '0';
2207 str++;
2208 }
2209
2210 if (str + 1 >= end || *str != ':')
2211 return 1;
2212 str++;
2213
2214 offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
2215
2216 while (str < end && my_isdigit(&my_charset_latin1, *str))
2217 {
2218 number_tmp= number_tmp * 10 + *str - '0';
2219 str++;
2220 }
2221
2222 if (str != end)
2223 return 1;
2224
2225 offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
2226
2227 if (negative)
2228 offset_tmp= -offset_tmp;
2229
2230 /*
2231 Check if offset is in range prescribed by standard
2232 (from -12:59 to 13:00).
2233 */
2234
2235 if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
2236 offset_tmp > 13 * SECS_PER_HOUR)
2237 return 1;
2238
2239 *offset= offset_tmp;
2240
2241 return 0;
2242}
2243
2244
2245/*
2246 Get Time_zone object for specified time zone.
2247
2248 SYNOPSIS
2249 my_tz_find()
2250 thd - pointer to thread THD structure
2251 name - time zone specification
2252
2253 DESCRIPTION
2254 This function checks if name is one of time zones described in db,
2255 predefined SYSTEM time zone or valid time zone specification as
2256 offset from UTC (In last case it will create proper Time_zone_offset
2257 object if there were not any.). If name is ok it returns corresponding
2258 Time_zone object.
2259
2260 Clients of this function are not responsible for releasing resources
2261 occupied by returned Time_zone object so they can just forget pointers
2262 to Time_zone object if they are not needed longer.
2263
2264 Other important property of this function: if some Time_zone found once
2265 it will be for sure found later, so this function can also be used for
2266 checking if proper Time_zone object exists (and if there will be error
2267 it will be reported during first call).
2268
2269 If name pointer is 0 then this function returns 0 (this allows to pass 0
2270 values as parameter without additional external check and this property
2271 is used by @@time_zone variable handling code).
2272
2273 It will perform lookup in system tables (mysql.time_zone*),
2274 opening and locking them, and closing afterwards. It won't perform
2275 such lookup if no time zone describing tables were found during
2276 server start up.
2277
2278 RETURN VALUE
2279 Pointer to corresponding Time_zone object. 0 - in case of bad time zone
2280 specification or other error.
2281
2282*/
2283Time_zone *
2284my_tz_find(THD *thd, const String *name)
2285{
2286 Tz_names_entry *tmp_tzname;
2287 Time_zone *result_tz= 0;
2288 long offset;
2289 DBUG_ENTER("my_tz_find");
2290 DBUG_PRINT("enter", ("time zone name='%s'",
2291 name ? ((String *)name)->c_ptr_safe() : "NULL"));
2292
2293 if (!name || name->is_empty())
2294 DBUG_RETURN(0);
2295
2296 mysql_mutex_lock(&tz_LOCK);
2297
2298 if (!str_to_offset(name->ptr(), name->length(), &offset))
2299 {
2300 if (!(result_tz= (Time_zone_offset *)my_hash_search(&offset_tzs,
2301 (const uchar *)&offset,
2302 sizeof(long))))
2303 {
2304 DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
2305
2306 if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
2307 my_hash_insert(&offset_tzs, (const uchar *) result_tz))
2308 {
2309 result_tz= 0;
2310 sql_print_error("Fatal error: Out of memory "
2311 "while setting new time zone");
2312 }
2313 }
2314 }
2315 else
2316 {
2317 result_tz= 0;
2318 if ((tmp_tzname= (Tz_names_entry *)my_hash_search(&tz_names,
2319 (const uchar *)
2320 name->ptr(),
2321 name->length())))
2322 result_tz= tmp_tzname->tz;
2323 else if (time_zone_tables_exist)
2324 {
2325 TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
2326 Open_tables_backup open_tables_state_backup;
2327
2328 tz_init_table_list(tz_tables);
2329 init_mdl_requests(tz_tables);
2330 if (!open_system_tables_for_read(thd, tz_tables,
2331 &open_tables_state_backup))
2332 {
2333 result_tz= tz_load_from_open_tables(name, tz_tables);
2334 close_system_tables(thd, &open_tables_state_backup);
2335 }
2336 }
2337 }
2338
2339 mysql_mutex_unlock(&tz_LOCK);
2340
2341 if (result_tz && result_tz != my_tz_SYSTEM && result_tz != my_tz_UTC)
2342 status_var_increment(thd->status_var.feature_timezone);
2343
2344 DBUG_RETURN(result_tz);
2345}
2346
2347
2348/**
2349 Convert leap seconds into non-leap
2350
2351 This function will convert the leap seconds added by the OS to
2352 non-leap seconds, e.g. 23:59:59, 23:59:60 -> 23:59:59, 00:00:01 ...
2353 This check is not checking for years on purpose : although it's not a
2354 complete check this way it doesn't require looking (and having installed)
2355 the leap seconds table.
2356
2357 @param[in,out] broken down time structure as filled in by the OS
2358*/
2359
2360void Time_zone::adjust_leap_second(MYSQL_TIME *t)
2361{
2362 if (t->second == 60 || t->second == 61)
2363 t->second= 59;
2364}
2365
2366#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
2367
2368
2369#ifdef TZINFO2SQL
2370/*
2371 This code belongs to mysql_tzinfo_to_sql converter command line utility.
2372 This utility should be used by db admin for populating mysql.time_zone
2373 tables.
2374*/
2375
2376/*
2377 Print info about time zone described by TIME_ZONE_INFO struct as
2378 SQL statements populating mysql.time_zone* tables.
2379
2380 SYNOPSIS
2381 print_tz_as_sql()
2382 tz_name - name of time zone
2383 sp - structure describing time zone
2384*/
2385void
2386print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
2387{
2388 uint i;
2389
2390 /* Here we assume that all time zones have same leap correction tables */
2391 printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
2392 sp->leapcnt ? "Y" : "N");
2393 printf("SET @time_zone_id= LAST_INSERT_ID();\n");
2394 printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
2395('%s', @time_zone_id);\n", tz_name);
2396
2397 if (sp->timecnt)
2398 {
2399 printf("INSERT INTO time_zone_transition \
2400(Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
2401 for (i= 0; i < sp->timecnt; i++)
2402 printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
2403 (uint)sp->types[i]);
2404 printf(";\n");
2405 }
2406
2407 printf("INSERT INTO time_zone_transition_type \
2408(Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
2409
2410 for (i= 0; i < sp->typecnt; i++)
2411 printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i,
2412 sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
2413 sp->chars + sp->ttis[i].tt_abbrind);
2414 printf(";\n");
2415}
2416
2417
2418/*
2419 Print info about leap seconds in time zone as SQL statements
2420 populating mysql.time_zone_leap_second table.
2421
2422 SYNOPSIS
2423 print_tz_leaps_as_sql()
2424 sp - structure describing time zone
2425*/
2426void
2427print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
2428{
2429 uint i;
2430
2431 /*
2432 We are assuming that there are only one list of leap seconds
2433 For all timezones.
2434 */
2435 printf("TRUNCATE TABLE time_zone_leap_second;\n");
2436
2437 if (sp->leapcnt)
2438 {
2439 printf("INSERT INTO time_zone_leap_second \
2440(Transition_time, Correction) VALUES\n");
2441 for (i= 0; i < sp->leapcnt; i++)
2442 printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
2443 sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
2444 printf(";\n");
2445 }
2446
2447 printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n");
2448}
2449
2450
2451/*
2452 Some variables used as temporary or as parameters
2453 in recursive scan_tz_dir() code.
2454*/
2455TIME_ZONE_INFO tz_info;
2456MEM_ROOT tz_storage;
2457char fullname[FN_REFLEN + 1];
2458char *root_name_end;
2459
2460/*
2461 known file types that exist in the zoneinfo directory that are safe to
2462 silently skip
2463*/
2464const char *known_extensions[]= {
2465 ".tab",
2466 NullS
2467};
2468
2469
2470/*
2471 Recursively scan zoneinfo directory and print all found time zone
2472 descriptions as SQL.
2473
2474 SYNOPSIS
2475 scan_tz_dir()
2476 name_end - pointer to end of path to directory to be searched.
2477 symlink_recursion_level How many symlink directory levels are used
2478 verbose >0 if we should print warnings
2479
2480 DESCRIPTION
2481 This auxiliary recursive function also uses several global
2482 variables as in parameters and for storing temporary values.
2483
2484 fullname - path to directory that should be scanned.
2485 root_name_end - pointer to place in fullname where part with
2486 path to initial directory ends.
2487 current_tz_id - last used time zone id
2488
2489 RETURN VALUE
2490 0 - Ok, 1 - Fatal error
2491
2492*/
2493my_bool
2494scan_tz_dir(char * name_end, uint symlink_recursion_level, uint verbose)
2495{
2496 MY_DIR *cur_dir;
2497 char *name_end_tmp;
2498 uint i;
2499
2500 /* Sort directory data, to pass mtr tests on different platforms. */
2501 if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT|MY_WANT_SORT))))
2502 return 1;
2503
2504 name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
2505
2506 for (i= 0; i < cur_dir->number_of_files; i++)
2507 {
2508 if (cur_dir->dir_entry[i].name[0] != '.' &&
2509 strcmp(cur_dir->dir_entry[i].name, "Factory"))
2510 {
2511 name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
2512 FN_REFLEN - (name_end - fullname));
2513
2514 if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
2515 {
2516 my_bool is_symlink;
2517 if ((is_symlink= my_is_symlink(fullname)) &&
2518 symlink_recursion_level > 0)
2519 {
2520 /*
2521 The timezone definition data in some Linux distributions
2522 (e.g. the "timezone-data-2013f" package in Gentoo)
2523 may have synlimks like:
2524 /usr/share/zoneinfo/posix/ -> /usr/share/zoneinfo/,
2525 so the same timezone files are available under two names
2526 (e.g. "CET" and "posix/CET").
2527
2528 We allow one level of symlink recursion for backward
2529 compatibility with earlier timezone data packages that have
2530 duplicate copies of the same timezone files inside the root
2531 directory and the "posix" subdirectory (instead of symlinking).
2532 This makes "posix/CET" still available, but helps to avoid
2533 following such symlinks infinitely:
2534 /usr/share/zoneinfo/posix/posix/posix/.../posix/
2535 */
2536
2537 /*
2538 This is a normal case and not critical. only print warning if
2539 verbose mode is choosen.
2540 */
2541 if (verbose > 0)
2542 {
2543 fflush(stdout);
2544 fprintf(stderr, "Warning: Skipping directory '%s': "
2545 "to avoid infinite symlink recursion.\n", fullname);
2546 }
2547 continue;
2548 }
2549 if (scan_tz_dir(name_end_tmp, symlink_recursion_level + is_symlink,
2550 verbose))
2551 {
2552 my_dirend(cur_dir);
2553 return 1;
2554 }
2555 }
2556 else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
2557 {
2558 init_alloc_root(&tz_storage, "timezone_storage", 32768, 0,
2559 MYF(MY_THREAD_SPECIFIC));
2560 if (!tz_load(fullname, &tz_info, &tz_storage))
2561 print_tz_as_sql(root_name_end + 1, &tz_info);
2562 else
2563 {
2564 /*
2565 Some systems (like debian, opensuse etc) have description
2566 files (.tab). We skip these silently if verbose is > 0
2567 */
2568 const char *current_ext= fn_ext(fullname);
2569 my_bool known_ext= 0;
2570
2571 for (const char **ext= known_extensions ; *ext ; ext++)
2572 {
2573 if (!strcmp(*ext, current_ext))
2574 {
2575 known_ext= 1;
2576 break;
2577 }
2578 }
2579 if (verbose > 0 || !known_ext)
2580 {
2581 fflush(stdout);
2582 fprintf(stderr,
2583 "Warning: Unable to load '%s' as time zone. Skipping it.\n",
2584 fullname);
2585 }
2586 }
2587 free_root(&tz_storage, MYF(0));
2588 }
2589 else
2590 {
2591 fflush(stdout);
2592 fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
2593 fullname);
2594 }
2595 }
2596 }
2597
2598 my_dirend(cur_dir);
2599
2600 return 0;
2601}
2602
2603
2604my_bool opt_leap, opt_verbose;
2605
2606static const char *load_default_groups[]=
2607{ "mysql_tzinfo_to_sql", 0};
2608
2609static struct my_option my_long_options[] =
2610{
2611 {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
2612 0, 0, 0, 0, 0, 0},
2613#ifdef DBUG_OFF
2614 {"debug", '#', "This is a non-debug version. Catch this and exit",
2615 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
2616#else
2617 {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
2618 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
2619#endif
2620 {"leap", 'l', "Print the leap second information from the given time zone file. By convention, when --leap is used the next argument is the timezonefile",
2621 &opt_leap, &opt_leap, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2622 {"verbose", 'v', "Write non critical warnings",
2623 &opt_verbose, &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
2624 {"version", 'V', "Output version information and exit.",
2625 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
2626 { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
2627};
2628
2629
2630C_MODE_START
2631static my_bool get_one_option(int optid, const struct my_option *,
2632 char *argument);
2633C_MODE_END
2634
2635static void print_version(void)
2636{
2637 printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname, PROGRAM_VERSION,
2638 MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
2639}
2640
2641static void print_usage(void)
2642{
2643 fprintf(stderr, "Usage:\n");
2644 fprintf(stderr, " %s [options] timezonedir\n", my_progname);
2645 fprintf(stderr, " %s [options] timezonefile timezonename\n", my_progname);
2646 print_defaults("my",load_default_groups);
2647 puts("");
2648 my_print_help(my_long_options);
2649 my_print_variables(my_long_options);
2650}
2651
2652
2653static my_bool
2654get_one_option(int optid, const struct my_option *opt, char *argument)
2655{
2656 switch(optid) {
2657 case '#':
2658#ifndef DBUG_OFF
2659 DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysq_tzinfo_to_sql.trace");
2660#endif
2661 break;
2662 case '?':
2663 print_version();
2664 puts("");
2665 print_usage();
2666 exit(0);
2667 case 'V':
2668 print_version();
2669 exit(0);
2670 }
2671 return 0;
2672}
2673
2674
2675int
2676main(int argc, char **argv)
2677{
2678 char **default_argv;
2679 MY_INIT(argv[0]);
2680
2681 load_defaults_or_exit("my", load_default_groups, &argc, &argv);
2682 default_argv= argv;
2683
2684 if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
2685 exit(1);
2686
2687 if ((argc != 1 && argc != 2) || (opt_leap && argc != 1))
2688 {
2689 print_usage();
2690 free_defaults(default_argv);
2691 return 1;
2692 }
2693
2694 // Replicate MyISAM DDL for this session, cf. lp:1161432
2695 // timezone info unfixable in XtraDB Cluster
2696 printf("set @prep=if((select count(*) from information_schema.global_variables where variable_name='wsrep_on'), 'SET GLOBAL wsrep_replicate_myisam=?', 'do ?');\n"
2697 "prepare set_wsrep_myisam from @prep;\n"
2698 "set @toggle=1; execute set_wsrep_myisam using @toggle;\n");
2699
2700 if (argc == 1 && !opt_leap)
2701 {
2702 /* Argument is timezonedir */
2703
2704 root_name_end= strmake_buf(fullname, argv[0]);
2705
2706 printf("TRUNCATE TABLE time_zone;\n");
2707 printf("TRUNCATE TABLE time_zone_name;\n");
2708 printf("TRUNCATE TABLE time_zone_transition;\n");
2709 printf("TRUNCATE TABLE time_zone_transition_type;\n");
2710
2711 if (scan_tz_dir(root_name_end, 0, opt_verbose))
2712 {
2713 fflush(stdout);
2714 fprintf(stderr,
2715 "There were fatal errors during processing "
2716 "of zoneinfo directory '%s'\n", fullname);
2717 return 1;
2718 }
2719
2720 printf("ALTER TABLE time_zone_transition "
2721 "ORDER BY Time_zone_id, Transition_time;\n");
2722 printf("ALTER TABLE time_zone_transition_type "
2723 "ORDER BY Time_zone_id, Transition_type_id;\n");
2724 }
2725 else
2726 {
2727 /*
2728 First argument is timezonefile.
2729 The second is timezonename if opt_leap is not given
2730 */
2731 init_alloc_root(&tz_storage, "timezone_storage", 32768, 0, MYF(0));
2732
2733 if (tz_load(argv[0], &tz_info, &tz_storage))
2734 {
2735 fflush(stdout);
2736 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[0]);
2737 return 1;
2738 }
2739 if (opt_leap)
2740 print_tz_leaps_as_sql(&tz_info);
2741 else
2742 print_tz_as_sql(argv[1], &tz_info);
2743
2744 free_root(&tz_storage, MYF(0));
2745 }
2746
2747 // Reset wsrep_replicate_myisam. lp:1161432
2748 printf("set @toggle=0; execute set_wsrep_myisam using @toggle;\n");
2749
2750 free_defaults(default_argv);
2751 my_end(0);
2752 return 0;
2753}
2754
2755#endif /* defined(TZINFO2SQL) */
2756
2757
2758#ifdef TESTTIME
2759
2760/*
2761 Some simple brute-force test which allowed to catch a pair of bugs.
2762 Also can provide interesting facts about system's time zone support
2763 implementation.
2764*/
2765
2766#ifndef CHAR_BIT
2767#define CHAR_BIT 8
2768#endif
2769
2770#ifndef TYPE_BIT
2771#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
2772#endif
2773
2774#ifndef TYPE_SIGNED
2775#define TYPE_SIGNED(type) (((type) -1) < 0)
2776#endif
2777
2778my_bool
2779is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg)
2780{
2781 return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) &&
2782 (time_arg->month == (uint)tm_arg->tm_mon+1) &&
2783 (time_arg->day == (uint)tm_arg->tm_mday) &&
2784 (time_arg->hour == (uint)tm_arg->tm_hour) &&
2785 (time_arg->minute == (uint)tm_arg->tm_min) &&
2786 (time_arg->second == (uint)tm_arg->tm_sec) &&
2787 time_arg->second_part == 0;
2788}
2789
2790
2791int
2792main(int argc, char **argv)
2793{
2794 my_bool localtime_negative;
2795 TIME_ZONE_INFO tz_info;
2796 struct tm tmp;
2797 MYSQL_TIME time_tmp;
2798 time_t t, t1, t2;
2799 char fullname[FN_REFLEN+1];
2800 char *str_end;
2801 MEM_ROOT tz_storage;
2802
2803 MY_INIT(argv[0]);
2804
2805 init_alloc_root(&tz_storage, "timezone_storage", 32768, MYF(0));
2806
2807 /* let us set some well known timezone */
2808 setenv("TZ", "MET", 1);
2809 tzset();
2810
2811 /* Some initial time zone related system info */
2812 printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned",
2813 (uint)TYPE_BIT(time_t));
2814 if (TYPE_SIGNED(time_t))
2815 {
2816 t= -100;
2817 localtime_negative= MY_TEST(localtime_r(&t, &tmp) != 0);
2818 printf("localtime_r %s negative params \
2819 (time_t=%d is %d-%d-%d %d:%d:%d)\n",
2820 (localtime_negative ? "supports" : "doesn't support"), (int)t,
2821 TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday,
2822 tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
2823
2824 printf("mktime %s negative results (%d)\n",
2825 (t == mktime(&tmp) ? "doesn't support" : "supports"),
2826 (int)mktime(&tmp));
2827 }
2828
2829 tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30;
2830 tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1;
2831 t= mktime(&tmp);
2832 printf("mktime returns %s for spring time gap (%d)\n",
2833 (t != (time_t)-1 ? "something" : "error"), (int)t);
2834
2835 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2836 tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0;
2837 t= mktime(&tmp);
2838 printf("mktime returns %s for non existing date (%d)\n",
2839 (t != (time_t)-1 ? "something" : "error"), (int)t);
2840
2841 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2842 tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1;
2843 t= mktime(&tmp);
2844 printf("mktime %s unnormalized input (%d)\n",
2845 (t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t);
2846
2847 tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26;
2848 tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1;
2849 mktime(&tmp);
2850 tmp.tm_hour= 2; tmp.tm_isdst= -1;
2851 t= mktime(&tmp);
2852 tmp.tm_hour= 4; tmp.tm_isdst= 0;
2853 mktime(&tmp);
2854 tmp.tm_hour= 2; tmp.tm_isdst= -1;
2855 t1= mktime(&tmp);
2856 printf("mktime is %s (%d %d)\n",
2857 (t == t1 ? "determenistic" : "is non-determenistic"),
2858 (int)t, (int)t1);
2859
2860 /* Let us load time zone description */
2861 str_end= strmake_buf(fullname, TZDIR);
2862 strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
2863
2864 if (tz_load(fullname, &tz_info, &tz_storage))
2865 {
2866 printf("Unable to load time zone info from '%s'\n", fullname);
2867 free_root(&tz_storage, MYF(0));
2868 return 1;
2869 }
2870
2871 printf("Testing our implementation\n");
2872
2873 if (TYPE_SIGNED(time_t) && localtime_negative)
2874 {
2875 for (t= -40000; t < 20000; t++)
2876 {
2877 localtime_r(&t, &tmp);
2878 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2879 if (!is_equal_TIME_tm(&time_tmp, &tmp))
2880 {
2881 printf("Problem with negative time_t = %d\n", (int)t);
2882 free_root(&tz_storage, MYF(0));
2883 return 1;
2884 }
2885 }
2886 printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n");
2887 }
2888
2889 for (t= 1000000000; t < 1100000000; t+= 13)
2890 {
2891 localtime_r(&t,&tmp);
2892 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2893
2894 if (!is_equal_TIME_tm(&time_tmp, &tmp))
2895 {
2896 printf("Problem with time_t = %d\n", (int)t);
2897 free_root(&tz_storage, MYF(0));
2898 return 1;
2899 }
2900 }
2901 printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n");
2902
2903 my_init_time();
2904
2905 /*
2906 Be careful here! my_system_gmt_sec doesn't fully handle unnormalized
2907 dates.
2908 */
2909 for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++)
2910 {
2911 for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++)
2912 {
2913 for (time_tmp.day= 1;
2914 time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1];
2915 time_tmp.day++)
2916 {
2917 for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++)
2918 {
2919 for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5)
2920 {
2921 for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25)
2922 {
2923 long not_used;
2924 uint not_used_2;
2925 t= (time_t)my_system_gmt_sec(&time_tmp, &not_used, &not_used_2);
2926 t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, &not_used_2);
2927 if (t != t1)
2928 {
2929 /*
2930 We need special handling during autumn since my_system_gmt_sec
2931 prefers greater time_t values (in MET) for ambiguity.
2932 And BTW that is a bug which should be fixed !!!
2933 */
2934 tmp.tm_year= time_tmp.year - TM_YEAR_BASE;
2935 tmp.tm_mon= time_tmp.month - 1;
2936 tmp.tm_mday= time_tmp.day;
2937 tmp.tm_hour= time_tmp.hour;
2938 tmp.tm_min= time_tmp.minute;
2939 tmp.tm_sec= time_tmp.second;
2940 tmp.tm_isdst= 1;
2941
2942 t2= mktime(&tmp);
2943
2944 if (t1 == t2)
2945 continue;
2946
2947 printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n",
2948 time_tmp.year, time_tmp.month, time_tmp.day,
2949 time_tmp.hour, time_tmp.minute, time_tmp.second,
2950 (int)t,(int)t1);
2951
2952 free_root(&tz_storage, MYF(0));
2953 return 1;
2954 }
2955 }
2956 }
2957 }
2958 }
2959 }
2960 }
2961
2962 printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n");
2963
2964 free_root(&tz_storage, MYF(0));
2965 return 0;
2966}
2967
2968#endif /* defined(TESTTIME) */
2969