~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to drizzled/tztime.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-03-18 12:12:31 UTC
  • Revision ID: james.westby@ubuntu.com-20100318121231-k6g1xe6cshbwa0f8
Tags: upstream-2010.03.1347
ImportĀ upstreamĀ versionĀ 2010.03.1347

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