1 /* Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23 /*
24 Most of the following code and structures were derived from
25 public domain code from ftp://elsie.nci.nih.gov/pub
26 (We will refer to this code as to elsie-code further.)
27 */
28
29 /*
30 We should not include sql_priv.h in mysql_tzinfo_to_sql utility since
31 it creates unsolved link dependencies on some platforms.
32 */
33
34 #include <my_global.h>
35 #include <algorithm>
36
37 #if !defined(TZINFO2SQL) && !defined(TESTTIME)
38 #include "sql_priv.h"
39 #include "unireg.h"
40 #include "tztime.h"
41 #include "sql_time.h" // localtime_to_TIME
42 #include "sql_base.h" // open_system_tables_for_read,
43 // close_system_tables
44 #else
45 #include <my_time.h>
46 #include "tztime.h"
47 #include <my_sys.h>
48 #endif
49
50 #include "tzfile.h"
51 #include <m_string.h>
52 #include <my_dir.h>
53 #include <mysql/psi/mysql_file.h>
54 #include "debug_sync.h"
55
56 using std::min;
57
58 /*
59 Now we don't use abbreviations in server but we will do this in future.
60 */
61 #if defined(TZINFO2SQL) || defined(TESTTIME)
62 #define ABBR_ARE_USED
63 #else
64 #if !defined(DBUG_OFF)
65 /* Let use abbreviations for debug purposes */
66 #undef ABBR_ARE_USED
67 #define ABBR_ARE_USED
68 #endif /* !defined(DBUG_OFF) */
69 #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
70
71 /* Structure describing local time type (e.g. Moscow summer time (MSD)) */
72 typedef struct ttinfo
73 {
74 long tt_gmtoff; // Offset from UTC in seconds
75 uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst
76 #ifdef ABBR_ARE_USED
77 uint tt_abbrind; // Index of start of abbreviation for this time type.
78 #endif
79 /*
80 We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code
81 struct since we don't support POSIX-style TZ descriptions in variables.
82 */
83 } TRAN_TYPE_INFO;
84
85 /* Structure describing leap-second corrections. */
86 typedef struct lsinfo
87 {
88 my_time_t ls_trans; // Transition time
89 long ls_corr; // Correction to apply
90 } LS_INFO;
91
92 /*
93 Structure with information describing ranges of my_time_t shifted to local
94 time (my_time_t + offset). Used for local MYSQL_TIME -> my_time_t conversion.
95 See comments for TIME_to_gmt_sec() for more info.
96 */
97 typedef struct revtinfo
98 {
99 long rt_offset; // Offset of local time from UTC in seconds
100 uint rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap
101 } REVT_INFO;
102
103 #ifdef TZNAME_MAX
104 #define MY_TZNAME_MAX TZNAME_MAX
105 #endif
106 #ifndef TZNAME_MAX
107 #define MY_TZNAME_MAX 255
108 #endif
109
110 /*
111 Structure which fully describes time zone which is
112 described in our db or in zoneinfo files.
113 */
114 typedef struct st_time_zone_info
115 {
116 uint leapcnt; // Number of leap-second corrections
117 uint timecnt; // Number of transitions between time types
118 uint typecnt; // Number of local time types
119 uint charcnt; // Number of characters used for abbreviations
120 uint revcnt; // Number of transition descr. for TIME->my_time_t conversion
121 /* The following are dynamical arrays are allocated in MEM_ROOT */
122 my_time_t *ats; // Times of transitions between time types
123 uchar *types; // Local time types for transitions
124 TRAN_TYPE_INFO *ttis; // Local time types descriptions
125 #ifdef ABBR_ARE_USED
126 /* Storage for local time types abbreviations. They are stored as ASCIIZ */
127 char *chars;
128 #endif
129 /*
130 Leap seconds corrections descriptions, this array is shared by
131 all time zones who use leap seconds.
132 */
133 LS_INFO *lsis;
134 /*
135 Starting points and descriptions of shifted my_time_t (my_time_t + offset)
136 ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
137 Used for tm -> my_time_t conversion.
138 */
139 my_time_t *revts;
140 REVT_INFO *revtis;
141 /*
142 Time type which is used for times smaller than first transition or if
143 there are no transitions at all.
144 */
145 TRAN_TYPE_INFO *fallback_tti;
146
147 } TIME_ZONE_INFO;
148
149
150 static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);
151
152
153 #if defined(TZINFO2SQL) || defined(TESTTIME)
154
155 /*
156 Load time zone description from zoneinfo (TZinfo) file.
157
158 SYNOPSIS
159 tz_load()
160 name - path to zoneinfo file
161 sp - TIME_ZONE_INFO structure to fill
162
163 RETURN VALUES
164 0 - Ok
165 1 - Error
166 */
167 static my_bool
tz_load(const char * name,TIME_ZONE_INFO * sp,MEM_ROOT * storage)168 tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
169 {
170 uchar *p;
171 int read_from_file;
172 uint i;
173 MYSQL_FILE *file;
174
175 if (!(file= mysql_file_fopen(0, name, O_RDONLY|O_BINARY, MYF(MY_WME))))
176 return 1;
177 {
178 union
179 {
180 struct tzhead tzhead;
181 uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
182 TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
183 #ifdef ABBR_ARE_USED
184 MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
185 #endif
186 sizeof(LS_INFO) * TZ_MAX_LEAPS];
187 } u;
188 uint ttisstdcnt;
189 uint ttisgmtcnt;
190 char *tzinfo_buf;
191
192 read_from_file= mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
193
194 if (mysql_file_fclose(file, MYF(MY_WME)) != 0)
195 return 1;
196
197 if (read_from_file < (int)sizeof(struct tzhead))
198 return 1;
199
200 ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt);
201 ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt);
202 sp->leapcnt= int4net(u.tzhead.tzh_leapcnt);
203 sp->timecnt= int4net(u.tzhead.tzh_timecnt);
204 sp->typecnt= int4net(u.tzhead.tzh_typecnt);
205 sp->charcnt= int4net(u.tzhead.tzh_charcnt);
206 p= u.tzhead.tzh_charcnt + sizeof(u.tzhead.tzh_charcnt);
207 if (sp->leapcnt > TZ_MAX_LEAPS ||
208 sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES ||
209 sp->timecnt > TZ_MAX_TIMES ||
210 sp->charcnt > TZ_MAX_CHARS ||
211 (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
212 (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
213 return 1;
214 if ((uint)(read_from_file - (p - u.buf)) <
215 sp->timecnt * 4 + /* ats */
216 sp->timecnt + /* types */
217 sp->typecnt * (4 + 2) + /* ttinfos */
218 sp->charcnt + /* chars */
219 sp->leapcnt * (4 + 4) + /* lsinfos */
220 ttisstdcnt + /* ttisstds */
221 ttisgmtcnt) /* ttisgmts */
222 return 1;
223
224 if (!(tzinfo_buf= (char *)alloc_root(storage,
225 ALIGN_SIZE(sp->timecnt *
226 sizeof(my_time_t)) +
227 ALIGN_SIZE(sp->timecnt) +
228 ALIGN_SIZE(sp->typecnt *
229 sizeof(TRAN_TYPE_INFO)) +
230 #ifdef ABBR_ARE_USED
231 ALIGN_SIZE(sp->charcnt) +
232 #endif
233 sp->leapcnt * sizeof(LS_INFO))))
234 return 1;
235
236 sp->ats= (my_time_t *)tzinfo_buf;
237 tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
238 sp->types= (uchar *)tzinfo_buf;
239 tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
240 sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
241 tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
242 #ifdef ABBR_ARE_USED
243 sp->chars= tzinfo_buf;
244 tzinfo_buf+= ALIGN_SIZE(sp->charcnt);
245 #endif
246 sp->lsis= (LS_INFO *)tzinfo_buf;
247
248 for (i= 0; i < sp->timecnt; i++, p+= 4)
249 sp->ats[i]= int4net(p);
250
251 for (i= 0; i < sp->timecnt; i++)
252 {
253 sp->types[i]= (uchar) *p++;
254 if (sp->types[i] >= sp->typecnt)
255 return 1;
256 }
257 for (i= 0; i < sp->typecnt; i++)
258 {
259 TRAN_TYPE_INFO * ttisp;
260
261 ttisp= &sp->ttis[i];
262 ttisp->tt_gmtoff= int4net(p);
263 p+= 4;
264 ttisp->tt_isdst= (uchar) *p++;
265 if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
266 return 1;
267 ttisp->tt_abbrind= (uchar) *p++;
268 if (ttisp->tt_abbrind > sp->charcnt)
269 return 1;
270 }
271 for (i= 0; i < sp->charcnt; i++)
272 sp->chars[i]= *p++;
273 sp->chars[i]= '\0'; /* ensure '\0' at end */
274 for (i= 0; i < sp->leapcnt; i++)
275 {
276 LS_INFO *lsisp;
277
278 lsisp= &sp->lsis[i];
279 lsisp->ls_trans= int4net(p);
280 p+= 4;
281 lsisp->ls_corr= int4net(p);
282 p+= 4;
283 }
284 /*
285 Since we don't support POSIX style TZ definitions in variables we
286 don't read further like glibc or elsie code.
287 */
288 }
289
290 return prepare_tz_info(sp, storage);
291 }
292 #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
293
294
295 /*
296 Finish preparation of time zone description for use in TIME_to_gmt_sec()
297 and gmt_sec_to_TIME() functions.
298
299 SYNOPSIS
300 prepare_tz_info()
301 sp - pointer to time zone description
302 storage - pointer to MEM_ROOT where arrays for map allocated
303
304 DESCRIPTION
305 First task of this function is to find fallback time type which will
306 be used if there are no transitions or we have moment in time before
307 any transitions.
308 Second task is to build "shifted my_time_t" -> my_time_t map used in
309 MYSQL_TIME -> my_time_t conversion.
310 Note: See description of TIME_to_gmt_sec() function first.
311 In order to perform MYSQL_TIME -> my_time_t conversion we need to build table
312 which defines "shifted by tz offset and leap seconds my_time_t" ->
313 my_time_t function wich is almost the same (except ranges of ambiguity)
314 as reverse function to piecewise linear function used for my_time_t ->
315 "shifted my_time_t" conversion and which is also specified as table in
316 zoneinfo file or in our db (It is specified as start of time type ranges
317 and time type offsets). So basic idea is very simple - let us iterate
318 through my_time_t space from one point of discontinuity of my_time_t ->
319 "shifted my_time_t" function to another and build our approximation of
320 reverse function. (Actually we iterate through ranges on which
321 my_time_t -> "shifted my_time_t" is linear function).
322
323 RETURN VALUES
324 0 Ok
325 1 Error
326 */
327 static my_bool
prepare_tz_info(TIME_ZONE_INFO * sp,MEM_ROOT * storage)328 prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
329 {
330 my_time_t cur_t= MY_TIME_T_MIN;
331 my_time_t cur_l, end_t, end_l;
332 my_time_t cur_max_seen_l= MY_TIME_T_MIN;
333 long cur_offset, cur_corr, cur_off_and_corr;
334 uint next_trans_idx, next_leap_idx;
335 uint i;
336 /*
337 Temporary arrays where we will store tables. Needed because
338 we don't know table sizes ahead. (Well we can estimate their
339 upper bound but this will take extra space.)
340 */
341 my_time_t revts[TZ_MAX_REV_RANGES];
342 REVT_INFO revtis[TZ_MAX_REV_RANGES];
343
344 LINT_INIT(end_l);
345
346 /*
347 Let us setup fallback time type which will be used if we have not any
348 transitions or if we have moment of time before first transition.
349 We will find first non-DST local time type and use it (or use first
350 local time type if all of them are DST types).
351 */
352 for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
353 /* no-op */ ;
354 if (i == sp->typecnt)
355 i= 0;
356 sp->fallback_tti= &(sp->ttis[i]);
357
358
359 /*
360 Let us build shifted my_time_t -> my_time_t map.
361 */
362 sp->revcnt= 0;
363
364 /* Let us find initial offset */
365 if (sp->timecnt == 0 || cur_t < sp->ats[0])
366 {
367 /*
368 If we have not any transitions or t is before first transition we are using
369 already found fallback time type which index is already in i.
370 */
371 next_trans_idx= 0;
372 }
373 else
374 {
375 /* cur_t == sp->ats[0] so we found transition */
376 i= sp->types[0];
377 next_trans_idx= 1;
378 }
379
380 cur_offset= sp->ttis[i].tt_gmtoff;
381
382
383 /* let us find leap correction... unprobable, but... */
384 for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
385 cur_t >= sp->lsis[next_leap_idx].ls_trans;
386 ++next_leap_idx)
387 continue;
388
389 if (next_leap_idx > 0)
390 cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
391 else
392 cur_corr= 0;
393
394 /* Iterate trough t space */
395 while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
396 {
397 cur_off_and_corr= cur_offset - cur_corr;
398
399 /*
400 We assuming that cur_t could be only overflowed downwards,
401 we also assume that end_t won't be overflowed in this case.
402 */
403 if (cur_off_and_corr < 0 &&
404 cur_t < MY_TIME_T_MIN - cur_off_and_corr)
405 cur_t= MY_TIME_T_MIN - cur_off_and_corr;
406
407 cur_l= cur_t + cur_off_and_corr;
408
409 /*
410 Let us choose end_t as point before next time type change or leap
411 second correction.
412 */
413 end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
414 MY_TIME_T_MAX,
415 (next_leap_idx < sp->leapcnt) ?
416 sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
417 /*
418 again assuming that end_t can be overlowed only in positive side
419 we also assume that end_t won't be overflowed in this case.
420 */
421 if (cur_off_and_corr > 0 &&
422 end_t > MY_TIME_T_MAX - cur_off_and_corr)
423 end_t= MY_TIME_T_MAX - cur_off_and_corr;
424
425 end_l= end_t + cur_off_and_corr;
426
427
428 if (end_l > cur_max_seen_l)
429 {
430 /* We want special handling in the case of first range */
431 if (cur_max_seen_l == MY_TIME_T_MIN)
432 {
433 revts[sp->revcnt]= cur_l;
434 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
435 revtis[sp->revcnt].rt_type= 0;
436 sp->revcnt++;
437 cur_max_seen_l= end_l;
438 }
439 else
440 {
441 if (cur_l > cur_max_seen_l + 1)
442 {
443 /* We have a spring time-gap and we are not at the first range */
444 revts[sp->revcnt]= cur_max_seen_l + 1;
445 revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
446 revtis[sp->revcnt].rt_type= 1;
447 sp->revcnt++;
448 if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
449 break; /* That was too much */
450 cur_max_seen_l= cur_l - 1;
451 }
452
453 /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */
454
455 revts[sp->revcnt]= cur_max_seen_l + 1;
456 revtis[sp->revcnt].rt_offset= cur_off_and_corr;
457 revtis[sp->revcnt].rt_type= 0;
458 sp->revcnt++;
459 cur_max_seen_l= end_l;
460 }
461 }
462
463 if (end_t == MY_TIME_T_MAX ||
464 ((cur_off_and_corr > 0) &&
465 (end_t >= MY_TIME_T_MAX - cur_off_and_corr)))
466 /* end of t space */
467 break;
468
469 cur_t= end_t + 1;
470
471 /*
472 Let us find new offset and correction. Because of our choice of end_t
473 cur_t can only be point where new time type starts or/and leap
474 correction is performed.
475 */
476 if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
477 if (next_trans_idx < sp->timecnt &&
478 cur_t == sp->ats[next_trans_idx])
479 {
480 /* We are at offset point */
481 cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
482 ++next_trans_idx;
483 }
484
485 if (next_leap_idx < sp->leapcnt &&
486 cur_t == sp->lsis[next_leap_idx].ls_trans)
487 {
488 /* we are at leap point */
489 cur_corr= sp->lsis[next_leap_idx].ls_corr;
490 ++next_leap_idx;
491 }
492 }
493
494 /* check if we have had enough space */
495 if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
496 return 1;
497
498 /* set maximum end_l as finisher */
499 revts[sp->revcnt]= end_l;
500
501 /* Allocate arrays of proper size in sp and copy result there */
502 if (!(sp->revts= (my_time_t *)alloc_root(storage,
503 sizeof(my_time_t) * (sp->revcnt + 1))) ||
504 !(sp->revtis= (REVT_INFO *)alloc_root(storage,
505 sizeof(REVT_INFO) * sp->revcnt)))
506 return 1;
507
508 memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
509 memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
510
511 return 0;
512 }
513
514
515 #if !defined(TZINFO2SQL)
516
517 static const uint mon_lengths[2][MONS_PER_YEAR]=
518 {
519 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
520 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
521 };
522
523 static const uint mon_starts[2][MONS_PER_YEAR]=
524 {
525 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
526 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
527 };
528
529 static const uint year_lengths[2]=
530 {
531 DAYS_PER_NYEAR, DAYS_PER_LYEAR
532 };
533
534 #define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
535
536
537 /*
538 Converts time from my_time_t representation (seconds in UTC since Epoch)
539 to broken down representation using given local time zone offset.
540
541 SYNOPSIS
542 sec_to_TIME()
543 tmp - pointer to structure for broken down representation
544 t - my_time_t value to be converted
545 offset - local time zone offset
546
547 DESCRIPTION
548 Convert my_time_t with offset to MYSQL_TIME struct. Differs from timesub
549 (from elsie code) because doesn't contain any leap correction and
550 TM_GMTOFF and is_dst setting and contains some MySQL specific
551 initialization. Funny but with removing of these we almost have
552 glibc's offtime function.
553 */
554 static void
sec_to_TIME(MYSQL_TIME * tmp,my_time_t t,long offset)555 sec_to_TIME(MYSQL_TIME * tmp, my_time_t t, long offset)
556 {
557 long days;
558 long rem;
559 int y;
560 int yleap;
561 const uint *ip;
562
563 days= (long) (t / SECS_PER_DAY);
564 rem= (long) (t % SECS_PER_DAY);
565
566 /*
567 We do this as separate step after dividing t, because this
568 allows us handle times near my_time_t bounds without overflows.
569 */
570 rem+= offset;
571 while (rem < 0)
572 {
573 rem+= SECS_PER_DAY;
574 days--;
575 }
576 while (rem >= SECS_PER_DAY)
577 {
578 rem -= SECS_PER_DAY;
579 days++;
580 }
581 tmp->hour= (uint)(rem / SECS_PER_HOUR);
582 rem= rem % SECS_PER_HOUR;
583 tmp->minute= (uint)(rem / SECS_PER_MIN);
584 /*
585 A positive leap second requires a special
586 representation. This uses "... ??:59:60" et seq.
587 */
588 tmp->second= (uint)(rem % SECS_PER_MIN);
589
590 y= EPOCH_YEAR;
591 while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
592 {
593 int newy;
594
595 newy= y + days / DAYS_PER_NYEAR;
596 if (days < 0)
597 newy--;
598 days-= (newy - y) * DAYS_PER_NYEAR +
599 LEAPS_THRU_END_OF(newy - 1) -
600 LEAPS_THRU_END_OF(y - 1);
601 y= newy;
602 }
603 tmp->year= y;
604
605 ip= mon_lengths[yleap];
606 for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
607 days= days - (long) ip[tmp->month];
608 tmp->month++;
609 tmp->day= (uint)(days + 1);
610
611 /* filling MySQL specific MYSQL_TIME members */
612 tmp->neg= 0; tmp->second_part= 0;
613 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
614 }
615
616
617 /*
618 Find time range wich contains given my_time_t value
619
620 SYNOPSIS
621 find_time_range()
622 t - my_time_t value for which we looking for range
623 range_boundaries - sorted array of range starts.
624 higher_bound - number of ranges
625
626 DESCRIPTION
627 Performs binary search for range which contains given my_time_t value.
628 It has sense if number of ranges is greater than zero and my_time_t value
629 is greater or equal than beginning of first range. It also assumes that
630 t belongs to some range specified or end of last is MY_TIME_T_MAX.
631
632 With this localtime_r on real data may takes less time than with linear
633 search (I've seen 30% speed up).
634
635 RETURN VALUE
636 Index of range to which t belongs
637 */
638 static uint
find_time_range(my_time_t t,const my_time_t * range_boundaries,uint higher_bound)639 find_time_range(my_time_t t, const my_time_t *range_boundaries,
640 uint higher_bound)
641 {
642 uint i, lower_bound= 0;
643
644 /*
645 Function will work without this assertion but result would be meaningless.
646 */
647 DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
648
649 /*
650 Do binary search for minimal interval which contain t. We preserve:
651 range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
652 invariant and decrease this higher_bound - lower_bound gap twice
653 times on each step.
654 */
655
656 while (higher_bound - lower_bound > 1)
657 {
658 i= (lower_bound + higher_bound) >> 1;
659 if (range_boundaries[i] <= t)
660 lower_bound= i;
661 else
662 higher_bound= i;
663 }
664 return lower_bound;
665 }
666
667 /*
668 Find local time transition for given my_time_t.
669
670 SYNOPSIS
671 find_transition_type()
672 t - my_time_t value to be converted
673 sp - pointer to struct with time zone description
674
675 RETURN VALUE
676 Pointer to structure in time zone description describing
677 local time type for given my_time_t.
678 */
679 static
680 const TRAN_TYPE_INFO *
find_transition_type(my_time_t t,const TIME_ZONE_INFO * sp)681 find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
682 {
683 if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
684 {
685 /*
686 If we have not any transitions or t is before first transition let
687 us use fallback time type.
688 */
689 return sp->fallback_tti;
690 }
691
692 /*
693 Do binary search for minimal interval between transitions which
694 contain t. With this localtime_r on real data may takes less
695 time than with linear search (I've seen 30% speed up).
696 */
697 return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
698 }
699
700
701 /*
702 Converts time in my_time_t representation (seconds in UTC since Epoch) to
703 broken down MYSQL_TIME representation in local time zone.
704
705 SYNOPSIS
706 gmt_sec_to_TIME()
707 tmp - pointer to structure for broken down represenatation
708 sec_in_utc - my_time_t value to be converted
709 sp - pointer to struct with time zone description
710
711 TODO
712 We can improve this function by creating joined array of transitions and
713 leap corrections. This will require adding extra field to TRAN_TYPE_INFO
714 for storing number of "extra" seconds to minute occured due to correction
715 (60th and 61st second, look how we calculate them as "hit" in this
716 function).
717 Under realistic assumptions about frequency of transitions the same array
718 can be used fot MYSQL_TIME -> my_time_t conversion. For this we need to
719 implement tweaked binary search which will take into account that some
720 MYSQL_TIME has two matching my_time_t ranges and some of them have none.
721 */
722 static void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t sec_in_utc,const TIME_ZONE_INFO * sp)723 gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
724 {
725 const TRAN_TYPE_INFO *ttisp;
726 const LS_INFO *lp;
727 long corr= 0;
728 int hit= 0;
729 int i;
730
731 /*
732 Find proper transition (and its local time type) for our sec_in_utc value.
733 Funny but again by separating this step in function we receive code
734 which very close to glibc's code. No wonder since they obviously use
735 the same base and all steps are sensible.
736 */
737 ttisp= find_transition_type(sec_in_utc, sp);
738
739 /*
740 Let us find leap correction for our sec_in_utc value and number of extra
741 secs to add to this minute.
742 This loop is rarely used because most users will use time zones without
743 leap seconds, and even in case when we have such time zone there won't
744 be many iterations (we have about 22 corrections at this moment (2004)).
745 */
746 for ( i= sp->leapcnt; i-- > 0; )
747 {
748 lp= &sp->lsis[i];
749 if (sec_in_utc >= lp->ls_trans)
750 {
751 if (sec_in_utc == lp->ls_trans)
752 {
753 hit= ((i == 0 && lp->ls_corr > 0) ||
754 lp->ls_corr > sp->lsis[i - 1].ls_corr);
755 if (hit)
756 {
757 while (i > 0 &&
758 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
759 sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
760 {
761 hit++;
762 i--;
763 }
764 }
765 }
766 corr= lp->ls_corr;
767 break;
768 }
769 }
770
771 sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
772
773 tmp->second+= hit;
774 }
775
776
777 /*
778 Converts local time in broken down representation to local
779 time zone analog of my_time_t represenation.
780
781 SYNOPSIS
782 sec_since_epoch()
783 year, mon, mday, hour, min, sec - broken down representation.
784
785 DESCRIPTION
786 Converts time in broken down representation to my_time_t representation
787 ignoring time zone. Note that we cannot convert back some valid _local_
788 times near ends of my_time_t range because of my_time_t overflow. But we
789 ignore this fact now since MySQL will never pass such argument.
790
791 RETURN VALUE
792 Seconds since epoch time representation.
793 */
794 static my_time_t
sec_since_epoch(int year,int mon,int mday,int hour,int min,int sec)795 sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
796 {
797 /* Guard against my_time_t overflow(on system with 32 bit my_time_t) */
798 DBUG_ASSERT(!(year == TIMESTAMP_MAX_YEAR && mon == 1 && mday > 17));
799 #ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
800 /*
801 It turns out that only whenever month is normalized or unnormalized
802 plays role.
803 */
804 DBUG_ASSERT(mon > 0 && mon < 13);
805 long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
806 LEAPS_THRU_END_OF(year - 1) -
807 LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
808 days+= mon_starts[isleap(year)][mon - 1];
809 #else
810 long norm_month= (mon - 1) % MONS_PER_YEAR;
811 long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
812 long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
813 LEAPS_THRU_END_OF(a_year - 1) -
814 LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
815 days+= mon_starts[isleap(a_year)]
816 [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
817 #endif
818 days+= mday - 1;
819
820 return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
821 SECS_PER_MIN + sec;
822 }
823
824 /*
825 Converts local time in broken down MYSQL_TIME representation to my_time_t
826 representation.
827
828 SYNOPSIS
829 TIME_to_gmt_sec()
830 t - pointer to structure for broken down represenatation
831 sp - pointer to struct with time zone description
832 in_dst_time_gap - pointer to bool which is set to true if datetime
833 value passed doesn't really exist (i.e. falls into
834 spring time-gap) and is not touched otherwise.
835
836 DESCRIPTION
837 This is mktime analog for MySQL. It is essentially different
838 from mktime (or hypotetical my_mktime) because:
839 - It has no idea about tm_isdst member so if it
840 has two answers it will give the smaller one
841 - If we are in spring time gap then it will return
842 beginning of the gap
843 - It can give wrong results near the ends of my_time_t due to
844 overflows, but we are safe since in MySQL we will never
845 call this function for such dates (its restriction for year
846 between 1970 and 2038 gives us several days of reserve).
847 - By default it doesn't support un-normalized input. But if
848 sec_since_epoch() function supports un-normalized dates
849 then this function should handle un-normalized input right,
850 altough it won't normalize structure TIME.
851
852 Traditional approach to problem of conversion from broken down
853 representation to time_t is iterative. Both elsie's and glibc
854 implementation try to guess what time_t value should correspond to
855 this broken-down value. They perform localtime_r function on their
856 guessed value and then calculate the difference and try to improve
857 their guess. Elsie's code guesses time_t value in bit by bit manner,
858 Glibc's code tries to add difference between broken-down value
859 corresponding to guess and target broken-down value to current guess.
860 It also uses caching of last found correction... So Glibc's approach
861 is essentially faster but introduces some undetermenism (in case if
862 is_dst member of broken-down representation (tm struct) is not known
863 and we have two possible answers).
864
865 We use completely different approach. It is better since it is both
866 faster than iterative implementations and fully determenistic. If you
867 look at my_time_t to MYSQL_TIME conversion then you'll find that it consist
868 of two steps:
869 The first is calculating shifted my_time_t value and the second - TIME
870 calculation from shifted my_time_t value (well it is a bit simplified
871 picture). The part in which we are interested in is my_time_t -> shifted
872 my_time_t conversion. It is piecewise linear function which is defined
873 by combination of transition times as break points and times offset
874 as changing function parameter. The possible inverse function for this
875 converison would be ambiguos but with MySQL's restrictions we can use
876 some function which is the same as inverse function on unambigiuos
877 ranges and coincides with one of branches of inverse function in
878 other ranges. Thus we just need to build table which will determine
879 this shifted my_time_t -> my_time_t conversion similar to existing
880 (my_time_t -> shifted my_time_t table). We do this in
881 prepare_tz_info function.
882
883 TODO
884 If we can even more improve this function. For doing this we will need to
885 build joined map of transitions and leap corrections for gmt_sec_to_TIME()
886 function (similar to revts/revtis). Under realistic assumptions about
887 frequency of transitions we can use the same array for TIME_to_gmt_sec().
888 We need to implement special version of binary search for this. Such step
889 will be beneficial to CPU cache since we will decrease data-set used for
890 conversion twice.
891
892 RETURN VALUE
893 Seconds in UTC since Epoch.
894 0 in case of error.
895 */
896 static my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,const TIME_ZONE_INFO * sp,my_bool * in_dst_time_gap)897 TIME_to_gmt_sec(const MYSQL_TIME *t, const TIME_ZONE_INFO *sp,
898 my_bool *in_dst_time_gap)
899 {
900 my_time_t local_t;
901 uint saved_seconds;
902 uint i;
903 int shift= 0;
904
905 DBUG_ENTER("TIME_to_gmt_sec");
906
907 if (!validate_timestamp_range(t))
908 DBUG_RETURN(0);
909
910
911 /* We need this for correct leap seconds handling */
912 if (t->second < SECS_PER_MIN)
913 saved_seconds= 0;
914 else
915 saved_seconds= t->second;
916
917 /*
918 NOTE: to convert full my_time_t range we do a shift of the
919 boundary dates here to avoid overflow of my_time_t.
920 We use alike approach in my_system_gmt_sec().
921
922 However in that function we also have to take into account
923 overflow near 0 on some platforms. That's because my_system_gmt_sec
924 uses localtime_r(), which doesn't work with negative values correctly
925 on platforms with unsigned time_t (QNX). Here we don't use localtime()
926 => we negative values of local_t are ok.
927 */
928
929 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
930 {
931 /*
932 We will pass (t->day - shift) to sec_since_epoch(), and
933 want this value to be a positive number, so we shift
934 only dates > 4.01.2038 (to avoid owerflow).
935 */
936 shift= 2;
937 }
938
939
940 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
941 t->hour, t->minute,
942 saved_seconds ? 0 : t->second);
943
944 /* We have at least one range */
945 DBUG_ASSERT(sp->revcnt >= 1);
946
947 if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
948 {
949 /*
950 This means that source time can't be represented as my_time_t due to
951 limited my_time_t range.
952 */
953 DBUG_RETURN(0);
954 }
955
956 /* binary search for our range */
957 i= find_time_range(local_t, sp->revts, sp->revcnt);
958
959 /*
960 As there are no offset switches at the end of TIMESTAMP range,
961 we could simply check for overflow here (and don't need to bother
962 about DST gaps etc)
963 */
964 if (shift)
965 {
966 if (local_t > (my_time_t) (TIMESTAMP_MAX_VALUE - shift * SECS_PER_DAY +
967 sp->revtis[i].rt_offset - saved_seconds))
968 {
969 DBUG_RETURN(0); /* my_time_t overflow */
970 }
971 local_t+= shift * SECS_PER_DAY;
972 }
973
974 if (sp->revtis[i].rt_type)
975 {
976 /*
977 Oops! We are in spring time gap.
978 May be we should return error here?
979 Now we are returning my_time_t value corresponding to the
980 beginning of the gap.
981 */
982 *in_dst_time_gap= 1;
983 local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
984 }
985 else
986 local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
987
988 /* check for TIMESTAMP_MAX_VALUE was already done above */
989 if (local_t < TIMESTAMP_MIN_VALUE)
990 local_t= 0;
991
992 DBUG_RETURN(local_t);
993 }
994
995
996 /*
997 End of elsie derived code.
998 */
999 #endif /* !defined(TZINFO2SQL) */
1000
1001
1002 #if !defined(TESTTIME) && !defined(TZINFO2SQL)
1003
1004 /*
1005 String with names of SYSTEM time zone.
1006 */
1007 static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
1008
1009
1010 /*
1011 Instance of this class represents local time zone used on this system
1012 (specified by TZ environment variable or via any other system mechanism).
1013 It uses system functions (localtime_r, my_system_gmt_sec) for conversion
1014 and is always available. Because of this it is used by default - if there
1015 were no explicit time zone specified. On the other hand because of this
1016 conversion methods provided by this class is significantly slower and
1017 possibly less multi-threaded-friendly than corresponding Time_zone_db
1018 methods so the latter should be preffered there it is possible.
1019 */
1020 class Time_zone_system : public Time_zone
1021 {
1022 public:
Time_zone_system()1023 Time_zone_system() {} /* Remove gcc warning */
1024 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1025 my_bool *in_dst_time_gap) const;
1026 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1027 virtual const String * get_name() const;
1028 };
1029
1030
1031 /*
1032 Converts local time in system time zone in MYSQL_TIME representation
1033 to its my_time_t representation.
1034
1035 SYNOPSIS
1036 TIME_to_gmt_sec()
1037 t - pointer to MYSQL_TIME structure with local time in
1038 broken-down representation.
1039 in_dst_time_gap - pointer to bool which is set to true if datetime
1040 value passed doesn't really exist (i.e. falls into
1041 spring time-gap) and is not touched otherwise.
1042
1043 DESCRIPTION
1044 This method uses system function (localtime_r()) for conversion
1045 local time in system time zone in MYSQL_TIME structure to its my_time_t
1046 representation. Unlike the same function for Time_zone_db class
1047 it it won't handle unnormalized input properly. Still it will
1048 return lowest possible my_time_t in case of ambiguity or if we
1049 provide time corresponding to the time-gap.
1050
1051 You should call my_init_time() function before using this function.
1052
1053 RETURN VALUE
1054 Corresponding my_time_t value or 0 in case of error
1055 */
1056 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1057 Time_zone_system::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1058 {
1059 long not_used;
1060 return my_system_gmt_sec(t, ¬_used, in_dst_time_gap);
1061 }
1062
1063
1064 /*
1065 Converts time from UTC seconds since Epoch (my_time_t) representation
1066 to system local time zone broken-down representation.
1067
1068 SYNOPSIS
1069 gmt_sec_to_TIME()
1070 tmp - pointer to MYSQL_TIME structure to fill-in
1071 t - my_time_t value to be converted
1072
1073 NOTE
1074 We assume that value passed to this function will fit into time_t range
1075 supported by localtime_r. This conversion is putting restriction on
1076 TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
1077 for interaction with client then we can extend TIMESTAMP range down to
1078 the 1902 easily.
1079 */
1080 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1081 Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1082 {
1083 struct tm tmp_tm;
1084 time_t tmp_t= (time_t)t;
1085
1086 localtime_r(&tmp_t, &tmp_tm);
1087 localtime_to_TIME(tmp, &tmp_tm);
1088 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1089 adjust_leap_second(tmp);
1090 }
1091
1092
1093 /*
1094 Get name of time zone
1095
1096 SYNOPSIS
1097 get_name()
1098
1099 RETURN VALUE
1100 Name of time zone as String
1101 */
1102 const String *
get_name() const1103 Time_zone_system::get_name() const
1104 {
1105 return &tz_SYSTEM_name;
1106 }
1107
1108
1109 /*
1110 Instance of this class represents UTC time zone. It uses system gmtime_r
1111 function for conversions and is always available. It is used only for
1112 my_time_t -> MYSQL_TIME conversions in various UTC_... functions, it is not
1113 intended for MYSQL_TIME -> my_time_t conversions and shouldn't be exposed to user.
1114 */
1115 class Time_zone_utc : public Time_zone
1116 {
1117 public:
Time_zone_utc()1118 Time_zone_utc() {} /* Remove gcc warning */
1119 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1120 my_bool *in_dst_time_gap) const;
1121 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1122 virtual const String * get_name() const;
1123 };
1124
1125
1126 /*
1127 Convert UTC time from MYSQL_TIME representation to its my_time_t representation.
1128
1129 SYNOPSIS
1130 TIME_to_gmt_sec()
1131 t - pointer to MYSQL_TIME structure with local time
1132 in broken-down representation.
1133 in_dst_time_gap - pointer to bool which is set to true if datetime
1134 value passed doesn't really exist (i.e. falls into
1135 spring time-gap) and is not touched otherwise.
1136
1137 DESCRIPTION
1138 Since Time_zone_utc is used only internally for my_time_t -> TIME
1139 conversions, this function of Time_zone interface is not implemented for
1140 this class and should not be called.
1141
1142 RETURN VALUE
1143 0
1144 */
1145 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1146 Time_zone_utc::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1147 {
1148 /* Should be never called */
1149 DBUG_ASSERT(0);
1150 return 0;
1151 }
1152
1153
1154 /*
1155 Converts time from UTC seconds since Epoch (my_time_t) representation
1156 to broken-down representation (also in UTC).
1157
1158 SYNOPSIS
1159 gmt_sec_to_TIME()
1160 tmp - pointer to MYSQL_TIME structure to fill-in
1161 t - my_time_t value to be converted
1162
1163 NOTE
1164 See note for apropriate Time_zone_system method.
1165 */
1166 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1167 Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1168 {
1169 struct tm tmp_tm;
1170 time_t tmp_t= (time_t)t;
1171 gmtime_r(&tmp_t, &tmp_tm);
1172 localtime_to_TIME(tmp, &tmp_tm);
1173 tmp->time_type= MYSQL_TIMESTAMP_DATETIME;
1174 adjust_leap_second(tmp);
1175 }
1176
1177
1178 /*
1179 Get name of time zone
1180
1181 SYNOPSIS
1182 get_name()
1183
1184 DESCRIPTION
1185 Since Time_zone_utc is used only internally by SQL's UTC_* functions it
1186 is not accessible directly, and hence this function of Time_zone
1187 interface is not implemented for this class and should not be called.
1188
1189 RETURN VALUE
1190 0
1191 */
1192 const String *
get_name() const1193 Time_zone_utc::get_name() const
1194 {
1195 /* Should be never called */
1196 DBUG_ASSERT(0);
1197 return 0;
1198 }
1199
1200
1201 /*
1202 Instance of this class represents some time zone which is
1203 described in mysql.time_zone family of tables.
1204 */
1205 class Time_zone_db : public Time_zone
1206 {
1207 public:
1208 Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
1209 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1210 my_bool *in_dst_time_gap) const;
1211 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1212 virtual const String * get_name() const;
1213 private:
1214 TIME_ZONE_INFO *tz_info;
1215 const String *tz_name;
1216 };
1217
1218
1219 /*
1220 Initializes object representing time zone described by mysql.time_zone
1221 tables.
1222
1223 SYNOPSIS
1224 Time_zone_db()
1225 tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
1226 according to db or other time zone description
1227 (for example by my_tz_init()).
1228 Several Time_zone_db instances can share one
1229 TIME_ZONE_INFO structure.
1230 tz_name_arg - name of time zone.
1231 */
Time_zone_db(TIME_ZONE_INFO * tz_info_arg,const String * tz_name_arg)1232 Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
1233 const String *tz_name_arg):
1234 tz_info(tz_info_arg), tz_name(tz_name_arg)
1235 {
1236 }
1237
1238
1239 /*
1240 Converts local time in time zone described from TIME
1241 representation to its my_time_t representation.
1242
1243 SYNOPSIS
1244 TIME_to_gmt_sec()
1245 t - pointer to MYSQL_TIME structure with local time
1246 in broken-down representation.
1247 in_dst_time_gap - pointer to bool which is set to true if datetime
1248 value passed doesn't really exist (i.e. falls into
1249 spring time-gap) and is not touched otherwise.
1250
1251 DESCRIPTION
1252 Please see ::TIME_to_gmt_sec for function description and
1253 parameter restrictions.
1254
1255 RETURN VALUE
1256 Corresponding my_time_t value or 0 in case of error
1257 */
1258 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1259 Time_zone_db::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1260 {
1261 return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
1262 }
1263
1264
1265 /*
1266 Converts time from UTC seconds since Epoch (my_time_t) representation
1267 to local time zone described in broken-down representation.
1268
1269 SYNOPSIS
1270 gmt_sec_to_TIME()
1271 tmp - pointer to MYSQL_TIME structure to fill-in
1272 t - my_time_t value to be converted
1273 */
1274 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1275 Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1276 {
1277 ::gmt_sec_to_TIME(tmp, t, tz_info);
1278 adjust_leap_second(tmp);
1279 }
1280
1281
1282 /*
1283 Get name of time zone
1284
1285 SYNOPSIS
1286 get_name()
1287
1288 RETURN VALUE
1289 Name of time zone as ASCIIZ-string
1290 */
1291 const String *
get_name() const1292 Time_zone_db::get_name() const
1293 {
1294 return tz_name;
1295 }
1296
1297
1298 /*
1299 Instance of this class represents time zone which
1300 was specified as offset from UTC.
1301 */
1302 class Time_zone_offset : public Time_zone
1303 {
1304 public:
1305 Time_zone_offset(long tz_offset_arg);
1306 virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t,
1307 my_bool *in_dst_time_gap) const;
1308 virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
1309 virtual const String * get_name() const;
1310 /*
1311 This have to be public because we want to be able to access it from
1312 my_offset_tzs_get_key() function
1313 */
1314 long offset;
1315 private:
1316 /* Extra reserve because of snprintf */
1317 char name_buff[7+16];
1318 String name;
1319 };
1320
1321
1322 /*
1323 Initializes object representing time zone described by its offset from UTC.
1324
1325 SYNOPSIS
1326 Time_zone_offset()
1327 tz_offset_arg - offset from UTC in seconds.
1328 Positive for direction to east.
1329 */
Time_zone_offset(long tz_offset_arg)1330 Time_zone_offset::Time_zone_offset(long tz_offset_arg):
1331 offset(tz_offset_arg)
1332 {
1333 uint hours= abs((int)(offset / SECS_PER_HOUR));
1334 uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN));
1335 ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
1336 (offset>=0) ? "+" : "-", hours, minutes);
1337 name.set(name_buff, length, &my_charset_latin1);
1338 }
1339
1340
1341 /*
1342 Converts local time in time zone described as offset from UTC
1343 from MYSQL_TIME representation to its my_time_t representation.
1344
1345 SYNOPSIS
1346 TIME_to_gmt_sec()
1347 t - pointer to MYSQL_TIME structure with local time
1348 in broken-down representation.
1349 in_dst_time_gap - pointer to bool which should be set to true if
1350 datetime value passed doesn't really exist
1351 (i.e. falls into spring time-gap) and is not
1352 touched otherwise.
1353 It is not really used in this class.
1354
1355 RETURN VALUE
1356 Corresponding my_time_t value or 0 in case of error
1357 */
1358 my_time_t
TIME_to_gmt_sec(const MYSQL_TIME * t,my_bool * in_dst_time_gap) const1359 Time_zone_offset::TIME_to_gmt_sec(const MYSQL_TIME *t, my_bool *in_dst_time_gap) const
1360 {
1361 my_time_t local_t;
1362 int shift= 0;
1363
1364 /*
1365 Check timestamp range.we have to do this as calling function relies on
1366 us to make all validation checks here.
1367 */
1368 if (!validate_timestamp_range(t))
1369 return 0;
1370
1371 /*
1372 Do a temporary shift of the boundary dates to avoid
1373 overflow of my_time_t if the time value is near it's
1374 maximum range
1375 */
1376 if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
1377 shift= 2;
1378
1379 local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
1380 t->hour, t->minute, t->second) -
1381 offset;
1382
1383 if (shift)
1384 {
1385 /* Add back the shifted time */
1386 local_t+= shift * SECS_PER_DAY;
1387 }
1388
1389 if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
1390 return local_t;
1391
1392 /* range error*/
1393 return 0;
1394 }
1395
1396
1397 /*
1398 Converts time from UTC seconds since Epoch (my_time_t) representation
1399 to local time zone described as offset from UTC and in broken-down
1400 representation.
1401
1402 SYNOPSIS
1403 gmt_sec_to_TIME()
1404 tmp - pointer to MYSQL_TIME structure to fill-in
1405 t - my_time_t value to be converted
1406 */
1407 void
gmt_sec_to_TIME(MYSQL_TIME * tmp,my_time_t t) const1408 Time_zone_offset::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
1409 {
1410 sec_to_TIME(tmp, t, offset);
1411 }
1412
1413
1414 /*
1415 Get name of time zone
1416
1417 SYNOPSIS
1418 get_name()
1419
1420 RETURN VALUE
1421 Name of time zone as pointer to String object
1422 */
1423 const String *
get_name() const1424 Time_zone_offset::get_name() const
1425 {
1426 return &name;
1427 }
1428
1429
1430 static Time_zone_utc tz_UTC;
1431 static Time_zone_system tz_SYSTEM;
1432 static Time_zone_offset tz_OFFSET0(0);
1433
1434 Time_zone *my_tz_OFFSET0= &tz_OFFSET0;
1435 Time_zone *my_tz_UTC= &tz_UTC;
1436 Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
1437
1438 static HASH tz_names;
1439 static HASH offset_tzs;
1440 static MEM_ROOT tz_storage;
1441
1442 /*
1443 These mutex protects offset_tzs and tz_storage.
1444 These protection needed only when we are trying to set
1445 time zone which is specified as offset, and searching for existing
1446 time zone in offset_tzs or creating if it didn't existed before in
1447 tz_storage. So contention is low.
1448 */
1449 static mysql_mutex_t tz_LOCK;
1450 static bool tz_inited= 0;
1451
1452 /*
1453 This two static variables are inteded for holding info about leap seconds
1454 shared by all time zones.
1455 */
1456 static uint tz_leapcnt= 0;
1457 static LS_INFO *tz_lsis= 0;
1458
1459 /*
1460 Shows whenever we have found time zone tables during start-up.
1461 Used for avoiding of putting those tables to global table list
1462 for queries that use time zone info.
1463 */
1464 static bool time_zone_tables_exist= 1;
1465
1466
1467 /*
1468 Names of tables (with their lengths) that are needed
1469 for dynamical loading of time zone descriptions.
1470 */
1471
1472 static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]=
1473 {
1474 { C_STRING_WITH_LEN("time_zone_name")},
1475 { C_STRING_WITH_LEN("time_zone")},
1476 { C_STRING_WITH_LEN("time_zone_transition_type")},
1477 { C_STRING_WITH_LEN("time_zone_transition")}
1478 };
1479
1480 /* Name of database to which those tables belong. */
1481
1482 static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")};
1483
1484
1485 class Tz_names_entry: public Sql_alloc
1486 {
1487 public:
1488 String name;
1489 Time_zone *tz;
1490 };
1491
1492
1493 /*
1494 We are going to call both of these functions from C code so
1495 they should obey C calling conventions.
1496 */
1497
1498 extern "C" uchar *
my_tz_names_get_key(Tz_names_entry * entry,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1499 my_tz_names_get_key(Tz_names_entry *entry, size_t *length,
1500 my_bool not_used MY_ATTRIBUTE((unused)))
1501 {
1502 *length= entry->name.length();
1503 return (uchar*) entry->name.ptr();
1504 }
1505
1506 extern "C" uchar *
my_offset_tzs_get_key(Time_zone_offset * entry,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1507 my_offset_tzs_get_key(Time_zone_offset *entry,
1508 size_t *length,
1509 my_bool not_used MY_ATTRIBUTE((unused)))
1510 {
1511 *length= sizeof(long);
1512 return (uchar*) &entry->offset;
1513 }
1514
1515
1516 /*
1517 Prepare table list with time zone related tables from preallocated array.
1518
1519 SYNOPSIS
1520 tz_init_table_list()
1521 tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT
1522 TABLE_LIST objects
1523
1524 DESCRIPTION
1525 This function prepares list of TABLE_LIST objects which can be used
1526 for opening of time zone tables from preallocated array.
1527 */
1528
1529 static void
tz_init_table_list(TABLE_LIST * tz_tabs)1530 tz_init_table_list(TABLE_LIST *tz_tabs)
1531 {
1532 memset(static_cast<void*>(tz_tabs), 0,
1533 sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT);
1534
1535 for (int i= 0; i < MY_TZ_TABLES_COUNT; i++)
1536 {
1537 tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str;
1538 tz_tabs[i].table_name_length= tz_tables_names[i].length;
1539 tz_tabs[i].db= tz_tables_db_name.str;
1540 tz_tabs[i].db_length= tz_tables_db_name.length;
1541 tz_tabs[i].lock_type= TL_READ;
1542
1543 if (i != MY_TZ_TABLES_COUNT - 1)
1544 tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1];
1545 if (i != 0)
1546 tz_tabs[i].prev_global= &tz_tabs[i-1].next_global;
1547 }
1548 }
1549
1550 #ifdef HAVE_PSI_INTERFACE
1551 static PSI_mutex_key key_tz_LOCK;
1552
1553 static PSI_mutex_info all_tz_mutexes[]=
1554 {
1555 { & key_tz_LOCK, "tz_LOCK", PSI_FLAG_GLOBAL}
1556 };
1557
init_tz_psi_keys(void)1558 static void init_tz_psi_keys(void)
1559 {
1560 const char* category= "sql";
1561 int count;
1562
1563 count= array_elements(all_tz_mutexes);
1564 mysql_mutex_register(category, all_tz_mutexes, count);
1565 }
1566 #endif /* HAVE_PSI_INTERFACE */
1567
1568
1569 /*
1570 Initialize time zone support infrastructure.
1571
1572 SYNOPSIS
1573 my_tz_init()
1574 thd - current thread object
1575 default_tzname - default time zone or 0 if none.
1576 bootstrap - indicates whenever we are in bootstrap mode
1577
1578 DESCRIPTION
1579 This function will init memory structures needed for time zone support,
1580 it will register mandatory SYSTEM time zone in them. It will try to open
1581 mysql.time_zone* tables and load information about default time zone and
1582 information which further will be shared among all time zones loaded.
1583 If system tables with time zone descriptions don't exist it won't fail
1584 (unless default_tzname is time zone from tables). If bootstrap parameter
1585 is true then this routine assumes that we are in bootstrap mode and won't
1586 load time zone descriptions unless someone specifies default time zone
1587 which is supposedly stored in those tables.
1588 It'll also set default time zone if it is specified.
1589
1590 RETURN VALUES
1591 0 - ok
1592 1 - Error
1593 */
1594 my_bool
my_tz_init(THD * org_thd,const char * default_tzname,my_bool bootstrap)1595 my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
1596 {
1597 THD *thd;
1598 TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
1599 TABLE *table;
1600 Tz_names_entry *tmp_tzname;
1601 my_bool return_val= 1;
1602 char db[]= "mysql";
1603 int res;
1604 DBUG_ENTER("my_tz_init");
1605
1606 #ifdef HAVE_PSI_INTERFACE
1607 init_tz_psi_keys();
1608 #endif
1609
1610 /*
1611 To be able to run this from boot, we allocate a temporary THD
1612 */
1613 if (!(thd= new THD))
1614 DBUG_RETURN(1);
1615 thd->thread_stack= (char*) &thd;
1616 thd->store_globals();
1617
1618 /* Init all memory structures that require explicit destruction */
1619 if (my_hash_init(&tz_names, &my_charset_latin1, 20,
1620 0, 0, (my_hash_get_key) my_tz_names_get_key, 0, 0))
1621 {
1622 sql_print_error("Fatal error: OOM while initializing time zones");
1623 goto end;
1624 }
1625 if (my_hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
1626 (my_hash_get_key)my_offset_tzs_get_key, 0, 0))
1627 {
1628 sql_print_error("Fatal error: OOM while initializing time zones");
1629 my_hash_free(&tz_names);
1630 goto end;
1631 }
1632 init_sql_alloc(&tz_storage, 32 * 1024, 0);
1633 mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
1634 tz_inited= 1;
1635
1636 /* Add 'SYSTEM' time zone to tz_names hash */
1637 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()))
1638 {
1639 sql_print_error("Fatal error: OOM while initializing time zones");
1640 goto end_with_cleanup;
1641 }
1642 tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1);
1643 tmp_tzname->tz= my_tz_SYSTEM;
1644 if (my_hash_insert(&tz_names, (const uchar *)tmp_tzname))
1645 {
1646 sql_print_error("Fatal error: OOM while initializing time zones");
1647 goto end_with_cleanup;
1648 }
1649
1650 if (bootstrap)
1651 {
1652 /* If we are in bootstrap mode we should not load time zone tables */
1653 return_val= time_zone_tables_exist= 0;
1654 goto end_with_setting_default_tz;
1655 }
1656
1657 /*
1658 After this point all memory structures are inited and we even can live
1659 without time zone description tables. Now try to load information about
1660 leap seconds shared by all time zones.
1661 */
1662
1663 thd->set_db(db, sizeof(db)-1);
1664 memset(static_cast<void*>(&tz_tables[0]), 0, sizeof(TABLE_LIST));
1665 tz_tables[0].alias= tz_tables[0].table_name=
1666 (char*)"time_zone_leap_second";
1667 tz_tables[0].table_name_length= 21;
1668 tz_tables[0].db= db;
1669 tz_tables[0].db_length= sizeof(db)-1;
1670 tz_tables[0].lock_type= TL_READ;
1671
1672 tz_init_table_list(tz_tables+1);
1673 tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
1674 tz_tables[1].prev_global= &tz_tables[0].next_global;
1675 init_mdl_requests(tz_tables);
1676
1677 /*
1678 We need to open only mysql.time_zone_leap_second, but we try to
1679 open all time zone tables to see if they exist.
1680 */
1681 if (open_and_lock_tables(thd, tz_tables, FALSE,
1682 MYSQL_OPEN_IGNORE_FLUSH | MYSQL_LOCK_IGNORE_TIMEOUT))
1683 {
1684 sql_print_warning("Can't open and lock time zone table: %s "
1685 "trying to live without them", thd->get_stmt_da()->message());
1686 /* We will try emulate that everything is ok */
1687 return_val= time_zone_tables_exist= 0;
1688 goto end_with_setting_default_tz;
1689 }
1690
1691 for (TABLE_LIST *tl= tz_tables; tl; tl= tl->next_global)
1692 {
1693 tl->table->use_all_columns();
1694 /* Force close at the end of the function to free memory. */
1695 tl->table->m_needs_reopen= TRUE;
1696 }
1697
1698 /*
1699 Now we are going to load leap seconds descriptions that are shared
1700 between all time zones that use them. We are using index for getting
1701 records in proper order. Since we share the same MEM_ROOT between
1702 all time zones we just allocate enough memory for it first.
1703 */
1704 if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
1705 sizeof(LS_INFO) * TZ_MAX_LEAPS)))
1706 {
1707 sql_print_error("Fatal error: Out of memory while loading "
1708 "mysql.time_zone_leap_second table");
1709 goto end_with_close;
1710 }
1711
1712 table= tz_tables[0].table;
1713
1714 if (table->file->ha_index_init(0, 1))
1715 goto end_with_close;
1716 table->use_all_columns();
1717
1718 tz_leapcnt= 0;
1719
1720 res= table->file->ha_index_first(table->record[0]);
1721
1722 while (!res)
1723 {
1724 if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
1725 {
1726 sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
1727 " table: too much leaps");
1728 table->file->ha_index_end();
1729 goto end_with_close;
1730 }
1731
1732 tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
1733 tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();
1734
1735 tz_leapcnt++;
1736
1737 DBUG_PRINT("info",
1738 ("time_zone_leap_second table: tz_leapcnt: %u tt_time: %lu offset: %ld",
1739 tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans,
1740 tz_lsis[tz_leapcnt-1].ls_corr));
1741
1742 res= table->file->ha_index_next(table->record[0]);
1743 }
1744
1745 (void)table->file->ha_index_end();
1746
1747 if (res != HA_ERR_END_OF_FILE)
1748 {
1749 sql_print_error("Fatal error: Error while loading "
1750 "mysql.time_zone_leap_second table");
1751 goto end_with_close;
1752 }
1753
1754 /*
1755 Loading of info about leap seconds succeeded
1756 */
1757
1758 return_val= 0;
1759
1760
1761 end_with_setting_default_tz:
1762 /* If we have default time zone try to load it */
1763 if (default_tzname)
1764 {
1765 String tmp_tzname2(default_tzname, &my_charset_latin1);
1766 /*
1767 Time zone tables may be open here, and my_tz_find() may open
1768 most of them once more, but this is OK for system tables open
1769 for READ.
1770 */
1771 if (!(global_system_variables.time_zone= my_tz_find(thd, &tmp_tzname2)))
1772 {
1773 sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
1774 default_tzname);
1775 return_val= 1;
1776 }
1777 }
1778
1779 end_with_close:
1780 if (time_zone_tables_exist)
1781 close_mysql_tables(thd);
1782
1783 end_with_cleanup:
1784
1785 /* if there were error free time zone describing structs */
1786 if (return_val)
1787 my_tz_free();
1788 end:
1789 delete thd;
1790 if (org_thd)
1791 org_thd->store_globals(); /* purecov: inspected */
1792 else
1793 {
1794 /* Remember that we don't have a THD */
1795 my_pthread_setspecific_ptr(THR_THD, 0);
1796 my_pthread_setspecific_ptr(THR_MALLOC, 0);
1797 }
1798
1799 default_tz= default_tz_name ? global_system_variables.time_zone
1800 : my_tz_SYSTEM;
1801
1802 DBUG_RETURN(return_val);
1803 }
1804
1805
1806 /*
1807 Free resources used by time zone support infrastructure.
1808
1809 SYNOPSIS
1810 my_tz_free()
1811 */
1812
my_tz_free()1813 void my_tz_free()
1814 {
1815 if (tz_inited)
1816 {
1817 tz_inited= 0;
1818 mysql_mutex_destroy(&tz_LOCK);
1819 my_hash_free(&offset_tzs);
1820 my_hash_free(&tz_names);
1821 free_root(&tz_storage, MYF(0));
1822 }
1823 }
1824
1825
1826 /*
1827 Load time zone description from system tables.
1828
1829 SYNOPSIS
1830 tz_load_from_open_tables()
1831 tz_name - name of time zone that should be loaded.
1832 tz_tables - list of tables from which time zone description
1833 should be loaded
1834
1835 DESCRIPTION
1836 This function will try to load information about time zone specified
1837 from the list of the already opened and locked tables (first table in
1838 tz_tables should be time_zone_name, next time_zone, then
1839 time_zone_transition_type and time_zone_transition should be last).
1840 It will also update information in hash used for time zones lookup.
1841
1842 RETURN VALUES
1843 Returns pointer to newly created Time_zone object or 0 in case of error.
1844
1845 */
1846
1847 static Time_zone*
tz_load_from_open_tables(const String * tz_name,TABLE_LIST * tz_tables)1848 tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
1849 {
1850 TABLE *table= 0;
1851 TIME_ZONE_INFO *tz_info= NULL;
1852 Tz_names_entry *tmp_tzname;
1853 Time_zone *return_val= 0;
1854 int res;
1855 uint tzid, ttid;
1856 my_time_t ttime;
1857 char buff[MAX_FIELD_WIDTH];
1858 String abbr(buff, sizeof(buff), &my_charset_latin1);
1859 char *alloc_buff= NULL;
1860 char *tz_name_buff= NULL;
1861 /*
1862 Temporary arrays that are used for loading of data for filling
1863 TIME_ZONE_INFO structure
1864 */
1865 my_time_t ats[TZ_MAX_TIMES];
1866 uchar types[TZ_MAX_TIMES];
1867 TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
1868 #ifdef ABBR_ARE_USED
1869 char chars[MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
1870 #endif
1871 /*
1872 Used as a temporary tz_info until we decide that we actually want to
1873 allocate and keep the tz info and tz name in tz_storage.
1874 */
1875 TIME_ZONE_INFO tmp_tz_info;
1876 memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
1877
1878 DBUG_ENTER("tz_load_from_open_tables");
1879
1880 /*
1881 Let us find out time zone id by its name (there is only one index
1882 and it is specifically for this purpose).
1883 */
1884 table= tz_tables->table;
1885 tz_tables= tz_tables->next_local;
1886 table->field[0]->store(tz_name->ptr(), tz_name->length(),
1887 &my_charset_latin1);
1888
1889 if (table->file->ha_index_init(0, 1))
1890 goto end;
1891
1892 if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
1893 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1894 {
1895 #ifdef EXTRA_DEBUG
1896 /*
1897 Most probably user has mistyped time zone name, so no need to bark here
1898 unless we need it for debugging.
1899 */
1900 sql_print_error("Can't find description of time zone '%.*s'",
1901 tz_name->length(), tz_name->ptr());
1902 #endif
1903 goto end;
1904 }
1905
1906 tzid= (uint)table->field[1]->val_int();
1907
1908 (void)table->file->ha_index_end();
1909
1910 /*
1911 Now we need to lookup record in mysql.time_zone table in order to
1912 understand whenever this timezone uses leap seconds (again we are
1913 using the only index in this table).
1914 */
1915 table= tz_tables->table;
1916 tz_tables= tz_tables->next_local;
1917 table->field[0]->store((longlong) tzid, TRUE);
1918 if (table->file->ha_index_init(0, 1))
1919 goto end;
1920
1921 if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
1922 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
1923 {
1924 sql_print_error("Can't find description of time zone '%u'", tzid);
1925 goto end;
1926 }
1927
1928 /* If Uses_leap_seconds == 'Y' */
1929 if (table->field[1]->val_int() == 1)
1930 {
1931 tmp_tz_info.leapcnt= tz_leapcnt;
1932 tmp_tz_info.lsis= tz_lsis;
1933 }
1934
1935 (void)table->file->ha_index_end();
1936
1937 /*
1938 Now we will iterate through records for out time zone in
1939 mysql.time_zone_transition_type table. Because we want records
1940 only for our time zone guess what are we doing?
1941 Right - using special index.
1942 */
1943 table= tz_tables->table;
1944 tz_tables= tz_tables->next_local;
1945 table->field[0]->store((longlong) tzid, TRUE);
1946 if (table->file->ha_index_init(0, 1))
1947 goto end;
1948
1949 res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
1950 (key_part_map)1, HA_READ_KEY_EXACT);
1951 while (!res)
1952 {
1953 ttid= (uint)table->field[1]->val_int();
1954
1955 if (ttid >= TZ_MAX_TYPES)
1956 {
1957 sql_print_error("Error while loading time zone description from "
1958 "mysql.time_zone_transition_type table: too big "
1959 "transition type id");
1960 goto end;
1961 }
1962
1963 ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int();
1964 ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0);
1965
1966 #ifdef ABBR_ARE_USED
1967 // FIXME should we do something with duplicates here ?
1968 table->field[4]->val_str(&abbr, &abbr);
1969 if (tmp_tz_info.charcnt + abbr.length() + 1 > sizeof(chars))
1970 {
1971 sql_print_error("Error while loading time zone description from "
1972 "mysql.time_zone_transition_type table: not enough "
1973 "room for abbreviations");
1974 goto end;
1975 }
1976 ttis[ttid].tt_abbrind= tmp_tz_info.charcnt;
1977 memcpy(chars + tmp_tz_info.charcnt, abbr.ptr(), abbr.length());
1978 tmp_tz_info.charcnt+= abbr.length();
1979 chars[tmp_tz_info.charcnt]= 0;
1980 tmp_tz_info.charcnt++;
1981
1982 DBUG_PRINT("info",
1983 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1984 "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff,
1985 chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst));
1986 #else
1987 DBUG_PRINT("info",
1988 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
1989 "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst));
1990 #endif
1991
1992 /* ttid is increasing because we are reading using index */
1993 DBUG_ASSERT(ttid >= tmp_tz_info.typecnt);
1994
1995 tmp_tz_info.typecnt= ttid + 1;
1996
1997 res= table->file->ha_index_next_same(table->record[0],
1998 table->field[0]->ptr, 4);
1999 }
2000
2001 if (res != HA_ERR_END_OF_FILE)
2002 {
2003 sql_print_error("Error while loading time zone description from "
2004 "mysql.time_zone_transition_type table");
2005 goto end;
2006 }
2007
2008 (void)table->file->ha_index_end();
2009
2010
2011 /*
2012 At last we are doing the same thing for records in
2013 mysql.time_zone_transition table. Here we additionaly need records
2014 in ascending order by index scan also satisfies us.
2015 */
2016 table= tz_tables->table;
2017 table->field[0]->store((longlong) tzid, TRUE);
2018 if (table->file->ha_index_init(0, 1))
2019 goto end;
2020
2021 res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
2022 (key_part_map)1, HA_READ_KEY_EXACT);
2023 while (!res)
2024 {
2025 ttime= (my_time_t)table->field[1]->val_int();
2026 ttid= (uint)table->field[2]->val_int();
2027
2028 if (tmp_tz_info.timecnt + 1 > TZ_MAX_TIMES)
2029 {
2030 sql_print_error("Error while loading time zone description from "
2031 "mysql.time_zone_transition table: "
2032 "too much transitions");
2033 goto end;
2034 }
2035 if (ttid + 1 > tmp_tz_info.typecnt)
2036 {
2037 sql_print_error("Error while loading time zone description from "
2038 "mysql.time_zone_transition table: "
2039 "bad transition type id");
2040 goto end;
2041 }
2042
2043 ats[tmp_tz_info.timecnt]= ttime;
2044 types[tmp_tz_info.timecnt]= ttid;
2045 tmp_tz_info.timecnt++;
2046
2047 DBUG_PRINT("info",
2048 ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
2049 tzid, (ulong) ttime, ttid));
2050
2051 res= table->file->ha_index_next_same(table->record[0],
2052 table->field[0]->ptr, 4);
2053 }
2054
2055 /*
2056 We have to allow HA_ERR_KEY_NOT_FOUND because some time zones
2057 for example UTC have no transitons.
2058 */
2059 if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
2060 {
2061 sql_print_error("Error while loading time zone description from "
2062 "mysql.time_zone_transition table");
2063 goto end;
2064 }
2065
2066 (void)table->file->ha_index_end();
2067 table= 0;
2068
2069 /*
2070 Let us check how correct our time zone description is. We don't check for
2071 tz->timecnt < 1 since it is ok for GMT.
2072 */
2073 if (tmp_tz_info.typecnt < 1)
2074 {
2075 sql_print_error("loading time zone without transition types");
2076 goto end;
2077 }
2078
2079 /* Allocate memory for the timezone info and timezone name in tz_storage. */
2080 if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
2081 tz_name->length() + 1)))
2082 {
2083 sql_print_error("Out of memory while loading time zone description");
2084 return 0;
2085 }
2086
2087 /* Move the temporary tz_info into the allocated area */
2088 tz_info= (TIME_ZONE_INFO *)alloc_buff;
2089 memcpy(tz_info, &tmp_tz_info, sizeof(TIME_ZONE_INFO));
2090 tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
2091 /*
2092 By writing zero to the end we guarantee that we can call ptr()
2093 instead of c_ptr() for time zone name.
2094 */
2095 strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
2096
2097 /*
2098 Now we will allocate memory and init TIME_ZONE_INFO structure.
2099 */
2100 if (!(alloc_buff= (char*) alloc_root(&tz_storage,
2101 ALIGN_SIZE(sizeof(my_time_t) *
2102 tz_info->timecnt) +
2103 ALIGN_SIZE(tz_info->timecnt) +
2104 #ifdef ABBR_ARE_USED
2105 ALIGN_SIZE(tz_info->charcnt) +
2106 #endif
2107 sizeof(TRAN_TYPE_INFO) *
2108 tz_info->typecnt)))
2109 {
2110 sql_print_error("Out of memory while loading time zone description");
2111 goto end;
2112 }
2113
2114 tz_info->ats= (my_time_t *) alloc_buff;
2115 memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
2116 alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
2117 tz_info->types= (uchar *)alloc_buff;
2118 memcpy(tz_info->types, types, tz_info->timecnt);
2119 alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
2120 #ifdef ABBR_ARE_USED
2121 tz_info->chars= alloc_buff;
2122 memcpy(tz_info->chars, chars, tz_info->charcnt);
2123 alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
2124 #endif
2125 tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
2126 memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
2127
2128 /* Build reversed map. */
2129 if (prepare_tz_info(tz_info, &tz_storage))
2130 {
2131 sql_print_error("Unable to build mktime map for time zone");
2132 goto end;
2133 }
2134
2135
2136 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) ||
2137 !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
2138 &(tmp_tzname->name))) ||
2139 (tmp_tzname->name.set(tz_name_buff, tz_name->length(),
2140 &my_charset_latin1),
2141 my_hash_insert(&tz_names, (const uchar *)tmp_tzname)))
2142 {
2143 sql_print_error("Out of memory while loading time zone");
2144 goto end;
2145 }
2146
2147 /*
2148 Loading of time zone succeeded
2149 */
2150 return_val= tmp_tzname->tz;
2151
2152 end:
2153
2154 if (table && table->file->inited)
2155 (void) table->file->ha_index_end();
2156
2157 DBUG_RETURN(return_val);
2158 }
2159
2160
2161 /*
2162 Parse string that specifies time zone as offset from UTC.
2163
2164 SYNOPSIS
2165 str_to_offset()
2166 str - pointer to string which contains offset
2167 length - length of string
2168 offset - out parameter for storing found offset in seconds.
2169
2170 DESCRIPTION
2171 This function parses string which contains time zone offset
2172 in form similar to '+10:00' and converts found value to
2173 seconds from UTC form (east is positive).
2174
2175 RETURN VALUE
2176 0 - Ok
2177 1 - String doesn't contain valid time zone offset
2178 */
2179 my_bool
str_to_offset(const char * str,uint length,long * offset)2180 str_to_offset(const char *str, uint length, long *offset)
2181 {
2182 const char *end= str + length;
2183 my_bool negative;
2184 ulong number_tmp;
2185 long offset_tmp;
2186
2187 if (length < 4)
2188 return 1;
2189
2190 if (*str == '+')
2191 negative= 0;
2192 else if (*str == '-')
2193 negative= 1;
2194 else
2195 return 1;
2196 str++;
2197
2198 number_tmp= 0;
2199
2200 while (str < end && my_isdigit(&my_charset_latin1, *str))
2201 {
2202 number_tmp= number_tmp*10 + *str - '0';
2203 str++;
2204 }
2205
2206 if (str + 1 >= end || *str != ':')
2207 return 1;
2208 str++;
2209
2210 offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
2211
2212 while (str < end && my_isdigit(&my_charset_latin1, *str))
2213 {
2214 number_tmp= number_tmp * 10 + *str - '0';
2215 str++;
2216 }
2217
2218 if (str != end)
2219 return 1;
2220
2221 offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
2222
2223 if (negative)
2224 offset_tmp= -offset_tmp;
2225
2226 /*
2227 Check if offset is in range prescribed by standard
2228 (from -12:59 to 13:00).
2229 */
2230
2231 if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
2232 offset_tmp > 13 * SECS_PER_HOUR)
2233 return 1;
2234
2235 *offset= offset_tmp;
2236
2237 return 0;
2238 }
2239
2240
2241 /*
2242 Get Time_zone object for specified time zone.
2243
2244 SYNOPSIS
2245 my_tz_find()
2246 thd - pointer to thread THD structure
2247 name - time zone specification
2248
2249 DESCRIPTION
2250 This function checks if name is one of time zones described in db,
2251 predefined SYSTEM time zone or valid time zone specification as
2252 offset from UTC (In last case it will create proper Time_zone_offset
2253 object if there were not any.). If name is ok it returns corresponding
2254 Time_zone object.
2255
2256 Clients of this function are not responsible for releasing resources
2257 occupied by returned Time_zone object so they can just forget pointers
2258 to Time_zone object if they are not needed longer.
2259
2260 Other important property of this function: if some Time_zone found once
2261 it will be for sure found later, so this function can also be used for
2262 checking if proper Time_zone object exists (and if there will be error
2263 it will be reported during first call).
2264
2265 If name pointer is 0 then this function returns 0 (this allows to pass 0
2266 values as parameter without additional external check and this property
2267 is used by @@time_zone variable handling code).
2268
2269 It will perform lookup in system tables (mysql.time_zone*),
2270 opening and locking them, and closing afterwards. It won't perform
2271 such lookup if no time zone describing tables were found during
2272 server start up.
2273
2274 RETURN VALUE
2275 Pointer to corresponding Time_zone object. 0 - in case of bad time zone
2276 specification or other error.
2277
2278 */
2279 Time_zone *
my_tz_find(THD * thd,const String * name)2280 my_tz_find(THD *thd, const String *name)
2281 {
2282 Tz_names_entry *tmp_tzname;
2283 Time_zone *result_tz= 0;
2284 long offset;
2285 DBUG_ENTER("my_tz_find");
2286 DBUG_PRINT("enter", ("time zone name='%s'",
2287 name ? ((String *)name)->c_ptr_safe() : "NULL"));
2288
2289 if (!name || name->is_empty())
2290 DBUG_RETURN(0);
2291
2292 mysql_mutex_lock(&tz_LOCK);
2293
2294 if (!str_to_offset(name->ptr(), name->length(), &offset))
2295 {
2296
2297 if (!(result_tz= (Time_zone_offset *)my_hash_search(&offset_tzs,
2298 (const uchar *)&offset,
2299 sizeof(long))))
2300 {
2301 DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
2302
2303 if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
2304 my_hash_insert(&offset_tzs, (const uchar *) result_tz))
2305 {
2306 result_tz= 0;
2307 sql_print_error("Fatal error: Out of memory "
2308 "while setting new time zone");
2309 }
2310 }
2311 }
2312 else
2313 {
2314 result_tz= 0;
2315 if ((tmp_tzname= (Tz_names_entry *)my_hash_search(&tz_names,
2316 (const uchar *)
2317 name->ptr(),
2318 name->length())))
2319 result_tz= tmp_tzname->tz;
2320 else if (time_zone_tables_exist)
2321 {
2322 TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
2323 Open_tables_backup open_tables_state_backup;
2324
2325 tz_init_table_list(tz_tables);
2326 init_mdl_requests(tz_tables);
2327 DEBUG_SYNC(thd, "my_tz_find");
2328 if (!open_system_tables_for_read(thd, tz_tables,
2329 &open_tables_state_backup))
2330 {
2331 result_tz= tz_load_from_open_tables(name, tz_tables);
2332 close_system_tables(thd, &open_tables_state_backup);
2333 }
2334 }
2335 }
2336
2337 mysql_mutex_unlock(&tz_LOCK);
2338
2339 DBUG_RETURN(result_tz);
2340 }
2341
2342
2343 /**
2344 Convert leap seconds into non-leap
2345
2346 This function will convert the leap seconds added by the OS to
2347 non-leap seconds, e.g. 23:59:59, 23:59:60 -> 23:59:59, 00:00:01 ...
2348 This check is not checking for years on purpose : although it's not a
2349 complete check this way it doesn't require looking (and having installed)
2350 the leap seconds table.
2351
2352 @param[in,out] broken down time structure as filled in by the OS
2353 */
2354
adjust_leap_second(MYSQL_TIME * t)2355 void Time_zone::adjust_leap_second(MYSQL_TIME *t)
2356 {
2357 if (t->second == 60 || t->second == 61)
2358 t->second= 59;
2359 }
2360
2361 #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
2362
2363
2364 #ifdef TZINFO2SQL
2365 /*
2366 This code belongs to mysql_tzinfo_to_sql converter command line utility.
2367 This utility should be used by db admin for populating mysql.time_zone
2368 tables.
2369 */
2370
2371
2372 /*
2373 Print info about time zone described by TIME_ZONE_INFO struct as
2374 SQL statements populating mysql.time_zone* tables.
2375
2376 SYNOPSIS
2377 print_tz_as_sql()
2378 tz_name - name of time zone
2379 sp - structure describing time zone
2380 */
2381 void
print_tz_as_sql(const char * tz_name,const TIME_ZONE_INFO * sp)2382 print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
2383 {
2384 uint i;
2385
2386 /* Here we assume that all time zones have same leap correction tables */
2387 printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
2388 sp->leapcnt ? "Y" : "N");
2389 printf("SET @time_zone_id= LAST_INSERT_ID();\n");
2390 printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
2391 ('%s', @time_zone_id);\n", tz_name);
2392
2393 if (sp->timecnt)
2394 {
2395 printf("INSERT INTO time_zone_transition \
2396 (Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
2397 for (i= 0; i < sp->timecnt; i++)
2398 printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
2399 (uint)sp->types[i]);
2400 printf(";\n");
2401 }
2402
2403 printf("INSERT INTO time_zone_transition_type \
2404 (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
2405
2406 for (i= 0; i < sp->typecnt; i++)
2407 printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i,
2408 sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
2409 sp->chars + sp->ttis[i].tt_abbrind);
2410 printf(";\n");
2411 }
2412
2413
2414 /*
2415 Print info about leap seconds in time zone as SQL statements
2416 populating mysql.time_zone_leap_second table.
2417
2418 SYNOPSIS
2419 print_tz_leaps_as_sql()
2420 sp - structure describing time zone
2421 */
2422 void
print_tz_leaps_as_sql(const TIME_ZONE_INFO * sp)2423 print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
2424 {
2425 uint i;
2426
2427 /*
2428 We are assuming that there are only one list of leap seconds
2429 For all timezones.
2430 */
2431 printf("TRUNCATE TABLE time_zone_leap_second;\n");
2432
2433 if (sp->leapcnt)
2434 {
2435 printf("INSERT INTO time_zone_leap_second \
2436 (Transition_time, Correction) VALUES\n");
2437 for (i= 0; i < sp->leapcnt; i++)
2438 printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
2439 sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
2440 printf(";\n");
2441 }
2442
2443 printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n");
2444 }
2445
2446
2447 /*
2448 Some variables used as temporary or as parameters
2449 in recursive scan_tz_dir() code.
2450 */
2451 TIME_ZONE_INFO tz_info;
2452 MEM_ROOT tz_storage;
2453 char fullname[FN_REFLEN + 1];
2454 char *root_name_end;
2455
2456
2457 /*
2458 Recursively scan zoneinfo directory and print all found time zone
2459 descriptions as SQL.
2460
2461 SYNOPSIS
2462 scan_tz_dir()
2463 name_end - pointer to end of path to directory to be searched.
2464
2465 DESCRIPTION
2466 This auxiliary recursive function also uses several global
2467 variables as in parameters and for storing temporary values.
2468
2469 fullname - path to directory that should be scanned.
2470 root_name_end - pointer to place in fullname where part with
2471 path to initial directory ends.
2472 current_tz_id - last used time zone id
2473
2474 RETURN VALUE
2475 0 - Ok, 1 - Fatal error
2476
2477 */
2478 my_bool
scan_tz_dir(char * name_end)2479 scan_tz_dir(char * name_end)
2480 {
2481 MY_DIR *cur_dir;
2482 char *name_end_tmp;
2483 uint i;
2484
2485 if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
2486 return 1;
2487
2488 name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
2489
2490 for (i= 0; i < cur_dir->number_off_files; i++)
2491 {
2492 if (cur_dir->dir_entry[i].name[0] != '.')
2493 {
2494 name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
2495 FN_REFLEN - (name_end - fullname));
2496
2497 if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
2498 {
2499 if (scan_tz_dir(name_end_tmp))
2500 {
2501 my_dirend(cur_dir);
2502 return 1;
2503 }
2504 }
2505 else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
2506 {
2507 init_alloc_root(&tz_storage, 32768, 0);
2508 if (!tz_load(fullname, &tz_info, &tz_storage))
2509 print_tz_as_sql(root_name_end + 1, &tz_info);
2510 else
2511 fprintf(stderr,
2512 "Warning: Unable to load '%s' as time zone. Skipping it.\n",
2513 fullname);
2514 free_root(&tz_storage, MYF(0));
2515 }
2516 else
2517 fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
2518 fullname);
2519 }
2520 }
2521
2522 my_dirend(cur_dir);
2523
2524 return 0;
2525 }
2526
2527
2528 int
main(int argc,char ** argv)2529 main(int argc, char **argv)
2530 {
2531 MY_INIT(argv[0]);
2532
2533 if (argc != 2 && argc != 3)
2534 {
2535 fprintf(stderr, "Usage:\n");
2536 fprintf(stderr, " %s timezonedir\n", argv[0]);
2537 fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
2538 fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
2539 return 1;
2540 }
2541
2542 if (argc == 2)
2543 {
2544 root_name_end= strmake(fullname, argv[1], FN_REFLEN);
2545
2546 printf("TRUNCATE TABLE time_zone;\n");
2547 printf("TRUNCATE TABLE time_zone_name;\n");
2548 printf("TRUNCATE TABLE time_zone_transition;\n");
2549 printf("TRUNCATE TABLE time_zone_transition_type;\n");
2550
2551 if (scan_tz_dir(root_name_end))
2552 {
2553 fprintf(stderr, "There were fatal errors during processing "
2554 "of zoneinfo directory\n");
2555 return 1;
2556 }
2557
2558 printf("ALTER TABLE time_zone_transition "
2559 "ORDER BY Time_zone_id, Transition_time;\n");
2560 printf("ALTER TABLE time_zone_transition_type "
2561 "ORDER BY Time_zone_id, Transition_type_id;\n");
2562 }
2563 else
2564 {
2565 init_alloc_root(&tz_storage, 32768, 0);
2566
2567 if (strcmp(argv[1], "--leap") == 0)
2568 {
2569 if (tz_load(argv[2], &tz_info, &tz_storage))
2570 {
2571 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2572 return 1;
2573 }
2574 print_tz_leaps_as_sql(&tz_info);
2575 }
2576 else
2577 {
2578 if (tz_load(argv[1], &tz_info, &tz_storage))
2579 {
2580 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
2581 return 1;
2582 }
2583 print_tz_as_sql(argv[2], &tz_info);
2584 }
2585
2586 free_root(&tz_storage, MYF(0));
2587 }
2588
2589 return 0;
2590 }
2591
2592 #endif /* defined(TZINFO2SQL) */
2593
2594
2595 #ifdef TESTTIME
2596
2597 /*
2598 Some simple brute-force test wich allowed to catch a pair of bugs.
2599 Also can provide interesting facts about system's time zone support
2600 implementation.
2601 */
2602
2603 #ifndef CHAR_BIT
2604 #define CHAR_BIT 8
2605 #endif
2606
2607 #ifndef TYPE_BIT
2608 #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
2609 #endif
2610
2611 #ifndef TYPE_SIGNED
2612 #define TYPE_SIGNED(type) (((type) -1) < 0)
2613 #endif
2614
2615 my_bool
is_equal_TIME_tm(const TIME * time_arg,const struct tm * tm_arg)2616 is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg)
2617 {
2618 return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) &&
2619 (time_arg->month == (uint)tm_arg->tm_mon+1) &&
2620 (time_arg->day == (uint)tm_arg->tm_mday) &&
2621 (time_arg->hour == (uint)tm_arg->tm_hour) &&
2622 (time_arg->minute == (uint)tm_arg->tm_min) &&
2623 (time_arg->second == (uint)tm_arg->tm_sec) &&
2624 time_arg->second_part == 0;
2625 }
2626
2627
2628 int
main(int argc,char ** argv)2629 main(int argc, char **argv)
2630 {
2631 my_bool localtime_negative;
2632 TIME_ZONE_INFO tz_info;
2633 struct tm tmp;
2634 MYSQL_TIME time_tmp;
2635 time_t t, t1, t2;
2636 char fullname[FN_REFLEN+1];
2637 char *str_end;
2638 MEM_ROOT tz_storage;
2639
2640 MY_INIT(argv[0]);
2641
2642 init_alloc_root(&tz_storage, 32768, 0);
2643
2644 /* let us set some well known timezone */
2645 setenv("TZ", "MET", 1);
2646 tzset();
2647
2648 /* Some initial time zone related system info */
2649 printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned",
2650 (uint)TYPE_BIT(time_t));
2651 if (TYPE_SIGNED(time_t))
2652 {
2653 t= -100;
2654 localtime_negative= MY_TEST(localtime_r(&t, &tmp) != 0);
2655 printf("localtime_r %s negative params \
2656 (time_t=%d is %d-%d-%d %d:%d:%d)\n",
2657 (localtime_negative ? "supports" : "doesn't support"), (int)t,
2658 TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday,
2659 tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
2660
2661 printf("mktime %s negative results (%d)\n",
2662 (t == mktime(&tmp) ? "doesn't support" : "supports"),
2663 (int)mktime(&tmp));
2664 }
2665
2666 tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30;
2667 tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1;
2668 t= mktime(&tmp);
2669 printf("mktime returns %s for spring time gap (%d)\n",
2670 (t != (time_t)-1 ? "something" : "error"), (int)t);
2671
2672 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2673 tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0;
2674 t= mktime(&tmp);
2675 printf("mktime returns %s for non existing date (%d)\n",
2676 (t != (time_t)-1 ? "something" : "error"), (int)t);
2677
2678 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
2679 tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1;
2680 t= mktime(&tmp);
2681 printf("mktime %s unnormalized input (%d)\n",
2682 (t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t);
2683
2684 tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26;
2685 tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1;
2686 mktime(&tmp);
2687 tmp.tm_hour= 2; tmp.tm_isdst= -1;
2688 t= mktime(&tmp);
2689 tmp.tm_hour= 4; tmp.tm_isdst= 0;
2690 mktime(&tmp);
2691 tmp.tm_hour= 2; tmp.tm_isdst= -1;
2692 t1= mktime(&tmp);
2693 printf("mktime is %s (%d %d)\n",
2694 (t == t1 ? "determenistic" : "is non-determenistic"),
2695 (int)t, (int)t1);
2696
2697 /* Let us load time zone description */
2698 str_end= strmake(fullname, TZDIR, FN_REFLEN);
2699 strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
2700
2701 if (tz_load(fullname, &tz_info, &tz_storage))
2702 {
2703 printf("Unable to load time zone info from '%s'\n", fullname);
2704 free_root(&tz_storage, MYF(0));
2705 return 1;
2706 }
2707
2708 printf("Testing our implementation\n");
2709
2710 if (TYPE_SIGNED(time_t) && localtime_negative)
2711 {
2712 for (t= -40000; t < 20000; t++)
2713 {
2714 localtime_r(&t, &tmp);
2715 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2716 if (!is_equal_TIME_tm(&time_tmp, &tmp))
2717 {
2718 printf("Problem with negative time_t = %d\n", (int)t);
2719 free_root(&tz_storage, MYF(0));
2720 return 1;
2721 }
2722 }
2723 printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n");
2724 }
2725
2726 for (t= 1000000000; t < 1100000000; t+= 13)
2727 {
2728 localtime_r(&t,&tmp);
2729 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info);
2730
2731 if (!is_equal_TIME_tm(&time_tmp, &tmp))
2732 {
2733 printf("Problem with time_t = %d\n", (int)t);
2734 free_root(&tz_storage, MYF(0));
2735 return 1;
2736 }
2737 }
2738 printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n");
2739
2740 my_init_time();
2741
2742 /*
2743 Be careful here! my_system_gmt_sec doesn't fully handle unnormalized
2744 dates.
2745 */
2746 for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++)
2747 {
2748 for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++)
2749 {
2750 for (time_tmp.day= 1;
2751 time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1];
2752 time_tmp.day++)
2753 {
2754 for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++)
2755 {
2756 for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5)
2757 {
2758 for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25)
2759 {
2760 long not_used;
2761 my_bool not_used_2;
2762 t= (time_t)my_system_gmt_sec(&time_tmp, ¬_used, ¬_used_2);
2763 t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, ¬_used_2);
2764 if (t != t1)
2765 {
2766 /*
2767 We need special handling during autumn since my_system_gmt_sec
2768 prefers greater time_t values (in MET) for ambiguity.
2769 And BTW that is a bug which should be fixed !!!
2770 */
2771 tmp.tm_year= time_tmp.year - TM_YEAR_BASE;
2772 tmp.tm_mon= time_tmp.month - 1;
2773 tmp.tm_mday= time_tmp.day;
2774 tmp.tm_hour= time_tmp.hour;
2775 tmp.tm_min= time_tmp.minute;
2776 tmp.tm_sec= time_tmp.second;
2777 tmp.tm_isdst= 1;
2778
2779 t2= mktime(&tmp);
2780
2781 if (t1 == t2)
2782 continue;
2783
2784 printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n",
2785 time_tmp.year, time_tmp.month, time_tmp.day,
2786 time_tmp.hour, time_tmp.minute, time_tmp.second,
2787 (int)t,(int)t1);
2788
2789 free_root(&tz_storage, MYF(0));
2790 return 1;
2791 }
2792 }
2793 }
2794 }
2795 }
2796 }
2797 }
2798
2799 printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n");
2800
2801 free_root(&tz_storage, MYF(0));
2802 return 0;
2803 }
2804
2805 #endif /* defined(TESTTIME) */
2806