00001 /* Copyright (C) 2004 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* 00018 Most of the following code and structures were derived from 00019 public domain code from ftp://elsie.nci.nih.gov/pub 00020 (We will refer to this code as to elsie-code further.) 00021 */ 00022 00023 /* 00024 We should not include mysql_priv.h in mysql_tzinfo_to_sql utility since 00025 it creates unsolved link dependencies on some platforms. 00026 */ 00027 00028 #ifdef USE_PRAGMA_IMPLEMENTATION 00029 #pragma implementation // gcc: Class implementation 00030 #endif 00031 00032 #include <my_global.h> 00033 #if !defined(TZINFO2SQL) && !defined(TESTTIME) 00034 #include "mysql_priv.h" 00035 #else 00036 #include <my_time.h> 00037 #include "tztime.h" 00038 #include <my_sys.h> 00039 #endif 00040 00041 #include "tzfile.h" 00042 #include <m_string.h> 00043 #include <my_dir.h> 00044 00045 /* 00046 Now we don't use abbreviations in server but we will do this in future. 00047 */ 00048 #if defined(TZINFO2SQL) || defined(TESTTIME) 00049 #define ABBR_ARE_USED 00050 #else 00051 #if !defined(DBUG_OFF) 00052 /* Let use abbreviations for debug purposes */ 00053 #undef ABBR_ARE_USED 00054 #define ABBR_ARE_USED 00055 #endif /* !defined(DBUG_OFF) */ 00056 #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */ 00057 00058 /* Structure describing local time type (e.g. Moscow summer time (MSD)) */ 00059 typedef struct ttinfo 00060 { 00061 long tt_gmtoff; // Offset from UTC in seconds 00062 uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst 00063 #ifdef ABBR_ARE_USED 00064 uint tt_abbrind; // Index of start of abbreviation for this time type. 00065 #endif 00066 /* 00067 We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code 00068 struct since we don't support POSIX-style TZ descriptions in variables. 00069 */ 00070 } TRAN_TYPE_INFO; 00071 00072 /* Structure describing leap-second corrections. */ 00073 typedef struct lsinfo 00074 { 00075 my_time_t ls_trans; // Transition time 00076 long ls_corr; // Correction to apply 00077 } LS_INFO; 00078 00079 /* 00080 Structure with information describing ranges of my_time_t shifted to local 00081 time (my_time_t + offset). Used for local TIME -> my_time_t conversion. 00082 See comments for TIME_to_gmt_sec() for more info. 00083 */ 00084 typedef struct revtinfo 00085 { 00086 long rt_offset; // Offset of local time from UTC in seconds 00087 uint rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap 00088 } REVT_INFO; 00089 00090 #ifdef TZNAME_MAX 00091 #define MY_TZNAME_MAX TZNAME_MAX 00092 #endif 00093 #ifndef TZNAME_MAX 00094 #define MY_TZNAME_MAX 255 00095 #endif 00096 00097 /* 00098 Structure which fully describes time zone which is 00099 described in our db or in zoneinfo files. 00100 */ 00101 typedef struct st_time_zone_info 00102 { 00103 uint leapcnt; // Number of leap-second corrections 00104 uint timecnt; // Number of transitions between time types 00105 uint typecnt; // Number of local time types 00106 uint charcnt; // Number of characters used for abbreviations 00107 uint revcnt; // Number of transition descr. for TIME->my_time_t conversion 00108 /* The following are dynamical arrays are allocated in MEM_ROOT */ 00109 my_time_t *ats; // Times of transitions between time types 00110 uchar *types; // Local time types for transitions 00111 TRAN_TYPE_INFO *ttis; // Local time types descriptions 00112 #ifdef ABBR_ARE_USED 00113 /* Storage for local time types abbreviations. They are stored as ASCIIZ */ 00114 char *chars; 00115 #endif 00116 /* 00117 Leap seconds corrections descriptions, this array is shared by 00118 all time zones who use leap seconds. 00119 */ 00120 LS_INFO *lsis; 00121 /* 00122 Starting points and descriptions of shifted my_time_t (my_time_t + offset) 00123 ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined. 00124 Used for tm -> my_time_t conversion. 00125 */ 00126 my_time_t *revts; 00127 REVT_INFO *revtis; 00128 /* 00129 Time type which is used for times smaller than first transition or if 00130 there are no transitions at all. 00131 */ 00132 TRAN_TYPE_INFO *fallback_tti; 00133 00134 } TIME_ZONE_INFO; 00135 00136 00137 static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage); 00138 00139 00140 #if defined(TZINFO2SQL) || defined(TESTTIME) 00141 00142 /* 00143 Load time zone description from zoneinfo (TZinfo) file. 00144 00145 SYNOPSIS 00146 tz_load() 00147 name - path to zoneinfo file 00148 sp - TIME_ZONE_INFO structure to fill 00149 00150 RETURN VALUES 00151 0 - Ok 00152 1 - Error 00153 */ 00154 static my_bool 00155 tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) 00156 { 00157 char *p; 00158 int read_from_file; 00159 uint i; 00160 FILE *file; 00161 00162 if (!(file= my_fopen(name, O_RDONLY|O_BINARY, MYF(MY_WME)))) 00163 return 1; 00164 { 00165 union 00166 { 00167 struct tzhead tzhead; 00168 char buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES + 00169 TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES + 00170 #ifdef ABBR_ARE_USED 00171 max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) + 00172 #endif 00173 sizeof(LS_INFO) * TZ_MAX_LEAPS]; 00174 } u; 00175 uint ttisstdcnt; 00176 uint ttisgmtcnt; 00177 char *tzinfo_buf; 00178 00179 read_from_file= my_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME)); 00180 00181 if (my_fclose(file, MYF(MY_WME)) != 0) 00182 return 1; 00183 00184 if (read_from_file < (int)sizeof(struct tzhead)) 00185 return 1; 00186 00187 ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt); 00188 ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt); 00189 sp->leapcnt= int4net(u.tzhead.tzh_leapcnt); 00190 sp->timecnt= int4net(u.tzhead.tzh_timecnt); 00191 sp->typecnt= int4net(u.tzhead.tzh_typecnt); 00192 sp->charcnt= int4net(u.tzhead.tzh_charcnt); 00193 p= u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; 00194 if (sp->leapcnt > TZ_MAX_LEAPS || 00195 sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES || 00196 sp->timecnt > TZ_MAX_TIMES || 00197 sp->charcnt > TZ_MAX_CHARS || 00198 (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || 00199 (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) 00200 return 1; 00201 if ((uint)(read_from_file - (p - u.buf)) < 00202 sp->timecnt * 4 + /* ats */ 00203 sp->timecnt + /* types */ 00204 sp->typecnt * (4 + 2) + /* ttinfos */ 00205 sp->charcnt + /* chars */ 00206 sp->leapcnt * (4 + 4) + /* lsinfos */ 00207 ttisstdcnt + /* ttisstds */ 00208 ttisgmtcnt) /* ttisgmts */ 00209 return 1; 00210 00211 if (!(tzinfo_buf= (char *)alloc_root(storage, 00212 ALIGN_SIZE(sp->timecnt * 00213 sizeof(my_time_t)) + 00214 ALIGN_SIZE(sp->timecnt) + 00215 ALIGN_SIZE(sp->typecnt * 00216 sizeof(TRAN_TYPE_INFO)) + 00217 #ifdef ABBR_ARE_USED 00218 ALIGN_SIZE(sp->charcnt) + 00219 #endif 00220 sp->leapcnt * sizeof(LS_INFO)))) 00221 return 1; 00222 00223 sp->ats= (my_time_t *)tzinfo_buf; 00224 tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t)); 00225 sp->types= (uchar *)tzinfo_buf; 00226 tzinfo_buf+= ALIGN_SIZE(sp->timecnt); 00227 sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf; 00228 tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO)); 00229 #ifdef ABBR_ARE_USED 00230 sp->chars= tzinfo_buf; 00231 tzinfo_buf+= ALIGN_SIZE(sp->charcnt); 00232 #endif 00233 sp->lsis= (LS_INFO *)tzinfo_buf; 00234 00235 for (i= 0; i < sp->timecnt; i++, p+= 4) 00236 sp->ats[i]= int4net(p); 00237 00238 for (i= 0; i < sp->timecnt; i++) 00239 { 00240 sp->types[i]= (uchar) *p++; 00241 if (sp->types[i] >= sp->typecnt) 00242 return 1; 00243 } 00244 for (i= 0; i < sp->typecnt; i++) 00245 { 00246 TRAN_TYPE_INFO * ttisp; 00247 00248 ttisp= &sp->ttis[i]; 00249 ttisp->tt_gmtoff= int4net(p); 00250 p+= 4; 00251 ttisp->tt_isdst= (uchar) *p++; 00252 if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) 00253 return 1; 00254 ttisp->tt_abbrind= (uchar) *p++; 00255 if (ttisp->tt_abbrind > sp->charcnt) 00256 return 1; 00257 } 00258 for (i= 0; i < sp->charcnt; i++) 00259 sp->chars[i]= *p++; 00260 sp->chars[i]= '\0'; /* ensure '\0' at end */ 00261 for (i= 0; i < sp->leapcnt; i++) 00262 { 00263 LS_INFO *lsisp; 00264 00265 lsisp= &sp->lsis[i]; 00266 lsisp->ls_trans= int4net(p); 00267 p+= 4; 00268 lsisp->ls_corr= int4net(p); 00269 p+= 4; 00270 } 00271 /* 00272 Since we don't support POSIX style TZ definitions in variables we 00273 don't read further like glibc or elsie code. 00274 */ 00275 } 00276 00277 return prepare_tz_info(sp, storage); 00278 } 00279 #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */ 00280 00281 00282 /* 00283 Finish preparation of time zone description for use in TIME_to_gmt_sec() 00284 and gmt_sec_to_TIME() functions. 00285 00286 SYNOPSIS 00287 prepare_tz_info() 00288 sp - pointer to time zone description 00289 storage - pointer to MEM_ROOT where arrays for map allocated 00290 00291 DESCRIPTION 00292 First task of this function is to find fallback time type which will 00293 be used if there are no transitions or we have moment in time before 00294 any transitions. 00295 Second task is to build "shifted my_time_t" -> my_time_t map used in 00296 TIME -> my_time_t conversion. 00297 Note: See description of TIME_to_gmt_sec() function first. 00298 In order to perform TIME -> my_time_t conversion we need to build table 00299 which defines "shifted by tz offset and leap seconds my_time_t" -> 00300 my_time_t function wich is almost the same (except ranges of ambiguity) 00301 as reverse function to piecewise linear function used for my_time_t -> 00302 "shifted my_time_t" conversion and which is also specified as table in 00303 zoneinfo file or in our db (It is specified as start of time type ranges 00304 and time type offsets). So basic idea is very simple - let us iterate 00305 through my_time_t space from one point of discontinuity of my_time_t -> 00306 "shifted my_time_t" function to another and build our approximation of 00307 reverse function. (Actually we iterate through ranges on which 00308 my_time_t -> "shifted my_time_t" is linear function). 00309 00310 RETURN VALUES 00311 0 Ok 00312 1 Error 00313 */ 00314 static my_bool 00315 prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage) 00316 { 00317 my_time_t cur_t= MY_TIME_T_MIN; 00318 my_time_t cur_l, end_t, end_l; 00319 my_time_t cur_max_seen_l= MY_TIME_T_MIN; 00320 long cur_offset, cur_corr, cur_off_and_corr; 00321 uint next_trans_idx, next_leap_idx; 00322 uint i; 00323 /* 00324 Temporary arrays where we will store tables. Needed because 00325 we don't know table sizes ahead. (Well we can estimate their 00326 upper bound but this will take extra space.) 00327 */ 00328 my_time_t revts[TZ_MAX_REV_RANGES]; 00329 REVT_INFO revtis[TZ_MAX_REV_RANGES]; 00330 00331 LINT_INIT(end_l); 00332 00333 /* 00334 Let us setup fallback time type which will be used if we have not any 00335 transitions or if we have moment of time before first transition. 00336 We will find first non-DST local time type and use it (or use first 00337 local time type if all of them are DST types). 00338 */ 00339 for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++) 00340 /* no-op */ ; 00341 if (i == sp->typecnt) 00342 i= 0; 00343 sp->fallback_tti= &(sp->ttis[i]); 00344 00345 00346 /* 00347 Let us build shifted my_time_t -> my_time_t map. 00348 */ 00349 sp->revcnt= 0; 00350 00351 /* Let us find initial offset */ 00352 if (sp->timecnt == 0 || cur_t < sp->ats[0]) 00353 { 00354 /* 00355 If we have not any transitions or t is before first transition we are using 00356 already found fallback time type which index is already in i. 00357 */ 00358 next_trans_idx= 0; 00359 } 00360 else 00361 { 00362 /* cur_t == sp->ats[0] so we found transition */ 00363 i= sp->types[0]; 00364 next_trans_idx= 1; 00365 } 00366 00367 cur_offset= sp->ttis[i].tt_gmtoff; 00368 00369 00370 /* let us find leap correction... unprobable, but... */ 00371 for (next_leap_idx= 0; next_leap_idx < sp->leapcnt && 00372 cur_t >= sp->lsis[next_leap_idx].ls_trans; 00373 ++next_leap_idx) 00374 continue; 00375 00376 if (next_leap_idx > 0) 00377 cur_corr= sp->lsis[next_leap_idx - 1].ls_corr; 00378 else 00379 cur_corr= 0; 00380 00381 /* Iterate trough t space */ 00382 while (sp->revcnt < TZ_MAX_REV_RANGES - 1) 00383 { 00384 cur_off_and_corr= cur_offset - cur_corr; 00385 00386 /* 00387 We assuming that cur_t could be only overflowed downwards, 00388 we also assume that end_t won't be overflowed in this case. 00389 */ 00390 if (cur_off_and_corr < 0 && 00391 cur_t < MY_TIME_T_MIN - cur_off_and_corr) 00392 cur_t= MY_TIME_T_MIN - cur_off_and_corr; 00393 00394 cur_l= cur_t + cur_off_and_corr; 00395 00396 /* 00397 Let us choose end_t as point before next time type change or leap 00398 second correction. 00399 */ 00400 end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1: 00401 MY_TIME_T_MAX, 00402 (next_leap_idx < sp->leapcnt) ? 00403 sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX); 00404 /* 00405 again assuming that end_t can be overlowed only in positive side 00406 we also assume that end_t won't be overflowed in this case. 00407 */ 00408 if (cur_off_and_corr > 0 && 00409 end_t > MY_TIME_T_MAX - cur_off_and_corr) 00410 end_t= MY_TIME_T_MAX - cur_off_and_corr; 00411 00412 end_l= end_t + cur_off_and_corr; 00413 00414 00415 if (end_l > cur_max_seen_l) 00416 { 00417 /* We want special handling in the case of first range */ 00418 if (cur_max_seen_l == MY_TIME_T_MIN) 00419 { 00420 revts[sp->revcnt]= cur_l; 00421 revtis[sp->revcnt].rt_offset= cur_off_and_corr; 00422 revtis[sp->revcnt].rt_type= 0; 00423 sp->revcnt++; 00424 cur_max_seen_l= end_l; 00425 } 00426 else 00427 { 00428 if (cur_l > cur_max_seen_l + 1) 00429 { 00430 /* We have a spring time-gap and we are not at the first range */ 00431 revts[sp->revcnt]= cur_max_seen_l + 1; 00432 revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset; 00433 revtis[sp->revcnt].rt_type= 1; 00434 sp->revcnt++; 00435 if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1) 00436 break; /* That was too much */ 00437 cur_max_seen_l= cur_l - 1; 00438 } 00439 00440 /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */ 00441 00442 revts[sp->revcnt]= cur_max_seen_l + 1; 00443 revtis[sp->revcnt].rt_offset= cur_off_and_corr; 00444 revtis[sp->revcnt].rt_type= 0; 00445 sp->revcnt++; 00446 cur_max_seen_l= end_l; 00447 } 00448 } 00449 00450 if (end_t == MY_TIME_T_MAX || 00451 (cur_off_and_corr > 0) && 00452 (end_t >= MY_TIME_T_MAX - cur_off_and_corr)) 00453 /* end of t space */ 00454 break; 00455 00456 cur_t= end_t + 1; 00457 00458 /* 00459 Let us find new offset and correction. Because of our choice of end_t 00460 cur_t can only be point where new time type starts or/and leap 00461 correction is performed. 00462 */ 00463 if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */ 00464 if (next_trans_idx < sp->timecnt && 00465 cur_t == sp->ats[next_trans_idx]) 00466 { 00467 /* We are at offset point */ 00468 cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff; 00469 ++next_trans_idx; 00470 } 00471 00472 if (next_leap_idx < sp->leapcnt && 00473 cur_t == sp->lsis[next_leap_idx].ls_trans) 00474 { 00475 /* we are at leap point */ 00476 cur_corr= sp->lsis[next_leap_idx].ls_corr; 00477 ++next_leap_idx; 00478 } 00479 } 00480 00481 /* check if we have had enough space */ 00482 if (sp->revcnt == TZ_MAX_REV_RANGES - 1) 00483 return 1; 00484 00485 /* set maximum end_l as finisher */ 00486 revts[sp->revcnt]= end_l; 00487 00488 /* Allocate arrays of proper size in sp and copy result there */ 00489 if (!(sp->revts= (my_time_t *)alloc_root(storage, 00490 sizeof(my_time_t) * (sp->revcnt + 1))) || 00491 !(sp->revtis= (REVT_INFO *)alloc_root(storage, 00492 sizeof(REVT_INFO) * sp->revcnt))) 00493 return 1; 00494 00495 memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1)); 00496 memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt); 00497 00498 return 0; 00499 } 00500 00501 00502 #if !defined(TZINFO2SQL) 00503 00504 static const uint mon_lengths[2][MONS_PER_YEAR]= 00505 { 00506 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 00507 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 00508 }; 00509 00510 static const uint mon_starts[2][MONS_PER_YEAR]= 00511 { 00512 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 00513 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 00514 }; 00515 00516 static const uint year_lengths[2]= 00517 { 00518 DAYS_PER_NYEAR, DAYS_PER_LYEAR 00519 }; 00520 00521 #define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) 00522 00523 00524 /* 00525 Converts time from my_time_t representation (seconds in UTC since Epoch) 00526 to broken down representation using given local time zone offset. 00527 00528 SYNOPSIS 00529 sec_to_TIME() 00530 tmp - pointer to structure for broken down representation 00531 t - my_time_t value to be converted 00532 offset - local time zone offset 00533 00534 DESCRIPTION 00535 Convert my_time_t with offset to TIME struct. Differs from timesub 00536 (from elsie code) because doesn't contain any leap correction and 00537 TM_GMTOFF and is_dst setting and contains some MySQL specific 00538 initialization. Funny but with removing of these we almost have 00539 glibc's offtime function. 00540 */ 00541 static void 00542 sec_to_TIME(TIME * tmp, my_time_t t, long offset) 00543 { 00544 long days; 00545 long rem; 00546 int y; 00547 int yleap; 00548 const uint *ip; 00549 00550 days= t / SECS_PER_DAY; 00551 rem= t % SECS_PER_DAY; 00552 00553 /* 00554 We do this as separate step after dividing t, because this 00555 allows us handle times near my_time_t bounds without overflows. 00556 */ 00557 rem+= offset; 00558 while (rem < 0) 00559 { 00560 rem+= SECS_PER_DAY; 00561 days--; 00562 } 00563 while (rem >= SECS_PER_DAY) 00564 { 00565 rem -= SECS_PER_DAY; 00566 days++; 00567 } 00568 tmp->hour= (uint)(rem / SECS_PER_HOUR); 00569 rem= rem % SECS_PER_HOUR; 00570 tmp->minute= (uint)(rem / SECS_PER_MIN); 00571 /* 00572 A positive leap second requires a special 00573 representation. This uses "... ??:59:60" et seq. 00574 */ 00575 tmp->second= (uint)(rem % SECS_PER_MIN); 00576 00577 y= EPOCH_YEAR; 00578 while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)]) 00579 { 00580 int newy; 00581 00582 newy= y + days / DAYS_PER_NYEAR; 00583 if (days < 0) 00584 newy--; 00585 days-= (newy - y) * DAYS_PER_NYEAR + 00586 LEAPS_THRU_END_OF(newy - 1) - 00587 LEAPS_THRU_END_OF(y - 1); 00588 y= newy; 00589 } 00590 tmp->year= y; 00591 00592 ip= mon_lengths[yleap]; 00593 for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++) 00594 days= days - (long) ip[tmp->month]; 00595 tmp->month++; 00596 tmp->day= (uint)(days + 1); 00597 00598 /* filling MySQL specific TIME members */ 00599 tmp->neg= 0; tmp->second_part= 0; 00600 tmp->time_type= MYSQL_TIMESTAMP_DATETIME; 00601 } 00602 00603 00604 /* 00605 Find time range wich contains given my_time_t value 00606 00607 SYNOPSIS 00608 find_time_range() 00609 t - my_time_t value for which we looking for range 00610 range_boundaries - sorted array of range starts. 00611 higher_bound - number of ranges 00612 00613 DESCRIPTION 00614 Performs binary search for range which contains given my_time_t value. 00615 It has sense if number of ranges is greater than zero and my_time_t value 00616 is greater or equal than beginning of first range. It also assumes that 00617 t belongs to some range specified or end of last is MY_TIME_T_MAX. 00618 00619 With this localtime_r on real data may takes less time than with linear 00620 search (I've seen 30% speed up). 00621 00622 RETURN VALUE 00623 Index of range to which t belongs 00624 */ 00625 static uint 00626 find_time_range(my_time_t t, const my_time_t *range_boundaries, 00627 uint higher_bound) 00628 { 00629 uint i, lower_bound= 0; 00630 00631 /* 00632 Function will work without this assertion but result would be meaningless. 00633 */ 00634 DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]); 00635 00636 /* 00637 Do binary search for minimal interval which contain t. We preserve: 00638 range_boundaries[lower_bound] <= t < range_boundaries[higher_bound] 00639 invariant and decrease this higher_bound - lower_bound gap twice 00640 times on each step. 00641 */ 00642 00643 while (higher_bound - lower_bound > 1) 00644 { 00645 i= (lower_bound + higher_bound) >> 1; 00646 if (range_boundaries[i] <= t) 00647 lower_bound= i; 00648 else 00649 higher_bound= i; 00650 } 00651 return lower_bound; 00652 } 00653 00654 /* 00655 Find local time transition for given my_time_t. 00656 00657 SYNOPSIS 00658 find_transition_type() 00659 t - my_time_t value to be converted 00660 sp - pointer to struct with time zone description 00661 00662 RETURN VALUE 00663 Pointer to structure in time zone description describing 00664 local time type for given my_time_t. 00665 */ 00666 static 00667 const TRAN_TYPE_INFO * 00668 find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp) 00669 { 00670 if (unlikely(sp->timecnt == 0 || t < sp->ats[0])) 00671 { 00672 /* 00673 If we have not any transitions or t is before first transition let 00674 us use fallback time type. 00675 */ 00676 return sp->fallback_tti; 00677 } 00678 00679 /* 00680 Do binary search for minimal interval between transitions which 00681 contain t. With this localtime_r on real data may takes less 00682 time than with linear search (I've seen 30% speed up). 00683 */ 00684 return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]); 00685 } 00686 00687 00688 /* 00689 Converts time in my_time_t representation (seconds in UTC since Epoch) to 00690 broken down TIME representation in local time zone. 00691 00692 SYNOPSIS 00693 gmt_sec_to_TIME() 00694 tmp - pointer to structure for broken down represenatation 00695 sec_in_utc - my_time_t value to be converted 00696 sp - pointer to struct with time zone description 00697 00698 TODO 00699 We can improve this function by creating joined array of transitions and 00700 leap corrections. This will require adding extra field to TRAN_TYPE_INFO 00701 for storing number of "extra" seconds to minute occured due to correction 00702 (60th and 61st second, look how we calculate them as "hit" in this 00703 function). 00704 Under realistic assumptions about frequency of transitions the same array 00705 can be used fot TIME -> my_time_t conversion. For this we need to 00706 implement tweaked binary search which will take into account that some 00707 TIME has two matching my_time_t ranges and some of them have none. 00708 */ 00709 static void 00710 gmt_sec_to_TIME(TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp) 00711 { 00712 const TRAN_TYPE_INFO *ttisp; 00713 const LS_INFO *lp; 00714 long corr= 0; 00715 int hit= 0; 00716 int i; 00717 00718 /* 00719 Find proper transition (and its local time type) for our sec_in_utc value. 00720 Funny but again by separating this step in function we receive code 00721 which very close to glibc's code. No wonder since they obviously use 00722 the same base and all steps are sensible. 00723 */ 00724 ttisp= find_transition_type(sec_in_utc, sp); 00725 00726 /* 00727 Let us find leap correction for our sec_in_utc value and number of extra 00728 secs to add to this minute. 00729 This loop is rarely used because most users will use time zones without 00730 leap seconds, and even in case when we have such time zone there won't 00731 be many iterations (we have about 22 corrections at this moment (2004)). 00732 */ 00733 for ( i= sp->leapcnt; i-- > 0; ) 00734 { 00735 lp= &sp->lsis[i]; 00736 if (sec_in_utc >= lp->ls_trans) 00737 { 00738 if (sec_in_utc == lp->ls_trans) 00739 { 00740 hit= ((i == 0 && lp->ls_corr > 0) || 00741 lp->ls_corr > sp->lsis[i - 1].ls_corr); 00742 if (hit) 00743 { 00744 while (i > 0 && 00745 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 && 00746 sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1) 00747 { 00748 hit++; 00749 i--; 00750 } 00751 } 00752 } 00753 corr= lp->ls_corr; 00754 break; 00755 } 00756 } 00757 00758 sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr); 00759 00760 tmp->second+= hit; 00761 } 00762 00763 00764 /* 00765 Converts local time in broken down representation to local 00766 time zone analog of my_time_t represenation. 00767 00768 SYNOPSIS 00769 sec_since_epoch() 00770 year, mon, mday, hour, min, sec - broken down representation. 00771 00772 DESCRIPTION 00773 Converts time in broken down representation to my_time_t representation 00774 ignoring time zone. Note that we cannot convert back some valid _local_ 00775 times near ends of my_time_t range because of my_time_t overflow. But we 00776 ignore this fact now since MySQL will never pass such argument. 00777 00778 RETURN VALUE 00779 Seconds since epoch time representation. 00780 */ 00781 static my_time_t 00782 sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec) 00783 { 00784 #ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES 00785 /* 00786 It turns out that only whenever month is normalized or unnormalized 00787 plays role. 00788 */ 00789 DBUG_ASSERT(mon > 0 && mon < 13); 00790 long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR + 00791 LEAPS_THRU_END_OF(year - 1) - 00792 LEAPS_THRU_END_OF(EPOCH_YEAR - 1); 00793 days+= mon_starts[isleap(year)][mon - 1]; 00794 #else 00795 long norm_month= (mon - 1) % MONS_PER_YEAR; 00796 long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0); 00797 long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR + 00798 LEAPS_THRU_END_OF(a_year - 1) - 00799 LEAPS_THRU_END_OF(EPOCH_YEAR - 1); 00800 days+= mon_starts[isleap(a_year)] 00801 [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)]; 00802 #endif 00803 days+= mday - 1; 00804 00805 return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) * 00806 SECS_PER_MIN + sec; 00807 } 00808 00809 00810 /* 00811 Works like sec_since_epoch but expects TIME structure as parameter. 00812 */ 00813 00814 my_time_t 00815 sec_since_epoch_TIME(TIME *t) 00816 { 00817 return sec_since_epoch(t->year, t->month, t->day, 00818 t->hour, t->minute, t->second); 00819 } 00820 00821 00822 /* 00823 Converts local time in broken down TIME representation to my_time_t 00824 representation. 00825 00826 SYNOPSIS 00827 TIME_to_gmt_sec() 00828 t - pointer to structure for broken down represenatation 00829 sp - pointer to struct with time zone description 00830 in_dst_time_gap - pointer to bool which is set to true if datetime 00831 value passed doesn't really exist (i.e. falls into 00832 spring time-gap) and is not touched otherwise. 00833 00834 DESCRIPTION 00835 This is mktime analog for MySQL. It is essentially different 00836 from mktime (or hypotetical my_mktime) because: 00837 - It has no idea about tm_isdst member so if it 00838 has two answers it will give the smaller one 00839 - If we are in spring time gap then it will return 00840 beginning of the gap 00841 - It can give wrong results near the ends of my_time_t due to 00842 overflows, but we are safe since in MySQL we will never 00843 call this function for such dates (its restriction for year 00844 between 1970 and 2038 gives us several days of reserve). 00845 - By default it doesn't support un-normalized input. But if 00846 sec_since_epoch() function supports un-normalized dates 00847 then this function should handle un-normalized input right, 00848 altough it won't normalize structure TIME. 00849 00850 Traditional approach to problem of conversion from broken down 00851 representation to time_t is iterative. Both elsie's and glibc 00852 implementation try to guess what time_t value should correspond to 00853 this broken-down value. They perform localtime_r function on their 00854 guessed value and then calculate the difference and try to improve 00855 their guess. Elsie's code guesses time_t value in bit by bit manner, 00856 Glibc's code tries to add difference between broken-down value 00857 corresponding to guess and target broken-down value to current guess. 00858 It also uses caching of last found correction... So Glibc's approach 00859 is essentially faster but introduces some undetermenism (in case if 00860 is_dst member of broken-down representation (tm struct) is not known 00861 and we have two possible answers). 00862 00863 We use completely different approach. It is better since it is both 00864 faster than iterative implementations and fully determenistic. If you 00865 look at my_time_t to TIME conversion then you'll find that it consist 00866 of two steps: 00867 The first is calculating shifted my_time_t value and the second - TIME 00868 calculation from shifted my_time_t value (well it is a bit simplified 00869 picture). The part in which we are interested in is my_time_t -> shifted 00870 my_time_t conversion. It is piecewise linear function which is defined 00871 by combination of transition times as break points and times offset 00872 as changing function parameter. The possible inverse function for this 00873 converison would be ambiguos but with MySQL's restrictions we can use 00874 some function which is the same as inverse function on unambigiuos 00875 ranges and coincides with one of branches of inverse function in 00876 other ranges. Thus we just need to build table which will determine 00877 this shifted my_time_t -> my_time_t conversion similar to existing 00878 (my_time_t -> shifted my_time_t table). We do this in 00879 prepare_tz_info function. 00880 00881 TODO 00882 If we can even more improve this function. For doing this we will need to 00883 build joined map of transitions and leap corrections for gmt_sec_to_TIME() 00884 function (similar to revts/revtis). Under realistic assumptions about 00885 frequency of transitions we can use the same array for TIME_to_gmt_sec(). 00886 We need to implement special version of binary search for this. Such step 00887 will be beneficial to CPU cache since we will decrease data-set used for 00888 conversion twice. 00889 00890 RETURN VALUE 00891 Seconds in UTC since Epoch. 00892 0 in case of error. 00893 */ 00894 static my_time_t 00895 TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, 00896 my_bool *in_dst_time_gap) 00897 { 00898 my_time_t local_t; 00899 uint saved_seconds; 00900 uint i; 00901 DBUG_ENTER("TIME_to_gmt_sec"); 00902 00903 /* We need this for correct leap seconds handling */ 00904 if (t->second < SECS_PER_MIN) 00905 saved_seconds= 0; 00906 else 00907 saved_seconds= t->second; 00908 00909 /* 00910 NOTE If we want to convert full my_time_t range without MySQL 00911 restrictions we should catch overflow here somehow. 00912 */ 00913 00914 local_t= sec_since_epoch(t->year, t->month, t->day, 00915 t->hour, t->minute, 00916 saved_seconds ? 0 : t->second); 00917 00918 /* We have at least one range */ 00919 DBUG_ASSERT(sp->revcnt >= 1); 00920 00921 if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt]) 00922 { 00923 /* 00924 This means that source time can't be represented as my_time_t due to 00925 limited my_time_t range. 00926 */ 00927 DBUG_RETURN(0); 00928 } 00929 00930 /* binary search for our range */ 00931 i= find_time_range(local_t, sp->revts, sp->revcnt); 00932 00933 if (sp->revtis[i].rt_type) 00934 { 00935 /* 00936 Oops! We are in spring time gap. 00937 May be we should return error here? 00938 Now we are returning my_time_t value corresponding to the 00939 beginning of the gap. 00940 */ 00941 *in_dst_time_gap= 1; 00942 DBUG_RETURN(sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds); 00943 } 00944 else 00945 DBUG_RETURN(local_t - sp->revtis[i].rt_offset + saved_seconds); 00946 } 00947 00948 00949 /* 00950 End of elsie derived code. 00951 */ 00952 #endif /* !defined(TZINFO2SQL) */ 00953 00954 00955 #if !defined(TESTTIME) && !defined(TZINFO2SQL) 00956 00957 /* 00958 String with names of SYSTEM time zone. 00959 */ 00960 static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1); 00961 00962 00963 /* 00964 Instance of this class represents local time zone used on this system 00965 (specified by TZ environment variable or via any other system mechanism). 00966 It uses system functions (localtime_r, my_system_gmt_sec) for conversion 00967 and is always available. Because of this it is used by default - if there 00968 were no explicit time zone specified. On the other hand because of this 00969 conversion methods provided by this class is significantly slower and 00970 possibly less multi-threaded-friendly than corresponding Time_zone_db 00971 methods so the latter should be preffered there it is possible. 00972 */ 00973 class Time_zone_system : public Time_zone 00974 { 00975 public: 00976 Time_zone_system() {} /* Remove gcc warning */ 00977 virtual my_time_t TIME_to_gmt_sec(const TIME *t, 00978 my_bool *in_dst_time_gap) const; 00979 virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; 00980 virtual const String * get_name() const; 00981 }; 00982 00983 00984 /* 00985 Converts local time in system time zone in TIME representation 00986 to its my_time_t representation. 00987 00988 SYNOPSIS 00989 TIME_to_gmt_sec() 00990 t - pointer to TIME structure with local time in 00991 broken-down representation. 00992 in_dst_time_gap - pointer to bool which is set to true if datetime 00993 value passed doesn't really exist (i.e. falls into 00994 spring time-gap) and is not touched otherwise. 00995 00996 DESCRIPTION 00997 This method uses system function (localtime_r()) for conversion 00998 local time in system time zone in TIME structure to its my_time_t 00999 representation. Unlike the same function for Time_zone_db class 01000 it it won't handle unnormalized input properly. Still it will 01001 return lowest possible my_time_t in case of ambiguity or if we 01002 provide time corresponding to the time-gap. 01003 01004 You should call init_time() function before using this function. 01005 01006 RETURN VALUE 01007 Corresponding my_time_t value or 0 in case of error 01008 */ 01009 my_time_t 01010 Time_zone_system::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const 01011 { 01012 long not_used; 01013 return my_system_gmt_sec(t, ¬_used, in_dst_time_gap); 01014 } 01015 01016 01017 /* 01018 Converts time from UTC seconds since Epoch (my_time_t) representation 01019 to system local time zone broken-down representation. 01020 01021 SYNOPSIS 01022 gmt_sec_to_TIME() 01023 tmp - pointer to TIME structure to fill-in 01024 t - my_time_t value to be converted 01025 01026 NOTE 01027 We assume that value passed to this function will fit into time_t range 01028 supported by localtime_r. This conversion is putting restriction on 01029 TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least 01030 for interaction with client then we can extend TIMESTAMP range down to 01031 the 1902 easily. 01032 */ 01033 void 01034 Time_zone_system::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const 01035 { 01036 struct tm tmp_tm; 01037 time_t tmp_t= (time_t)t; 01038 01039 localtime_r(&tmp_t, &tmp_tm); 01040 localtime_to_TIME(tmp, &tmp_tm); 01041 tmp->time_type= MYSQL_TIMESTAMP_DATETIME; 01042 } 01043 01044 01045 /* 01046 Get name of time zone 01047 01048 SYNOPSIS 01049 get_name() 01050 01051 RETURN VALUE 01052 Name of time zone as String 01053 */ 01054 const String * 01055 Time_zone_system::get_name() const 01056 { 01057 return &tz_SYSTEM_name; 01058 } 01059 01060 01061 /* 01062 Instance of this class represents UTC time zone. It uses system gmtime_r 01063 function for conversions and is always available. It is used only for 01064 my_time_t -> TIME conversions in various UTC_... functions, it is not 01065 intended for TIME -> my_time_t conversions and shouldn't be exposed to user. 01066 */ 01067 class Time_zone_utc : public Time_zone 01068 { 01069 public: 01070 Time_zone_utc() {} /* Remove gcc warning */ 01071 virtual my_time_t TIME_to_gmt_sec(const TIME *t, 01072 my_bool *in_dst_time_gap) const; 01073 virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; 01074 virtual const String * get_name() const; 01075 }; 01076 01077 01078 /* 01079 Convert UTC time from TIME representation to its my_time_t representation. 01080 01081 SYNOPSIS 01082 TIME_to_gmt_sec() 01083 t - pointer to TIME structure with local time 01084 in broken-down representation. 01085 in_dst_time_gap - pointer to bool which is set to true if datetime 01086 value passed doesn't really exist (i.e. falls into 01087 spring time-gap) and is not touched otherwise. 01088 01089 DESCRIPTION 01090 Since Time_zone_utc is used only internally for my_time_t -> TIME 01091 conversions, this function of Time_zone interface is not implemented for 01092 this class and should not be called. 01093 01094 RETURN VALUE 01095 0 01096 */ 01097 my_time_t 01098 Time_zone_utc::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const 01099 { 01100 /* Should be never called */ 01101 DBUG_ASSERT(0); 01102 return 0; 01103 } 01104 01105 01106 /* 01107 Converts time from UTC seconds since Epoch (my_time_t) representation 01108 to broken-down representation (also in UTC). 01109 01110 SYNOPSIS 01111 gmt_sec_to_TIME() 01112 tmp - pointer to TIME structure to fill-in 01113 t - my_time_t value to be converted 01114 01115 NOTE 01116 See note for apropriate Time_zone_system method. 01117 */ 01118 void 01119 Time_zone_utc::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const 01120 { 01121 struct tm tmp_tm; 01122 time_t tmp_t= (time_t)t; 01123 gmtime_r(&tmp_t, &tmp_tm); 01124 localtime_to_TIME(tmp, &tmp_tm); 01125 tmp->time_type= MYSQL_TIMESTAMP_DATETIME; 01126 } 01127 01128 01129 /* 01130 Get name of time zone 01131 01132 SYNOPSIS 01133 get_name() 01134 01135 DESCRIPTION 01136 Since Time_zone_utc is used only internally by SQL's UTC_* functions it 01137 is not accessible directly, and hence this function of Time_zone 01138 interface is not implemented for this class and should not be called. 01139 01140 RETURN VALUE 01141 0 01142 */ 01143 const String * 01144 Time_zone_utc::get_name() const 01145 { 01146 /* Should be never called */ 01147 DBUG_ASSERT(0); 01148 return 0; 01149 } 01150 01151 01152 /* 01153 Instance of this class represents some time zone which is 01154 described in mysql.time_zone family of tables. 01155 */ 01156 class Time_zone_db : public Time_zone 01157 { 01158 public: 01159 Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg); 01160 virtual my_time_t TIME_to_gmt_sec(const TIME *t, 01161 my_bool *in_dst_time_gap) const; 01162 virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; 01163 virtual const String * get_name() const; 01164 private: 01165 TIME_ZONE_INFO *tz_info; 01166 const String *tz_name; 01167 }; 01168 01169 01170 /* 01171 Initializes object representing time zone described by mysql.time_zone 01172 tables. 01173 01174 SYNOPSIS 01175 Time_zone_db() 01176 tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled 01177 according to db or other time zone description 01178 (for example by my_tz_init()). 01179 Several Time_zone_db instances can share one 01180 TIME_ZONE_INFO structure. 01181 tz_name_arg - name of time zone. 01182 */ 01183 Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg, 01184 const String *tz_name_arg): 01185 tz_info(tz_info_arg), tz_name(tz_name_arg) 01186 { 01187 } 01188 01189 01190 /* 01191 Converts local time in time zone described from TIME 01192 representation to its my_time_t representation. 01193 01194 SYNOPSIS 01195 TIME_to_gmt_sec() 01196 t - pointer to TIME structure with local time 01197 in broken-down representation. 01198 in_dst_time_gap - pointer to bool which is set to true if datetime 01199 value passed doesn't really exist (i.e. falls into 01200 spring time-gap) and is not touched otherwise. 01201 01202 DESCRIPTION 01203 Please see ::TIME_to_gmt_sec for function description and 01204 parameter restrictions. 01205 01206 RETURN VALUE 01207 Corresponding my_time_t value or 0 in case of error 01208 */ 01209 my_time_t 01210 Time_zone_db::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const 01211 { 01212 return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap); 01213 } 01214 01215 01216 /* 01217 Converts time from UTC seconds since Epoch (my_time_t) representation 01218 to local time zone described in broken-down representation. 01219 01220 SYNOPSIS 01221 gmt_sec_to_TIME() 01222 tmp - pointer to TIME structure to fill-in 01223 t - my_time_t value to be converted 01224 */ 01225 void 01226 Time_zone_db::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const 01227 { 01228 ::gmt_sec_to_TIME(tmp, t, tz_info); 01229 } 01230 01231 01232 /* 01233 Get name of time zone 01234 01235 SYNOPSIS 01236 get_name() 01237 01238 RETURN VALUE 01239 Name of time zone as ASCIIZ-string 01240 */ 01241 const String * 01242 Time_zone_db::get_name() const 01243 { 01244 return tz_name; 01245 } 01246 01247 01248 /* 01249 Instance of this class represents time zone which 01250 was specified as offset from UTC. 01251 */ 01252 class Time_zone_offset : public Time_zone 01253 { 01254 public: 01255 Time_zone_offset(long tz_offset_arg); 01256 virtual my_time_t TIME_to_gmt_sec(const TIME *t, 01257 my_bool *in_dst_time_gap) const; 01258 virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; 01259 virtual const String * get_name() const; 01260 /* 01261 This have to be public because we want to be able to access it from 01262 my_offset_tzs_get_key() function 01263 */ 01264 long offset; 01265 private: 01266 /* Extra reserve because of snprintf */ 01267 char name_buff[7+16]; 01268 String name; 01269 }; 01270 01271 01272 /* 01273 Initializes object representing time zone described by its offset from UTC. 01274 01275 SYNOPSIS 01276 Time_zone_offset() 01277 tz_offset_arg - offset from UTC in seconds. 01278 Positive for direction to east. 01279 */ 01280 Time_zone_offset::Time_zone_offset(long tz_offset_arg): 01281 offset(tz_offset_arg) 01282 { 01283 uint hours= abs((int)(offset / SECS_PER_HOUR)); 01284 uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN)); 01285 ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d", 01286 (offset>=0) ? "+" : "-", hours, minutes); 01287 name.set(name_buff, length, &my_charset_latin1); 01288 } 01289 01290 01291 /* 01292 Converts local time in time zone described as offset from UTC 01293 from TIME representation to its my_time_t representation. 01294 01295 SYNOPSIS 01296 TIME_to_gmt_sec() 01297 t - pointer to TIME structure with local time 01298 in broken-down representation. 01299 in_dst_time_gap - pointer to bool which should be set to true if 01300 datetime value passed doesn't really exist 01301 (i.e. falls into spring time-gap) and is not 01302 touched otherwise. 01303 It is not really used in this class. 01304 01305 RETURN VALUE 01306 Corresponding my_time_t value or 0 in case of error 01307 */ 01308 my_time_t 01309 Time_zone_offset::TIME_to_gmt_sec(const TIME *t, my_bool *in_dst_time_gap) const 01310 { 01311 return sec_since_epoch(t->year, t->month, t->day, 01312 t->hour, t->minute, t->second) - 01313 offset; 01314 } 01315 01316 01317 /* 01318 Converts time from UTC seconds since Epoch (my_time_t) representation 01319 to local time zone described as offset from UTC and in broken-down 01320 representation. 01321 01322 SYNOPSIS 01323 gmt_sec_to_TIME() 01324 tmp - pointer to TIME structure to fill-in 01325 t - my_time_t value to be converted 01326 */ 01327 void 01328 Time_zone_offset::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const 01329 { 01330 sec_to_TIME(tmp, t, offset); 01331 } 01332 01333 01334 /* 01335 Get name of time zone 01336 01337 SYNOPSIS 01338 get_name() 01339 01340 RETURN VALUE 01341 Name of time zone as pointer to String object 01342 */ 01343 const String * 01344 Time_zone_offset::get_name() const 01345 { 01346 return &name; 01347 } 01348 01349 01350 static Time_zone_utc tz_UTC; 01351 static Time_zone_system tz_SYSTEM; 01352 01353 Time_zone *my_tz_UTC= &tz_UTC; 01354 Time_zone *my_tz_SYSTEM= &tz_SYSTEM; 01355 01356 static HASH tz_names; 01357 static HASH offset_tzs; 01358 static MEM_ROOT tz_storage; 01359 01360 /* 01361 These mutex protects offset_tzs and tz_storage. 01362 These protection needed only when we are trying to set 01363 time zone which is specified as offset, and searching for existing 01364 time zone in offset_tzs or creating if it didn't existed before in 01365 tz_storage. So contention is low. 01366 */ 01367 static pthread_mutex_t tz_LOCK; 01368 static bool tz_inited= 0; 01369 01370 /* 01371 This two static variables are inteded for holding info about leap seconds 01372 shared by all time zones. 01373 */ 01374 static uint tz_leapcnt= 0; 01375 static LS_INFO *tz_lsis= 0; 01376 01377 /* 01378 Shows whenever we have found time zone tables during start-up. 01379 Used for avoiding of putting those tables to global table list 01380 for queries that use time zone info. 01381 */ 01382 static bool time_zone_tables_exist= 1; 01383 01384 01385 /* 01386 Names of tables (with their lengths) that are needed 01387 for dynamical loading of time zone descriptions. 01388 */ 01389 01390 static const LEX_STRING tz_tables_names[MY_TZ_TABLES_COUNT]= 01391 { 01392 { C_STRING_WITH_LEN("time_zone_name")}, 01393 { C_STRING_WITH_LEN("time_zone")}, 01394 { C_STRING_WITH_LEN("time_zone_transition_type")}, 01395 { C_STRING_WITH_LEN("time_zone_transition")} 01396 }; 01397 01398 /* Name of database to which those tables belong. */ 01399 01400 static const LEX_STRING tz_tables_db_name= { C_STRING_WITH_LEN("mysql")}; 01401 01402 01403 class Tz_names_entry: public Sql_alloc 01404 { 01405 public: 01406 String name; 01407 Time_zone *tz; 01408 }; 01409 01410 01411 /* 01412 We are going to call both of these functions from C code so 01413 they should obey C calling conventions. 01414 */ 01415 01416 extern "C" byte* my_tz_names_get_key(Tz_names_entry *entry, uint *length, 01417 my_bool not_used __attribute__((unused))) 01418 { 01419 *length= entry->name.length(); 01420 return (byte*) entry->name.ptr(); 01421 } 01422 01423 extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length, 01424 my_bool not_used __attribute__((unused))) 01425 { 01426 *length= sizeof(long); 01427 return (byte*) &entry->offset; 01428 } 01429 01430 01431 /* 01432 Prepare table list with time zone related tables from preallocated array 01433 and add to global table list. 01434 01435 SYNOPSIS 01436 tz_init_table_list() 01437 tz_tabs - pointer to preallocated array of MY_TZ_TABLES_COUNT 01438 TABLE_LIST objects 01439 global_next_ptr - pointer to variable which points to global_next member 01440 of last element of global table list (or list root 01441 then list is empty) (in/out). 01442 01443 DESCRIPTION 01444 This function prepares list of TABLE_LIST objects which can be used 01445 for opening of time zone tables from preallocated array. It also links 01446 this list to the end of global table list (it will read and update 01447 accordingly variable pointed by global_next_ptr for this). 01448 */ 01449 01450 static void 01451 tz_init_table_list(TABLE_LIST *tz_tabs, TABLE_LIST ***global_next_ptr) 01452 { 01453 bzero(tz_tabs, sizeof(TABLE_LIST) * MY_TZ_TABLES_COUNT); 01454 01455 for (int i= 0; i < MY_TZ_TABLES_COUNT; i++) 01456 { 01457 tz_tabs[i].alias= tz_tabs[i].table_name= tz_tables_names[i].str; 01458 tz_tabs[i].table_name_length= tz_tables_names[i].length; 01459 tz_tabs[i].db= tz_tables_db_name.str; 01460 tz_tabs[i].db_length= tz_tables_db_name.length; 01461 tz_tabs[i].lock_type= TL_READ; 01462 01463 if (i != MY_TZ_TABLES_COUNT - 1) 01464 tz_tabs[i].next_global= tz_tabs[i].next_local= &tz_tabs[i+1]; 01465 if (i != 0) 01466 tz_tabs[i].prev_global= &tz_tabs[i-1].next_global; 01467 } 01468 01469 /* Link into global list */ 01470 tz_tabs[0].prev_global= *global_next_ptr; 01471 **global_next_ptr= tz_tabs; 01472 /* Update last-global-pointer to point to pointer in last table */ 01473 *global_next_ptr= &tz_tabs[MY_TZ_TABLES_COUNT-1].next_global; 01474 } 01475 01476 01477 /* 01478 Fake table list object, pointer to which is returned by 01479 my_tz_get_tables_list() as indication of error. 01480 */ 01481 TABLE_LIST fake_time_zone_tables_list; 01482 01483 /* 01484 Create table list with time zone related tables and add it to the end 01485 of global table list. 01486 01487 SYNOPSIS 01488 my_tz_get_table_list() 01489 thd - current thread object 01490 global_next_ptr - pointer to variable which points to global_next member 01491 of last element of global table list (or list root 01492 then list is empty) (in/out). 01493 01494 DESCRIPTION 01495 This function creates list of TABLE_LIST objects allocated in thd's 01496 memroot, which can be used for opening of time zone tables. It will also 01497 link this list to the end of global table list (it will read and update 01498 accordingly variable pointed by global_next_ptr for this). 01499 01500 NOTE 01501 my_tz_check_n_skip_implicit_tables() function depends on fact that 01502 elements of list created are allocated as TABLE_LIST[MY_TZ_TABLES_COUNT] 01503 array. 01504 01505 RETURN VALUES 01506 Returns pointer to first TABLE_LIST object, (could be 0 if time zone 01507 tables don't exist) and &fake_time_zone_tables_list in case of error. 01508 */ 01509 01510 TABLE_LIST * 01511 my_tz_get_table_list(THD *thd, TABLE_LIST ***global_next_ptr) 01512 { 01513 TABLE_LIST *tz_tabs; 01514 DBUG_ENTER("my_tz_get_table_list"); 01515 01516 if (!time_zone_tables_exist) 01517 DBUG_RETURN(0); 01518 01519 if (!(tz_tabs= (TABLE_LIST *)thd->alloc(sizeof(TABLE_LIST) * 01520 MY_TZ_TABLES_COUNT))) 01521 DBUG_RETURN(&fake_time_zone_tables_list); 01522 01523 tz_init_table_list(tz_tabs, global_next_ptr); 01524 01525 DBUG_RETURN(tz_tabs); 01526 } 01527 01528 01529 /* 01530 Initialize time zone support infrastructure. 01531 01532 SYNOPSIS 01533 my_tz_init() 01534 thd - current thread object 01535 default_tzname - default time zone or 0 if none. 01536 bootstrap - indicates whenever we are in bootstrap mode 01537 01538 DESCRIPTION 01539 This function will init memory structures needed for time zone support, 01540 it will register mandatory SYSTEM time zone in them. It will try to open 01541 mysql.time_zone* tables and load information about default time zone and 01542 information which further will be shared among all time zones loaded. 01543 If system tables with time zone descriptions don't exist it won't fail 01544 (unless default_tzname is time zone from tables). If bootstrap parameter 01545 is true then this routine assumes that we are in bootstrap mode and won't 01546 load time zone descriptions unless someone specifies default time zone 01547 which is supposedly stored in those tables. 01548 It'll also set default time zone if it is specified. 01549 01550 RETURN VALUES 01551 0 - ok 01552 1 - Error 01553 */ 01554 my_bool 01555 my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) 01556 { 01557 THD *thd; 01558 TABLE_LIST *tables= 0; 01559 TABLE_LIST tables_buff[1+MY_TZ_TABLES_COUNT], **last_global_next_ptr; 01560 TABLE *table; 01561 Tz_names_entry *tmp_tzname; 01562 my_bool return_val= 1; 01563 char db[]= "mysql"; 01564 int res; 01565 DBUG_ENTER("my_tz_init"); 01566 01567 /* 01568 To be able to run this from boot, we allocate a temporary THD 01569 */ 01570 if (!(thd= new THD)) 01571 DBUG_RETURN(1); 01572 thd->thread_stack= (char*) &thd; 01573 thd->store_globals(); 01574 01575 /* Init all memory structures that require explicit destruction */ 01576 if (hash_init(&tz_names, &my_charset_latin1, 20, 01577 0, 0, (hash_get_key)my_tz_names_get_key, 0, 0)) 01578 { 01579 sql_print_error("Fatal error: OOM while initializing time zones"); 01580 goto end; 01581 } 01582 if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0, 01583 (hash_get_key)my_offset_tzs_get_key, 0, 0)) 01584 { 01585 sql_print_error("Fatal error: OOM while initializing time zones"); 01586 hash_free(&tz_names); 01587 goto end; 01588 } 01589 init_alloc_root(&tz_storage, 32 * 1024, 0); 01590 VOID(pthread_mutex_init(&tz_LOCK, MY_MUTEX_INIT_FAST)); 01591 tz_inited= 1; 01592 01593 /* Add 'SYSTEM' time zone to tz_names hash */ 01594 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry())) 01595 { 01596 sql_print_error("Fatal error: OOM while initializing time zones"); 01597 goto end_with_cleanup; 01598 } 01599 tmp_tzname->name.set(STRING_WITH_LEN("SYSTEM"), &my_charset_latin1); 01600 tmp_tzname->tz= my_tz_SYSTEM; 01601 if (my_hash_insert(&tz_names, (const byte *)tmp_tzname)) 01602 { 01603 sql_print_error("Fatal error: OOM while initializing time zones"); 01604 goto end_with_cleanup; 01605 } 01606 01607 if (bootstrap) 01608 { 01609 /* If we are in bootstrap mode we should not load time zone tables */ 01610 return_val= time_zone_tables_exist= 0; 01611 goto end_with_setting_default_tz; 01612 } 01613 01614 /* 01615 After this point all memory structures are inited and we even can live 01616 without time zone description tables. Now try to load information about 01617 leap seconds shared by all time zones. 01618 */ 01619 01620 thd->set_db(db, sizeof(db)-1); 01621 bzero((char*) &tables_buff, sizeof(TABLE_LIST)); 01622 tables_buff[0].alias= tables_buff[0].table_name= 01623 (char*)"time_zone_leap_second"; 01624 tables_buff[0].lock_type= TL_READ; 01625 tables_buff[0].db= db; 01626 /* 01627 Fill TABLE_LIST for the rest of the time zone describing tables 01628 and link it to first one. 01629 */ 01630 last_global_next_ptr= &(tables_buff[0].next_global); 01631 tz_init_table_list(tables_buff + 1, &last_global_next_ptr); 01632 01633 if (simple_open_n_lock_tables(thd, tables_buff)) 01634 { 01635 sql_print_warning("Can't open and lock time zone table: %s " 01636 "trying to live without them", thd->net.last_error); 01637 /* We will try emulate that everything is ok */ 01638 return_val= time_zone_tables_exist= 0; 01639 goto end_with_setting_default_tz; 01640 } 01641 tables= tables_buff + 1; 01642 01643 /* 01644 Now we are going to load leap seconds descriptions that are shared 01645 between all time zones that use them. We are using index for getting 01646 records in proper order. Since we share the same MEM_ROOT between 01647 all time zones we just allocate enough memory for it first. 01648 */ 01649 if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage, 01650 sizeof(LS_INFO) * TZ_MAX_LEAPS))) 01651 { 01652 sql_print_error("Fatal error: Out of memory while loading " 01653 "mysql.time_zone_leap_second table"); 01654 goto end_with_close; 01655 } 01656 01657 table= tables_buff[0].table; 01658 /* 01659 It is OK to ignore ha_index_init()/ha_index_end() return values since 01660 mysql.time_zone* tables are MyISAM and these operations always succeed 01661 for MyISAM. 01662 */ 01663 (void)table->file->ha_index_init(0, 1); 01664 table->use_all_columns(); 01665 01666 tz_leapcnt= 0; 01667 01668 res= table->file->index_first(table->record[0]); 01669 01670 while (!res) 01671 { 01672 if (tz_leapcnt + 1 > TZ_MAX_LEAPS) 01673 { 01674 sql_print_error("Fatal error: While loading mysql.time_zone_leap_second" 01675 " table: too much leaps"); 01676 table->file->ha_index_end(); 01677 goto end_with_close; 01678 } 01679 01680 tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int(); 01681 tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int(); 01682 01683 tz_leapcnt++; 01684 01685 DBUG_PRINT("info", 01686 ("time_zone_leap_second table: tz_leapcnt=%u tt_time=%lld offset=%ld", 01687 tz_leapcnt, (longlong)tz_lsis[tz_leapcnt-1].ls_trans, 01688 tz_lsis[tz_leapcnt-1].ls_corr)); 01689 01690 res= table->file->index_next(table->record[0]); 01691 } 01692 01693 (void)table->file->ha_index_end(); 01694 01695 if (res != HA_ERR_END_OF_FILE) 01696 { 01697 sql_print_error("Fatal error: Error while loading " 01698 "mysql.time_zone_leap_second table"); 01699 goto end_with_close; 01700 } 01701 01702 /* 01703 Loading of info about leap seconds succeeded 01704 */ 01705 01706 return_val= 0; 01707 01708 01709 end_with_setting_default_tz: 01710 /* If we have default time zone try to load it */ 01711 if (default_tzname) 01712 { 01713 String tmp_tzname(default_tzname, &my_charset_latin1); 01714 if (!(global_system_variables.time_zone= my_tz_find(&tmp_tzname, tables))) 01715 { 01716 sql_print_error("Fatal error: Illegal or unknown default time zone '%s'", 01717 default_tzname); 01718 return_val= 1; 01719 } 01720 } 01721 01722 end_with_close: 01723 thd->version--; /* Force close to free memory */ 01724 close_thread_tables(thd); 01725 01726 end_with_cleanup: 01727 01728 /* if there were error free time zone describing structs */ 01729 if (return_val) 01730 my_tz_free(); 01731 end: 01732 delete thd; 01733 if (org_thd) 01734 org_thd->store_globals(); /* purecov: inspected */ 01735 else 01736 { 01737 /* Remember that we don't have a THD */ 01738 my_pthread_setspecific_ptr(THR_THD, 0); 01739 my_pthread_setspecific_ptr(THR_MALLOC, 0); 01740 } 01741 DBUG_RETURN(return_val); 01742 } 01743 01744 01745 /* 01746 Free resources used by time zone support infrastructure. 01747 01748 SYNOPSIS 01749 my_tz_free() 01750 */ 01751 01752 void my_tz_free() 01753 { 01754 if (tz_inited) 01755 { 01756 tz_inited= 0; 01757 VOID(pthread_mutex_destroy(&tz_LOCK)); 01758 hash_free(&offset_tzs); 01759 hash_free(&tz_names); 01760 free_root(&tz_storage, MYF(0)); 01761 } 01762 } 01763 01764 01765 /* 01766 Load time zone description from system tables. 01767 01768 SYNOPSIS 01769 tz_load_from_open_tables() 01770 tz_name - name of time zone that should be loaded. 01771 tz_tables - list of tables from which time zone description 01772 should be loaded 01773 01774 DESCRIPTION 01775 This function will try to load information about time zone specified 01776 from the list of the already opened and locked tables (first table in 01777 tz_tables should be time_zone_name, next time_zone, then 01778 time_zone_transition_type and time_zone_transition should be last). 01779 It will also update information in hash used for time zones lookup. 01780 01781 RETURN VALUES 01782 Returns pointer to newly created Time_zone object or 0 in case of error. 01783 01784 */ 01785 01786 static Time_zone* 01787 tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) 01788 { 01789 TABLE *table= 0; 01790 TIME_ZONE_INFO *tz_info; 01791 Tz_names_entry *tmp_tzname; 01792 Time_zone *return_val= 0; 01793 int res; 01794 uint tzid, ttid; 01795 my_time_t ttime; 01796 char buff[MAX_FIELD_WIDTH]; 01797 String abbr(buff, sizeof(buff), &my_charset_latin1); 01798 char *alloc_buff, *tz_name_buff; 01799 /* 01800 Temporary arrays that are used for loading of data for filling 01801 TIME_ZONE_INFO structure 01802 */ 01803 my_time_t ats[TZ_MAX_TIMES]; 01804 uchar types[TZ_MAX_TIMES]; 01805 TRAN_TYPE_INFO ttis[TZ_MAX_TYPES]; 01806 #ifdef ABBR_ARE_USED 01807 char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; 01808 #endif 01809 DBUG_ENTER("tz_load_from_open_tables"); 01810 01811 /* Prepare tz_info for loading also let us make copy of time zone name */ 01812 if (!(alloc_buff= alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) + 01813 tz_name->length() + 1))) 01814 { 01815 sql_print_error("Out of memory while loading time zone description"); 01816 return 0; 01817 } 01818 tz_info= (TIME_ZONE_INFO *)alloc_buff; 01819 bzero(tz_info, sizeof(TIME_ZONE_INFO)); 01820 tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO); 01821 /* 01822 By writing zero to the end we guarantee that we can call ptr() 01823 instead of c_ptr() for time zone name. 01824 */ 01825 strmake(tz_name_buff, tz_name->ptr(), tz_name->length()); 01826 01827 /* 01828 Let us find out time zone id by its name (there is only one index 01829 and it is specifically for this purpose). 01830 */ 01831 table= tz_tables->table; 01832 tz_tables= tz_tables->next_local; 01833 table->use_all_columns(); 01834 table->field[0]->store(tz_name->ptr(), tz_name->length(), 01835 &my_charset_latin1); 01836 /* 01837 It is OK to ignore ha_index_init()/ha_index_end() return values since 01838 mysql.time_zone* tables are MyISAM and these operations always succeed 01839 for MyISAM. 01840 */ 01841 (void)table->file->ha_index_init(0, 1); 01842 01843 if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, 01844 0, HA_READ_KEY_EXACT)) 01845 { 01846 #ifdef EXTRA_DEBUG 01847 /* 01848 Most probably user has mistyped time zone name, so no need to bark here 01849 unless we need it for debugging. 01850 */ 01851 sql_print_error("Can't find description of time zone '%s'", tz_name_buff); 01852 #endif 01853 goto end; 01854 } 01855 01856 tzid= (uint)table->field[1]->val_int(); 01857 01858 (void)table->file->ha_index_end(); 01859 01860 /* 01861 Now we need to lookup record in mysql.time_zone table in order to 01862 understand whenever this timezone uses leap seconds (again we are 01863 using the only index in this table). 01864 */ 01865 table= tz_tables->table; 01866 table->use_all_columns(); 01867 tz_tables= tz_tables->next_local; 01868 table->field[0]->store((longlong) tzid, TRUE); 01869 (void)table->file->ha_index_init(0, 1); 01870 01871 if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, 01872 0, HA_READ_KEY_EXACT)) 01873 { 01874 sql_print_error("Can't find description of time zone '%u'", tzid); 01875 goto end; 01876 } 01877 01878 /* If Uses_leap_seconds == 'Y' */ 01879 if (table->field[1]->val_int() == 1) 01880 { 01881 tz_info->leapcnt= tz_leapcnt; 01882 tz_info->lsis= tz_lsis; 01883 } 01884 01885 (void)table->file->ha_index_end(); 01886 01887 /* 01888 Now we will iterate through records for out time zone in 01889 mysql.time_zone_transition_type table. Because we want records 01890 only for our time zone guess what are we doing? 01891 Right - using special index. 01892 */ 01893 table= tz_tables->table; 01894 table->use_all_columns(); 01895 tz_tables= tz_tables->next_local; 01896 table->field[0]->store((longlong) tzid, TRUE); 01897 (void)table->file->ha_index_init(0, 1); 01898 01899 // FIXME Is there any better approach than explicitly specifying 4 ??? 01900 res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, 01901 4, HA_READ_KEY_EXACT); 01902 while (!res) 01903 { 01904 ttid= (uint)table->field[1]->val_int(); 01905 01906 if (ttid >= TZ_MAX_TYPES) 01907 { 01908 sql_print_error("Error while loading time zone description from " 01909 "mysql.time_zone_transition_type table: too big " 01910 "transition type id"); 01911 goto end; 01912 } 01913 01914 ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int(); 01915 ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0); 01916 01917 #ifdef ABBR_ARE_USED 01918 // FIXME should we do something with duplicates here ? 01919 table->field[4]->val_str(&abbr, &abbr); 01920 if (tz_info->charcnt + abbr.length() + 1 > sizeof(chars)) 01921 { 01922 sql_print_error("Error while loading time zone description from " 01923 "mysql.time_zone_transition_type table: not enough " 01924 "room for abbreviations"); 01925 goto end; 01926 } 01927 ttis[ttid].tt_abbrind= tz_info->charcnt; 01928 memcpy(chars + tz_info->charcnt, abbr.ptr(), abbr.length()); 01929 tz_info->charcnt+= abbr.length(); 01930 chars[tz_info->charcnt]= 0; 01931 tz_info->charcnt++; 01932 01933 DBUG_PRINT("info", 01934 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld " 01935 "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, 01936 chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst)); 01937 #else 01938 DBUG_PRINT("info", 01939 ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld " 01940 "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst)); 01941 #endif 01942 01943 /* ttid is increasing because we are reading using index */ 01944 DBUG_ASSERT(ttid >= tz_info->typecnt); 01945 01946 tz_info->typecnt= ttid + 1; 01947 01948 res= table->file->index_next_same(table->record[0], 01949 (byte*)table->field[0]->ptr, 4); 01950 } 01951 01952 if (res != HA_ERR_END_OF_FILE) 01953 { 01954 sql_print_error("Error while loading time zone description from " 01955 "mysql.time_zone_transition_type table"); 01956 goto end; 01957 } 01958 01959 (void)table->file->ha_index_end(); 01960 01961 01962 /* 01963 At last we are doing the same thing for records in 01964 mysql.time_zone_transition table. Here we additionaly need records 01965 in ascending order by index scan also satisfies us. 01966 */ 01967 table= tz_tables->table; 01968 table->use_all_columns(); 01969 table->field[0]->store((longlong) tzid, TRUE); 01970 (void)table->file->ha_index_init(0, 1); 01971 01972 // FIXME Is there any better approach than explicitly specifying 4 ??? 01973 res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, 01974 4, HA_READ_KEY_EXACT); 01975 while (!res) 01976 { 01977 ttime= (my_time_t)table->field[1]->val_int(); 01978 ttid= (uint)table->field[2]->val_int(); 01979 01980 if (tz_info->timecnt + 1 > TZ_MAX_TIMES) 01981 { 01982 sql_print_error("Error while loading time zone description from " 01983 "mysql.time_zone_transition table: " 01984 "too much transitions"); 01985 goto end; 01986 } 01987 if (ttid + 1 > tz_info->typecnt) 01988 { 01989 sql_print_error("Error while loading time zone description from " 01990 "mysql.time_zone_transition table: " 01991 "bad transition type id"); 01992 goto end; 01993 } 01994 01995 ats[tz_info->timecnt]= ttime; 01996 types[tz_info->timecnt]= ttid; 01997 tz_info->timecnt++; 01998 01999 DBUG_PRINT("info", 02000 ("time_zone_transition table: tz_id=%u tt_time=%lld tt_id=%u", 02001 tzid, (longlong)ttime, ttid)); 02002 02003 res= table->file->index_next_same(table->record[0], 02004 (byte*)table->field[0]->ptr, 4); 02005 } 02006 02007 /* 02008 We have to allow HA_ERR_KEY_NOT_FOUND because some time zones 02009 for example UTC have no transitons. 02010 */ 02011 if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND) 02012 { 02013 sql_print_error("Error while loading time zone description from " 02014 "mysql.time_zone_transition table"); 02015 goto end; 02016 } 02017 02018 (void)table->file->ha_index_end(); 02019 table= 0; 02020 02021 /* 02022 Now we will allocate memory and init TIME_ZONE_INFO structure. 02023 */ 02024 if (!(alloc_buff= alloc_root(&tz_storage, 02025 ALIGN_SIZE(sizeof(my_time_t) * 02026 tz_info->timecnt) + 02027 ALIGN_SIZE(tz_info->timecnt) + 02028 #ifdef ABBR_ARE_USED 02029 ALIGN_SIZE(tz_info->charcnt) + 02030 #endif 02031 sizeof(TRAN_TYPE_INFO) * tz_info->typecnt))) 02032 { 02033 sql_print_error("Out of memory while loading time zone description"); 02034 goto end; 02035 } 02036 02037 02038 tz_info->ats= (my_time_t *)alloc_buff; 02039 memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t)); 02040 alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt); 02041 tz_info->types= (uchar *)alloc_buff; 02042 memcpy(tz_info->types, types, tz_info->timecnt); 02043 alloc_buff+= ALIGN_SIZE(tz_info->timecnt); 02044 #ifdef ABBR_ARE_USED 02045 tz_info->chars= alloc_buff; 02046 memcpy(tz_info->chars, chars, tz_info->charcnt); 02047 alloc_buff+= ALIGN_SIZE(tz_info->charcnt); 02048 #endif 02049 tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff; 02050 memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO)); 02051 02052 /* 02053 Let us check how correct our time zone description and build 02054 reversed map. We don't check for tz->timecnt < 1 since it ok for GMT. 02055 */ 02056 if (tz_info->typecnt < 1) 02057 { 02058 sql_print_error("loading time zone without transition types"); 02059 goto end; 02060 } 02061 if (prepare_tz_info(tz_info, &tz_storage)) 02062 { 02063 sql_print_error("Unable to build mktime map for time zone"); 02064 goto end; 02065 } 02066 02067 02068 if (!(tmp_tzname= new (&tz_storage) Tz_names_entry()) || 02069 !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info, 02070 &(tmp_tzname->name))) || 02071 (tmp_tzname->name.set(tz_name_buff, tz_name->length(), 02072 &my_charset_latin1), 02073 my_hash_insert(&tz_names, (const byte *)tmp_tzname))) 02074 { 02075 sql_print_error("Out of memory while loading time zone"); 02076 goto end; 02077 } 02078 02079 /* 02080 Loading of time zone succeeded 02081 */ 02082 return_val= tmp_tzname->tz; 02083 02084 end: 02085 02086 if (table) 02087 (void)table->file->ha_index_end(); 02088 02089 DBUG_RETURN(return_val); 02090 } 02091 02092 02093 /* 02094 Parse string that specifies time zone as offset from UTC. 02095 02096 SYNOPSIS 02097 str_to_offset() 02098 str - pointer to string which contains offset 02099 length - length of string 02100 offset - out parameter for storing found offset in seconds. 02101 02102 DESCRIPTION 02103 This function parses string which contains time zone offset 02104 in form similar to '+10:00' and converts found value to 02105 seconds from UTC form (east is positive). 02106 02107 RETURN VALUE 02108 0 - Ok 02109 1 - String doesn't contain valid time zone offset 02110 */ 02111 my_bool 02112 str_to_offset(const char *str, uint length, long *offset) 02113 { 02114 const char *end= str + length; 02115 my_bool negative; 02116 ulong number_tmp; 02117 long offset_tmp; 02118 02119 if (length < 4) 02120 return 1; 02121 02122 if (*str == '+') 02123 negative= 0; 02124 else if (*str == '-') 02125 negative= 1; 02126 else 02127 return 1; 02128 str++; 02129 02130 number_tmp= 0; 02131 02132 while (str < end && my_isdigit(&my_charset_latin1, *str)) 02133 { 02134 number_tmp= number_tmp*10 + *str - '0'; 02135 str++; 02136 } 02137 02138 if (str + 1 >= end || *str != ':') 02139 return 1; 02140 str++; 02141 02142 offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0; 02143 02144 while (str < end && my_isdigit(&my_charset_latin1, *str)) 02145 { 02146 number_tmp= number_tmp * 10 + *str - '0'; 02147 str++; 02148 } 02149 02150 if (str != end) 02151 return 1; 02152 02153 offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN; 02154 02155 if (negative) 02156 offset_tmp= -offset_tmp; 02157 02158 /* 02159 Check if offset is in range prescribed by standard 02160 (from -12:59 to 13:00). 02161 */ 02162 02163 if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 || 02164 offset_tmp > 13 * SECS_PER_HOUR) 02165 return 1; 02166 02167 *offset= offset_tmp; 02168 02169 return 0; 02170 } 02171 02172 02173 /* 02174 Get Time_zone object for specified time zone. 02175 02176 SYNOPSIS 02177 my_tz_find() 02178 name - time zone specification 02179 tz_tables - list of opened'n'locked time zone describing tables 02180 02181 DESCRIPTION 02182 This function checks if name is one of time zones described in db, 02183 predefined SYSTEM time zone or valid time zone specification as 02184 offset from UTC (In last case it will create proper Time_zone_offset 02185 object if there were not any.). If name is ok it returns corresponding 02186 Time_zone object. 02187 02188 Clients of this function are not responsible for releasing resources 02189 occupied by returned Time_zone object so they can just forget pointers 02190 to Time_zone object if they are not needed longer. 02191 02192 Other important property of this function: if some Time_zone found once 02193 it will be for sure found later, so this function can also be used for 02194 checking if proper Time_zone object exists (and if there will be error 02195 it will be reported during first call). 02196 02197 If name pointer is 0 then this function returns 0 (this allows to pass 0 02198 values as parameter without additional external check and this property 02199 is used by @@time_zone variable handling code). 02200 02201 It will perform lookup in system tables (mysql.time_zone*) if needed 02202 using tz_tables as list of already opened tables (for info about this 02203 list look at tz_load_from_open_tables() description). It won't perform 02204 such lookup if no time zone describing tables were found during server 02205 start up. 02206 02207 RETURN VALUE 02208 Pointer to corresponding Time_zone object. 0 - in case of bad time zone 02209 specification or other error. 02210 02211 */ 02212 Time_zone * 02213 my_tz_find(const String * name, TABLE_LIST *tz_tables) 02214 { 02215 Tz_names_entry *tmp_tzname; 02216 Time_zone *result_tz= 0; 02217 long offset; 02218 DBUG_ENTER("my_tz_find"); 02219 DBUG_PRINT("enter", ("time zone name='%s'", 02220 name ? ((String *)name)->c_ptr_safe() : "NULL")); 02221 DBUG_ASSERT(!time_zone_tables_exist || tz_tables || 02222 current_thd->slave_thread); 02223 02224 if (!name) 02225 DBUG_RETURN(0); 02226 02227 VOID(pthread_mutex_lock(&tz_LOCK)); 02228 02229 if (!str_to_offset(name->ptr(), name->length(), &offset)) 02230 { 02231 02232 if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs, 02233 (const byte *)&offset, 02234 sizeof(long)))) 02235 { 02236 DBUG_PRINT("info", ("Creating new Time_zone_offset object")); 02237 02238 if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) || 02239 my_hash_insert(&offset_tzs, (const byte *) result_tz)) 02240 { 02241 result_tz= 0; 02242 sql_print_error("Fatal error: Out of memory " 02243 "while setting new time zone"); 02244 } 02245 } 02246 } 02247 else 02248 { 02249 result_tz= 0; 02250 if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names, 02251 (const byte *)name->ptr(), 02252 name->length()))) 02253 result_tz= tmp_tzname->tz; 02254 else if (time_zone_tables_exist && tz_tables) 02255 result_tz= tz_load_from_open_tables(name, tz_tables); 02256 } 02257 02258 VOID(pthread_mutex_unlock(&tz_LOCK)); 02259 02260 DBUG_RETURN(result_tz); 02261 } 02262 02263 02264 /* 02265 A more standalone version of my_tz_find(): will open tz tables if needed. 02266 This is so far only used by replication, where time zone setting does not 02267 happen in the usual query context. 02268 02269 SYNOPSIS 02270 my_tz_find_with_opening_tz_tables() 02271 thd - pointer to thread's THD structure 02272 name - time zone specification 02273 02274 DESCRIPTION 02275 This function tries to find a time zone which matches the named passed in 02276 argument. If it fails, it will open time zone tables and re-try the 02277 search. 02278 This function is needed for the slave SQL thread, which does not do the 02279 addition of time zone tables which is usually done during query parsing 02280 (as time zone setting by slave does not happen in mysql_parse() but 02281 before). So it needs to open tz tables by itself if needed. 02282 See notes of my_tz_find() as they also apply here. 02283 02284 RETURN VALUE 02285 Pointer to corresponding Time_zone object. 0 - in case of bad time zone 02286 specification or other error. 02287 */ 02288 02289 Time_zone *my_tz_find_with_opening_tz_tables(THD *thd, const String *name) 02290 { 02291 Time_zone *tz; 02292 DBUG_ENTER("my_tz_find_with_opening_tables"); 02293 DBUG_ASSERT(thd); 02294 DBUG_ASSERT(thd->slave_thread); // intended for use with slave thread only 02295 02296 if (!(tz= my_tz_find(name, 0)) && time_zone_tables_exist) 02297 { 02298 /* 02299 Probably we have not loaded this time zone yet so let us look it up in 02300 our time zone tables. Note that if we don't have tz tables on this 02301 slave, we don't even try. 02302 */ 02303 TABLE_LIST tables[MY_TZ_TABLES_COUNT]; 02304 TABLE_LIST *dummy; 02305 TABLE_LIST **dummyp= &dummy; 02306 tz_init_table_list(tables, &dummyp); 02307 if (simple_open_n_lock_tables(thd, tables)) 02308 DBUG_RETURN(0); 02309 tz= my_tz_find(name, tables); 02310 /* We need to close tables _now_ to not pollute coming query */ 02311 close_thread_tables(thd); 02312 } 02313 DBUG_RETURN(tz); 02314 } 02315 02316 #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ 02317 02318 02319 #ifdef TZINFO2SQL 02320 /* 02321 This code belongs to mysql_tzinfo_to_sql converter command line utility. 02322 This utility should be used by db admin for populating mysql.time_zone 02323 tables. 02324 */ 02325 02326 02327 /* 02328 Print info about time zone described by TIME_ZONE_INFO struct as 02329 SQL statements populating mysql.time_zone* tables. 02330 02331 SYNOPSIS 02332 print_tz_as_sql() 02333 tz_name - name of time zone 02334 sp - structure describing time zone 02335 */ 02336 void 02337 print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp) 02338 { 02339 uint i; 02340 02341 /* Here we assume that all time zones have same leap correction tables */ 02342 printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n", 02343 sp->leapcnt ? "Y" : "N"); 02344 printf("SET @time_zone_id= LAST_INSERT_ID();\n"); 02345 printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \ 02346 ('%s', @time_zone_id);\n", tz_name); 02347 02348 if (sp->timecnt) 02349 { 02350 printf("INSERT INTO time_zone_transition \ 02351 (Time_zone_id, Transition_time, Transition_type_id) VALUES\n"); 02352 for (i= 0; i < sp->timecnt; i++) 02353 printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i], 02354 (uint)sp->types[i]); 02355 printf(";\n"); 02356 } 02357 02358 printf("INSERT INTO time_zone_transition_type \ 02359 (Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n"); 02360 02361 for (i= 0; i < sp->typecnt; i++) 02362 printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i, 02363 sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst, 02364 sp->chars + sp->ttis[i].tt_abbrind); 02365 printf(";\n"); 02366 } 02367 02368 02369 /* 02370 Print info about leap seconds in time zone as SQL statements 02371 populating mysql.time_zone_leap_second table. 02372 02373 SYNOPSIS 02374 print_tz_leaps_as_sql() 02375 sp - structure describing time zone 02376 */ 02377 void 02378 print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp) 02379 { 02380 uint i; 02381 02382 /* 02383 We are assuming that there are only one list of leap seconds 02384 For all timezones. 02385 */ 02386 printf("TRUNCATE TABLE time_zone_leap_second;\n"); 02387 02388 if (sp->leapcnt) 02389 { 02390 printf("INSERT INTO time_zone_leap_second \ 02391 (Transition_time, Correction) VALUES\n"); 02392 for (i= 0; i < sp->leapcnt; i++) 02393 printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","), 02394 sp->lsis[i].ls_trans, sp->lsis[i].ls_corr); 02395 printf(";\n"); 02396 } 02397 02398 printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n"); 02399 } 02400 02401 02402 /* 02403 Some variables used as temporary or as parameters 02404 in recursive scan_tz_dir() code. 02405 */ 02406 TIME_ZONE_INFO tz_info; 02407 MEM_ROOT tz_storage; 02408 char fullname[FN_REFLEN + 1]; 02409 char *root_name_end; 02410 02411 02412 /* 02413 Recursively scan zoneinfo directory and print all found time zone 02414 descriptions as SQL. 02415 02416 SYNOPSIS 02417 scan_tz_dir() 02418 name_end - pointer to end of path to directory to be searched. 02419 02420 DESCRIPTION 02421 This auxiliary recursive function also uses several global 02422 variables as in parameters and for storing temporary values. 02423 02424 fullname - path to directory that should be scanned. 02425 root_name_end - pointer to place in fullname where part with 02426 path to initial directory ends. 02427 current_tz_id - last used time zone id 02428 02429 RETURN VALUE 02430 0 - Ok, 1 - Fatal error 02431 02432 */ 02433 my_bool 02434 scan_tz_dir(char * name_end) 02435 { 02436 MY_DIR *cur_dir; 02437 char *name_end_tmp; 02438 uint i; 02439 02440 if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT)))) 02441 return 1; 02442 02443 name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname)); 02444 02445 for (i= 0; i < cur_dir->number_off_files; i++) 02446 { 02447 if (cur_dir->dir_entry[i].name[0] != '.') 02448 { 02449 name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name, 02450 FN_REFLEN - (name_end - fullname)); 02451 02452 if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode)) 02453 { 02454 if (scan_tz_dir(name_end_tmp)) 02455 { 02456 my_dirend(cur_dir); 02457 return 1; 02458 } 02459 } 02460 else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode)) 02461 { 02462 init_alloc_root(&tz_storage, 32768, 0); 02463 if (!tz_load(fullname, &tz_info, &tz_storage)) 02464 print_tz_as_sql(root_name_end + 1, &tz_info); 02465 else 02466 fprintf(stderr, 02467 "Warning: Unable to load '%s' as time zone. Skipping it.\n", 02468 fullname); 02469 free_root(&tz_storage, MYF(0)); 02470 } 02471 else 02472 fprintf(stderr, "Warning: '%s' is not regular file or directory\n", 02473 fullname); 02474 } 02475 } 02476 02477 my_dirend(cur_dir); 02478 02479 return 0; 02480 } 02481 02482 02483 int 02484 main(int argc, char **argv) 02485 { 02486 #ifndef __NETWARE__ 02487 MY_INIT(argv[0]); 02488 02489 if (argc != 2 && argc != 3) 02490 { 02491 fprintf(stderr, "Usage:\n"); 02492 fprintf(stderr, " %s timezonedir\n", argv[0]); 02493 fprintf(stderr, " %s timezonefile timezonename\n", argv[0]); 02494 fprintf(stderr, " %s --leap timezonefile\n", argv[0]); 02495 return 1; 02496 } 02497 02498 if (argc == 2) 02499 { 02500 root_name_end= strmake(fullname, argv[1], FN_REFLEN); 02501 02502 printf("TRUNCATE TABLE time_zone;\n"); 02503 printf("TRUNCATE TABLE time_zone_name;\n"); 02504 printf("TRUNCATE TABLE time_zone_transition;\n"); 02505 printf("TRUNCATE TABLE time_zone_transition_type;\n"); 02506 02507 if (scan_tz_dir(root_name_end)) 02508 { 02509 fprintf(stderr, "There were fatal errors during processing " 02510 "of zoneinfo directory\n"); 02511 return 1; 02512 } 02513 02514 printf("ALTER TABLE time_zone_transition " 02515 "ORDER BY Time_zone_id, Transition_time;\n"); 02516 printf("ALTER TABLE time_zone_transition_type " 02517 "ORDER BY Time_zone_id, Transition_type_id;\n"); 02518 } 02519 else 02520 { 02521 init_alloc_root(&tz_storage, 32768, 0); 02522 02523 if (strcmp(argv[1], "--leap") == 0) 02524 { 02525 if (tz_load(argv[2], &tz_info, &tz_storage)) 02526 { 02527 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]); 02528 return 1; 02529 } 02530 print_tz_leaps_as_sql(&tz_info); 02531 } 02532 else 02533 { 02534 if (tz_load(argv[1], &tz_info, &tz_storage)) 02535 { 02536 fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]); 02537 return 1; 02538 } 02539 print_tz_as_sql(argv[2], &tz_info); 02540 } 02541 02542 free_root(&tz_storage, MYF(0)); 02543 } 02544 02545 #else 02546 fprintf(stderr, "This tool has not been ported to NetWare\n"); 02547 #endif /* __NETWARE__ */ 02548 02549 return 0; 02550 } 02551 02552 #endif /* defined(TZINFO2SQL) */ 02553 02554 02555 #ifdef TESTTIME 02556 02557 /* 02558 Some simple brute-force test wich allowed to catch a pair of bugs. 02559 Also can provide interesting facts about system's time zone support 02560 implementation. 02561 */ 02562 02563 #ifndef CHAR_BIT 02564 #define CHAR_BIT 8 02565 #endif 02566 02567 #ifndef TYPE_BIT 02568 #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) 02569 #endif 02570 02571 #ifndef TYPE_SIGNED 02572 #define TYPE_SIGNED(type) (((type) -1) < 0) 02573 #endif 02574 02575 my_bool 02576 is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg) 02577 { 02578 return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) && 02579 (time_arg->month == (uint)tm_arg->tm_mon+1) && 02580 (time_arg->day == (uint)tm_arg->tm_mday) && 02581 (time_arg->hour == (uint)tm_arg->tm_hour) && 02582 (time_arg->minute == (uint)tm_arg->tm_min) && 02583 (time_arg->second == (uint)tm_arg->tm_sec) && 02584 time_arg->second_part == 0; 02585 } 02586 02587 02588 int 02589 main(int argc, char **argv) 02590 { 02591 my_bool localtime_negative; 02592 TIME_ZONE_INFO tz_info; 02593 struct tm tmp; 02594 TIME time_tmp; 02595 time_t t, t1, t2; 02596 char fullname[FN_REFLEN+1]; 02597 char *str_end; 02598 MEM_ROOT tz_storage; 02599 02600 MY_INIT(argv[0]); 02601 02602 init_alloc_root(&tz_storage, 32768, 0); 02603 02604 /* let us set some well known timezone */ 02605 setenv("TZ", "MET", 1); 02606 tzset(); 02607 02608 /* Some initial time zone related system info */ 02609 printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned", 02610 (uint)TYPE_BIT(time_t)); 02611 if (TYPE_SIGNED(time_t)) 02612 { 02613 t= -100; 02614 localtime_negative= test(localtime_r(&t, &tmp) != 0); 02615 printf("localtime_r %s negative params \ 02616 (time_t=%d is %d-%d-%d %d:%d:%d)\n", 02617 (localtime_negative ? "supports" : "doesn't support"), (int)t, 02618 TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday, 02619 tmp.tm_hour, tmp.tm_min, tmp.tm_sec); 02620 02621 printf("mktime %s negative results (%d)\n", 02622 (t == mktime(&tmp) ? "doesn't support" : "supports"), 02623 (int)mktime(&tmp)); 02624 } 02625 02626 tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30; 02627 tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1; 02628 t= mktime(&tmp); 02629 printf("mktime returns %s for spring time gap (%d)\n", 02630 (t != (time_t)-1 ? "something" : "error"), (int)t); 02631 02632 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1; 02633 tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0; 02634 t= mktime(&tmp); 02635 printf("mktime returns %s for non existing date (%d)\n", 02636 (t != (time_t)-1 ? "something" : "error"), (int)t); 02637 02638 tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1; 02639 tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1; 02640 t= mktime(&tmp); 02641 printf("mktime %s unnormalized input (%d)\n", 02642 (t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t); 02643 02644 tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26; 02645 tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1; 02646 mktime(&tmp); 02647 tmp.tm_hour= 2; tmp.tm_isdst= -1; 02648 t= mktime(&tmp); 02649 tmp.tm_hour= 4; tmp.tm_isdst= 0; 02650 mktime(&tmp); 02651 tmp.tm_hour= 2; tmp.tm_isdst= -1; 02652 t1= mktime(&tmp); 02653 printf("mktime is %s (%d %d)\n", 02654 (t == t1 ? "determenistic" : "is non-determenistic"), 02655 (int)t, (int)t1); 02656 02657 /* Let us load time zone description */ 02658 str_end= strmake(fullname, TZDIR, FN_REFLEN); 02659 strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname)); 02660 02661 if (tz_load(fullname, &tz_info, &tz_storage)) 02662 { 02663 printf("Unable to load time zone info from '%s'\n", fullname); 02664 free_root(&tz_storage, MYF(0)); 02665 return 1; 02666 } 02667 02668 printf("Testing our implementation\n"); 02669 02670 if (TYPE_SIGNED(time_t) && localtime_negative) 02671 { 02672 for (t= -40000; t < 20000; t++) 02673 { 02674 localtime_r(&t, &tmp); 02675 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info); 02676 if (!is_equal_TIME_tm(&time_tmp, &tmp)) 02677 { 02678 printf("Problem with negative time_t = %d\n", (int)t); 02679 free_root(&tz_storage, MYF(0)); 02680 return 1; 02681 } 02682 } 02683 printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n"); 02684 } 02685 02686 for (t= 1000000000; t < 1100000000; t+= 13) 02687 { 02688 localtime_r(&t,&tmp); 02689 gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info); 02690 02691 if (!is_equal_TIME_tm(&time_tmp, &tmp)) 02692 { 02693 printf("Problem with time_t = %d\n", (int)t); 02694 free_root(&tz_storage, MYF(0)); 02695 return 1; 02696 } 02697 } 02698 printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n"); 02699 02700 init_time(); 02701 02702 /* 02703 Be careful here! my_system_gmt_sec doesn't fully handle unnormalized 02704 dates. 02705 */ 02706 for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++) 02707 { 02708 for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++) 02709 { 02710 for (time_tmp.day= 1; 02711 time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1]; 02712 time_tmp.day++) 02713 { 02714 for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++) 02715 { 02716 for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5) 02717 { 02718 for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25) 02719 { 02720 long not_used; 02721 my_bool not_used_2; 02722 t= (time_t)my_system_gmt_sec(&time_tmp, ¬_used, ¬_used_2); 02723 t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, ¬_used_2); 02724 if (t != t1) 02725 { 02726 /* 02727 We need special handling during autumn since my_system_gmt_sec 02728 prefers greater time_t values (in MET) for ambiguity. 02729 And BTW that is a bug which should be fixed !!! 02730 */ 02731 tmp.tm_year= time_tmp.year - TM_YEAR_BASE; 02732 tmp.tm_mon= time_tmp.month - 1; 02733 tmp.tm_mday= time_tmp.day; 02734 tmp.tm_hour= time_tmp.hour; 02735 tmp.tm_min= time_tmp.minute; 02736 tmp.tm_sec= time_tmp.second; 02737 tmp.tm_isdst= 1; 02738 02739 t2= mktime(&tmp); 02740 02741 if (t1 == t2) 02742 continue; 02743 02744 printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n", 02745 time_tmp.year, time_tmp.month, time_tmp.day, 02746 time_tmp.hour, time_tmp.minute, time_tmp.second, 02747 (int)t,(int)t1); 02748 02749 free_root(&tz_storage, MYF(0)); 02750 return 1; 02751 } 02752 } 02753 } 02754 } 02755 } 02756 } 02757 } 02758 02759 printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n"); 02760 02761 free_root(&tz_storage, MYF(0)); 02762 return 0; 02763 } 02764 02765 #endif /* defined(TESTTIME) */
1.4.7

