~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2004 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
#ifdef USE_PRAGMA_IMPLEMENTATION
17
#pragma implementation				// gcc: Class implementation
18
#endif
19
20
#include "mysql_priv.h"
212.5.31 by Monty Taylor
Moved sql_common.h and my_time.h to libdrizzle.
21
#include <libdrizzle/my_time.h>
1 by brian
clean slate
22
23
#include "tzfile.h"
24
#include <m_string.h>
25
26
/* Structure describing local time type (e.g. Moscow summer time (MSD)) */
27
typedef struct ttinfo
28
{
29
  long tt_gmtoff; // Offset from UTC in seconds
30
  uint tt_isdst;   // Is daylight saving time or not. Used to set tm_isdst
31
  uint tt_abbrind; // Index of start of abbreviation for this time type.
32
  /*
33
    We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
34
    struct since we don't support POSIX-style TZ descriptions in variables.
35
  */
36
} TRAN_TYPE_INFO;
37
38
/* Structure describing leap-second corrections. */
39
typedef struct lsinfo
40
{
41
  my_time_t ls_trans; // Transition time
42
  long      ls_corr;  // Correction to apply
43
} LS_INFO;
44
45
/*
46
  Structure with information describing ranges of my_time_t shifted to local
47
  time (my_time_t + offset). Used for local MYSQL_TIME -> my_time_t conversion.
48
  See comments for TIME_to_gmt_sec() for more info.
49
*/
50
typedef struct revtinfo
51
{
52
  long rt_offset; // Offset of local time from UTC in seconds
53
  uint rt_type;    // Type of period 0 - Normal period. 1 - Spring time-gap
54
} REVT_INFO;
55
56
#ifdef TZNAME_MAX
57
#define MY_TZNAME_MAX	TZNAME_MAX
58
#endif
59
#ifndef TZNAME_MAX
60
#define MY_TZNAME_MAX	255
61
#endif
62
63
/*
64
  Structure which fully describes time zone which is
65
  described in our db or in zoneinfo files.
66
*/
67
typedef struct st_time_zone_info
68
{
69
  uint leapcnt;  // Number of leap-second corrections
70
  uint timecnt;  // Number of transitions between time types
71
  uint typecnt;  // Number of local time types
72
  uint charcnt;  // Number of characters used for abbreviations
73
  uint revcnt;   // Number of transition descr. for TIME->my_time_t conversion
74
  /* The following are dynamical arrays are allocated in MEM_ROOT */
75
  my_time_t *ats;       // Times of transitions between time types
76
  uchar	*types; // Local time types for transitions
77
  TRAN_TYPE_INFO *ttis; // Local time types descriptions
78
  /* Storage for local time types abbreviations. They are stored as ASCIIZ */
79
  char *chars;
80
  /*
81
    Leap seconds corrections descriptions, this array is shared by
82
    all time zones who use leap seconds.
83
  */
84
  LS_INFO *lsis;
85
  /*
86
    Starting points and descriptions of shifted my_time_t (my_time_t + offset)
87
    ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
88
    Used for tm -> my_time_t conversion.
89
  */
90
  my_time_t *revts;
91
  REVT_INFO *revtis;
92
  /*
93
    Time type which is used for times smaller than first transition or if
94
    there are no transitions at all.
95
  */
96
  TRAN_TYPE_INFO *fallback_tti;
97
98
} TIME_ZONE_INFO;
99
100
101
#if !defined(TZINFO2SQL)
102
103
static const uint mon_lengths[2][MONS_PER_YEAR]=
104
{
105
  { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
106
  { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
107
};
108
109
static const uint mon_starts[2][MONS_PER_YEAR]=
110
{
111
  { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
112
  { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
113
};
114
115
static const uint year_lengths[2]=
116
{
117
  DAYS_PER_NYEAR, DAYS_PER_LYEAR
118
};
119
120
#define LEAPS_THRU_END_OF(y)  ((y) / 4 - (y) / 100 + (y) / 400)
121
122
123
/*
124
  Converts time from my_time_t representation (seconds in UTC since Epoch)
125
  to broken down representation using given local time zone offset.
126
127
  SYNOPSIS
128
    sec_to_TIME()
129
      tmp    - pointer to structure for broken down representation
130
      t      - my_time_t value to be converted
131
      offset - local time zone offset
132
133
  DESCRIPTION
134
    Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
135
    (from elsie code) because doesn't contain any leap correction and
136
    TM_GMTOFF and is_dst setting and contains some MySQL specific
137
    initialization. Funny but with removing of these we almost have
138
    glibc's offtime function.
139
*/
140
static void
141
sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
142
{
143
  long days;
144
  long rem;
145
  int y;
146
  int yleap;
147
  const uint *ip;
148
149
  days= (long) (t / SECS_PER_DAY);
150
  rem=  (long) (t % SECS_PER_DAY);
151
152
  /*
153
    We do this as separate step after dividing t, because this
154
    allows us handle times near my_time_t bounds without overflows.
155
  */
156
  rem+= offset;
157
  while (rem < 0)
158
  {
159
    rem+= SECS_PER_DAY;
160
    days--;
161
  }
162
  while (rem >= SECS_PER_DAY)
163
  {
164
    rem -= SECS_PER_DAY;
165
    days++;
166
  }
167
  tmp->hour= (uint)(rem / SECS_PER_HOUR);
168
  rem= rem % SECS_PER_HOUR;
169
  tmp->minute= (uint)(rem / SECS_PER_MIN);
170
  /*
171
    A positive leap second requires a special
172
    representation.  This uses "... ??:59:60" et seq.
173
  */
174
  tmp->second= (uint)(rem % SECS_PER_MIN);
175
176
  y= EPOCH_YEAR;
177
  while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
178
  {
179
    int	newy;
180
181
    newy= y + days / DAYS_PER_NYEAR;
182
    if (days < 0)
183
      newy--;
184
    days-= (newy - y) * DAYS_PER_NYEAR +
185
           LEAPS_THRU_END_OF(newy - 1) -
186
           LEAPS_THRU_END_OF(y - 1);
187
    y= newy;
188
  }
189
  tmp->year= y;
190
191
  ip= mon_lengths[yleap];
192
  for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
193
    days= days - (long) ip[tmp->month];
194
  tmp->month++;
195
  tmp->day= (uint)(days + 1);
196
197
  /* filling MySQL specific MYSQL_TIME members */
198
  tmp->neg= 0; tmp->second_part= 0;
199
  tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
200
}
201
202
203
/*
204
  Find time range wich contains given my_time_t value
205
206
  SYNOPSIS
207
    find_time_range()
208
      t                - my_time_t value for which we looking for range
209
      range_boundaries - sorted array of range starts.
210
      higher_bound     - number of ranges
211
212
  DESCRIPTION
213
    Performs binary search for range which contains given my_time_t value.
214
    It has sense if number of ranges is greater than zero and my_time_t value
215
    is greater or equal than beginning of first range. It also assumes that
216
    t belongs to some range specified or end of last is MY_TIME_T_MAX.
217
218
    With this localtime_r on real data may takes less time than with linear
219
    search (I've seen 30% speed up).
220
221
  RETURN VALUE
222
    Index of range to which t belongs
223
*/
224
static uint
225
find_time_range(my_time_t t, const my_time_t *range_boundaries,
226
                uint higher_bound)
227
{
228
  uint i, lower_bound= 0;
229
230
  /*
231
    Function will work without this assertion but result would be meaningless.
232
  */
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
233
  assert(higher_bound > 0 && t >= range_boundaries[0]);
1 by brian
clean slate
234
235
  /*
236
    Do binary search for minimal interval which contain t. We preserve:
237
    range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
238
    invariant and decrease this higher_bound - lower_bound gap twice
239
    times on each step.
240
  */
241
242
  while (higher_bound - lower_bound > 1)
243
  {
244
    i= (lower_bound + higher_bound) >> 1;
245
    if (range_boundaries[i] <= t)
246
      lower_bound= i;
247
    else
248
      higher_bound= i;
249
  }
250
  return lower_bound;
251
}
252
253
/*
254
  Find local time transition for given my_time_t.
255
256
  SYNOPSIS
257
    find_transition_type()
258
      t   - my_time_t value to be converted
259
      sp  - pointer to struct with time zone description
260
261
  RETURN VALUE
262
    Pointer to structure in time zone description describing
263
    local time type for given my_time_t.
264
*/
265
static
266
const TRAN_TYPE_INFO *
267
find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
268
{
269
  if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
270
  {
271
    /*
272
      If we have not any transitions or t is before first transition let
273
      us use fallback time type.
274
    */
275
    return sp->fallback_tti;
276
  }
277
278
  /*
279
    Do binary search for minimal interval between transitions which
280
    contain t. With this localtime_r on real data may takes less
281
    time than with linear search (I've seen 30% speed up).
282
  */
283
  return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
284
}
285
286
287
/*
288
  Converts time in my_time_t representation (seconds in UTC since Epoch) to
289
  broken down MYSQL_TIME representation in local time zone.
290
291
  SYNOPSIS
292
    gmt_sec_to_TIME()
293
      tmp          - pointer to structure for broken down represenatation
294
      sec_in_utc   - my_time_t value to be converted
295
      sp           - pointer to struct with time zone description
296
297
  TODO
298
    We can improve this function by creating joined array of transitions and
299
    leap corrections. This will require adding extra field to TRAN_TYPE_INFO
300
    for storing number of "extra" seconds to minute occured due to correction
301
    (60th and 61st second, look how we calculate them as "hit" in this
302
    function).
303
    Under realistic assumptions about frequency of transitions the same array
304
    can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
305
    implement tweaked binary search which will take into account that some
306
    MYSQL_TIME has two matching my_time_t ranges and some of them have none.
307
*/
308
static void
309
gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
310
{
311
  const TRAN_TYPE_INFO *ttisp;
312
  const LS_INFO *lp;
313
  long  corr= 0;
314
  int   hit= 0;
315
  int   i;
316
317
  /*
318
    Find proper transition (and its local time type) for our sec_in_utc value.
319
    Funny but again by separating this step in function we receive code
320
    which very close to glibc's code. No wonder since they obviously use
321
    the same base and all steps are sensible.
322
  */
323
  ttisp= find_transition_type(sec_in_utc, sp);
324
325
  /*
326
    Let us find leap correction for our sec_in_utc value and number of extra
327
    secs to add to this minute.
328
    This loop is rarely used because most users will use time zones without
329
    leap seconds, and even in case when we have such time zone there won't
330
    be many iterations (we have about 22 corrections at this moment (2004)).
331
  */
332
  for ( i= sp->leapcnt; i-- > 0; )
333
  {
334
    lp= &sp->lsis[i];
335
    if (sec_in_utc >= lp->ls_trans)
336
    {
337
      if (sec_in_utc == lp->ls_trans)
338
      {
339
        hit= ((i == 0 && lp->ls_corr > 0) ||
340
              lp->ls_corr > sp->lsis[i - 1].ls_corr);
341
        if (hit)
342
        {
343
          while (i > 0 &&
344
                 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
345
                 sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
346
          {
347
            hit++;
348
            i--;
349
          }
350
        }
351
      }
352
      corr= lp->ls_corr;
353
      break;
354
    }
355
  }
356
357
  sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
358
359
  tmp->second+= hit;
360
}
361
362
363
/*
364
  Converts local time in broken down representation to local
365
  time zone analog of my_time_t represenation.
366
367
  SYNOPSIS
368
    sec_since_epoch()
369
      year, mon, mday, hour, min, sec - broken down representation.
370
371
  DESCRIPTION
372
    Converts time in broken down representation to my_time_t representation
373
    ignoring time zone. Note that we cannot convert back some valid _local_
374
    times near ends of my_time_t range because of my_time_t  overflow. But we
375
    ignore this fact now since MySQL will never pass such argument.
376
377
  RETURN VALUE
378
    Seconds since epoch time representation.
379
*/
380
static my_time_t
381
sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
382
{
383
  /* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
384
  assert(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
1 by brian
clean slate
385
#ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
386
  /*
387
    It turns out that only whenever month is normalized or unnormalized
388
    plays role.
389
  */
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
390
  assert(mon > 0 && mon < 13);
1 by brian
clean slate
391
  long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
392
             LEAPS_THRU_END_OF(year - 1) -
393
             LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
394
  days+= mon_starts[isleap(year)][mon - 1];
395
#else
396
  long norm_month= (mon - 1) % MONS_PER_YEAR;
397
  long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
398
  long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
399
             LEAPS_THRU_END_OF(a_year - 1) -
400
             LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
401
  days+= mon_starts[isleap(a_year)]
402
                    [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
403
#endif
404
  days+= mday - 1;
405
406
  return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
407
         SECS_PER_MIN + sec;
408
}
409
410
/*
411
  Converts local time in broken down MYSQL_TIME representation to my_time_t
412
  representation.
413
414
  SYNOPSIS
415
    TIME_to_gmt_sec()
416
      t               - pointer to structure for broken down represenatation
417
      sp              - pointer to struct with time zone description
418
      in_dst_time_gap - pointer to bool which is set to true if datetime
419
                        value passed doesn't really exist (i.e. falls into
420
                        spring time-gap) and is not touched otherwise.
421
422
  DESCRIPTION
423
    This is mktime analog for MySQL. It is essentially different
424
    from mktime (or hypotetical my_mktime) because:
425
    - It has no idea about tm_isdst member so if it
426
      has two answers it will give the smaller one
427
    - If we are in spring time gap then it will return
428
      beginning of the gap
429
    - It can give wrong results near the ends of my_time_t due to
430
      overflows, but we are safe since in MySQL we will never
431
      call this function for such dates (its restriction for year
432
      between 1970 and 2038 gives us several days of reserve).
433
    - By default it doesn't support un-normalized input. But if
434
      sec_since_epoch() function supports un-normalized dates
435
      then this function should handle un-normalized input right,
436
      altough it won't normalize structure TIME.
437
438
    Traditional approach to problem of conversion from broken down
439
    representation to time_t is iterative. Both elsie's and glibc
440
    implementation try to guess what time_t value should correspond to
441
    this broken-down value. They perform localtime_r function on their
442
    guessed value and then calculate the difference and try to improve
443
    their guess. Elsie's code guesses time_t value in bit by bit manner,
444
    Glibc's code tries to add difference between broken-down value
445
    corresponding to guess and target broken-down value to current guess.
446
    It also uses caching of last found correction... So Glibc's approach
447
    is essentially faster but introduces some undetermenism (in case if
448
    is_dst member of broken-down representation (tm struct) is not known
449
    and we have two possible answers).
450
451
    We use completely different approach. It is better since it is both
452
    faster than iterative implementations and fully determenistic. If you
453
    look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
454
    of two steps:
455
    The first is calculating shifted my_time_t value and the second - TIME
456
    calculation from shifted my_time_t value (well it is a bit simplified
457
    picture). The part in which we are interested in is my_time_t -> shifted
458
    my_time_t conversion. It is piecewise linear function which is defined
459
    by combination of transition times as break points and times offset
460
    as changing function parameter. The possible inverse function for this
461
    converison would be ambiguos but with MySQL's restrictions we can use
462
    some function which is the same as inverse function on unambigiuos
463
    ranges and coincides with one of branches of inverse function in
464
    other ranges. Thus we just need to build table which will determine
465
    this shifted my_time_t -> my_time_t conversion similar to existing
466
    (my_time_t -> shifted my_time_t table). We do this in
467
    prepare_tz_info function.
468
469
  TODO
470
    If we can even more improve this function. For doing this we will need to
471
    build joined map of transitions and leap corrections for gmt_sec_to_TIME()
472
    function (similar to revts/revtis). Under realistic assumptions about
473
    frequency of transitions we can use the same array for TIME_to_gmt_sec().
474
    We need to implement special version of binary search for this. Such step
475
    will be beneficial to CPU cache since we will decrease data-set used for
476
    conversion twice.
477
478
  RETURN VALUE
479
    Seconds in UTC since Epoch.
480
    0 in case of error.
481
*/
482
static my_time_t
483
TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp,
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
484
                bool *in_dst_time_gap)
1 by brian
clean slate
485
{
486
  my_time_t local_t;
487
  uint saved_seconds;
488
  uint i;
489
  int shift= 0;
490
491
  if (!validate_timestamp_range(t))
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
492
    return(0);
1 by brian
clean slate
493
494
495
  /* We need this for correct leap seconds handling */
496
  if (t->second < SECS_PER_MIN)
497
    saved_seconds= 0;
498
  else
499
    saved_seconds= t->second;
500
501
  /*
502
    NOTE: to convert full my_time_t range we do a shift of the
503
    boundary dates here to avoid overflow of my_time_t.
504
    We use alike approach in my_system_gmt_sec().
505
506
    However in that function we also have to take into account
507
    overflow near 0 on some platforms. That's because my_system_gmt_sec
508
    uses localtime_r(), which doesn't work with negative values correctly
509
    on platforms with unsigned time_t (QNX). Here we don't use localtime()
510
    => we negative values of local_t are ok.
511
  */
512
513
  if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
514
  {
515
    /*
516
      We will pass (t->day - shift) to sec_since_epoch(), and
517
      want this value to be a positive number, so we shift
518
      only dates > 4.01.2038 (to avoid owerflow).
519
    */
520
    shift= 2;
521
  }
522
523
524
  local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
525
                           t->hour, t->minute,
526
                           saved_seconds ? 0 : t->second);
527
528
  /* We have at least one range */
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
529
  assert(sp->revcnt >= 1);
1 by brian
clean slate
530
531
  if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
532
  {
533
    /*
534
      This means that source time can't be represented as my_time_t due to
535
      limited my_time_t range.
536
    */
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
537
    return(0);
1 by brian
clean slate
538
  }
539
540
  /* binary search for our range */
541
  i= find_time_range(local_t, sp->revts, sp->revcnt);
542
543
  /*
544
    As there are no offset switches at the end of TIMESTAMP range,
545
    we could simply check for overflow here (and don't need to bother
546
    about DST gaps etc)
547
  */
548
  if (shift)
549
  {
550
    if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
551
                               sp->revtis[i].rt_offset - saved_seconds))
552
    {
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
553
      return(0);                           /* my_time_t overflow */
1 by brian
clean slate
554
    }
555
    local_t+= shift * SECS_PER_DAY;
556
  }
557
558
  if (sp->revtis[i].rt_type)
559
  {
560
    /*
561
      Oops! We are in spring time gap.
562
      May be we should return error here?
563
      Now we are returning my_time_t value corresponding to the
564
      beginning of the gap.
565
    */
566
    *in_dst_time_gap= 1;
567
    local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
568
  }
569
  else
570
    local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
571
572
  /* check for TIMESTAMP_MAX_VALUE was already done above */
573
  if (local_t < TIMESTAMP_MIN_VALUE)
574
    local_t= 0;
575
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
576
  return(local_t);
1 by brian
clean slate
577
}
578
579
580
/*
581
  End of elsie derived code.
582
*/
583
#endif /* !defined(TZINFO2SQL) */
584
585
586
/*
587
  String with names of SYSTEM time zone.
588
*/
589
static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
590
591
592
/*
593
  Instance of this class represents local time zone used on this system
594
  (specified by TZ environment variable or via any other system mechanism).
595
  It uses system functions (localtime_r, my_system_gmt_sec) for conversion
596
  and is always available. Because of this it is used by default - if there
597
  were no explicit time zone specified. On the other hand because of this
598
  conversion methods provided by this class is significantly slower and
599
  possibly less multi-threaded-friendly than corresponding Time_zone_db
600
  methods so the latter should be preffered there it is possible.
601
*/
602
class Time_zone_system : public Time_zone
603
{
604
public:
605
  Time_zone_system() {}                       /* Remove gcc warning */
606
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
607
                                    bool *in_dst_time_gap) const;
1 by brian
clean slate
608
  virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
609
  virtual const String * get_name() const;
610
};
611
612
613
/*
614
  Converts local time in system time zone in MYSQL_TIME representation
615
  to its my_time_t representation.
616
617
  SYNOPSIS
618
    TIME_to_gmt_sec()
619
      t               - pointer to MYSQL_TIME structure with local time in
620
                        broken-down representation.
621
      in_dst_time_gap - pointer to bool which is set to true if datetime
622
                        value passed doesn't really exist (i.e. falls into
623
                        spring time-gap) and is not touched otherwise.
624
625
  DESCRIPTION
626
    This method uses system function (localtime_r()) for conversion
627
    local time in system time zone in MYSQL_TIME structure to its my_time_t
628
    representation. Unlike the same function for Time_zone_db class
629
    it it won't handle unnormalized input properly. Still it will
630
    return lowest possible my_time_t in case of ambiguity or if we
631
    provide time corresponding to the time-gap.
632
633
    You should call init_time() function before using this function.
634
635
  RETURN VALUE
636
    Corresponding my_time_t value or 0 in case of error
637
*/
638
my_time_t
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
639
Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, bool *in_dst_time_gap) const
1 by brian
clean slate
640
{
641
  long not_used;
642
  return my_system_gmt_sec(t, &not_used, in_dst_time_gap);
643
}
644
645
646
/*
647
  Converts time from UTC seconds since Epoch (my_time_t) representation
648
  to system local time zone broken-down representation.
649
650
  SYNOPSIS
651
    gmt_sec_to_TIME()
652
      tmp - pointer to MYSQL_TIME structure to fill-in
653
      t   - my_time_t value to be converted
654
655
  NOTE
656
    We assume that value passed to this function will fit into time_t range
657
    supported by localtime_r. This conversion is putting restriction on
658
    TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
659
    for interaction with client then we can extend TIMESTAMP range down to
660
    the 1902 easily.
661
*/
662
void
663
Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
664
{
665
  struct tm tmp_tm;
666
  time_t tmp_t= (time_t)t;
667
668
  localtime_r(&tmp_t, &tmp_tm);
669
  localtime_to_TIME(tmp, &tmp_tm);
670
  tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
671
}
672
673
674
/*
675
  Get name of time zone
676
677
  SYNOPSIS
678
    get_name()
679
680
  RETURN VALUE
681
    Name of time zone as String
682
*/
683
const String *
684
Time_zone_system::get_name() const
685
{
686
  return &tz_SYSTEM_name;
687
}
688
689
690
/*
691
  Instance of this class represents UTC time zone. It uses system gmtime_r
692
  function for conversions and is always available. It is used only for
693
  my_time_t -> MYSQL_TIME conversions in various UTC_...  functions, it is not
694
  intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
695
*/
696
class Time_zone_utc : public Time_zone
697
{
698
public:
699
  Time_zone_utc() {}                          /* Remove gcc warning */
700
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
701
                                    bool *in_dst_time_gap) const;
1 by brian
clean slate
702
  virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
703
  virtual const String * get_name() const;
704
};
705
706
707
/*
708
  Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
709
710
  SYNOPSIS
711
    TIME_to_gmt_sec()
712
      t               - pointer to MYSQL_TIME structure with local time
713
                        in broken-down representation.
714
      in_dst_time_gap - pointer to bool which is set to true if datetime
715
                        value passed doesn't really exist (i.e. falls into
716
                        spring time-gap) and is not touched otherwise.
717
718
  DESCRIPTION
719
    Since Time_zone_utc is used only internally for my_time_t -> TIME
720
    conversions, this function of Time_zone interface is not implemented for
721
    this class and should not be called.
722
723
  RETURN VALUE
724
    0
725
*/
726
my_time_t
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
727
Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t __attribute__((unused)),
728
                               bool *in_dst_time_gap __attribute__((unused))) const
1 by brian
clean slate
729
{
730
  /* Should be never called */
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
731
  assert(0);
1 by brian
clean slate
732
  return 0;
733
}
734
735
736
/*
737
  Converts time from UTC seconds since Epoch (my_time_t) representation
738
  to broken-down representation (also in UTC).
739
740
  SYNOPSIS
741
    gmt_sec_to_TIME()
742
      tmp - pointer to MYSQL_TIME structure to fill-in
743
      t   - my_time_t value to be converted
744
745
  NOTE
746
    See note for apropriate Time_zone_system method.
747
*/
748
void
749
Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
750
{
751
  struct tm tmp_tm;
752
  time_t tmp_t= (time_t)t;
753
  gmtime_r(&tmp_t, &tmp_tm);
754
  localtime_to_TIME(tmp, &tmp_tm);
755
  tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
756
}
757
758
759
/*
760
  Get name of time zone
761
762
  SYNOPSIS
763
    get_name()
764
765
  DESCRIPTION
766
    Since Time_zone_utc is used only internally by SQL's UTC_* functions it
767
    is not accessible directly, and hence this function of Time_zone
768
    interface is not implemented for this class and should not be called.
769
770
  RETURN VALUE
771
    0
772
*/
773
const String *
774
Time_zone_utc::get_name() const
775
{
776
  /* Should be never called */
51.1.72 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
777
  assert(0);
1 by brian
clean slate
778
  return 0;
779
}
780
781
782
/*
783
  Instance of this class represents some time zone which is
784
  described in mysql.time_zone family of tables.
785
*/
786
class Time_zone_db : public Time_zone
787
{
788
public:
789
  Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
790
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
791
                                    bool *in_dst_time_gap) const;
1 by brian
clean slate
792
  virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
793
  virtual const String * get_name() const;
794
private:
795
  TIME_ZONE_INFO *tz_info;
796
  const String *tz_name;
797
};
798
799
800
/*
801
  Initializes object representing time zone described by mysql.time_zone
802
  tables.
803
804
  SYNOPSIS
805
    Time_zone_db()
806
      tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
807
                    according to db or other time zone description
808
                    (for example by my_tz_init()).
809
                    Several Time_zone_db instances can share one
810
                    TIME_ZONE_INFO structure.
811
      tz_name_arg - name of time zone.
812
*/
813
Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
814
                           const String *tz_name_arg):
815
  tz_info(tz_info_arg), tz_name(tz_name_arg)
816
{
817
}
818
819
820
/*
821
  Converts local time in time zone described from TIME
822
  representation to its my_time_t representation.
823
824
  SYNOPSIS
825
    TIME_to_gmt_sec()
826
      t               - pointer to MYSQL_TIME structure with local time
827
                        in broken-down representation.
828
      in_dst_time_gap - pointer to bool which is set to true if datetime
829
                        value passed doesn't really exist (i.e. falls into
830
                        spring time-gap) and is not touched otherwise.
831
832
  DESCRIPTION
833
    Please see ::TIME_to_gmt_sec for function description and
834
    parameter restrictions.
835
836
  RETURN VALUE
837
    Corresponding my_time_t value or 0 in case of error
838
*/
839
my_time_t
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
840
Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, bool *in_dst_time_gap) const
1 by brian
clean slate
841
{
842
  return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
843
}
844
845
846
/*
847
  Converts time from UTC seconds since Epoch (my_time_t) representation
848
  to local time zone described in broken-down representation.
849
850
  SYNOPSIS
851
    gmt_sec_to_TIME()
852
      tmp - pointer to MYSQL_TIME structure to fill-in
853
      t   - my_time_t value to be converted
854
*/
855
void
856
Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
857
{
858
  ::gmt_sec_to_TIME(tmp, t, tz_info);
859
}
860
861
862
/*
863
  Get name of time zone
864
865
  SYNOPSIS
866
    get_name()
867
868
  RETURN VALUE
869
    Name of time zone as ASCIIZ-string
870
*/
871
const String *
872
Time_zone_db::get_name() const
873
{
874
  return tz_name;
875
}
876
877
878
/*
879
  Instance of this class represents time zone which
880
  was specified as offset from UTC.
881
*/
882
class Time_zone_offset : public Time_zone
883
{
884
public:
885
  Time_zone_offset(long tz_offset_arg);
886
  virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
887
                                    bool *in_dst_time_gap) const;
1 by brian
clean slate
888
  virtual void   gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
889
  virtual const String * get_name() const;
890
  /*
891
    This have to be public because we want to be able to access it from
892
    my_offset_tzs_get_key() function
893
  */
894
  long offset;
895
private:
896
  /* Extra reserve because of snprintf */
897
  char name_buff[7+16];
898
  String name;
899
};
900
901
902
/*
903
  Initializes object representing time zone described by its offset from UTC.
904
905
  SYNOPSIS
906
    Time_zone_offset()
907
      tz_offset_arg - offset from UTC in seconds.
908
                      Positive for direction to east.
909
*/
910
Time_zone_offset::Time_zone_offset(long tz_offset_arg):
911
  offset(tz_offset_arg)
912
{
913
  uint hours= abs((int)(offset / SECS_PER_HOUR));
914
  uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
77.1.18 by Monty Taylor
Removed my_vsnprintf and my_snprintf.
915
  ulong length= snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
916
                         (offset>=0) ? "+" : "-", hours, minutes);
1 by brian
clean slate
917
  name.set(name_buff, length, &my_charset_latin1);
918
}
919
920
921
/*
922
  Converts local time in time zone described as offset from UTC
923
  from MYSQL_TIME representation to its my_time_t representation.
924
925
  SYNOPSIS
926
    TIME_to_gmt_sec()
927
      t               - pointer to MYSQL_TIME structure with local time
928
                        in broken-down representation.
929
      in_dst_time_gap - pointer to bool which should be set to true if
930
                        datetime  value passed doesn't really exist
931
                        (i.e. falls into spring time-gap) and is not
932
                        touched otherwise.
933
                        It is not really used in this class.
934
935
  RETURN VALUE
936
    Corresponding my_time_t value or 0 in case of error
937
*/
938
my_time_t
77.1.46 by Monty Taylor
Finished the warnings work!
939
Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
940
                                  bool *in_dst_time_gap __attribute__((unused))) const
1 by brian
clean slate
941
{
942
  my_time_t local_t;
943
  int shift= 0;
944
945
  /*
946
    Check timestamp range.we have to do this as calling function relies on
947
    us to make all validation checks here.
948
  */
949
  if (!validate_timestamp_range(t))
950
    return 0;
951
952
  /*
953
    Do a temporary shift of the boundary dates to avoid
954
    overflow of my_time_t if the time value is near it's
955
    maximum range
956
  */
957
  if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
958
    shift= 2;
959
960
  local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
961
                           t->hour, t->minute, t->second) -
962
           offset;
963
964
  if (shift)
965
  {
966
    /* Add back the shifted time */
967
    local_t+= shift * SECS_PER_DAY;
968
  }
969
970
  if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
971
    return local_t;
972
973
  /* range error*/
974
  return 0;
975
}
976
977
978
/*
979
  Converts time from UTC seconds since Epoch (my_time_t) representation
980
  to local time zone described as offset from UTC and in broken-down
981
  representation.
982
983
  SYNOPSIS
984
    gmt_sec_to_TIME()
985
      tmp - pointer to MYSQL_TIME structure to fill-in
986
      t   - my_time_t value to be converted
987
*/
988
void
989
Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
990
{
991
  sec_to_TIME(tmp, t, offset);
992
}
993
994
995
/*
996
  Get name of time zone
997
998
  SYNOPSIS
999
    get_name()
1000
1001
  RETURN VALUE
1002
    Name of time zone as pointer to String object
1003
*/
1004
const String *
1005
Time_zone_offset::get_name() const
1006
{
1007
  return &name;
1008
}
1009
1010
1011
static Time_zone_utc tz_UTC;
1012
static Time_zone_system tz_SYSTEM;
1013
static Time_zone_offset tz_OFFSET0(0);
1014
1015
Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1016
Time_zone *my_tz_UTC= &tz_UTC;
1017
Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1018
1019
class Tz_names_entry: public Sql_alloc
1020
{
1021
public:
1022
  String name;
1023
  Time_zone *tz;
1024
};
1025
1026
1027
/*
1028
  Initialize time zone support infrastructure.
1029
1030
  SYNOPSIS
1031
    my_tz_init()
1032
      thd            - current thread object
1033
      default_tzname - default time zone or 0 if none.
1034
      bootstrap      - indicates whenever we are in bootstrap mode
1035
1036
  DESCRIPTION
1037
    This function will init memory structures needed for time zone support,
1038
    it will register mandatory SYSTEM time zone in them. It will try to open
1039
    mysql.time_zone* tables and load information about default time zone and
1040
    information which further will be shared among all time zones loaded.
1041
    If system tables with time zone descriptions don't exist it won't fail
1042
    (unless default_tzname is time zone from tables). If bootstrap parameter
1043
    is true then this routine assumes that we are in bootstrap mode and won't
1044
    load time zone descriptions unless someone specifies default time zone
1045
    which is supposedly stored in those tables.
1046
    It'll also set default time zone if it is specified.
1047
1048
  RETURN VALUES
1049
    0 - ok
1050
    1 - Error
1051
*/
53.1.1 by Brian Aker
First pass through removing timezone. Left infrastructure for possible call
1052
bool
77.1.46 by Monty Taylor
Finished the warnings work!
1053
my_tz_init(THD *thd, const char *default_tzname,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1054
           bool bootstrap __attribute__((unused)))
1 by brian
clean slate
1055
{
1056
  if (default_tzname)
1057
  {
1058
    String tmp_tzname2(default_tzname, &my_charset_latin1);
1059
    /*
1060
      Time zone tables may be open here, and my_tz_find() may open
1061
      most of them once more, but this is OK for system tables open
1062
      for READ.
1063
    */
1064
    if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
1065
    {
1066
      sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
1067
                      default_tzname);
53.1.1 by Brian Aker
First pass through removing timezone. Left infrastructure for possible call
1068
      return true;
1 by brian
clean slate
1069
    }
1070
  }
1071
53.1.1 by Brian Aker
First pass through removing timezone. Left infrastructure for possible call
1072
  return false;
1 by brian
clean slate
1073
}
1074
1075
1076
/*
1077
  Free resources used by time zone support infrastructure.
1078
*/
1079
1080
void my_tz_free()
1081
{
1082
}
1083
1084
1085
/*
1086
  Parse string that specifies time zone as offset from UTC.
1087
1088
  SYNOPSIS
1089
    str_to_offset()
1090
      str    - pointer to string which contains offset
1091
      length - length of string
1092
      offset - out parameter for storing found offset in seconds.
1093
1094
  DESCRIPTION
1095
    This function parses string which contains time zone offset
1096
    in form similar to '+10:00' and converts found value to
1097
    seconds from UTC form (east is positive).
1098
1099
  RETURN VALUE
1100
    0 - Ok
1101
    1 - String doesn't contain valid time zone offset
1102
*/
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
1103
bool
1 by brian
clean slate
1104
str_to_offset(const char *str, uint length, long *offset)
1105
{
1106
  const char *end= str + length;
93 by Brian Aker
Convert tztime.cc to bool from my_bool.
1107
  bool negative;
1 by brian
clean slate
1108
  ulong number_tmp;
1109
  long offset_tmp;
1110
1111
  if (length < 4)
1112
    return 1;
1113
1114
  if (*str == '+')
1115
    negative= 0;
1116
  else if (*str == '-')
1117
    negative= 1;
1118
  else
1119
    return 1;
1120
  str++;
1121
1122
  number_tmp= 0;
1123
1124
  while (str < end && my_isdigit(&my_charset_latin1, *str))
1125
  {
1126
    number_tmp= number_tmp*10 + *str - '0';
1127
    str++;
1128
  }
1129
1130
  if (str + 1 >= end || *str != ':')
1131
    return 1;
1132
  str++;
1133
1134
  offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
1135
1136
  while (str < end && my_isdigit(&my_charset_latin1, *str))
1137
  {
1138
    number_tmp= number_tmp * 10 + *str - '0';
1139
    str++;
1140
  }
1141
1142
  if (str != end)
1143
    return 1;
1144
1145
  offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
1146
1147
  if (negative)
1148
    offset_tmp= -offset_tmp;
1149
1150
  /*
1151
    Check if offset is in range prescribed by standard
1152
    (from -12:59 to 13:00).
1153
  */
1154
1155
  if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
1156
      offset_tmp > 13 * SECS_PER_HOUR)
1157
    return 1;
1158
1159
  *offset= offset_tmp;
1160
1161
  return 0;
1162
}
1163
1164
1165
/*
1166
  Get Time_zone object for specified time zone.
1167
53.1.1 by Brian Aker
First pass through removing timezone. Left infrastructure for possible call
1168
  Not implemented yet. This needs to hook into some sort of OS system call.
1 by brian
clean slate
1169
1170
*/
1171
Time_zone *
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1172
my_tz_find(THD *thd __attribute__((unused)),
1173
           const String *name __attribute__((unused)))
1 by brian
clean slate
1174
{
53.1.1 by Brian Aker
First pass through removing timezone. Left infrastructure for possible call
1175
  return NULL;
1176
}