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