~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql/tztime.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

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