~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/tztime.cc

  • Committer: Brian Aker
  • Date: 2010-11-11 04:16:59 UTC
  • mto: (1932.2.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 1930.
  • Revision ID: brian@tangent.org-20101111041659-5xb7ymjrasq1520p
Adding in support for EXECUTE to have WITH NO RETURN.

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
 
#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
 
*/
 
32
/**
 
33
 * String with names of SYSTEM time zone.
 
34
 */
588
35
static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_utf8_general_ci);
589
36
 
590
37
 
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
 
*/
 
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
 */
601
48
class Time_zone_system : public Time_zone
602
49
{
603
50
public:
609
56
};
610
57
 
611
58
 
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
 
*/
 
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
 */
637
83
time_t
638
84
Time_zone_system::TIME_to_gmt_sec(const DRIZZLE_TIME *t, bool *in_dst_time_gap) const
639
85
{
642
88
}
643
89
 
644
90
 
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
 
*/
 
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
 */
661
105
void
662
106
Time_zone_system::gmt_sec_to_TIME(DRIZZLE_TIME *tmp, time_t t) const
663
107
{
670
114
}
671
115
 
672
116
 
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
 
*/
 
117
/**
 
118
 * @brief
 
119
 * Get name of time zone
 
120
 *
 
121
 * @return
 
122
 * Name of time zone as String
 
123
 */
682
124
const String *
683
125
Time_zone_system::get_name() const
684
126
{
685
127
  return &tz_SYSTEM_name;
686
128
}
687
129
 
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
130
static Time_zone_system tz_SYSTEM;
1012
 
static Time_zone_offset tz_OFFSET0(0);
1013
131
 
1014
132
Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1015
133
 
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
 
*/
 
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
 */
1049
159
bool
1050
160
my_tz_init(Session *session, const char *default_tzname)
1051
161
{
1069
179
  return false;
1070
180
}
1071
181
 
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
 
*/
 
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
 */
1078
189
Time_zone *
1079
190
my_tz_find(Session *,
1080
191
           const String *)