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