tztime.cc 82.5 KB
Newer Older
Marc Alff's avatar
Marc Alff committed
1
/* Copyright (C) 2004 MySQL AB, 2008-2009 Sun Microsystems, Inc
2 3 4

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
6 7 8 9 10 11 12 13 14 15 16

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
unknown's avatar
unknown committed
17 18
   Most of the following code and structures were derived from
   public domain code from ftp://elsie.nci.nih.gov/pub
19 20 21
   (We will refer to this code as to elsie-code further.)
*/

22 23 24 25
/*
  We should not include mysql_priv.h in mysql_tzinfo_to_sql utility since
  it creates unsolved link dependencies on some platforms.
*/
unknown's avatar
unknown committed
26 27 28 29 30

#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation				// gcc: Class implementation
#endif

31
#include <my_global.h>
32
#if !defined(TZINFO2SQL) && !defined(TESTTIME)
33
#include "mysql_priv.h"
34
#else
35
#include <my_time.h>
36 37 38 39
#include "tztime.h"
#include <my_sys.h>
#endif

40 41 42
#include "tzfile.h"
#include <m_string.h>
#include <my_dir.h>
Marc Alff's avatar
Marc Alff committed
43
#include <mysql/psi/mysql_file.h>
44 45 46 47 48 49

/*
  Now we don't use abbreviations in server but we will do this in future.
*/
#if defined(TZINFO2SQL) || defined(TESTTIME)
#define ABBR_ARE_USED
50 51 52
#else
#if !defined(DBUG_OFF)
/* Let use abbreviations for debug purposes */
53 54
#undef ABBR_ARE_USED
#define ABBR_ARE_USED
55 56
#endif /* !defined(DBUG_OFF) */
#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
57 58 59 60 61

/* Structure describing local time type (e.g. Moscow summer time (MSD)) */
typedef struct ttinfo
{
  long tt_gmtoff; // Offset from UTC in seconds
62
  uint tt_isdst;   // Is daylight saving time or not. Used to set tm_isdst
63
#ifdef ABBR_ARE_USED
64
  uint tt_abbrind; // Index of start of abbreviation for this time type.
65
#endif
unknown's avatar
unknown committed
66
  /*
67 68
    We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
    struct since we don't support POSIX-style TZ descriptions in variables.
69 70 71 72
  */
} TRAN_TYPE_INFO;

/* Structure describing leap-second corrections. */
unknown's avatar
unknown committed
73 74
typedef struct lsinfo
{
75 76 77 78 79
  my_time_t ls_trans; // Transition time
  long      ls_corr;  // Correction to apply
} LS_INFO;

/*
unknown's avatar
unknown committed
80
  Structure with information describing ranges of my_time_t shifted to local
81
  time (my_time_t + offset). Used for local MYSQL_TIME -> my_time_t conversion.
82 83
  See comments for TIME_to_gmt_sec() for more info.
*/
unknown's avatar
unknown committed
84
typedef struct revtinfo
85 86
{
  long rt_offset; // Offset of local time from UTC in seconds
87
  uint rt_type;    // Type of period 0 - Normal period. 1 - Spring time-gap
88 89 90 91 92 93 94 95 96 97
} REVT_INFO;

#ifdef TZNAME_MAX
#define MY_TZNAME_MAX	TZNAME_MAX
#endif
#ifndef TZNAME_MAX
#define MY_TZNAME_MAX	255
#endif

/*
unknown's avatar
unknown committed
98
  Structure which fully describes time zone which is
99 100
  described in our db or in zoneinfo files.
*/
unknown's avatar
unknown committed
101
typedef struct st_time_zone_info
102
{
103 104 105 106 107
  uint leapcnt;  // Number of leap-second corrections
  uint timecnt;  // Number of transitions between time types
  uint typecnt;  // Number of local time types
  uint charcnt;  // Number of characters used for abbreviations
  uint revcnt;   // Number of transition descr. for TIME->my_time_t conversion
108 109
  /* The following are dynamical arrays are allocated in MEM_ROOT */
  my_time_t *ats;       // Times of transitions between time types
unknown's avatar
unknown committed
110
  uchar	*types; // Local time types for transitions
111 112 113 114 115
  TRAN_TYPE_INFO *ttis; // Local time types descriptions
#ifdef ABBR_ARE_USED
  /* Storage for local time types abbreviations. They are stored as ASCIIZ */
  char *chars;
#endif
unknown's avatar
unknown committed
116 117
  /*
    Leap seconds corrections descriptions, this array is shared by
118 119 120
    all time zones who use leap seconds.
  */
  LS_INFO *lsis;
unknown's avatar
unknown committed
121 122
  /*
    Starting points and descriptions of shifted my_time_t (my_time_t + offset)
123 124 125 126 127 128 129 130 131 132
    ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
    Used for tm -> my_time_t conversion.
  */
  my_time_t *revts;
  REVT_INFO *revtis;
  /*
    Time type which is used for times smaller than first transition or if
    there are no transitions at all.
  */
  TRAN_TYPE_INFO *fallback_tti;
unknown's avatar
unknown committed
133

134 135 136 137 138 139 140 141 142 143
} TIME_ZONE_INFO;


static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);


#if defined(TZINFO2SQL) || defined(TESTTIME)

/*
  Load time zone description from zoneinfo (TZinfo) file.
unknown's avatar
unknown committed
144

145 146 147
  SYNOPSIS
    tz_load()
      name - path to zoneinfo file
unknown's avatar
unknown committed
148
      sp   - TIME_ZONE_INFO structure to fill
149 150 151 152 153 154 155 156

  RETURN VALUES
    0 - Ok
    1 - Error
*/
static my_bool
tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
{
157
  uchar *p;
158 159
  int read_from_file;
  uint i;
Marc Alff's avatar
Marc Alff committed
160
  MYSQL_FILE *file;
unknown's avatar
unknown committed
161

Marc Alff's avatar
Marc Alff committed
162
  if (!(file= mysql_file_fopen(0, name, O_RDONLY|O_BINARY, MYF(MY_WME))))
163 164 165 166 167
    return 1;
  {
    union
    {
      struct tzhead tzhead;
168 169
      uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
                TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
170 171 172 173 174
#ifdef ABBR_ARE_USED
               max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
#endif
               sizeof(LS_INFO) * TZ_MAX_LEAPS];
    } u;
175 176
    uint ttisstdcnt;
    uint ttisgmtcnt;
177
    char *tzinfo_buf;
unknown's avatar
unknown committed
178

Marc Alff's avatar
Marc Alff committed
179
    read_from_file= mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
180

Marc Alff's avatar
Marc Alff committed
181
    if (mysql_file_fclose(file, MYF(MY_WME)) != 0)
182 183
      return 1;

184
    if (read_from_file < (int)sizeof(struct tzhead))
185
      return 1;
unknown's avatar
unknown committed
186

187 188 189 190 191 192
    ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt);
    ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt);
    sp->leapcnt= int4net(u.tzhead.tzh_leapcnt);
    sp->timecnt= int4net(u.tzhead.tzh_timecnt);
    sp->typecnt= int4net(u.tzhead.tzh_typecnt);
    sp->charcnt= int4net(u.tzhead.tzh_charcnt);
193
    p= u.tzhead.tzh_charcnt + sizeof(u.tzhead.tzh_charcnt);
194 195 196 197
    if (sp->leapcnt > TZ_MAX_LEAPS ||
        sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES ||
        sp->timecnt > TZ_MAX_TIMES ||
        sp->charcnt > TZ_MAX_CHARS ||
198 199 200
        (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
        (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
      return 1;
unknown's avatar
unknown committed
201
    if ((uint)(read_from_file - (p - u.buf)) <
202
        sp->timecnt * 4 +                       /* ats */
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
        sp->timecnt +                           /* types */
        sp->typecnt * (4 + 2) +                 /* ttinfos */
        sp->charcnt +                           /* chars */
        sp->leapcnt * (4 + 4) +                 /* lsinfos */
        ttisstdcnt +                            /* ttisstds */
        ttisgmtcnt)                             /* ttisgmts */
      return 1;

    if (!(tzinfo_buf= (char *)alloc_root(storage,
                                         ALIGN_SIZE(sp->timecnt *
                                                    sizeof(my_time_t)) +
                                         ALIGN_SIZE(sp->timecnt) +
                                         ALIGN_SIZE(sp->typecnt *
                                                    sizeof(TRAN_TYPE_INFO)) +
#ifdef ABBR_ARE_USED
                                         ALIGN_SIZE(sp->charcnt) +
#endif
                                         sp->leapcnt * sizeof(LS_INFO))))
      return 1;
unknown's avatar
unknown committed
222

223 224
    sp->ats= (my_time_t *)tzinfo_buf;
    tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
unknown's avatar
unknown committed
225
    sp->types= (uchar *)tzinfo_buf;
226 227 228 229 230 231 232 233
    tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
    sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
    tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
#ifdef ABBR_ARE_USED
    sp->chars= tzinfo_buf;
    tzinfo_buf+= ALIGN_SIZE(sp->charcnt);
#endif
    sp->lsis= (LS_INFO *)tzinfo_buf;
unknown's avatar
unknown committed
234

235 236
    for (i= 0; i < sp->timecnt; i++, p+= 4)
      sp->ats[i]= int4net(p);
unknown's avatar
unknown committed
237

238 239
    for (i= 0; i < sp->timecnt; i++)
    {
unknown's avatar
unknown committed
240
      sp->types[i]= (uchar) *p++;
241 242 243 244 245 246
      if (sp->types[i] >= sp->typecnt)
        return 1;
    }
    for (i= 0; i < sp->typecnt; i++)
    {
      TRAN_TYPE_INFO * ttisp;
unknown's avatar
unknown committed
247

248 249 250
      ttisp= &sp->ttis[i];
      ttisp->tt_gmtoff= int4net(p);
      p+= 4;
unknown's avatar
unknown committed
251
      ttisp->tt_isdst= (uchar) *p++;
252 253
      if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
        return 1;
unknown's avatar
unknown committed
254
      ttisp->tt_abbrind= (uchar) *p++;
255
      if (ttisp->tt_abbrind > sp->charcnt)
256 257 258 259 260 261 262 263
        return 1;
    }
    for (i= 0; i < sp->charcnt; i++)
      sp->chars[i]= *p++;
    sp->chars[i]= '\0';	/* ensure '\0' at end */
    for (i= 0; i < sp->leapcnt; i++)
    {
      LS_INFO *lsisp;
unknown's avatar
unknown committed
264

265 266 267 268 269 270
      lsisp= &sp->lsis[i];
      lsisp->ls_trans= int4net(p);
      p+= 4;
      lsisp->ls_corr= int4net(p);
      p+= 4;
    }
unknown's avatar
unknown committed
271
    /*
272
      Since we don't support POSIX style TZ definitions in variables we
unknown's avatar
unknown committed
273
      don't read further like glibc or elsie code.
274 275
    */
  }
unknown's avatar
unknown committed
276

277 278 279 280 281 282
  return prepare_tz_info(sp, storage);
}
#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */


/*
unknown's avatar
unknown committed
283
  Finish preparation of time zone description for use in TIME_to_gmt_sec()
284
  and gmt_sec_to_TIME() functions.
unknown's avatar
unknown committed
285

286 287 288 289
  SYNOPSIS
    prepare_tz_info()
      sp - pointer to time zone description
      storage - pointer to MEM_ROOT where arrays for map allocated
unknown's avatar
unknown committed
290

291
  DESCRIPTION
unknown's avatar
unknown committed
292 293 294 295
    First task of this function is to find fallback time type which will
    be used if there are no transitions or we have moment in time before
    any transitions.
    Second task is to build "shifted my_time_t" -> my_time_t map used in
296
    MYSQL_TIME -> my_time_t conversion.
unknown's avatar
unknown committed
297
    Note: See description of TIME_to_gmt_sec() function first.
298
    In order to perform MYSQL_TIME -> my_time_t conversion we need to build table
unknown's avatar
unknown committed
299 300 301
    which defines "shifted by tz offset and leap seconds my_time_t" ->
    my_time_t function wich is almost the same (except ranges of ambiguity)
    as reverse function to piecewise linear function used for my_time_t ->
302 303
    "shifted my_time_t" conversion and which is also specified as table in
    zoneinfo file or in our db (It is specified as start of time type ranges
unknown's avatar
unknown committed
304
    and time type offsets). So basic idea is very simple - let us iterate
305 306
    through my_time_t space from one point of discontinuity of my_time_t ->
    "shifted my_time_t" function to another and build our approximation of
unknown's avatar
unknown committed
307
    reverse function. (Actually we iterate through ranges on which
308
    my_time_t -> "shifted my_time_t" is linear function).
unknown's avatar
unknown committed
309

310 311
  RETURN VALUES
    0	Ok
unknown's avatar
unknown committed
312
    1	Error
313
*/
unknown's avatar
unknown committed
314
static my_bool
315 316 317 318 319 320
prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
{
  my_time_t cur_t= MY_TIME_T_MIN;
  my_time_t cur_l, end_t, end_l;
  my_time_t cur_max_seen_l= MY_TIME_T_MIN;
  long cur_offset, cur_corr, cur_off_and_corr;
321 322
  uint next_trans_idx, next_leap_idx;
  uint i;
unknown's avatar
unknown committed
323
  /*
324 325 326 327 328 329 330 331 332
    Temporary arrays where we will store tables. Needed because
    we don't know table sizes ahead. (Well we can estimate their
    upper bound but this will take extra space.)
  */
  my_time_t revts[TZ_MAX_REV_RANGES];
  REVT_INFO revtis[TZ_MAX_REV_RANGES];

  LINT_INIT(end_l);

unknown's avatar
unknown committed
333 334 335 336
  /*
    Let us setup fallback time type which will be used if we have not any
    transitions or if we have moment of time before first transition.
    We will find first non-DST local time type and use it (or use first
337 338 339 340 341 342 343
    local time type if all of them are DST types).
  */
  for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
    /* no-op */ ;
  if (i == sp->typecnt)
    i= 0;
  sp->fallback_tti= &(sp->ttis[i]);
unknown's avatar
unknown committed
344 345 346 347


  /*
    Let us build shifted my_time_t -> my_time_t map.
348 349
  */
  sp->revcnt= 0;
unknown's avatar
unknown committed
350

351 352 353
  /* Let us find initial offset */
  if (sp->timecnt == 0 || cur_t < sp->ats[0])
  {
unknown's avatar
unknown committed
354
    /*
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
      If we have not any transitions or t is before first transition we are using
      already found fallback time type which index is already in i.
    */
    next_trans_idx= 0;
  }
  else
  {
    /* cur_t == sp->ats[0] so we found transition */
    i= sp->types[0];
    next_trans_idx= 1;
  }

  cur_offset= sp->ttis[i].tt_gmtoff;


  /* let us find leap correction... unprobable, but... */
unknown's avatar
unknown committed
371
  for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
372 373 374 375 376 377 378 379 380 381 382 383 384
         cur_t >= sp->lsis[next_leap_idx].ls_trans;
         ++next_leap_idx)
    continue;

  if (next_leap_idx > 0)
    cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
  else
    cur_corr= 0;

  /* Iterate trough t space */
  while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
  {
    cur_off_and_corr= cur_offset - cur_corr;
unknown's avatar
unknown committed
385 386

    /*
387 388 389
      We assuming that cur_t could be only overflowed downwards,
      we also assume that end_t won't be overflowed in this case.
    */
unknown's avatar
unknown committed
390
    if (cur_off_and_corr < 0 &&
391 392
        cur_t < MY_TIME_T_MIN - cur_off_and_corr)
      cur_t= MY_TIME_T_MIN - cur_off_and_corr;
unknown's avatar
unknown committed
393

394
    cur_l= cur_t + cur_off_and_corr;
unknown's avatar
unknown committed
395 396

    /*
397 398 399 400 401
      Let us choose end_t as point before next time type change or leap
      second correction.
    */
    end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
                                                MY_TIME_T_MAX,
unknown's avatar
unknown committed
402
               (next_leap_idx < sp->leapcnt) ?
403
                 sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
unknown's avatar
unknown committed
404
    /*
405 406 407 408 409 410
      again assuming that end_t can be overlowed only in positive side
      we also assume that end_t won't be overflowed in this case.
    */
    if (cur_off_and_corr > 0 &&
        end_t > MY_TIME_T_MAX - cur_off_and_corr)
      end_t= MY_TIME_T_MAX - cur_off_and_corr;
unknown's avatar
unknown committed
411

412
    end_l= end_t + cur_off_and_corr;
unknown's avatar
unknown committed
413

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

    if (end_l > cur_max_seen_l)
    {
      /* We want special handling in the case of first range */
      if (cur_max_seen_l == MY_TIME_T_MIN)
      {
        revts[sp->revcnt]= cur_l;
        revtis[sp->revcnt].rt_offset= cur_off_and_corr;
        revtis[sp->revcnt].rt_type= 0;
        sp->revcnt++;
        cur_max_seen_l= end_l;
      }
      else
      {
        if (cur_l > cur_max_seen_l + 1)
        {
          /* We have a spring time-gap and we are not at the first range */
          revts[sp->revcnt]= cur_max_seen_l + 1;
          revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
          revtis[sp->revcnt].rt_type= 1;
          sp->revcnt++;
          if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
            break; /* That was too much */
          cur_max_seen_l= cur_l - 1;
        }
unknown's avatar
unknown committed
439

440 441 442 443 444 445 446 447 448 449
        /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */

        revts[sp->revcnt]= cur_max_seen_l + 1;
        revtis[sp->revcnt].rt_offset= cur_off_and_corr;
        revtis[sp->revcnt].rt_type= 0;
        sp->revcnt++;
        cur_max_seen_l= end_l;
      }
    }

unknown's avatar
unknown committed
450
    if (end_t == MY_TIME_T_MAX ||
451 452
        ((cur_off_and_corr > 0) &&
        (end_t >= MY_TIME_T_MAX - cur_off_and_corr)))
453 454
      /* end of t space */
      break;
unknown's avatar
unknown committed
455

456 457
    cur_t= end_t + 1;

unknown's avatar
unknown committed
458
    /*
459
      Let us find new offset and correction. Because of our choice of end_t
unknown's avatar
unknown committed
460
      cur_t can only be point where new time type starts or/and leap
461 462 463
      correction is performed.
    */
    if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
unknown's avatar
unknown committed
464
      if (next_trans_idx < sp->timecnt &&
465 466 467 468 469 470
          cur_t == sp->ats[next_trans_idx])
      {
        /* We are at offset point */
        cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
        ++next_trans_idx;
      }
unknown's avatar
unknown committed
471

472 473 474 475 476 477 478 479
    if (next_leap_idx < sp->leapcnt &&
        cur_t == sp->lsis[next_leap_idx].ls_trans)
    {
      /* we are at leap point */
      cur_corr= sp->lsis[next_leap_idx].ls_corr;
      ++next_leap_idx;
    }
  }
unknown's avatar
unknown committed
480

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
  /* check if we have had enough space */
  if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
    return 1;

  /* set maximum end_l as finisher */
  revts[sp->revcnt]= end_l;

  /* Allocate arrays of proper size in sp and copy result there */
  if (!(sp->revts= (my_time_t *)alloc_root(storage,
                                  sizeof(my_time_t) * (sp->revcnt + 1))) ||
      !(sp->revtis= (REVT_INFO *)alloc_root(storage,
                                  sizeof(REVT_INFO) * sp->revcnt)))
    return 1;

  memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
  memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
unknown's avatar
unknown committed
497

498 499 500 501
  return 0;
}


502 503
#if !defined(TZINFO2SQL)

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
static const uint mon_lengths[2][MONS_PER_YEAR]=
{
  { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};

static const uint mon_starts[2][MONS_PER_YEAR]=
{
  { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
  { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
};

static const uint year_lengths[2]=
{
  DAYS_PER_NYEAR, DAYS_PER_LYEAR
};

#define LEAPS_THRU_END_OF(y)  ((y) / 4 - (y) / 100 + (y) / 400)


unknown's avatar
unknown committed
524 525
/*
  Converts time from my_time_t representation (seconds in UTC since Epoch)
526
  to broken down representation using given local time zone offset.
unknown's avatar
unknown committed
527

528 529 530 531 532
  SYNOPSIS
    sec_to_TIME()
      tmp    - pointer to structure for broken down representation
      t      - my_time_t value to be converted
      offset - local time zone offset
unknown's avatar
unknown committed
533

534
  DESCRIPTION
535
    Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
unknown's avatar
unknown committed
536 537
    (from elsie code) because doesn't contain any leap correction and
    TM_GMTOFF and is_dst setting and contains some MySQL specific
538 539 540 541
    initialization. Funny but with removing of these we almost have
    glibc's offtime function.
*/
static void
542
sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
543 544 545 546 547 548 549
{
  long days;
  long rem;
  int y;
  int yleap;
  const uint *ip;

unknown's avatar
unknown committed
550 551
  days= (long) (t / SECS_PER_DAY);
  rem=  (long) (t % SECS_PER_DAY);
unknown's avatar
unknown committed
552 553 554

  /*
    We do this as separate step after dividing t, because this
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
    allows us handle times near my_time_t bounds without overflows.
  */
  rem+= offset;
  while (rem < 0)
  {
    rem+= SECS_PER_DAY;
    days--;
  }
  while (rem >= SECS_PER_DAY)
  {
    rem -= SECS_PER_DAY;
    days++;
  }
  tmp->hour= (uint)(rem / SECS_PER_HOUR);
  rem= rem % SECS_PER_HOUR;
  tmp->minute= (uint)(rem / SECS_PER_MIN);
  /*
    A positive leap second requires a special
    representation.  This uses "... ??:59:60" et seq.
  */
  tmp->second= (uint)(rem % SECS_PER_MIN);
unknown's avatar
unknown committed
576

577 578 579 580
  y= EPOCH_YEAR;
  while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
  {
    int	newy;
unknown's avatar
unknown committed
581

582 583 584 585 586 587 588 589 590
    newy= y + days / DAYS_PER_NYEAR;
    if (days < 0)
      newy--;
    days-= (newy - y) * DAYS_PER_NYEAR +
           LEAPS_THRU_END_OF(newy - 1) -
           LEAPS_THRU_END_OF(y - 1);
    y= newy;
  }
  tmp->year= y;
unknown's avatar
unknown committed
591

592 593 594 595 596 597
  ip= mon_lengths[yleap];
  for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
    days= days - (long) ip[tmp->month];
  tmp->month++;
  tmp->day= (uint)(days + 1);

598
  /* filling MySQL specific MYSQL_TIME members */
599
  tmp->neg= 0; tmp->second_part= 0;
600
  tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
601 602 603 604 605
}


/*
  Find time range wich contains given my_time_t value
unknown's avatar
unknown committed
606

607 608
  SYNOPSIS
    find_time_range()
unknown's avatar
unknown committed
609
      t                - my_time_t value for which we looking for range
610 611
      range_boundaries - sorted array of range starts.
      higher_bound     - number of ranges
unknown's avatar
unknown committed
612

613
  DESCRIPTION
unknown's avatar
unknown committed
614
    Performs binary search for range which contains given my_time_t value.
615 616 617
    It has sense if number of ranges is greater than zero and my_time_t value
    is greater or equal than beginning of first range. It also assumes that
    t belongs to some range specified or end of last is MY_TIME_T_MAX.
unknown's avatar
unknown committed
618

619 620
    With this localtime_r on real data may takes less time than with linear
    search (I've seen 30% speed up).
unknown's avatar
unknown committed
621

622 623 624
  RETURN VALUE
    Index of range to which t belongs
*/
unknown's avatar
unknown committed
625
static uint
626 627 628 629
find_time_range(my_time_t t, const my_time_t *range_boundaries,
                uint higher_bound)
{
  uint i, lower_bound= 0;
unknown's avatar
unknown committed
630 631

  /*
632 633 634
    Function will work without this assertion but result would be meaningless.
  */
  DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
unknown's avatar
unknown committed
635

636 637
  /*
    Do binary search for minimal interval which contain t. We preserve:
unknown's avatar
unknown committed
638 639
    range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
    invariant and decrease this higher_bound - lower_bound gap twice
640 641
    times on each step.
  */
unknown's avatar
unknown committed
642

643 644 645 646 647 648 649 650 651 652 653 654
  while (higher_bound - lower_bound > 1)
  {
    i= (lower_bound + higher_bound) >> 1;
    if (range_boundaries[i] <= t)
      lower_bound= i;
    else
      higher_bound= i;
  }
  return lower_bound;
}

/*
unknown's avatar
unknown committed
655 656
  Find local time transition for given my_time_t.

657 658 659 660
  SYNOPSIS
    find_transition_type()
      t   - my_time_t value to be converted
      sp  - pointer to struct with time zone description
unknown's avatar
unknown committed
661

662 663 664 665 666
  RETURN VALUE
    Pointer to structure in time zone description describing
    local time type for given my_time_t.
*/
static
unknown's avatar
unknown committed
667
const TRAN_TYPE_INFO *
668 669 670 671
find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
{
  if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
  {
unknown's avatar
unknown committed
672
    /*
673 674 675 676 677
      If we have not any transitions or t is before first transition let
      us use fallback time type.
    */
    return sp->fallback_tti;
  }
unknown's avatar
unknown committed
678

679 680
  /*
    Do binary search for minimal interval between transitions which
unknown's avatar
unknown committed
681
    contain t. With this localtime_r on real data may takes less
682 683 684 685 686 687 688 689
    time than with linear search (I've seen 30% speed up).
  */
  return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
}


/*
  Converts time in my_time_t representation (seconds in UTC since Epoch) to
690
  broken down MYSQL_TIME representation in local time zone.
unknown's avatar
unknown committed
691

692 693 694 695 696 697 698
  SYNOPSIS
    gmt_sec_to_TIME()
      tmp          - pointer to structure for broken down represenatation
      sec_in_utc   - my_time_t value to be converted
      sp           - pointer to struct with time zone description

  TODO
unknown's avatar
unknown committed
699
    We can improve this function by creating joined array of transitions and
700
    leap corrections. This will require adding extra field to TRAN_TYPE_INFO
unknown's avatar
unknown committed
701 702
    for storing number of "extra" seconds to minute occured due to correction
    (60th and 61st second, look how we calculate them as "hit" in this
703
    function).
unknown's avatar
unknown committed
704
    Under realistic assumptions about frequency of transitions the same array
705
    can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
unknown's avatar
unknown committed
706
    implement tweaked binary search which will take into account that some
707
    MYSQL_TIME has two matching my_time_t ranges and some of them have none.
708 709
*/
static void
710
gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
711 712 713 714 715 716 717
{
  const TRAN_TYPE_INFO *ttisp;
  const LS_INFO *lp;
  long  corr= 0;
  int   hit= 0;
  int   i;

unknown's avatar
unknown committed
718
  /*
719
    Find proper transition (and its local time type) for our sec_in_utc value.
unknown's avatar
unknown committed
720
    Funny but again by separating this step in function we receive code
721 722 723 724 725
    which very close to glibc's code. No wonder since they obviously use
    the same base and all steps are sensible.
  */
  ttisp= find_transition_type(sec_in_utc, sp);

unknown's avatar
unknown committed
726
  /*
727 728
    Let us find leap correction for our sec_in_utc value and number of extra
    secs to add to this minute.
unknown's avatar
unknown committed
729 730
    This loop is rarely used because most users will use time zones without
    leap seconds, and even in case when we have such time zone there won't
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
    be many iterations (we have about 22 corrections at this moment (2004)).
  */
  for ( i= sp->leapcnt; i-- > 0; )
  {
    lp= &sp->lsis[i];
    if (sec_in_utc >= lp->ls_trans)
    {
      if (sec_in_utc == lp->ls_trans)
      {
        hit= ((i == 0 && lp->ls_corr > 0) ||
              lp->ls_corr > sp->lsis[i - 1].ls_corr);
        if (hit)
        {
          while (i > 0 &&
                 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
                 sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
          {
            hit++;
            i--;
          }
        }
      }
      corr= lp->ls_corr;
      break;
    }
  }
unknown's avatar
unknown committed
757

758 759 760 761 762 763 764 765 766
  sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);

  tmp->second+= hit;
}


/*
  Converts local time in broken down representation to local
  time zone analog of my_time_t represenation.
unknown's avatar
unknown committed
767

768 769 770
  SYNOPSIS
    sec_since_epoch()
      year, mon, mday, hour, min, sec - broken down representation.
unknown's avatar
unknown committed
771

772 773 774
  DESCRIPTION
    Converts time in broken down representation to my_time_t representation
    ignoring time zone. Note that we cannot convert back some valid _local_
unknown's avatar
unknown committed
775
    times near ends of my_time_t range because of my_time_t  overflow. But we
776
    ignore this fact now since MySQL will never pass such argument.
unknown's avatar
unknown committed
777

778 779 780
  RETURN VALUE
    Seconds since epoch time representation.
*/
unknown's avatar
unknown committed
781
static my_time_t
782 783
sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
{
784 785
  /* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
  DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
786
#ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
unknown's avatar
unknown committed
787
  /*
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
    It turns out that only whenever month is normalized or unnormalized
    plays role.
  */
  DBUG_ASSERT(mon > 0 && mon < 13);
  long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
             LEAPS_THRU_END_OF(year - 1) -
             LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
  days+= mon_starts[isleap(year)][mon - 1];
#else
  long norm_month= (mon - 1) % MONS_PER_YEAR;
  long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
  long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
             LEAPS_THRU_END_OF(a_year - 1) -
             LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
  days+= mon_starts[isleap(a_year)]
                    [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
#endif
  days+= mday - 1;

unknown's avatar
unknown committed
807
  return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
808 809 810 811
         SECS_PER_MIN + sec;
}

/*
812
  Converts local time in broken down MYSQL_TIME representation to my_time_t
813
  representation.
unknown's avatar
unknown committed
814

815 816 817 818
  SYNOPSIS
    TIME_to_gmt_sec()
      t               - pointer to structure for broken down represenatation
      sp              - pointer to struct with time zone description
unknown's avatar
unknown committed
819
      in_dst_time_gap - pointer to bool which is set to true if datetime
820 821
                        value passed doesn't really exist (i.e. falls into
                        spring time-gap) and is not touched otherwise.
unknown's avatar
unknown committed
822

823
  DESCRIPTION
unknown's avatar
unknown committed
824
    This is mktime analog for MySQL. It is essentially different
825
    from mktime (or hypotetical my_mktime) because:
unknown's avatar
unknown committed
826
    - It has no idea about tm_isdst member so if it
827
      has two answers it will give the smaller one
unknown's avatar
unknown committed
828
    - If we are in spring time gap then it will return
829
      beginning of the gap
unknown's avatar
unknown committed
830 831
    - It can give wrong results near the ends of my_time_t due to
      overflows, but we are safe since in MySQL we will never
832 833
      call this function for such dates (its restriction for year
      between 1970 and 2038 gives us several days of reserve).
unknown's avatar
unknown committed
834
    - By default it doesn't support un-normalized input. But if
835
      sec_since_epoch() function supports un-normalized dates
unknown's avatar
unknown committed
836
      then this function should handle un-normalized input right,
837
      altough it won't normalize structure TIME.
unknown's avatar
unknown committed
838 839 840 841 842 843

    Traditional approach to problem of conversion from broken down
    representation to time_t is iterative. Both elsie's and glibc
    implementation try to guess what time_t value should correspond to
    this broken-down value. They perform localtime_r function on their
    guessed value and then calculate the difference and try to improve
844
    their guess. Elsie's code guesses time_t value in bit by bit manner,
unknown's avatar
unknown committed
845
    Glibc's code tries to add difference between broken-down value
846
    corresponding to guess and target broken-down value to current guess.
unknown's avatar
unknown committed
847 848
    It also uses caching of last found correction... So Glibc's approach
    is essentially faster but introduces some undetermenism (in case if
849 850 851
    is_dst member of broken-down representation (tm struct) is not known
    and we have two possible answers).

unknown's avatar
unknown committed
852
    We use completely different approach. It is better since it is both
853
    faster than iterative implementations and fully determenistic. If you
854
    look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
855 856
    of two steps:
    The first is calculating shifted my_time_t value and the second - TIME
unknown's avatar
unknown committed
857
    calculation from shifted my_time_t value (well it is a bit simplified
858 859
    picture). The part in which we are interested in is my_time_t -> shifted
    my_time_t conversion. It is piecewise linear function which is defined
unknown's avatar
unknown committed
860 861 862 863 864 865 866 867
    by combination of transition times as break points and times offset
    as changing function parameter. The possible inverse function for this
    converison would be ambiguos but with MySQL's restrictions we can use
    some function which is the same as inverse function on unambigiuos
    ranges and coincides with one of branches of inverse function in
    other ranges. Thus we just need to build table which will determine
    this shifted my_time_t -> my_time_t conversion similar to existing
    (my_time_t -> shifted my_time_t table). We do this in
868
    prepare_tz_info function.
unknown's avatar
unknown committed
869

870
  TODO
unknown's avatar
unknown committed
871
    If we can even more improve this function. For doing this we will need to
872
    build joined map of transitions and leap corrections for gmt_sec_to_TIME()
unknown's avatar
unknown committed
873
    function (similar to revts/revtis). Under realistic assumptions about
874 875 876 877
    frequency of transitions we can use the same array for TIME_to_gmt_sec().
    We need to implement special version of binary search for this. Such step
    will be beneficial to CPU cache since we will decrease data-set used for
    conversion twice.
unknown's avatar
unknown committed
878

879
  RETURN VALUE
unknown's avatar
unknown committed
880
    Seconds in UTC since Epoch.
881 882 883
    0 in case of error.
*/
static my_time_t
884
TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp,
885
                my_bool *in_dst_time_gap)
886 887 888 889
{
  my_time_t local_t;
  uint saved_seconds;
  uint i;
890
  int shift= 0;
891 892

  DBUG_ENTER("TIME_to_gmt_sec");
unknown's avatar
unknown committed
893

894
  if (!validate_timestamp_range(t))
unknown's avatar
unknown committed
895
    DBUG_RETURN(0);
896 897


898 899 900 901 902 903
  /* We need this for correct leap seconds handling */
  if (t->second < SECS_PER_MIN)
    saved_seconds= 0;
  else
    saved_seconds= t->second;

unknown's avatar
unknown committed
904
  /*
905 906 907 908 909 910 911 912 913
    NOTE: to convert full my_time_t range we do a shift of the
    boundary dates here to avoid overflow of my_time_t.
    We use alike approach in my_system_gmt_sec().

    However in that function we also have to take into account
    overflow near 0 on some platforms. That's because my_system_gmt_sec
    uses localtime_r(), which doesn't work with negative values correctly
    on platforms with unsigned time_t (QNX). Here we don't use localtime()
    => we negative values of local_t are ok.
914
  */
unknown's avatar
unknown committed
915

916 917 918 919 920 921 922 923 924 925 926 927
  if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
  {
    /*
      We will pass (t->day - shift) to sec_since_epoch(), and
      want this value to be a positive number, so we shift
      only dates > 4.01.2038 (to avoid owerflow).
    */
    shift= 2;
  }


  local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
unknown's avatar
unknown committed
928
                           t->hour, t->minute,
929 930 931 932 933 934 935
                           saved_seconds ? 0 : t->second);

  /* We have at least one range */
  DBUG_ASSERT(sp->revcnt >= 1);

  if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
  {
unknown's avatar
unknown committed
936
    /*
937 938 939 940 941 942 943 944
      This means that source time can't be represented as my_time_t due to
      limited my_time_t range.
    */
    DBUG_RETURN(0);
  }

  /* binary search for our range */
  i= find_time_range(local_t, sp->revts, sp->revcnt);
unknown's avatar
unknown committed
945

946 947 948 949 950 951 952
  /*
    As there are no offset switches at the end of TIMESTAMP range,
    we could simply check for overflow here (and don't need to bother
    about DST gaps etc)
  */
  if (shift)
  {
953
    if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
unknown's avatar
unknown committed
954
                               sp->revtis[i].rt_offset - saved_seconds))
955 956 957
    {
      DBUG_RETURN(0);                           /* my_time_t overflow */
    }
958
    local_t+= shift * SECS_PER_DAY;
959 960
  }

961 962
  if (sp->revtis[i].rt_type)
  {
unknown's avatar
unknown committed
963
    /*
964 965 966 967 968 969
      Oops! We are in spring time gap.
      May be we should return error here?
      Now we are returning my_time_t value corresponding to the
      beginning of the gap.
    */
    *in_dst_time_gap= 1;
970
    local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
971 972
  }
  else
973 974 975 976 977 978 979
    local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;

  /* check for TIMESTAMP_MAX_VALUE was already done above */
  if (local_t < TIMESTAMP_MIN_VALUE)
    local_t= 0;

  DBUG_RETURN(local_t);
980 981 982 983
}


/*
unknown's avatar
unknown committed
984
  End of elsie derived code.
985
*/
986
#endif /* !defined(TZINFO2SQL) */
987 988 989 990 991 992 993 994 995 996 997


#if !defined(TESTTIME) && !defined(TZINFO2SQL)

/*
  String with names of SYSTEM time zone.
*/
static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);


/*
unknown's avatar
unknown committed
998
  Instance of this class represents local time zone used on this system
999
  (specified by TZ environment variable or via any other system mechanism).
unknown's avatar
unknown committed
1000
  It uses system functions (localtime_r, my_system_gmt_sec) for conversion
1001 1002
  and is always available. Because of this it is used by default - if there
  were no explicit time zone specified. On the other hand because of this
unknown's avatar
unknown committed
1003 1004
  conversion methods provided by this class is significantly slower and
  possibly less multi-threaded-friendly than corresponding Time_zone_db
1005 1006
  methods so the latter should be preffered there it is possible.
*/
unknown's avatar
unknown committed
1007
class Time_zone_system : public Time_zone
1008 1009
{
public:
1010
  Time_zone_system() {}                       /* Remove gcc warning */
1011
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1012
                                    my_bool *in_dst_time_gap) const;
1013
  virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1014 1015 1016 1017 1018
  virtual const String * get_name() const;
};


/*
1019
  Converts local time in system time zone in MYSQL_TIME representation
1020
  to its my_time_t representation.
unknown's avatar
unknown committed
1021

1022 1023
  SYNOPSIS
    TIME_to_gmt_sec()
1024
      t               - pointer to MYSQL_TIME structure with local time in
1025
                        broken-down representation.
unknown's avatar
unknown committed
1026
      in_dst_time_gap - pointer to bool which is set to true if datetime
1027 1028 1029 1030 1031
                        value passed doesn't really exist (i.e. falls into
                        spring time-gap) and is not touched otherwise.

  DESCRIPTION
    This method uses system function (localtime_r()) for conversion
1032
    local time in system time zone in MYSQL_TIME structure to its my_time_t
1033
    representation. Unlike the same function for Time_zone_db class
unknown's avatar
unknown committed
1034 1035
    it it won't handle unnormalized input properly. Still it will
    return lowest possible my_time_t in case of ambiguity or if we
1036
    provide time corresponding to the time-gap.
unknown's avatar
unknown committed
1037

1038
    You should call my_init_time() function before using this function.
1039 1040 1041 1042

  RETURN VALUE
    Corresponding my_time_t value or 0 in case of error
*/
unknown's avatar
unknown committed
1043
my_time_t
1044
Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1045 1046 1047 1048 1049 1050 1051 1052 1053
{
  long not_used;
  return my_system_gmt_sec(t, &not_used, in_dst_time_gap);
}


/*
  Converts time from UTC seconds since Epoch (my_time_t) representation
  to system local time zone broken-down representation.
unknown's avatar
unknown committed
1054

1055 1056
  SYNOPSIS
    gmt_sec_to_TIME()
1057
      tmp - pointer to MYSQL_TIME structure to fill-in
unknown's avatar
unknown committed
1058
      t   - my_time_t value to be converted
1059

unknown's avatar
unknown committed
1060
  NOTE
1061
    We assume that value passed to this function will fit into time_t range
unknown's avatar
unknown committed
1062
    supported by localtime_r. This conversion is putting restriction on
1063
    TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
unknown's avatar
unknown committed
1064
    for interaction with client then we can extend TIMESTAMP range down to
1065 1066
    the 1902 easily.
*/
unknown's avatar
unknown committed
1067
void
1068
Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1069 1070 1071 1072 1073 1074
{
  struct tm tmp_tm;
  time_t tmp_t= (time_t)t;

  localtime_r(&tmp_t, &tmp_tm);
  localtime_to_TIME(tmp, &tmp_tm);
1075
  tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1076
  adjust_leap_second(tmp);
1077 1078 1079 1080 1081
}


/*
  Get name of time zone
unknown's avatar
unknown committed
1082

1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
  SYNOPSIS
    get_name()

  RETURN VALUE
    Name of time zone as String
*/
const String *
Time_zone_system::get_name() const
{
  return &tz_SYSTEM_name;
}


/*
unknown's avatar
unknown committed
1097 1098
  Instance of this class represents UTC time zone. It uses system gmtime_r
  function for conversions and is always available. It is used only for
1099 1100
  my_time_t -> MYSQL_TIME conversions in various UTC_...  functions, it is not
  intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
1101
*/
unknown's avatar
unknown committed
1102
class Time_zone_utc : public Time_zone
1103 1104
{
public:
1105
  Time_zone_utc() {}                          /* Remove gcc warning */
1106
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1107
                                    my_bool *in_dst_time_gap) const;
1108
  virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1109 1110 1111 1112 1113
  virtual const String * get_name() const;
};


/*
1114
  Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
unknown's avatar
unknown committed
1115

1116 1117
  SYNOPSIS
    TIME_to_gmt_sec()
1118
      t               - pointer to MYSQL_TIME structure with local time
1119
                        in broken-down representation.
unknown's avatar
unknown committed
1120
      in_dst_time_gap - pointer to bool which is set to true if datetime
1121 1122 1123 1124
                        value passed doesn't really exist (i.e. falls into
                        spring time-gap) and is not touched otherwise.

  DESCRIPTION
unknown's avatar
unknown committed
1125 1126
    Since Time_zone_utc is used only internally for my_time_t -> TIME
    conversions, this function of Time_zone interface is not implemented for
1127 1128 1129 1130 1131
    this class and should not be called.

  RETURN VALUE
    0
*/
unknown's avatar
unknown committed
1132
my_time_t
1133
Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1134 1135 1136 1137 1138
{
  /* Should be never called */
  DBUG_ASSERT(0);
  return 0;
}
unknown's avatar
unknown committed
1139

1140 1141 1142 1143

/*
  Converts time from UTC seconds since Epoch (my_time_t) representation
  to broken-down representation (also in UTC).
unknown's avatar
unknown committed
1144

1145 1146
  SYNOPSIS
    gmt_sec_to_TIME()
1147
      tmp - pointer to MYSQL_TIME structure to fill-in
unknown's avatar
unknown committed
1148 1149
      t   - my_time_t value to be converted

1150 1151 1152
  NOTE
    See note for apropriate Time_zone_system method.
*/
unknown's avatar
unknown committed
1153
void
1154
Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1155 1156 1157 1158 1159
{
  struct tm tmp_tm;
  time_t tmp_t= (time_t)t;
  gmtime_r(&tmp_t, &tmp_tm);
  localtime_to_TIME(tmp, &tmp_tm);
1160
  tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1161
  adjust_leap_second(tmp);
1162 1163 1164 1165 1166
}


/*
  Get name of time zone
unknown's avatar
unknown committed
1167

1168 1169 1170 1171
  SYNOPSIS
    get_name()

  DESCRIPTION
unknown's avatar
unknown committed
1172 1173
    Since Time_zone_utc is used only internally by SQL's UTC_* functions it
    is not accessible directly, and hence this function of Time_zone
1174
    interface is not implemented for this class and should not be called.
unknown's avatar
unknown committed
1175

1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
  RETURN VALUE
    0
*/
const String *
Time_zone_utc::get_name() const
{
  /* Should be never called */
  DBUG_ASSERT(0);
  return 0;
}


/*
unknown's avatar
unknown committed
1189 1190
  Instance of this class represents some time zone which is
  described in mysql.time_zone family of tables.
1191
*/
unknown's avatar
unknown committed
1192
class Time_zone_db : public Time_zone
1193 1194 1195
{
public:
  Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
1196
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1197
                                    my_bool *in_dst_time_gap) const;
1198
  virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1199 1200 1201 1202 1203 1204 1205 1206
  virtual const String * get_name() const;
private:
  TIME_ZONE_INFO *tz_info;
  const String *tz_name;
};


/*
unknown's avatar
unknown committed
1207
  Initializes object representing time zone described by mysql.time_zone
1208
  tables.
unknown's avatar
unknown committed
1209

1210 1211
  SYNOPSIS
    Time_zone_db()
unknown's avatar
unknown committed
1212 1213
      tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
                    according to db or other time zone description
1214
                    (for example by my_tz_init()).
unknown's avatar
unknown committed
1215
                    Several Time_zone_db instances can share one
1216 1217 1218
                    TIME_ZONE_INFO structure.
      tz_name_arg - name of time zone.
*/
unknown's avatar
unknown committed
1219
Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
1220 1221 1222 1223 1224 1225 1226
                           const String *tz_name_arg):
  tz_info(tz_info_arg), tz_name(tz_name_arg)
{
}


/*
unknown's avatar
unknown committed
1227
  Converts local time in time zone described from TIME
1228
  representation to its my_time_t representation.
unknown's avatar
unknown committed
1229

1230 1231
  SYNOPSIS
    TIME_to_gmt_sec()
1232
      t               - pointer to MYSQL_TIME structure with local time
1233
                        in broken-down representation.
unknown's avatar
unknown committed
1234
      in_dst_time_gap - pointer to bool which is set to true if datetime
1235 1236 1237 1238
                        value passed doesn't really exist (i.e. falls into
                        spring time-gap) and is not touched otherwise.

  DESCRIPTION
unknown's avatar
unknown committed
1239
    Please see ::TIME_to_gmt_sec for function description and
1240 1241 1242 1243 1244
    parameter restrictions.

  RETURN VALUE
    Corresponding my_time_t value or 0 in case of error
*/
unknown's avatar
unknown committed
1245
my_time_t
1246
Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1247 1248 1249 1250 1251 1252 1253 1254
{
  return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
}


/*
  Converts time from UTC seconds since Epoch (my_time_t) representation
  to local time zone described in broken-down representation.
unknown's avatar
unknown committed
1255

1256 1257
  SYNOPSIS
    gmt_sec_to_TIME()
1258
      tmp - pointer to MYSQL_TIME structure to fill-in
unknown's avatar
unknown committed
1259
      t   - my_time_t value to be converted
1260
*/
unknown's avatar
unknown committed
1261
void
1262
Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1263 1264
{
  ::gmt_sec_to_TIME(tmp, t, tz_info);
1265
  adjust_leap_second(tmp);
1266 1267 1268 1269 1270
}


/*
  Get name of time zone
unknown's avatar
unknown committed
1271

1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
  SYNOPSIS
    get_name()

  RETURN VALUE
    Name of time zone as ASCIIZ-string
*/
const String *
Time_zone_db::get_name() const
{
  return tz_name;
}


/*
unknown's avatar
unknown committed
1286
  Instance of this class represents time zone which
1287 1288
  was specified as offset from UTC.
*/
unknown's avatar
unknown committed
1289
class Time_zone_offset : public Time_zone
1290 1291 1292
{
public:
  Time_zone_offset(long tz_offset_arg);
1293
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1294
                                    my_bool *in_dst_time_gap) const;
1295
  virtual void   gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1296
  virtual const String * get_name() const;
unknown's avatar
unknown committed
1297
  /*
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310
    This have to be public because we want to be able to access it from
    my_offset_tzs_get_key() function
  */
  long offset;
private:
  /* Extra reserve because of snprintf */
  char name_buff[7+16];
  String name;
};


/*
  Initializes object representing time zone described by its offset from UTC.
unknown's avatar
unknown committed
1311

1312 1313
  SYNOPSIS
    Time_zone_offset()
unknown's avatar
unknown committed
1314
      tz_offset_arg - offset from UTC in seconds.
1315 1316 1317 1318 1319
                      Positive for direction to east.
*/
Time_zone_offset::Time_zone_offset(long tz_offset_arg):
  offset(tz_offset_arg)
{
1320 1321
  uint hours= abs((int)(offset / SECS_PER_HOUR));
  uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
unknown's avatar
unknown committed
1322
  ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
1323 1324 1325 1326 1327 1328 1329
                            (offset>=0) ? "+" : "-", hours, minutes);
  name.set(name_buff, length, &my_charset_latin1);
}


/*
  Converts local time in time zone described as offset from UTC
1330
  from MYSQL_TIME representation to its my_time_t representation.
unknown's avatar
unknown committed
1331

1332 1333
  SYNOPSIS
    TIME_to_gmt_sec()
1334
      t               - pointer to MYSQL_TIME structure with local time
1335
                        in broken-down representation.
unknown's avatar
unknown committed
1336 1337 1338
      in_dst_time_gap - pointer to bool which should be set to true if
                        datetime  value passed doesn't really exist
                        (i.e. falls into spring time-gap) and is not
1339 1340 1341 1342 1343 1344
                        touched otherwise.
                        It is not really used in this class.

  RETURN VALUE
    Corresponding my_time_t value or 0 in case of error
*/
unknown's avatar
unknown committed
1345
my_time_t
1346
Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1347
{
1348
  my_time_t local_t;
1349
  int shift= 0;
1350 1351 1352 1353 1354 1355 1356 1357

  /*
    Check timestamp range.we have to do this as calling function relies on
    us to make all validation checks here.
  */
  if (!validate_timestamp_range(t))
    return 0;

1358 1359 1360 1361 1362 1363 1364 1365 1366
  /*
    Do a temporary shift of the boundary dates to avoid
    overflow of my_time_t if the time value is near it's
    maximum range
  */
  if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
    shift= 2;

  local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
1367 1368 1369
                           t->hour, t->minute, t->second) -
           offset;

1370 1371 1372 1373 1374 1375
  if (shift)
  {
    /* Add back the shifted time */
    local_t+= shift * SECS_PER_DAY;
  }

1376 1377 1378 1379 1380
  if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
    return local_t;

  /* range error*/
  return 0;
1381 1382 1383 1384 1385
}


/*
  Converts time from UTC seconds since Epoch (my_time_t) representation
unknown's avatar
unknown committed
1386
  to local time zone described as offset from UTC and in broken-down
1387
  representation.
unknown's avatar
unknown committed
1388

1389 1390
  SYNOPSIS
    gmt_sec_to_TIME()
1391
      tmp - pointer to MYSQL_TIME structure to fill-in
unknown's avatar
unknown committed
1392
      t   - my_time_t value to be converted
1393
*/
unknown's avatar
unknown committed
1394
void
1395
Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1396 1397 1398 1399 1400 1401 1402
{
  sec_to_TIME(tmp, t, offset);
}


/*
  Get name of time zone
unknown's avatar
unknown committed
1403

1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
  SYNOPSIS
    get_name()

  RETURN VALUE
    Name of time zone as pointer to String object
*/
const String *
Time_zone_offset::get_name() const
{
  return &name;
}


static Time_zone_utc tz_UTC;
static Time_zone_system tz_SYSTEM;
1419
static Time_zone_offset tz_OFFSET0(0);
1420

1421
Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1422 1423 1424 1425 1426 1427 1428
Time_zone *my_tz_UTC= &tz_UTC;
Time_zone *my_tz_SYSTEM= &tz_SYSTEM;

static HASH tz_names;
static HASH offset_tzs;
static MEM_ROOT tz_storage;

unknown's avatar
unknown committed
1429
/*
1430
  These mutex protects offset_tzs and tz_storage.
unknown's avatar
unknown committed
1431 1432
  These protection needed only when we are trying to set
  time zone which is specified as offset, and searching for existing
1433 1434 1435
  time zone in offset_tzs or creating if it didn't existed before in
  tz_storage. So contention is low.
*/
Marc Alff's avatar
Marc Alff committed
1436
static mysql_mutex_t tz_LOCK;
1437
static bool tz_inited= 0;
1438 1439 1440 1441 1442 1443 1444 1445

/*
  This two static variables are inteded for holding info about leap seconds
  shared by all time zones.
*/
static uint tz_leapcnt= 0;
static LS_INFO *tz_lsis= 0;

1446 1447 1448 1449 1450 1451 1452
/*
  Shows whenever we have found time zone tables during start-up.
  Used for avoiding of putting those tables to global table list
  for queries that use time zone info.
*/
static bool time_zone_tables_exist= 1;

unknown's avatar
unknown committed
1453

1454 1455 1456 1457 1458 1459
/*
  Names of tables (with their lengths) that are needed
  for dynamical loading of time zone descriptions.
*/

static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
1460
{
unknown's avatar
unknown committed
1461 1462 1463 1464
  { C_STRING_WITH_LEN("time_zone_name")},
  { C_STRING_WITH_LEN("time_zone")},
  { C_STRING_WITH_LEN("time_zone_transition_type")},
  { C_STRING_WITH_LEN("time_zone_transition")}
1465 1466 1467 1468
};

/* Name of database to which those tables belong. */

unknown's avatar
unknown committed
1469
static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
1470 1471 1472 1473 1474


class Tz_names_entry: public Sql_alloc
{
public:
1475 1476
  String name;
  Time_zone *tz;
1477
};
1478 1479 1480


/*
unknown's avatar
unknown committed
1481 1482
  We are going to call both of these functions from C code so
  they should obey C calling conventions.
1483 1484
*/

1485 1486 1487
extern "C" uchar *
my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
                    my_bool not_used __attribute__((unused)))
1488 1489
{
  *length= entry->name.length();
1490
  return (uchar*) entry->name.ptr();
1491 1492
}

1493 1494 1495 1496
extern "C" uchar *
my_offset_tzs_get_key(Time_zone_offset *entry,
                      size_t *length,
                      my_bool not_used __attribute__((unused)))
1497 1498
{
  *length= sizeof(long);
1499
  return (uchar*) &entry->offset;
1500 1501 1502
}


1503
/*
1504
  Prepare table list with time zone related tables from preallocated array.
1505 1506 1507

  SYNOPSIS
    tz_init_table_list()
1508 1509
      tz_tabs         - pointer to preallocated array of MY_TZ_TABLES_COUNT
                        TABLE_LIST objects
1510 1511 1512

  DESCRIPTION
    This function prepares list of TABLE_LIST objects which can be used
1513
    for opening of time zone tables from preallocated array.
1514 1515
*/

1516
static void
1517
tz_init_table_list(TABLE_LIST *tz_tabs)
1518
{
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533
  bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT);

  for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
  {
    tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str;
    tz_tabs[i].table_name_length= tz_tables_names[i].length;
    tz_tabs[i].db= tz_tables_db_name.str;
    tz_tabs[i].db_length= tz_tables_db_name.length;
    tz_tabs[i].lock_type= TL_READ;

    if (i != MY_TZ_TABLES_COUNT - 1)
      tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
    if (i != 0)
      tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
  }
1534 1535
}

Marc Alff's avatar
Marc Alff committed
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_tz_LOCK;

static PSI_mutex_info all_tz_mutexes[]=
{
  { & key_tz_LOCK, "tz_LOCK", PSI_FLAG_GLOBAL}
};

static void init_tz_psi_keys(void)
{
  const char* category= "sql";
  int count;

  if (PSI_server == NULL)
    return;

  count= array_elements(all_tz_mutexes);
  PSI_server->register_mutex(category, all_tz_mutexes, count);
}
#endif /* HAVE_PSI_INTERFACE */

1557

1558 1559 1560 1561 1562 1563 1564 1565
/*
  Initialize time zone support infrastructure.

  SYNOPSIS
    my_tz_init()
      thd            - current thread object
      default_tzname - default time zone or 0 if none.
      bootstrap      - indicates whenever we are in bootstrap mode
unknown's avatar
unknown committed
1566

1567 1568 1569
  DESCRIPTION
    This function will init memory structures needed for time zone support,
    it will register mandatory SYSTEM time zone in them. It will try to open
1570 1571 1572 1573 1574 1575 1576
    mysql.time_zone* tables and load information about default time zone and
    information which further will be shared among all time zones loaded.
    If system tables with time zone descriptions don't exist it won't fail
    (unless default_tzname is time zone from tables). If bootstrap parameter
    is true then this routine assumes that we are in bootstrap mode and won't
    load time zone descriptions unless someone specifies default time zone
    which is supposedly stored in those tables.
1577
    It'll also set default time zone if it is specified.
unknown's avatar
unknown committed
1578

1579 1580
  RETURN VALUES
    0 - ok
unknown's avatar
unknown committed
1581
    1 - Error
1582
*/
unknown's avatar
unknown committed
1583
my_bool
1584 1585 1586
my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
{
  THD *thd;
1587
  TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
1588
  TABLE *table;
1589
  Tz_names_entry *tmp_tzname;
1590
  my_bool return_val= 1;
unknown's avatar
unknown committed
1591
  char db[]= "mysql";
1592 1593 1594
  int res;
  DBUG_ENTER("my_tz_init");

Marc Alff's avatar
Marc Alff committed
1595 1596 1597 1598
#ifdef HAVE_PSI_INTERFACE
  init_tz_psi_keys();
#endif

1599 1600 1601 1602 1603
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
  if (!(thd= new THD))
    DBUG_RETURN(1);
1604
  thd->thread_stack= (char*) &thd;
1605 1606 1607
  thd->store_globals();

  /* Init all memory structures that require explicit destruction */
Konstantin Osipov's avatar
Konstantin Osipov committed
1608 1609
  if (my_hash_init(&tz_names, &my_charset_latin1, 20,
                   0, 0, (my_hash_get_key) my_tz_names_get_key, 0, 0))
1610 1611 1612 1613
  {
    sql_print_error("Fatal error: OOM while initializing time zones");
    goto end;
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
1614 1615
  if (my_hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
                   (my_hash_get_key)my_offset_tzs_get_key, 0, 0))
1616 1617
  {
    sql_print_error("Fatal error: OOM while initializing time zones");
Konstantin Osipov's avatar
Konstantin Osipov committed
1618
    my_hash_free(&tz_names);
1619 1620
    goto end;
  }
1621
  init_sql_alloc(&tz_storage, 32 * 1024, 0);
Marc Alff's avatar
Marc Alff committed
1622
  mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
1623
  tz_inited= 1;
1624 1625

  /* Add 'SYSTEM' time zone to tz_names hash */
1626
  if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()))
1627 1628 1629 1630
  {
    sql_print_error("Fatal error: OOM while initializing time zones");
    goto end_with_cleanup;
  }
1631
  tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
1632
  tmp_tzname->tz= my_tz_SYSTEM;
1633
  if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
1634 1635 1636 1637
  {
    sql_print_error("Fatal error: OOM while initializing time zones");
    goto end_with_cleanup;
  }
unknown's avatar
unknown committed
1638

1639 1640 1641
  if (bootstrap)
  {
    /* If we are in bootstrap mode we should not load time zone tables */
1642
    return_val= time_zone_tables_exist= 0;
1643 1644
    goto end_with_setting_default_tz;
  }
unknown's avatar
unknown committed
1645 1646

  /*
1647 1648 1649 1650 1651
    After this point all memory structures are inited and we even can live
    without time zone description tables. Now try to load information about
    leap seconds shared by all time zones.
  */

unknown's avatar
unknown committed
1652
  thd->set_db(db, sizeof(db)-1);
1653 1654
  bzero((char*) &tz_tables[0], sizeof(TABLE_LIST));
  tz_tables[0].alias= tz_tables[0].table_name=
1655
    (char*)"time_zone_leap_second";
1656 1657 1658 1659 1660 1661 1662 1663
  tz_tables[0].table_name_length= 21;
  tz_tables[0].db= db;
  tz_tables[0].db_length= sizeof(db)-1;
  tz_tables[0].lock_type= TL_READ;

  tz_init_table_list(tz_tables+1);
  tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
  tz_tables[1].prev_global= &tz_tables[0].next_global;
Konstantin Osipov's avatar
Konstantin Osipov committed
1664
  init_mdl_requests(tz_tables);
1665

1666
  /*
1667 1668
    We need to open only mysql.time_zone_leap_second, but we try to
    open all time zone tables to see if they exist.
1669
  */
1670 1671
  if (open_and_lock_tables_derived(thd, tz_tables, FALSE,
                                   MYSQL_LOCK_IGNORE_FLUSH))
1672
  {
1673
    sql_print_warning("Can't open and lock time zone table: %s "
Marc Alff's avatar
Marc Alff committed
1674
                      "trying to live without them", thd->stmt_da->message());
1675
    /* We will try emulate that everything is ok */
1676
    return_val= time_zone_tables_exist= 0;
1677 1678 1679
    goto end_with_setting_default_tz;
  }

1680 1681 1682
  for (TABLE_LIST *tl= tz_tables; tl; tl= tl->next_global)
    tl->table->use_all_columns();

1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693
  /*
    Now we are going to load leap seconds descriptions that are shared
    between all time zones that use them. We are using index for getting
    records in proper order. Since we share the same MEM_ROOT between
    all time zones we just allocate enough memory for it first.
  */
  if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
                                       sizeof(LS_INFO) * TZ_MAX_LEAPS)))
  {
    sql_print_error("Fatal error: Out of memory while loading "
                    "mysql.time_zone_leap_second table");
1694
    goto end_with_close;
1695
  }
unknown's avatar
unknown committed
1696

1697
  table= tz_tables[0].table;
1698 1699 1700 1701 1702
  /*
    It is OK to ignore ha_index_init()/ha_index_end() return values since
    mysql.time_zone* tables are MyISAM and these operations always succeed
    for MyISAM.
  */
1703
  (void)table->file->ha_index_init(0, 1);
1704 1705
  table->use_all_columns();

1706
  tz_leapcnt= 0;
unknown's avatar
unknown committed
1707

1708 1709 1710 1711 1712 1713 1714 1715
  res= table->file->index_first(table->record[0]);

  while (!res)
  {
    if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
    {
      sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
                      " table: too much leaps");
unknown's avatar
unknown committed
1716
      table->file->ha_index_end();
1717
      goto end_with_close;
1718
    }
unknown's avatar
unknown committed
1719

1720 1721 1722 1723 1724 1725
    tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
    tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();

    tz_leapcnt++;

    DBUG_PRINT("info",
1726 1727 1728
               ("time_zone_leap_second table: tz_leapcnt: %u  tt_time: %lu  offset: %ld",
                tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
                tz_lsis[tz_leapcnt-1].ls_corr));
unknown's avatar
unknown committed
1729

1730 1731 1732
    res= table->file->index_next(table->record[0]);
  }

1733
  (void)table->file->ha_index_end();
unknown's avatar
unknown committed
1734

1735 1736 1737 1738
  if (res != HA_ERR_END_OF_FILE)
  {
    sql_print_error("Fatal error: Error while loading "
                    "mysql.time_zone_leap_second table");
1739
    goto end_with_close;
1740 1741 1742 1743 1744
  }

  /*
    Loading of info about leap seconds succeeded
  */
unknown's avatar
unknown committed
1745

1746 1747
  return_val= 0;

unknown's avatar
unknown committed
1748

1749
end_with_setting_default_tz:
1750 1751
  /* If we have default time zone try to load it */
  if (default_tzname)
1752
  {
1753
    String tmp_tzname2(default_tzname, &my_charset_latin1);
1754 1755 1756 1757 1758 1759
    /*
      Time zone tables may be open here, and my_tz_find() may open
      most of them once more, but this is OK for system tables open
      for READ.
    */
    if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
1760 1761 1762 1763 1764 1765
    {
      sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
                      default_tzname);
      return_val= 1;
    }
  }
unknown's avatar
unknown committed
1766

1767
end_with_close:
1768 1769 1770
  if (time_zone_tables_exist)
  {
    thd->version--; /* Force close to free memory */
1771 1772
    close_thread_tables(thd);
    thd->mdl_context.release_transactional_locks();
1773
  }
1774

1775
end_with_cleanup:
unknown's avatar
unknown committed
1776

1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789
  /* if there were error free time zone describing structs */
  if (return_val)
    my_tz_free();
end:
  delete thd;
  if (org_thd)
    org_thd->store_globals();			/* purecov: inspected */
  else
  {
    /* Remember that we don't have a THD */
    my_pthread_setspecific_ptr(THR_THD,  0);
    my_pthread_setspecific_ptr(THR_MALLOC,  0);
  }
1790 1791 1792 1793
  
  default_tz= default_tz_name ? global_system_variables.time_zone
                              : my_tz_SYSTEM;

1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
  DBUG_RETURN(return_val);
}


/*
  Free resources used by time zone support infrastructure.

  SYNOPSIS
    my_tz_free()
*/
1804

1805 1806
void my_tz_free()
{
1807 1808 1809
  if (tz_inited)
  {
    tz_inited= 0;
Marc Alff's avatar
Marc Alff committed
1810
    mysql_mutex_destroy(&tz_LOCK);
Konstantin Osipov's avatar
Konstantin Osipov committed
1811 1812
    my_hash_free(&offset_tzs);
    my_hash_free(&tz_names);
1813 1814
    free_root(&tz_storage, MYF(0));
  }
1815 1816 1817 1818 1819 1820 1821
}


/*
  Load time zone description from system tables.

  SYNOPSIS
1822 1823 1824 1825
    tz_load_from_open_tables()
      tz_name   - name of time zone that should be loaded.
      tz_tables - list of tables from which time zone description
                  should be loaded
unknown's avatar
unknown committed
1826

1827
  DESCRIPTION
1828 1829 1830 1831 1832
    This function will try to load information about time zone specified
    from the list of the already opened and locked tables (first table in
    tz_tables should be time_zone_name, next time_zone, then
    time_zone_transition_type and time_zone_transition should be last).
    It will also update information in hash used for time zones lookup.
unknown's avatar
unknown committed
1833

1834 1835 1836 1837
  RETURN VALUES
    Returns pointer to newly created Time_zone object or 0 in case of error.

*/
1838

unknown's avatar
unknown committed
1839
static Time_zone*
1840
tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
1841 1842 1843
{
  TABLE *table= 0;
  TIME_ZONE_INFO *tz_info;
1844
  Tz_names_entry *tmp_tzname;
1845 1846 1847 1848 1849 1850 1851
  Time_zone *return_val= 0;
  int res;
  uint tzid, ttid;
  my_time_t ttime;
  char buff[MAX_FIELD_WIDTH];
  String abbr(buff, sizeof(buff), &my_charset_latin1);
  char *alloc_buff, *tz_name_buff;
unknown's avatar
unknown committed
1852
  /*
1853 1854 1855 1856
    Temporary arrays that are used for loading of data for filling
    TIME_ZONE_INFO structure
  */
  my_time_t ats[TZ_MAX_TIMES];
unknown's avatar
unknown committed
1857
  uchar types[TZ_MAX_TIMES];
1858 1859 1860 1861
  TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
#ifdef ABBR_ARE_USED
  char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
#endif
1862 1863 1864 1865 1866 1867
  /* 
    Used as a temporary tz_info until we decide that we actually want to
    allocate and keep the tz info and tz name in tz_storage.
  */
  TIME_ZONE_INFO tmp_tz_info;
  memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
1868

1869
  DBUG_ENTER("tz_load_from_open_tables");
unknown's avatar
unknown committed
1870

1871
  /* Prepare tz_info for loading also let us make copy of time zone name */
1872 1873
  if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
                                       tz_name->length() + 1)))
1874
  {
1875
    sql_print_error("Out of memory while loading time zone description");
1876 1877 1878 1879 1880 1881 1882 1883 1884
    return 0;
  }
  tz_info= (TIME_ZONE_INFO *)alloc_buff;
  bzero(tz_info, sizeof(TIME_ZONE_INFO));
  tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
  /*
    By writing zero to the end we guarantee that we can call ptr()
    instead of c_ptr() for time zone name.
  */
unknown's avatar
unknown committed
1885
  strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
1886

unknown's avatar
unknown committed
1887 1888
  /*
    Let us find out time zone id by its name (there is only one index
1889 1890
    and it is specifically for this purpose).
  */
1891
  table= tz_tables->table;
1892 1893 1894
  tz_tables= tz_tables->next_local;
  table->field[0]->store(tz_name->ptr(), tz_name->length(),
                         &my_charset_latin1);
1895 1896 1897 1898 1899
  /*
    It is OK to ignore ha_index_init()/ha_index_end() return values since
    mysql.time_zone* tables are MyISAM and these operations always succeed
    for MyISAM.
  */
1900
  (void)table->file->ha_index_init(0, 1);
unknown's avatar
unknown committed
1901

1902 1903
  if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
                                  HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1904
  {
1905
#ifdef EXTRA_DEBUG
1906 1907 1908 1909
    /*
      Most probably user has mistyped time zone name, so no need to bark here
      unless we need it for debugging.
    */
1910 1911
     sql_print_error("Can't find description of time zone '%.*s'", 
                     tz_name->length(), tz_name->ptr());
1912
#endif
1913
    goto end;
1914
  }
unknown's avatar
unknown committed
1915

unknown's avatar
unknown committed
1916
  tzid= (uint)table->field[1]->val_int();
1917

1918
  (void)table->file->ha_index_end();
unknown's avatar
unknown committed
1919 1920

  /*
1921 1922 1923 1924
    Now we need to lookup record in mysql.time_zone table in order to
    understand whenever this timezone uses leap seconds (again we are
    using the only index in this table).
  */
1925
  table= tz_tables->table;
1926
  tz_tables= tz_tables->next_local;
1927
  table->field[0]->store((longlong) tzid, TRUE);
1928
  (void)table->file->ha_index_init(0, 1);
unknown's avatar
unknown committed
1929

1930 1931
  if (table->file->index_read_map(table->record[0], table->field[0]->ptr,
                                  HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1932
  {
unknown's avatar
unknown committed
1933
    sql_print_error("Can't find description of time zone '%u'", tzid);
1934
    goto end;
1935
  }
unknown's avatar
unknown committed
1936

1937 1938 1939
  /* If Uses_leap_seconds == 'Y' */
  if (table->field[1]->val_int() == 1)
  {
1940 1941
    tmp_tz_info.leapcnt= tz_leapcnt;
    tmp_tz_info.lsis= tz_lsis;
1942
  }
unknown's avatar
unknown committed
1943

1944
  (void)table->file->ha_index_end();
unknown's avatar
unknown committed
1945 1946 1947 1948 1949

  /*
    Now we will iterate through records for out time zone in
    mysql.time_zone_transition_type table. Because we want records
    only for our time zone guess what are we doing?
1950 1951
    Right - using special index.
  */
1952
  table= tz_tables->table;
1953
  tz_tables= tz_tables->next_local;
1954
  table->field[0]->store((longlong) tzid, TRUE);
1955
  (void)table->file->ha_index_init(0, 1);
unknown's avatar
unknown committed
1956

1957 1958
  res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
                                   (key_part_map)1, HA_READ_KEY_EXACT);
1959 1960
  while (!res)
  {
unknown's avatar
unknown committed
1961
    ttid= (uint)table->field[1]->val_int();
1962

1963
    if (ttid >= TZ_MAX_TYPES)
1964 1965 1966 1967
    {
      sql_print_error("Error while loading time zone description from "
                      "mysql.time_zone_transition_type table: too big "
                      "transition type id");
1968
      goto end;
1969 1970
    }

unknown's avatar
unknown committed
1971
    ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int();
1972 1973 1974 1975 1976
    ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0);

#ifdef ABBR_ARE_USED
    // FIXME should we do something with duplicates here ?
    table->field[4]->val_str(&abbr, &abbr);
1977
    if (tmp_tz_info.charcnt + abbr.length() + 1 > sizeof(chars))
1978 1979 1980 1981
    {
      sql_print_error("Error while loading time zone description from "
                      "mysql.time_zone_transition_type table: not enough "
                      "room for abbreviations");
1982
      goto end;
1983
    }
1984 1985 1986 1987 1988
    ttis[ttid].tt_abbrind= tmp_tz_info.charcnt;
    memcpy(chars + tmp_tz_info.charcnt, abbr.ptr(), abbr.length());
    tmp_tz_info.charcnt+= abbr.length();
    chars[tmp_tz_info.charcnt]= 0;
    tmp_tz_info.charcnt++;
unknown's avatar
unknown committed
1989

1990 1991
    DBUG_PRINT("info",
      ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
unknown's avatar
unknown committed
1992
       "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff,
1993 1994 1995 1996 1997 1998 1999 2000
       chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst));
#else
    DBUG_PRINT("info",
      ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
       "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst));
#endif

    /* ttid is increasing because we are reading using index */
2001
    DBUG_ASSERT(ttid >= tmp_tz_info.typecnt);
unknown's avatar
unknown committed
2002

2003
    tmp_tz_info.typecnt= ttid + 1;
unknown's avatar
unknown committed
2004 2005

    res= table->file->index_next_same(table->record[0],
2006
                                      table->field[0]->ptr, 4);
2007
  }
unknown's avatar
unknown committed
2008

2009 2010 2011 2012
  if (res != HA_ERR_END_OF_FILE)
  {
    sql_print_error("Error while loading time zone description from "
                    "mysql.time_zone_transition_type table");
2013
    goto end;
2014 2015
  }

2016
  (void)table->file->ha_index_end();
unknown's avatar
unknown committed
2017

2018 2019

  /*
unknown's avatar
unknown committed
2020 2021
    At last we are doing the same thing for records in
    mysql.time_zone_transition table. Here we additionaly need records
2022 2023
    in ascending order by index scan also satisfies us.
  */
2024
  table= tz_tables->table; 
2025
  table->field[0]->store((longlong) tzid, TRUE);
2026
  (void)table->file->ha_index_init(0, 1);
unknown's avatar
unknown committed
2027

2028 2029
  res= table->file->index_read_map(table->record[0], table->field[0]->ptr,
                                   (key_part_map)1, HA_READ_KEY_EXACT);
2030 2031 2032 2033 2034
  while (!res)
  {
    ttime= (my_time_t)table->field[1]->val_int();
    ttid= (uint)table->field[2]->val_int();

2035
    if (tmp_tz_info.timecnt + 1 > TZ_MAX_TIMES)
2036 2037 2038 2039
    {
      sql_print_error("Error while loading time zone description from "
                      "mysql.time_zone_transition table: "
                      "too much transitions");
2040
      goto end;
2041
    }
2042
    if (ttid + 1 > tmp_tz_info.typecnt)
2043 2044 2045 2046
    {
      sql_print_error("Error while loading time zone description from "
                      "mysql.time_zone_transition table: "
                      "bad transition type id");
2047
      goto end;
2048
    }
unknown's avatar
unknown committed
2049

2050 2051 2052
    ats[tmp_tz_info.timecnt]= ttime;
    types[tmp_tz_info.timecnt]= ttid;
    tmp_tz_info.timecnt++;
2053 2054

    DBUG_PRINT("info",
unknown's avatar
unknown committed
2055
      ("time_zone_transition table: tz_id: %u  tt_time: %lu  tt_id: %u",
unknown's avatar
unknown committed
2056
       tzid, (ulong) ttime, ttid));
unknown's avatar
unknown committed
2057 2058

    res= table->file->index_next_same(table->record[0],
2059
                                      table->field[0]->ptr, 4);
2060 2061
  }

unknown's avatar
unknown committed
2062
  /*
2063 2064 2065 2066 2067 2068 2069
    We have to allow HA_ERR_KEY_NOT_FOUND because some time zones
    for example UTC have no transitons.
  */
  if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
  {
    sql_print_error("Error while loading time zone description from "
                    "mysql.time_zone_transition table");
2070
    goto end;
2071
  }
unknown's avatar
unknown committed
2072

2073
  (void)table->file->ha_index_end();
2074
  table= 0;
unknown's avatar
unknown committed
2075

2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103
  /*
    Let us check how correct our time zone description is. We don't check for
    tz->timecnt < 1 since it is ok for GMT.
  */
  if (tmp_tz_info.typecnt < 1)
  {
    sql_print_error("loading time zone without transition types");
    goto end;
  }

  /* Allocate memory for the timezone info and timezone name in tz_storage. */
  if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
                                       tz_name->length() + 1)))
  {
    sql_print_error("Out of memory while loading time zone description");
    return 0;
  }

  /* Move the temporary tz_info into the allocated area */
  tz_info= (TIME_ZONE_INFO *)alloc_buff;
  memcpy(tz_info, &tmp_tz_info, sizeof(TIME_ZONE_INFO));
  tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
  /*
    By writing zero to the end we guarantee that we can call ptr()
    instead of c_ptr() for time zone name.
  */
  strmake(tz_name_buff, tz_name->ptr(), tz_name->length());

2104 2105 2106
  /*
    Now we will allocate memory and init TIME_ZONE_INFO structure.
  */
2107 2108 2109 2110
  if (!(alloc_buff= (char*) alloc_root(&tz_storage,
                                       ALIGN_SIZE(sizeof(my_time_t) *
                                                  tz_info->timecnt) +
                                       ALIGN_SIZE(tz_info->timecnt) +
2111
#ifdef ABBR_ARE_USED
2112
                                       ALIGN_SIZE(tz_info->charcnt) +
2113
#endif
2114 2115
                                       sizeof(TRAN_TYPE_INFO) *
                                       tz_info->typecnt)))
2116
  {
2117
    sql_print_error("Out of memory while loading time zone description");
2118
    goto end;
2119 2120
  }

2121
  tz_info->ats= (my_time_t *) alloc_buff;
2122 2123
  memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
  alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
unknown's avatar
unknown committed
2124
  tz_info->types= (uchar *)alloc_buff;
2125 2126 2127 2128 2129 2130 2131 2132 2133
  memcpy(tz_info->types, types, tz_info->timecnt);
  alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
#ifdef ABBR_ARE_USED
  tz_info->chars= alloc_buff;
  memcpy(tz_info->chars, chars, tz_info->charcnt);
  alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
#endif
  tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
  memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
unknown's avatar
unknown committed
2134

2135
  /* Build reversed map. */
2136 2137
  if (prepare_tz_info(tz_info, &tz_storage))
  {
2138
    sql_print_error("Unable to build mktime map for time zone");
2139
    goto end;
2140
  }
unknown's avatar
unknown committed
2141 2142


2143
  if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) ||
unknown's avatar
unknown committed
2144
      !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
2145
                                            &(tmp_tzname->name))) ||
unknown's avatar
unknown committed
2146
      (tmp_tzname->name.set(tz_name_buff, tz_name->length(),
2147
                            &my_charset_latin1),
2148
       my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
2149
  {
2150
    sql_print_error("Out of memory while loading time zone");
2151
    goto end;
2152 2153 2154 2155 2156 2157
  }

  /*
    Loading of time zone succeeded
  */
  return_val= tmp_tzname->tz;
unknown's avatar
unknown committed
2158

2159
end:
2160 2161

  if (table)
2162
    (void)table->file->ha_index_end();
2163 2164 2165 2166 2167 2168 2169 2170 2171 2172

  DBUG_RETURN(return_val);
}


/*
  Parse string that specifies time zone as offset from UTC.

  SYNOPSIS
    str_to_offset()
unknown's avatar
unknown committed
2173
      str    - pointer to string which contains offset
2174 2175 2176 2177
      length - length of string
      offset - out parameter for storing found offset in seconds.

  DESCRIPTION
unknown's avatar
unknown committed
2178 2179
    This function parses string which contains time zone offset
    in form similar to '+10:00' and converts found value to
2180
    seconds from UTC form (east is positive).
unknown's avatar
unknown committed
2181

2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192
  RETURN VALUE
    0 - Ok
    1 - String doesn't contain valid time zone offset
*/
my_bool
str_to_offset(const char *str, uint length, long *offset)
{
  const char *end= str + length;
  my_bool negative;
  ulong number_tmp;
  long offset_tmp;
unknown's avatar
unknown committed
2193

2194 2195
  if (length < 4)
    return 1;
unknown's avatar
unknown committed
2196

2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211
  if (*str == '+')
    negative= 0;
  else if (*str == '-')
    negative= 1;
  else
    return 1;
  str++;

  number_tmp= 0;

  while (str < end && my_isdigit(&my_charset_latin1, *str))
  {
    number_tmp= number_tmp*10 + *str - '0';
    str++;
  }
unknown's avatar
unknown committed
2212

2213 2214 2215 2216 2217
  if (str + 1 >= end || *str != ':')
    return 1;
  str++;

  offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
unknown's avatar
unknown committed
2218

2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232
  while (str < end && my_isdigit(&my_charset_latin1, *str))
  {
    number_tmp= number_tmp * 10 + *str - '0';
    str++;
  }

  if (str != end)
    return 1;

  offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;

  if (negative)
    offset_tmp= -offset_tmp;

unknown's avatar
unknown committed
2233
  /*
2234 2235 2236
    Check if offset is in range prescribed by standard
    (from -12:59 to 13:00).
  */
unknown's avatar
unknown committed
2237

2238 2239 2240
  if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
      offset_tmp > 13 * SECS_PER_HOUR)
    return 1;
unknown's avatar
unknown committed
2241

2242
  *offset= offset_tmp;
unknown's avatar
unknown committed
2243

2244 2245 2246 2247 2248 2249 2250 2251 2252
  return 0;
}


/*
  Get Time_zone object for specified time zone.

  SYNOPSIS
    my_tz_find()
2253
      thd  - pointer to thread THD structure
2254 2255 2256 2257
      name - time zone specification

  DESCRIPTION
    This function checks if name is one of time zones described in db,
unknown's avatar
unknown committed
2258
    predefined SYSTEM time zone or valid time zone specification as
2259 2260 2261 2262
    offset from UTC (In last case it will create proper Time_zone_offset
    object if there were not any.). If name is ok it returns corresponding
    Time_zone object.

unknown's avatar
unknown committed
2263 2264
    Clients of this function are not responsible for releasing resources
    occupied by returned Time_zone object so they can just forget pointers
2265
    to Time_zone object if they are not needed longer.
unknown's avatar
unknown committed
2266

2267 2268 2269 2270 2271 2272
    Other important property of this function: if some Time_zone found once
    it will be for sure found later, so this function can also be used for
    checking if proper Time_zone object exists (and if there will be error
    it will be reported during first call).

    If name pointer is 0 then this function returns 0 (this allows to pass 0
unknown's avatar
unknown committed
2273
    values as parameter without additional external check and this property
2274 2275
    is used by @@time_zone variable handling code).

2276 2277 2278 2279
    It will perform lookup in system tables (mysql.time_zone*),
    opening and locking them, and closing afterwards. It won't perform
    such lookup if no time zone describing tables were found during
    server start up.
unknown's avatar
unknown committed
2280

2281
  RETURN VALUE
unknown's avatar
unknown committed
2282
    Pointer to corresponding Time_zone object. 0 - in case of bad time zone
2283
    specification or other error.
unknown's avatar
unknown committed
2284

2285
*/
unknown's avatar
unknown committed
2286
Time_zone *
2287
my_tz_find(THD *thd, const String *name)
2288
{
2289
  Tz_names_entry *tmp_tzname;
2290 2291 2292
  Time_zone *result_tz= 0;
  long offset;
  DBUG_ENTER("my_tz_find");
unknown's avatar
unknown committed
2293
  DBUG_PRINT("enter", ("time zone name='%s'",
2294
                       name ? ((String *)name)->c_ptr_safe() : "NULL"));
2295

2296 2297
  if (!name)
    DBUG_RETURN(0);
unknown's avatar
unknown committed
2298

Marc Alff's avatar
Marc Alff committed
2299
  mysql_mutex_lock(&tz_LOCK);
unknown's avatar
unknown committed
2300

2301 2302
  if (!str_to_offset(name->ptr(), name->length(), &offset))
  {
unknown's avatar
unknown committed
2303

Konstantin Osipov's avatar
Konstantin Osipov committed
2304 2305 2306
    if (!(result_tz= (Time_zone_offset *)my_hash_search(&offset_tzs,
                                                        (const uchar *)&offset,
                                                        sizeof(long))))
2307 2308
    {
      DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
unknown's avatar
unknown committed
2309

2310
      if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
2311
          my_hash_insert(&offset_tzs, (const uchar *) result_tz))
2312
      {
unknown's avatar
unknown committed
2313
        result_tz= 0;
2314 2315 2316 2317
        sql_print_error("Fatal error: Out of memory "
                        "while setting new time zone");
      }
    }
unknown's avatar
unknown committed
2318 2319 2320 2321
  }
  else
  {
    result_tz= 0;
Konstantin Osipov's avatar
Konstantin Osipov committed
2322 2323 2324 2325
    if ((tmp_tzname= (Tz_names_entry *)my_hash_search(&tz_names,
                                                      (const uchar *)
                                                      name->ptr(),
                                                      name->length())))
2326
      result_tz= tmp_tzname->tz;
2327 2328 2329
    else if (time_zone_tables_exist)
    {
      TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
2330
      Open_tables_backup open_tables_state_backup;
2331 2332

      tz_init_table_list(tz_tables);
Konstantin Osipov's avatar
Konstantin Osipov committed
2333
      init_mdl_requests(tz_tables);
2334 2335 2336 2337 2338 2339 2340
      if (!open_system_tables_for_read(thd, tz_tables,
                                       &open_tables_state_backup))
      {
        result_tz= tz_load_from_open_tables(name, tz_tables);
        close_system_tables(thd, &open_tables_state_backup);
      }
    }
2341
  }
unknown's avatar
unknown committed
2342

Marc Alff's avatar
Marc Alff committed
2343
  mysql_mutex_unlock(&tz_LOCK);
2344 2345 2346 2347

  DBUG_RETURN(result_tz);
}

2348

2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366
/**
  Convert leap seconds into non-leap

  This function will convert the leap seconds added by the OS to 
  non-leap seconds, e.g. 23:59:59, 23:59:60 -> 23:59:59, 00:00:01 ...
  This check is not checking for years on purpose : although it's not a
  complete check this way it doesn't require looking (and having installed)
  the leap seconds table.

  @param[in,out] broken down time structure as filled in by the OS
*/

void Time_zone::adjust_leap_second(MYSQL_TIME *t)
{
  if (t->second == 60 || t->second == 61)
    t->second= 59;
}

2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378
#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */


#ifdef TZINFO2SQL
/*
  This code belongs to mysql_tzinfo_to_sql converter command line utility.
  This utility should be used by db admin for populating mysql.time_zone
  tables.
*/


/*
unknown's avatar
unknown committed
2379
  Print info about time zone described by TIME_ZONE_INFO struct as
2380 2381 2382 2383 2384
  SQL statements populating mysql.time_zone* tables.

  SYNOPSIS
    print_tz_as_sql()
      tz_name - name of time zone
unknown's avatar
unknown committed
2385
      sp      - structure describing time zone
2386 2387 2388 2389 2390 2391 2392
*/
void
print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
{
  uint i;

  /* Here we assume that all time zones have same leap correction tables */
unknown's avatar
unknown committed
2393
  printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407
         sp->leapcnt ? "Y" : "N");
  printf("SET @time_zone_id= LAST_INSERT_ID();\n");
  printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
('%s', @time_zone_id);\n", tz_name);

  if (sp->timecnt)
  {
    printf("INSERT INTO time_zone_transition \
(Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
    for (i= 0; i < sp->timecnt; i++)
      printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
             (uint)sp->types[i]);
    printf(";\n");
  }
unknown's avatar
unknown committed
2408

2409 2410
  printf("INSERT INTO time_zone_transition_type \
(Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
unknown's avatar
unknown committed
2411

2412 2413
  for (i= 0; i < sp->typecnt; i++)
    printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i,
unknown's avatar
unknown committed
2414
           sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425
           sp->chars + sp->ttis[i].tt_abbrind);
  printf(";\n");
}


/*
  Print info about leap seconds in time zone as SQL statements
  populating mysql.time_zone_leap_second table.

  SYNOPSIS
    print_tz_leaps_as_sql()
unknown's avatar
unknown committed
2426
      sp      - structure describing time zone
2427 2428 2429 2430 2431 2432
*/
void
print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
{
  uint i;

unknown's avatar
unknown committed
2433 2434
  /*
    We are assuming that there are only one list of leap seconds
2435 2436 2437
    For all timezones.
  */
  printf("TRUNCATE TABLE time_zone_leap_second;\n");
unknown's avatar
unknown committed
2438

2439 2440 2441 2442 2443
  if (sp->leapcnt)
  {
    printf("INSERT INTO time_zone_leap_second \
(Transition_time, Correction) VALUES\n");
    for (i= 0; i < sp->leapcnt; i++)
unknown's avatar
unknown committed
2444
      printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
2445 2446 2447 2448 2449 2450 2451 2452 2453
             sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
    printf(";\n");
  }

  printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n");
}


/*
unknown's avatar
unknown committed
2454
  Some variables used as temporary or as parameters
2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465
  in recursive scan_tz_dir() code.
*/
TIME_ZONE_INFO tz_info;
MEM_ROOT tz_storage;
char fullname[FN_REFLEN + 1];
char *root_name_end;


/*
  Recursively scan zoneinfo directory and print all found time zone
  descriptions as SQL.
unknown's avatar
unknown committed
2466

2467
  SYNOPSIS
unknown's avatar
unknown committed
2468
    scan_tz_dir()
2469
      name_end - pointer to end of path to directory to be searched.
unknown's avatar
unknown committed
2470

2471
  DESCRIPTION
unknown's avatar
unknown committed
2472
    This auxiliary recursive function also uses several global
2473
    variables as in parameters and for storing temporary values.
unknown's avatar
unknown committed
2474

2475
    fullname      - path to directory that should be scanned.
unknown's avatar
unknown committed
2476
    root_name_end - pointer to place in fullname where part with
2477 2478
                    path to initial directory ends.
    current_tz_id - last used time zone id
unknown's avatar
unknown committed
2479

2480 2481
  RETURN VALUE
    0 - Ok, 1 - Fatal error
unknown's avatar
unknown committed
2482

2483 2484 2485 2486 2487 2488 2489
*/
my_bool
scan_tz_dir(char * name_end)
{
  MY_DIR *cur_dir;
  char *name_end_tmp;
  uint i;
unknown's avatar
unknown committed
2490

2491 2492 2493 2494
  if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
    return 1;

  name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
unknown's avatar
unknown committed
2495

2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536
  for (i= 0; i < cur_dir->number_off_files; i++)
  {
    if (cur_dir->dir_entry[i].name[0] != '.')
    {
      name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
                            FN_REFLEN - (name_end - fullname));

      if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
      {
        if (scan_tz_dir(name_end_tmp))
        {
          my_dirend(cur_dir);
          return 1;
        }
      }
      else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
      {
        init_alloc_root(&tz_storage, 32768, 0);
        if (!tz_load(fullname, &tz_info, &tz_storage))
          print_tz_as_sql(root_name_end + 1, &tz_info);
        else
          fprintf(stderr,
                  "Warning: Unable to load '%s' as time zone. Skipping it.\n",
                  fullname);
        free_root(&tz_storage, MYF(0));
      }
      else
        fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
                fullname);
    }
  }

  my_dirend(cur_dir);

  return 0;
}


int
main(int argc, char **argv)
{
unknown's avatar
unknown committed
2537
#ifndef __NETWARE__
2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551
  MY_INIT(argv[0]);

  if (argc != 2 && argc != 3)
  {
    fprintf(stderr, "Usage:\n");
    fprintf(stderr, " %s timezonedir\n", argv[0]);
    fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
    fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
    return 1;
  }

  if (argc == 2)
  {
    root_name_end= strmake(fullname, argv[1], FN_REFLEN);
unknown's avatar
unknown committed
2552

2553 2554 2555 2556
    printf("TRUNCATE TABLE time_zone;\n");
    printf("TRUNCATE TABLE time_zone_name;\n");
    printf("TRUNCATE TABLE time_zone_transition;\n");
    printf("TRUNCATE TABLE time_zone_transition_type;\n");
unknown's avatar
unknown committed
2557

2558 2559 2560 2561 2562 2563
    if (scan_tz_dir(root_name_end))
    {
      fprintf(stderr, "There were fatal errors during processing "
                      "of zoneinfo directory\n");
      return 1;
    }
unknown's avatar
unknown committed
2564

2565 2566 2567 2568 2569 2570 2571 2572
    printf("ALTER TABLE time_zone_transition "
           "ORDER BY Time_zone_id, Transition_time;\n");
    printf("ALTER TABLE time_zone_transition_type "
           "ORDER BY Time_zone_id, Transition_type_id;\n");
  }
  else
  {
    init_alloc_root(&tz_storage, 32768, 0);
unknown's avatar
unknown committed
2573

2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591
    if (strcmp(argv[1], "--leap") == 0)
    {
      if (tz_load(argv[2], &tz_info, &tz_storage))
      {
        fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
        return 1;
      }
      print_tz_leaps_as_sql(&tz_info);
    }
    else
    {
      if (tz_load(argv[1], &tz_info, &tz_storage))
      {
        fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
        return 1;
      }
      print_tz_as_sql(argv[2], &tz_info);
    }
unknown's avatar
unknown committed
2592

2593 2594 2595
    free_root(&tz_storage, MYF(0));
  }

unknown's avatar
unknown committed
2596 2597 2598 2599
#else
  fprintf(stderr, "This tool has not been ported to NetWare\n");
#endif /* __NETWARE__ */

2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619
  return 0;
}

#endif /* defined(TZINFO2SQL) */


#ifdef TESTTIME

/*
   Some simple brute-force test wich allowed to catch a pair of bugs.
   Also can provide interesting facts about system's time zone support
   implementation.
*/

#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif

#ifndef TYPE_BIT
#define TYPE_BIT(type)	(sizeof (type) * CHAR_BIT)
unknown's avatar
unknown committed
2620
#endif
2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644

#ifndef TYPE_SIGNED
#define TYPE_SIGNED(type) (((type) -1) < 0)
#endif

my_bool
is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg)
{
  return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) &&
         (time_arg->month == (uint)tm_arg->tm_mon+1) &&
         (time_arg->day == (uint)tm_arg->tm_mday) &&
         (time_arg->hour == (uint)tm_arg->tm_hour) &&
         (time_arg->minute == (uint)tm_arg->tm_min) &&
         (time_arg->second == (uint)tm_arg->tm_sec) &&
         time_arg->second_part == 0;
}


int
main(int argc, char **argv)
{
  my_bool localtime_negative;
  TIME_ZONE_INFO tz_info;
  struct tm tmp;
2645
  MYSQL_TIME time_tmp;
2646
  time_t t, t1, t2;
2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657
  char fullname[FN_REFLEN+1];
  char *str_end;
  MEM_ROOT tz_storage;

  MY_INIT(argv[0]);

  init_alloc_root(&tz_storage, 32768, 0);

  /* let us set some well known timezone */
  setenv("TZ", "MET", 1);
  tzset();
unknown's avatar
unknown committed
2658

2659 2660 2661 2662 2663 2664 2665 2666
  /* Some initial time zone related system info */
  printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned",
                                (uint)TYPE_BIT(time_t));
  if (TYPE_SIGNED(time_t))
  {
    t= -100;
    localtime_negative= test(localtime_r(&t, &tmp) != 0);
    printf("localtime_r %s negative params \
unknown's avatar
unknown committed
2667
           (time_t=%d is %d-%d-%d %d:%d:%d)\n",
2668
           (localtime_negative ? "supports" : "doesn't support"), (int)t,
unknown's avatar
unknown committed
2669
           TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday,
2670
           tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
unknown's avatar
unknown committed
2671

2672
    printf("mktime %s negative results (%d)\n",
unknown's avatar
unknown committed
2673
           (t == mktime(&tmp) ? "doesn't support" : "supports"),
2674 2675 2676 2677 2678 2679 2680 2681
           (int)mktime(&tmp));
  }

  tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30;
  tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1;
  t= mktime(&tmp);
  printf("mktime returns %s for spring time gap (%d)\n",
         (t != (time_t)-1 ? "something" : "error"), (int)t);
unknown's avatar
unknown committed
2682

2683 2684 2685 2686 2687
  tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
  tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0;
  t= mktime(&tmp);
  printf("mktime returns %s for non existing date (%d)\n",
         (t != (time_t)-1 ? "something" : "error"), (int)t);
unknown's avatar
unknown committed
2688

2689 2690 2691 2692 2693 2694 2695 2696 2697 2698
  tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
  tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1;
  t= mktime(&tmp);
  printf("mktime %s unnormalized input (%d)\n",
         (t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t);

  tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26;
  tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1;
  mktime(&tmp);
  tmp.tm_hour= 2; tmp.tm_isdst= -1;
2699
  t= mktime(&tmp);
2700 2701 2702
  tmp.tm_hour= 4; tmp.tm_isdst= 0;
  mktime(&tmp);
  tmp.tm_hour= 2; tmp.tm_isdst= -1;
2703
  t1= mktime(&tmp);
2704
  printf("mktime is %s (%d %d)\n",
unknown's avatar
unknown committed
2705
         (t == t1 ? "determenistic" : "is non-determenistic"),
2706 2707 2708 2709 2710
         (int)t, (int)t1);

  /* Let us load time zone description */
  str_end= strmake(fullname, TZDIR, FN_REFLEN);
  strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
unknown's avatar
unknown committed
2711

2712 2713 2714 2715 2716 2717 2718 2719
  if (tz_load(fullname, &tz_info, &tz_storage))
  {
    printf("Unable to load time zone info from '%s'\n", fullname);
    free_root(&tz_storage, MYF(0));
    return 1;
  }

  printf("Testing our implementation\n");
unknown's avatar
unknown committed
2720

2721 2722 2723 2724
  if (TYPE_SIGNED(time_t) && localtime_negative)
  {
    for (t= -40000; t < 20000; t++)
    {
2725 2726
      localtime_r(&t, &tmp);
      gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2727 2728 2729 2730 2731 2732 2733 2734 2735
      if (!is_equal_TIME_tm(&time_tmp, &tmp))
      {
        printf("Problem with negative time_t = %d\n", (int)t);
        free_root(&tz_storage, MYF(0));
        return 1;
      }
    }
    printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n");
  }
unknown's avatar
unknown committed
2736

2737 2738 2739
  for (t= 1000000000; t < 1100000000; t+= 13)
  {
    localtime_r(&t,&tmp);
2740
    gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
unknown's avatar
unknown committed
2741

2742 2743 2744 2745 2746 2747 2748 2749 2750
    if (!is_equal_TIME_tm(&time_tmp, &tmp))
    {
      printf("Problem with time_t = %d\n", (int)t);
      free_root(&tz_storage, MYF(0));
      return 1;
    }
  }
  printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n");

2751
  my_init_time();
unknown's avatar
unknown committed
2752

2753 2754 2755 2756 2757
  /*
    Be careful here! my_system_gmt_sec doesn't fully handle unnormalized
    dates.
  */
  for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++)
2758
  {
2759
    for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++)
2760
    {
unknown's avatar
unknown committed
2761
      for (time_tmp.day= 1;
2762 2763
           time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1];
           time_tmp.day++)
2764
      {
2765
        for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++)
2766
        {
2767
          for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5)
2768
          {
2769 2770
            for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25)
            {
2771 2772
              long not_used;
              my_bool not_used_2;
2773 2774
              t= (time_t)my_system_gmt_sec(&time_tmp, &not_used, &not_used_2);
              t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, &not_used_2);
2775 2776
              if (t != t1)
              {
unknown's avatar
unknown committed
2777
                /*
2778 2779 2780
                  We need special handling during autumn since my_system_gmt_sec
                  prefers greater time_t values (in MET) for ambiguity.
                  And BTW that is a bug which should be fixed !!!
unknown's avatar
unknown committed
2781
                */
2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793
                tmp.tm_year= time_tmp.year - TM_YEAR_BASE;
                tmp.tm_mon= time_tmp.month - 1;
                tmp.tm_mday= time_tmp.day;
                tmp.tm_hour= time_tmp.hour;
                tmp.tm_min= time_tmp.minute;
                tmp.tm_sec= time_tmp.second;
                tmp.tm_isdst= 1;

                t2= mktime(&tmp);

                if (t1 == t2)
                  continue;
unknown's avatar
unknown committed
2794

2795 2796 2797 2798
                printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n",
                       time_tmp.year, time_tmp.month, time_tmp.day,
                       time_tmp.hour, time_tmp.minute, time_tmp.second,
                       (int)t,(int)t1);
unknown's avatar
unknown committed
2799

2800 2801 2802 2803
                free_root(&tz_storage, MYF(0));
                return 1;
              }
            }
2804 2805 2806 2807 2808
          }
        }
      }
    }
  }
unknown's avatar
unknown committed
2809

2810 2811 2812 2813 2814 2815 2816
  printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n");

  free_root(&tz_storage, MYF(0));
  return 0;
}

#endif /* defined(TESTTIME) */