~drizzle-trunk/drizzle/development

1 by brian
clean slate
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) */