00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "postgres_fe.h"
00014
00015 #include <fcntl.h>
00016 #include <sys/stat.h>
00017 #include <time.h>
00018
00019 #include "pgtz.h"
00020
00021
00022 extern const char *select_default_timezone(const char *share_path);
00023
00024
00025 #ifndef SYSTEMTZDIR
00026 static char tzdirpath[MAXPGPATH];
00027 #endif
00028
00029
00030
00031
00032
00033
00034
00035 static const char *
00036 pg_TZDIR(void)
00037 {
00038 #ifndef SYSTEMTZDIR
00039
00040 return tzdirpath;
00041 #else
00042
00043 return SYSTEMTZDIR;
00044 #endif
00045 }
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 int
00064 pg_open_tzfile(const char *name, char *canonname)
00065 {
00066 char fullname[MAXPGPATH];
00067
00068 if (canonname)
00069 strlcpy(canonname, name, TZ_STRLEN_MAX + 1);
00070
00071 strcpy(fullname, pg_TZDIR());
00072 if (strlen(fullname) + 1 + strlen(name) >= MAXPGPATH)
00073 return -1;
00074 strcat(fullname, "/");
00075 strcat(fullname, name);
00076
00077 return open(fullname, O_RDONLY | PG_BINARY, 0);
00078 }
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 static pg_tz *
00090 pg_load_tz(const char *name)
00091 {
00092 static pg_tz tz;
00093
00094 if (strlen(name) > TZ_STRLEN_MAX)
00095 return NULL;
00096
00097
00098
00099
00100 if (strcmp(name, "GMT") == 0)
00101 {
00102 if (tzparse(name, &tz.state, TRUE) != 0)
00103 {
00104
00105 return NULL;
00106 }
00107 }
00108 else if (tzload(name, NULL, &tz.state, TRUE) != 0)
00109 {
00110 if (name[0] == ':' || tzparse(name, &tz.state, FALSE) != 0)
00111 {
00112 return NULL;
00113 }
00114 }
00115
00116 strcpy(tz.TZname, name);
00117
00118 return &tz;
00119 }
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139 #ifndef WIN32
00140
00141 #define T_DAY ((time_t) (60*60*24))
00142 #define T_WEEK ((time_t) (60*60*24*7))
00143 #define T_MONTH ((time_t) (60*60*24*31))
00144
00145 #define MAX_TEST_TIMES (52*100)
00146
00147 struct tztry
00148 {
00149 int n_test_times;
00150 time_t test_times[MAX_TEST_TIMES];
00151 };
00152
00153 static void scan_available_timezones(char *tzdir, char *tzdirsub,
00154 struct tztry * tt,
00155 int *bestscore, char *bestzonename);
00156
00157
00158
00159
00160
00161 static int
00162 get_timezone_offset(struct tm * tm)
00163 {
00164 #if defined(HAVE_STRUCT_TM_TM_ZONE)
00165 return tm->tm_gmtoff;
00166 #elif defined(HAVE_INT_TIMEZONE)
00167 return -TIMEZONE_GLOBAL;
00168 #else
00169 #error No way to determine TZ? Can this happen?
00170 #endif
00171 }
00172
00173
00174
00175
00176 static time_t
00177 build_time_t(int year, int month, int day)
00178 {
00179 struct tm tm;
00180
00181 memset(&tm, 0, sizeof(tm));
00182 tm.tm_mday = day;
00183 tm.tm_mon = month - 1;
00184 tm.tm_year = year - 1900;
00185
00186 return mktime(&tm);
00187 }
00188
00189
00190
00191
00192 static bool
00193 compare_tm(struct tm * s, struct pg_tm * p)
00194 {
00195 if (s->tm_sec != p->tm_sec ||
00196 s->tm_min != p->tm_min ||
00197 s->tm_hour != p->tm_hour ||
00198 s->tm_mday != p->tm_mday ||
00199 s->tm_mon != p->tm_mon ||
00200 s->tm_year != p->tm_year ||
00201 s->tm_wday != p->tm_wday ||
00202 s->tm_yday != p->tm_yday ||
00203 s->tm_isdst != p->tm_isdst)
00204 return false;
00205 return true;
00206 }
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219 static int
00220 score_timezone(const char *tzname, struct tztry * tt)
00221 {
00222 int i;
00223 pg_time_t pgtt;
00224 struct tm *systm;
00225 struct pg_tm *pgtm;
00226 char cbuf[TZ_STRLEN_MAX + 1];
00227 pg_tz *tz;
00228
00229
00230 tz = pg_load_tz(tzname);
00231 if (!tz)
00232 return -1;
00233
00234
00235 if (!pg_tz_acceptable(tz))
00236 {
00237 #ifdef DEBUG_IDENTIFY_TIMEZONE
00238 fprintf(stderr, "Reject TZ \"%s\": uses leap seconds\n", tzname);
00239 #endif
00240 return -1;
00241 }
00242
00243
00244 for (i = 0; i < tt->n_test_times; i++)
00245 {
00246 pgtt = (pg_time_t) (tt->test_times[i]);
00247 pgtm = pg_localtime(&pgtt, tz);
00248 if (!pgtm)
00249 return -1;
00250 systm = localtime(&(tt->test_times[i]));
00251 if (!systm)
00252 {
00253 #ifdef DEBUG_IDENTIFY_TIMEZONE
00254 fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s, system had no data\n",
00255 tzname, i, (long) pgtt,
00256 pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
00257 pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
00258 pgtm->tm_isdst ? "dst" : "std");
00259 #endif
00260 return i;
00261 }
00262 if (!compare_tm(systm, pgtm))
00263 {
00264 #ifdef DEBUG_IDENTIFY_TIMEZONE
00265 fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s\n",
00266 tzname, i, (long) pgtt,
00267 pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
00268 pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
00269 pgtm->tm_isdst ? "dst" : "std",
00270 systm->tm_year + 1900, systm->tm_mon + 1, systm->tm_mday,
00271 systm->tm_hour, systm->tm_min, systm->tm_sec,
00272 systm->tm_isdst ? "dst" : "std");
00273 #endif
00274 return i;
00275 }
00276 if (systm->tm_isdst >= 0)
00277 {
00278
00279 if (pgtm->tm_zone == NULL)
00280 return -1;
00281 memset(cbuf, 0, sizeof(cbuf));
00282 strftime(cbuf, sizeof(cbuf) - 1, "%Z", systm);
00283 if (strcmp(cbuf, pgtm->tm_zone) != 0)
00284 {
00285 #ifdef DEBUG_IDENTIFY_TIMEZONE
00286 fprintf(stderr, "TZ \"%s\" scores %d: at %ld \"%s\" versus \"%s\"\n",
00287 tzname, i, (long) pgtt,
00288 pgtm->tm_zone, cbuf);
00289 #endif
00290 return i;
00291 }
00292 }
00293 }
00294
00295 #ifdef DEBUG_IDENTIFY_TIMEZONE
00296 fprintf(stderr, "TZ \"%s\" gets max score %d\n", tzname, i);
00297 #endif
00298
00299 return i;
00300 }
00301
00302
00303
00304
00305
00306
00307
00308
00309 static const char *
00310 identify_system_timezone(void)
00311 {
00312 static char resultbuf[TZ_STRLEN_MAX + 1];
00313 time_t tnow;
00314 time_t t;
00315 struct tztry tt;
00316 struct tm *tm;
00317 int thisyear;
00318 int bestscore;
00319 char tmptzdir[MAXPGPATH];
00320 int std_ofs;
00321 char std_zone_name[TZ_STRLEN_MAX + 1],
00322 dst_zone_name[TZ_STRLEN_MAX + 1];
00323 char cbuf[TZ_STRLEN_MAX + 1];
00324
00325
00326 tzset();
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347 tnow = time(NULL);
00348 tm = localtime(&tnow);
00349 if (!tm)
00350 return NULL;
00351 thisyear = tm->tm_year + 1900;
00352
00353 t = build_time_t(thisyear, 1, 15);
00354
00355
00356
00357
00358
00359
00360
00361 t -= (t % T_WEEK);
00362
00363 tt.n_test_times = 0;
00364 tt.test_times[tt.n_test_times++] = t;
00365
00366 t = build_time_t(thisyear, 7, 15);
00367 t -= (t % T_WEEK);
00368
00369 tt.test_times[tt.n_test_times++] = t;
00370
00371 while (tt.n_test_times < MAX_TEST_TIMES)
00372 {
00373 t -= T_WEEK;
00374 tt.test_times[tt.n_test_times++] = t;
00375 }
00376
00377
00378 strcpy(tmptzdir, pg_TZDIR());
00379 bestscore = -1;
00380 resultbuf[0] = '\0';
00381 scan_available_timezones(tmptzdir, tmptzdir + strlen(tmptzdir) + 1,
00382 &tt,
00383 &bestscore, resultbuf);
00384 if (bestscore > 0)
00385 {
00386
00387 if (strcmp(resultbuf, "Factory") == 0)
00388 return NULL;
00389 return resultbuf;
00390 }
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400 memset(std_zone_name, 0, sizeof(std_zone_name));
00401 memset(dst_zone_name, 0, sizeof(dst_zone_name));
00402 std_ofs = 0;
00403
00404 tnow = time(NULL);
00405
00406
00407
00408
00409
00410 tnow -= (tnow % T_DAY);
00411
00412
00413
00414
00415
00416
00417 for (t = tnow; t <= tnow + T_MONTH * 14; t += T_MONTH)
00418 {
00419 tm = localtime(&t);
00420 if (!tm)
00421 continue;
00422 if (tm->tm_isdst < 0)
00423 continue;
00424 if (tm->tm_isdst == 0 && std_zone_name[0] == '\0')
00425 {
00426
00427 memset(cbuf, 0, sizeof(cbuf));
00428 strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm);
00429 strcpy(std_zone_name, cbuf);
00430 std_ofs = get_timezone_offset(tm);
00431 }
00432 if (tm->tm_isdst > 0 && dst_zone_name[0] == '\0')
00433 {
00434
00435 memset(cbuf, 0, sizeof(cbuf));
00436 strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm);
00437 strcpy(dst_zone_name, cbuf);
00438 }
00439
00440 if (std_zone_name[0] && dst_zone_name[0])
00441 break;
00442 }
00443
00444
00445 if (std_zone_name[0] == '\0')
00446 {
00447 #ifdef DEBUG_IDENTIFY_TIMEZONE
00448 fprintf(stderr, "could not determine system time zone\n");
00449 #endif
00450 return NULL;
00451 }
00452
00453
00454 if (dst_zone_name[0] != '\0')
00455 {
00456 snprintf(resultbuf, sizeof(resultbuf), "%s%d%s",
00457 std_zone_name, -std_ofs / 3600, dst_zone_name);
00458 if (score_timezone(resultbuf, &tt) > 0)
00459 return resultbuf;
00460 }
00461
00462
00463 strcpy(resultbuf, std_zone_name);
00464 if (score_timezone(resultbuf, &tt) > 0)
00465 return resultbuf;
00466
00467
00468 snprintf(resultbuf, sizeof(resultbuf), "%s%d",
00469 std_zone_name, -std_ofs / 3600);
00470 if (score_timezone(resultbuf, &tt) > 0)
00471 return resultbuf;
00472
00473
00474
00475
00476
00477
00478
00479 snprintf(resultbuf, sizeof(resultbuf), "Etc/GMT%s%d",
00480 (-std_ofs > 0) ? "+" : "", -std_ofs / 3600);
00481
00482 #ifdef DEBUG_IDENTIFY_TIMEZONE
00483 fprintf(stderr, "could not recognize system time zone, using \"%s\"\n",
00484 resultbuf);
00485 #endif
00486 return resultbuf;
00487 }
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508 static void
00509 scan_available_timezones(char *tzdir, char *tzdirsub, struct tztry * tt,
00510 int *bestscore, char *bestzonename)
00511 {
00512 int tzdir_orig_len = strlen(tzdir);
00513 char **names;
00514 char **namep;
00515
00516 names = pgfnames(tzdir);
00517 if (!names)
00518 return;
00519
00520 for (namep = names; *namep; namep++)
00521 {
00522 char *name = *namep;
00523 struct stat statbuf;
00524
00525
00526 if (name[0] == '.')
00527 continue;
00528
00529 snprintf(tzdir + tzdir_orig_len, MAXPGPATH - tzdir_orig_len,
00530 "/%s", name);
00531
00532 if (stat(tzdir, &statbuf) != 0)
00533 {
00534 #ifdef DEBUG_IDENTIFY_TIMEZONE
00535 fprintf(stderr, "could not stat \"%s\": %s\n",
00536 tzdir, strerror(errno));
00537 #endif
00538 tzdir[tzdir_orig_len] = '\0';
00539 continue;
00540 }
00541
00542 if (S_ISDIR(statbuf.st_mode))
00543 {
00544
00545 scan_available_timezones(tzdir, tzdirsub, tt,
00546 bestscore, bestzonename);
00547 }
00548 else
00549 {
00550
00551 int score = score_timezone(tzdirsub, tt);
00552
00553 if (score > *bestscore)
00554 {
00555 *bestscore = score;
00556 strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
00557 }
00558 else if (score == *bestscore)
00559 {
00560
00561 if (strlen(tzdirsub) < strlen(bestzonename) ||
00562 (strlen(tzdirsub) == strlen(bestzonename) &&
00563 strcmp(tzdirsub, bestzonename) < 0))
00564 strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
00565 }
00566 }
00567
00568
00569 tzdir[tzdir_orig_len] = '\0';
00570 }
00571
00572 pgfnames_cleanup(names);
00573 }
00574 #else
00575
00576 static const struct
00577 {
00578 const char *stdname;
00579 const char *dstname;
00580 const char *pgtzname;
00581 } win32_tzmap[] =
00582
00583 {
00584
00585
00586
00587
00588
00589
00590
00591
00592 {
00593 "Afghanistan Standard Time", "Afghanistan Daylight Time",
00594 "Asia/Kabul"
00595 },
00596 {
00597 "Alaskan Standard Time", "Alaskan Daylight Time",
00598 "US/Alaska"
00599 },
00600 {
00601 "Arab Standard Time", "Arab Daylight Time",
00602 "Asia/Kuwait"
00603 },
00604 {
00605 "Arabian Standard Time", "Arabian Daylight Time",
00606 "Asia/Muscat"
00607 },
00608 {
00609 "Arabic Standard Time", "Arabic Daylight Time",
00610 "Asia/Baghdad"
00611 },
00612 {
00613 "Argentina Standard Time", "Argentina Daylight Time",
00614 "America/Buenos_Aires"
00615 },
00616 {
00617 "Armenian Standard Time", "Armenian Daylight Time",
00618 "Asia/Yerevan"
00619 },
00620 {
00621 "Atlantic Standard Time", "Atlantic Daylight Time",
00622 "Canada/Atlantic"
00623 },
00624 {
00625 "AUS Central Standard Time", "AUS Central Daylight Time",
00626 "Australia/Darwin"
00627 },
00628 {
00629 "AUS Eastern Standard Time", "AUS Eastern Daylight Time",
00630 "Australia/Canberra"
00631 },
00632 {
00633 "Azerbaijan Standard Time", "Azerbaijan Daylight Time",
00634 "Asia/Baku"
00635 },
00636 {
00637 "Azores Standard Time", "Azores Daylight Time",
00638 "Atlantic/Azores"
00639 },
00640 {
00641 "Bangladesh Standard Time", "Bangladesh Daylight Time",
00642 "Asia/Dhaka"
00643 },
00644 {
00645 "Canada Central Standard Time", "Canada Central Daylight Time",
00646 "Canada/Saskatchewan"
00647 },
00648 {
00649 "Cape Verde Standard Time", "Cape Verde Daylight Time",
00650 "Atlantic/Cape_Verde"
00651 },
00652 {
00653 "Caucasus Standard Time", "Caucasus Daylight Time",
00654 "Asia/Baku"
00655 },
00656 {
00657 "Cen. Australia Standard Time", "Cen. Australia Daylight Time",
00658 "Australia/Adelaide"
00659 },
00660
00661 {
00662 "Central America Standard Time", "Central America Daylight Time",
00663 "CST6"
00664 },
00665 {
00666 "Central Asia Standard Time", "Central Asia Daylight Time",
00667 "Asia/Dhaka"
00668 },
00669 {
00670 "Central Brazilian Standard Time", "Central Brazilian Daylight Time",
00671 "America/Cuiaba"
00672 },
00673 {
00674 "Central Europe Standard Time", "Central Europe Daylight Time",
00675 "Europe/Belgrade"
00676 },
00677
00678 {
00679 "Central European Standard Time", "Central European Daylight Time",
00680 "Europe/Sarajevo"
00681 },
00682
00683 {
00684 "Central Pacific Standard Time", "Central Pacific Daylight Time",
00685 "Pacific/Noumea"
00686 },
00687
00688 {
00689 "Central Standard Time", "Central Daylight Time",
00690 "US/Central"
00691 },
00692 {
00693 "Central Standard Time (Mexico)", "Central Daylight Time (Mexico)",
00694 "America/Mexico_City"
00695 },
00696
00697 {
00698 "China Standard Time", "China Daylight Time",
00699 "Asia/Hong_Kong"
00700 },
00701
00702 {
00703 "Dateline Standard Time", "Dateline Daylight Time",
00704 "Etc/GMT+12"
00705 },
00706 {
00707 "E. Africa Standard Time", "E. Africa Daylight Time",
00708 "Africa/Nairobi"
00709 },
00710 {
00711 "E. Australia Standard Time", "E. Australia Daylight Time",
00712 "Australia/Brisbane"
00713 },
00714 {
00715 "E. Europe Standard Time", "E. Europe Daylight Time",
00716 "Europe/Bucharest"
00717 },
00718 {
00719 "E. South America Standard Time", "E. South America Daylight Time",
00720 "America/Araguaina"
00721 },
00722 {
00723 "Eastern Standard Time", "Eastern Daylight Time",
00724 "US/Eastern"
00725 },
00726 {
00727 "Egypt Standard Time", "Egypt Daylight Time",
00728 "Africa/Cairo"
00729 },
00730 {
00731 "Ekaterinburg Standard Time", "Ekaterinburg Daylight Time",
00732 "Asia/Yekaterinburg"
00733 },
00734 {
00735 "Fiji Standard Time", "Fiji Daylight Time",
00736 "Pacific/Fiji"
00737 },
00738 {
00739 "FLE Standard Time", "FLE Daylight Time",
00740 "Europe/Helsinki"
00741 },
00742
00743 {
00744 "Georgian Standard Time", "Georgian Daylight Time",
00745 "Asia/Tbilisi"
00746 },
00747 {
00748 "GMT Standard Time", "GMT Daylight Time",
00749 "Europe/London"
00750 },
00751
00752 {
00753 "Greenland Standard Time", "Greenland Daylight Time",
00754 "America/Godthab"
00755 },
00756 {
00757 "Greenwich Standard Time", "Greenwich Daylight Time",
00758 "Africa/Casablanca"
00759 },
00760 {
00761 "GTB Standard Time", "GTB Daylight Time",
00762 "Europe/Athens"
00763 },
00764 {
00765 "Hawaiian Standard Time", "Hawaiian Daylight Time",
00766 "US/Hawaii"
00767 },
00768 {
00769 "India Standard Time", "India Daylight Time",
00770 "Asia/Calcutta"
00771 },
00772
00773 {
00774 "Iran Standard Time", "Iran Daylight Time",
00775 "Asia/Tehran"
00776 },
00777 {
00778 "Jerusalem Standard Time", "Jerusalem Daylight Time",
00779 "Asia/Jerusalem"
00780 },
00781 {
00782 "Jordan Standard Time", "Jordan Daylight Time",
00783 "Asia/Amman"
00784 },
00785 {
00786 "Kamchatka Standard Time", "Kamchatka Daylight Time",
00787 "Asia/Kamchatka"
00788 },
00789 {
00790 "Korea Standard Time", "Korea Daylight Time",
00791 "Asia/Seoul"
00792 },
00793 {
00794 "Mauritius Standard Time", "Mauritius Daylight Time",
00795 "Indian/Mauritius"
00796 },
00797 {
00798 "Mexico Standard Time", "Mexico Daylight Time",
00799 "America/Mexico_City"
00800 },
00801
00802 {
00803 "Mexico Standard Time 2", "Mexico Daylight Time 2",
00804 "America/Chihuahua"
00805 },
00806 {
00807 "Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time",
00808 "Atlantic/South_Georgia"
00809 },
00810 {
00811 "Middle East Standard Time", "Middle East Daylight Time",
00812 "Asia/Beirut"
00813 },
00814 {
00815 "Montevideo Standard Time", "Montevideo Daylight Time",
00816 "America/Montevideo"
00817 },
00818 {
00819 "Morocco Standard Time", "Morocco Daylight Time",
00820 "Africa/Casablanca"
00821 },
00822 {
00823 "Mountain Standard Time", "Mountain Daylight Time",
00824 "US/Mountain"
00825 },
00826 {
00827 "Mountain Standard Time (Mexico)", "Mountain Daylight Time (Mexico)",
00828 "America/Chihuahua"
00829 },
00830
00831 {
00832 "Myanmar Standard Time", "Myanmar Daylight Time",
00833 "Asia/Rangoon"
00834 },
00835 {
00836 "N. Central Asia Standard Time", "N. Central Asia Daylight Time",
00837 "Asia/Novosibirsk"
00838 },
00839 {
00840 "Namibia Standard Time", "Namibia Daylight Time",
00841 "Africa/Windhoek"
00842 },
00843 {
00844 "Nepal Standard Time", "Nepal Daylight Time",
00845 "Asia/Katmandu"
00846 },
00847 {
00848 "New Zealand Standard Time", "New Zealand Daylight Time",
00849 "Pacific/Auckland"
00850 },
00851 {
00852 "Newfoundland Standard Time", "Newfoundland Daylight Time",
00853 "Canada/Newfoundland"
00854 },
00855 {
00856 "North Asia East Standard Time", "North Asia East Daylight Time",
00857 "Asia/Irkutsk"
00858 },
00859 {
00860 "North Asia Standard Time", "North Asia Daylight Time",
00861 "Asia/Krasnoyarsk"
00862 },
00863 {
00864 "Pacific SA Standard Time", "Pacific SA Daylight Time",
00865 "America/Santiago"
00866 },
00867 {
00868 "Pacific Standard Time", "Pacific Daylight Time",
00869 "US/Pacific"
00870 },
00871
00872 {
00873 "Pacific Standard Time (Mexico)", "Pacific Daylight Time (Mexico)",
00874 "America/Tijuana"
00875 },
00876 {
00877 "Pakistan Standard Time", "Pakistan Daylight Time",
00878 "Asia/Karachi"
00879 },
00880 {
00881 "Paraguay Standard Time", "Paraguay Daylight Time",
00882 "America/Asuncion"
00883 },
00884 {
00885 "Romance Standard Time", "Romance Daylight Time",
00886 "Europe/Brussels"
00887 },
00888
00889 {
00890 "Russian Standard Time", "Russian Daylight Time",
00891 "Europe/Moscow"
00892 },
00893
00894 {
00895 "SA Eastern Standard Time", "SA Eastern Daylight Time",
00896 "America/Buenos_Aires"
00897 },
00898 {
00899 "SA Pacific Standard Time", "SA Pacific Daylight Time",
00900 "America/Bogota"
00901 },
00902 {
00903 "SA Western Standard Time", "SA Western Daylight Time",
00904 "America/Caracas"
00905 },
00906 {
00907 "Samoa Standard Time", "Samoa Daylight Time",
00908 "Pacific/Midway"
00909 },
00910 {
00911 "SE Asia Standard Time", "SE Asia Daylight Time",
00912 "Asia/Bangkok"
00913 },
00914 {
00915 "Malay Peninsula Standard Time", "Malay Peninsula Daylight Time",
00916 "Asia/Kuala_Lumpur"
00917 },
00918 {
00919 "South Africa Standard Time", "South Africa Daylight Time",
00920 "Africa/Harare"
00921 },
00922 {
00923 "Sri Lanka Standard Time", "Sri Lanka Daylight Time",
00924 "Asia/Colombo"
00925 },
00926 {
00927 "Taipei Standard Time", "Taipei Daylight Time",
00928 "Asia/Taipei"
00929 },
00930 {
00931 "Tasmania Standard Time", "Tasmania Daylight Time",
00932 "Australia/Hobart"
00933 },
00934 {
00935 "Tokyo Standard Time", "Tokyo Daylight Time",
00936 "Asia/Tokyo"
00937 },
00938 {
00939 "Tonga Standard Time", "Tonga Daylight Time",
00940 "Pacific/Tongatapu"
00941 },
00942 {
00943 "Ulaanbaatar Standard Time", "Ulaanbaatar Daylight Time",
00944 "Asia/Ulaanbaatar",
00945 },
00946 {
00947 "US Eastern Standard Time", "US Eastern Daylight Time",
00948 "US/Eastern"
00949 },
00950 {
00951 "US Mountain Standard Time", "US Mountain Daylight Time",
00952 "US/Arizona"
00953 },
00954 {
00955 "Coordinated Universal Time", "Coordinated Universal Time",
00956 "UTC"
00957 },
00958 {
00959 "UTC+12", "UTC+12",
00960 "Etc/GMT+12"
00961 },
00962 {
00963 "UTC-02", "UTC-02",
00964 "Etc/GMT-02"
00965 },
00966 {
00967 "UTC-11", "UTC-11",
00968 "Etc/GMT-11"
00969 },
00970 {
00971 "Venezuela Standard Time", "Venezuela Daylight Time",
00972 "America/Caracas",
00973 },
00974 {
00975 "Vladivostok Standard Time", "Vladivostok Daylight Time",
00976 "Asia/Vladivostok"
00977 },
00978 {
00979 "W. Australia Standard Time", "W. Australia Daylight Time",
00980 "Australia/Perth"
00981 },
00982 #ifdef NOT_USED
00983
00984 {
00985 "W. Central Africa Standard Time", "W. Central Africa Daylight Time",
00986 "WAT"
00987 },
00988 #endif
00989 {
00990 "W. Europe Standard Time", "W. Europe Daylight Time",
00991 "CET"
00992 },
00993
00994 {
00995 "West Asia Standard Time", "West Asia Daylight Time",
00996 "Asia/Karachi"
00997 },
00998 {
00999 "West Pacific Standard Time", "West Pacific Daylight Time",
01000 "Pacific/Guam"
01001 },
01002 {
01003 "Yakutsk Standard Time", "Yakutsk Daylight Time",
01004 "Asia/Yakutsk"
01005 },
01006 {
01007 NULL, NULL, NULL
01008 }
01009 };
01010
01011 static const char *
01012 identify_system_timezone(void)
01013 {
01014 int i;
01015 char tzname[128];
01016 char localtzname[256];
01017 time_t t = time(NULL);
01018 struct tm *tm = localtime(&t);
01019 HKEY rootKey;
01020 int idx;
01021
01022 if (!tm)
01023 {
01024 #ifdef DEBUG_IDENTIFY_TIMEZONE
01025 fprintf(stderr, "could not identify system time zone: localtime() failed\n");
01026 #endif
01027 return NULL;
01028 }
01029
01030 memset(tzname, 0, sizeof(tzname));
01031 strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
01032
01033 for (i = 0; win32_tzmap[i].stdname != NULL; i++)
01034 {
01035 if (strcmp(tzname, win32_tzmap[i].stdname) == 0 ||
01036 strcmp(tzname, win32_tzmap[i].dstname) == 0)
01037 {
01038 #ifdef DEBUG_IDENTIFY_TIMEZONE
01039 fprintf(stderr, "TZ \"%s\" matches system time zone \"%s\"\n",
01040 win32_tzmap[i].pgtzname, tzname);
01041 #endif
01042 return win32_tzmap[i].pgtzname;
01043 }
01044 }
01045
01046
01047
01048
01049
01050
01051 memset(localtzname, 0, sizeof(localtzname));
01052 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
01053 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
01054 0,
01055 KEY_READ,
01056 &rootKey) != ERROR_SUCCESS)
01057 {
01058 #ifdef DEBUG_IDENTIFY_TIMEZONE
01059 fprintf(stderr, "could not open registry key to identify system time zone: error code %lu\n",
01060 GetLastError());
01061 #endif
01062 return NULL;
01063 }
01064
01065 for (idx = 0;; idx++)
01066 {
01067 char keyname[256];
01068 char zonename[256];
01069 DWORD namesize;
01070 FILETIME lastwrite;
01071 HKEY key;
01072 LONG r;
01073
01074 memset(keyname, 0, sizeof(keyname));
01075 namesize = sizeof(keyname);
01076 if ((r = RegEnumKeyEx(rootKey,
01077 idx,
01078 keyname,
01079 &namesize,
01080 NULL,
01081 NULL,
01082 NULL,
01083 &lastwrite)) != ERROR_SUCCESS)
01084 {
01085 if (r == ERROR_NO_MORE_ITEMS)
01086 break;
01087 #ifdef DEBUG_IDENTIFY_TIMEZONE
01088 fprintf(stderr, "could not enumerate registry subkeys to identify system time zone: %d\n",
01089 (int) r);
01090 #endif
01091 break;
01092 }
01093
01094 if ((r = RegOpenKeyEx(rootKey, keyname, 0, KEY_READ, &key)) != ERROR_SUCCESS)
01095 {
01096 #ifdef DEBUG_IDENTIFY_TIMEZONE
01097 fprintf(stderr, "could not open registry subkey to identify system time zone: %d\n",
01098 (int) r);
01099 #endif
01100 break;
01101 }
01102
01103 memset(zonename, 0, sizeof(zonename));
01104 namesize = sizeof(zonename);
01105 if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
01106 {
01107 #ifdef DEBUG_IDENTIFY_TIMEZONE
01108 fprintf(stderr, "could not query value for key \"std\" to identify system time zone \"%s\": %d\n",
01109 keyname, (int) r);
01110 #endif
01111 RegCloseKey(key);
01112 continue;
01113 }
01114 if (strcmp(tzname, zonename) == 0)
01115 {
01116
01117 strcpy(localtzname, keyname);
01118 RegCloseKey(key);
01119 break;
01120 }
01121 memset(zonename, 0, sizeof(zonename));
01122 namesize = sizeof(zonename);
01123 if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
01124 {
01125 #ifdef DEBUG_IDENTIFY_TIMEZONE
01126 fprintf(stderr, "could not query value for key \"dlt\" to identify system time zone \"%s\": %d\n",
01127 keyname, (int) r);
01128 #endif
01129 RegCloseKey(key);
01130 continue;
01131 }
01132 if (strcmp(tzname, zonename) == 0)
01133 {
01134
01135 strcpy(localtzname, keyname);
01136 RegCloseKey(key);
01137 break;
01138 }
01139
01140 RegCloseKey(key);
01141 }
01142
01143 RegCloseKey(rootKey);
01144
01145 if (localtzname[0])
01146 {
01147
01148 for (i = 0; win32_tzmap[i].stdname != NULL; i++)
01149 {
01150 if (strcmp(localtzname, win32_tzmap[i].stdname) == 0 ||
01151 strcmp(localtzname, win32_tzmap[i].dstname) == 0)
01152 {
01153 #ifdef DEBUG_IDENTIFY_TIMEZONE
01154 fprintf(stderr, "TZ \"%s\" matches localized system time zone \"%s\" (\"%s\")\n",
01155 win32_tzmap[i].pgtzname, tzname, localtzname);
01156 #endif
01157 return win32_tzmap[i].pgtzname;
01158 }
01159 }
01160 }
01161
01162 #ifdef DEBUG_IDENTIFY_TIMEZONE
01163 fprintf(stderr, "could not find a match for system time zone \"%s\"\n",
01164 tzname);
01165 #endif
01166 return NULL;
01167 }
01168 #endif
01169
01170
01171
01172
01173
01174 static bool
01175 validate_zone(const char *tzname)
01176 {
01177 pg_tz *tz;
01178
01179 if (!tzname || !tzname[0])
01180 return false;
01181
01182 tz = pg_load_tz(tzname);
01183 if (!tz)
01184 return false;
01185
01186 if (!pg_tz_acceptable(tz))
01187 return false;
01188
01189 return true;
01190 }
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203 const char *
01204 select_default_timezone(const char *share_path)
01205 {
01206 const char *tzname;
01207
01208
01209 #ifndef SYSTEMTZDIR
01210 snprintf(tzdirpath, sizeof(tzdirpath), "%s/timezone", share_path);
01211 #endif
01212
01213
01214 tzname = getenv("TZ");
01215 if (validate_zone(tzname))
01216 return tzname;
01217
01218
01219 tzname = identify_system_timezone();
01220 if (validate_zone(tzname))
01221 return tzname;
01222
01223 return NULL;
01224 }