~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/tztime.cc

  • Committer: Stewart Smith
  • Date: 2010-11-03 03:27:09 UTC
  • mto: (1902.1.1 build) (1910.1.2 build)
  • mto: This revision was merged to the branch mainline in revision 1903.
  • Revision ID: stewart@flamingspork.com-20101103032709-oyvfrc6eb8fzj0mr
fix docs warning: docs/unlock.rst:2: (WARNING/2) Title underline too short.

Show diffs side-by-side

added added

removed removed

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