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