~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/tztime.cc

  • Committer: Monty Taylor
  • Date: 2010-01-02 05:11:55 UTC
  • mto: (1259.3.1 build)
  • mto: This revision was merged to the branch mainline in revision 1262.
  • Revision ID: mordred@inaugust.com-20100102051155-akdm8w5rr6xwzayu
pandora-build - proper detection of memcached.

Show diffs side-by-side

added added

removed removed

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