~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/tztime.cc

  • Committer: Monty Taylor
  • Date: 2011-02-13 17:26:39 UTC
  • mfrom: (2157.2.2 give-in-to-pkg-config)
  • mto: This revision was merged to the branch mainline in revision 2166.
  • Revision ID: mordred@inaugust.com-20110213172639-nhy7i72sfhoq13ms
Merged in pkg-config fixes.

Show diffs side-by-side

added added

removed removed

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