~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to sql/tztime.cc

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

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