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