~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/tztime.cc

  • Committer: Daniel Nichter
  • Date: 2011-10-23 16:01:37 UTC
  • mto: This revision was merged to the branch mainline in revision 2448.
  • Revision ID: daniel@percona.com-20111023160137-7ac3blgz8z4tf8za
Add Administration Getting Started and Logging.  Capitalize SQL clause keywords.

Show diffs side-by-side

added added

removed removed

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