YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/postgres/src/bin/initdb/findtimezone.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * findtimezone.c
4
 *    Functions for determining the default timezone to use.
5
 *
6
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7
 *
8
 * IDENTIFICATION
9
 *    src/bin/initdb/findtimezone.c
10
 *
11
 *-------------------------------------------------------------------------
12
 */
13
#include "postgres_fe.h"
14
15
#include <fcntl.h>
16
#include <sys/stat.h>
17
#include <time.h>
18
19
#include "pgtz.h"
20
21
/* Ideally this would be in a .h file, but it hardly seems worth the trouble */
22
extern const char *select_default_timezone(const char *share_path);
23
24
25
#ifndef SYSTEMTZDIR
26
static char tzdirpath[MAXPGPATH];
27
#endif
28
29
30
/*
31
 * Return full pathname of timezone data directory
32
 *
33
 * In this file, tzdirpath is assumed to be set up by select_default_timezone.
34
 */
35
static const char *
36
pg_TZDIR(void)
37
539k
{
38
539k
#ifndef SYSTEMTZDIR
39
  /* normal case: timezone stuff is under our share dir */
40
539k
  return tzdirpath;
41
#else
42
  /* we're configured to use system's timezone database */
43
  return SYSTEMTZDIR;
44
#endif
45
539k
}
46
47
48
/*
49
 * Given a timezone name, open() the timezone data file.  Return the
50
 * file descriptor if successful, -1 if not.
51
 *
52
 * This is simpler than the backend function of the same name because
53
 * we assume that the input string has the correct case already, so there
54
 * is no need for case-folding.  (This is obviously true if we got the file
55
 * name from the filesystem to start with.  The only other place it can come
56
 * from is the environment variable TZ, and there seems no need to allow
57
 * case variation in that; other programs aren't likely to.)
58
 *
59
 * If "canonname" is not NULL, then on success the canonical spelling of the
60
 * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
61
 * This is redundant but kept for compatibility with the backend code.
62
 */
63
int
64
pg_open_tzfile(const char *name, char *canonname)
65
538k
{
66
538k
  char    fullname[MAXPGPATH];
67
68
538k
  if (canonname)
69
538k
    strlcpy(canonname, name, TZ_STRLEN_MAX + 1);
70
71
538k
  strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
72
538k
  if (strlen(fullname) + 1 + strlen(name) >= MAXPGPATH)
73
0
    return -1;       /* not gonna fit */
74
538k
  strcat(fullname, "/");
75
538k
  strcat(fullname, name);
76
77
538k
  return open(fullname, O_RDONLY | PG_BINARY, 0);
78
538k
}
79
80
81
82
/*
83
 * Load a timezone definition.
84
 * Does not verify that the timezone is acceptable!
85
 *
86
 * This corresponds to the backend's pg_tzset(), except that we only support
87
 * one loaded timezone at a time.
88
 */
89
static pg_tz *
90
pg_load_tz(const char *name)
91
539k
{
92
539k
  static pg_tz tz;
93
94
539k
  if (strlen(name) > TZ_STRLEN_MAX)
95
0
    return NULL;     /* not going to fit */
96
97
  /*
98
   * "GMT" is always sent to tzparse(); see comments for pg_tzset().
99
   */
100
539k
  if (strcmp(name, "GMT") == 0)
101
907
  {
102
907
    if (!tzparse(name, &tz.state, true))
103
0
    {
104
      /* This really, really should not happen ... */
105
0
      return NULL;
106
0
    }
107
538k
  }
108
538k
  else if (tzload(name, NULL, &tz.state, true) != 0)
109
0
  {
110
0
    if (name[0] == ':' || !tzparse(name, &tz.state, false))
111
0
    {
112
0
      return NULL;    /* unknown timezone */
113
0
    }
114
539k
  }
115
116
539k
  strcpy(tz.TZname, name);
117
118
539k
  return &tz;
119
539k
}
120
121
122
/*
123
 * The following block of code attempts to determine which timezone in our
124
 * timezone database is the best match for the active system timezone.
125
 *
126
 * On most systems, we rely on trying to match the observable behavior of
127
 * the C library's localtime() function.  The database zone that matches
128
 * furthest into the past is the one to use.  Often there will be several
129
 * zones with identical rankings (since the Olson database assigns multiple
130
 * names to many zones).  We break ties arbitrarily by preferring shorter,
131
 * then alphabetically earlier zone names.
132
 *
133
 * Win32's native knowledge about timezones appears to be too incomplete
134
 * and too different from the Olson database for the above matching strategy
135
 * to be of any use. But there is just a limited number of timezones
136
 * available, so we can rely on a handmade mapping table instead.
137
 */
138
139
#ifndef WIN32
140
141
0
#define T_DAY ((time_t) (60*60*24))
142
4.71M
#define T_WEEK  ((time_t) (60*60*24*7))
143
0
#define T_MONTH ((time_t) (60*60*24*31))
144
145
4.71M
#define MAX_TEST_TIMES (52*100) /* 100 years */
146
147
struct tztry
148
{
149
  int     n_test_times;
150
  time_t    test_times[MAX_TEST_TIMES];
151
};
152
153
static void scan_available_timezones(char *tzdir, char *tzdirsub,
154
             struct tztry *tt,
155
             int *bestscore, char *bestzonename);
156
157
158
/*
159
 * Get GMT offset from a system struct tm
160
 */
161
static int
162
get_timezone_offset(struct tm *tm)
163
0
{
164
0
#if defined(HAVE_STRUCT_TM_TM_ZONE)
165
0
  return tm->tm_gmtoff;
166
#elif defined(HAVE_INT_TIMEZONE)
167
  return -TIMEZONE_GLOBAL;
168
#else
169
#error No way to determine TZ? Can this happen?
170
#endif
171
0
}
172
173
/*
174
 * Convenience subroutine to convert y/m/d to time_t (NOT pg_time_t)
175
 */
176
static time_t
177
build_time_t(int year, int month, int day)
178
1.81k
{
179
1.81k
  struct tm tm;
180
181
1.81k
  memset(&tm, 0, sizeof(tm));
182
1.81k
  tm.tm_mday = day;
183
1.81k
  tm.tm_mon = month - 1;
184
1.81k
  tm.tm_year = year - 1900;
185
1.81k
  tm.tm_isdst = -1;
186
187
1.81k
  return mktime(&tm);
188
1.81k
}
189
190
/*
191
 * Does a system tm value match one we computed ourselves?
192
 */
193
static bool
194
compare_tm(struct tm *s, struct pg_tm *p)
195
81.6M
{
196
81.6M
  if (s->tm_sec != p->tm_sec ||
197
81.6M
    s->tm_min != p->tm_min ||
198
81.5M
    s->tm_hour != p->tm_hour ||
199
81.0M
    s->tm_mday != p->tm_mday ||
200
81.0M
    s->tm_mon != p->tm_mon ||
201
81.0M
    s->tm_year != p->tm_year ||
202
81.0M
    s->tm_wday != p->tm_wday ||
203
81.0M
    s->tm_yday != p->tm_yday ||
204
81.0M
    s->tm_isdst != p->tm_isdst)
205
524k
    return false;
206
81.0M
  return true;
207
81.0M
}
208
209
/*
210
 * See how well a specific timezone setting matches the system behavior
211
 *
212
 * We score a timezone setting according to the number of test times it
213
 * matches.  (The test times are ordered later-to-earlier, but this routine
214
 * doesn't actually know that; it just scans until the first non-match.)
215
 *
216
 * We return -1 for a completely unusable setting; this is worse than the
217
 * score of zero for a setting that works but matches not even the first
218
 * test time.
219
 */
220
static int
221
score_timezone(const char *tzname, struct tztry *tt)
222
538k
{
223
538k
  int     i;
224
538k
  pg_time_t pgtt;
225
538k
  struct tm  *systm;
226
538k
  struct pg_tm *pgtm;
227
538k
  char    cbuf[TZ_STRLEN_MAX + 1];
228
538k
  pg_tz    *tz;
229
230
  /* Load timezone definition */
231
538k
  tz = pg_load_tz(tzname);
232
538k
  if (!tz)
233
0
    return -1;       /* unrecognized zone name */
234
235
  /* Reject if leap seconds involved */
236
538k
  if (!pg_tz_acceptable(tz))
237
0
  {
238
#ifdef DEBUG_IDENTIFY_TIMEZONE
239
    fprintf(stderr, "Reject TZ \"%s\": uses leap seconds\n", tzname);
240
#endif
241
0
    return -1;
242
0
  }
243
244
  /* Check for match at all the test times */
245
81.6M
  for (i = 0; i < tt->n_test_times; i++)
246
81.6M
  {
247
81.6M
    pgtt = (pg_time_t) (tt->test_times[i]);
248
81.6M
    pgtm = pg_localtime(&pgtt, tz);
249
81.6M
    if (!pgtm)
250
0
      return -1;     /* probably shouldn't happen */
251
81.6M
    systm = localtime(&(tt->test_times[i]));
252
81.6M
    if (!systm)
253
0
    {
254
#ifdef DEBUG_IDENTIFY_TIMEZONE
255
      fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s, system had no data\n",
256
          tzname, i, (long) pgtt,
257
          pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
258
          pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
259
          pgtm->tm_isdst ? "dst" : "std");
260
#endif
261
0
      return i;
262
0
    }
263
81.6M
    if (!compare_tm(systm, pgtm))
264
524k
    {
265
#ifdef DEBUG_IDENTIFY_TIMEZONE
266
      fprintf(stderr, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s\n",
267
          tzname, i, (long) pgtt,
268
          pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
269
          pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
270
          pgtm->tm_isdst ? "dst" : "std",
271
          systm->tm_year + 1900, systm->tm_mon + 1, systm->tm_mday,
272
          systm->tm_hour, systm->tm_min, systm->tm_sec,
273
          systm->tm_isdst ? "dst" : "std");
274
#endif
275
524k
      return i;
276
524k
    }
277
81.0M
    if (systm->tm_isdst >= 0)
278
81.0M
    {
279
      /* Check match of zone names, too */
280
81.0M
      if (pgtm->tm_zone == NULL)
281
0
        return -1;   /* probably shouldn't happen */
282
81.0M
      memset(cbuf, 0, sizeof(cbuf));
283
81.0M
      strftime(cbuf, sizeof(cbuf) - 1, "%Z", systm);  /* zone abbr */
284
81.0M
      if (strcmp(cbuf, pgtm->tm_zone) != 0)
285
13.6k
      {
286
#ifdef DEBUG_IDENTIFY_TIMEZONE
287
        fprintf(stderr, "TZ \"%s\" scores %d: at %ld \"%s\" versus \"%s\"\n",
288
            tzname, i, (long) pgtt,
289
            pgtm->tm_zone, cbuf);
290
#endif
291
13.6k
        return i;
292
13.6k
      }
293
81.0M
    }
294
81.0M
  }
295
296
#ifdef DEBUG_IDENTIFY_TIMEZONE
297
  fprintf(stderr, "TZ \"%s\" gets max score %d\n", tzname, i);
298
#endif
299
300
907
  return i;
301
538k
}
302
303
304
/*
305
 * Try to identify a timezone name (in our terminology) that best matches the
306
 * observed behavior of the system timezone library.  We cannot assume that
307
 * the system TZ environment setting (if indeed there is one) matches our
308
 * terminology, so we ignore it and just look at what localtime() returns.
309
 */
310
static const char *
311
identify_system_timezone(void)
312
907
{
313
907
  static char resultbuf[TZ_STRLEN_MAX + 1];
314
907
  time_t    tnow;
315
907
  time_t    t;
316
907
  struct tztry tt;
317
907
  struct tm  *tm;
318
907
  int     thisyear;
319
907
  int     bestscore;
320
907
  char    tmptzdir[MAXPGPATH];
321
907
  int     std_ofs;
322
907
  char    std_zone_name[TZ_STRLEN_MAX + 1],
323
907
        dst_zone_name[TZ_STRLEN_MAX + 1];
324
907
  char    cbuf[TZ_STRLEN_MAX + 1];
325
326
  /* Initialize OS timezone library */
327
907
  tzset();
328
329
  /*
330
   * Set up the list of dates to be probed to see how well our timezone
331
   * matches the system zone.  We first probe January and July of the
332
   * current year; this serves to quickly eliminate the vast majority of the
333
   * TZ database entries.  If those dates match, we probe every week for 100
334
   * years backwards from the current July.  (Weekly resolution is good
335
   * enough to identify DST transition rules, since everybody switches on
336
   * Sundays.)  This is sufficient to cover most of the Unix time_t range,
337
   * and we don't want to look further than that since many systems won't
338
   * have sane TZ behavior further back anyway.  The further back the zone
339
   * matches, the better we score it.  This may seem like a rather random
340
   * way of doing things, but experience has shown that system-supplied
341
   * timezone definitions are likely to have DST behavior that is right for
342
   * the recent past and not so accurate further back. Scoring in this way
343
   * allows us to recognize zones that have some commonality with the Olson
344
   * database, without insisting on exact match. (Note: we probe Thursdays,
345
   * not Sundays, to avoid triggering DST-transition bugs in localtime
346
   * itself.)
347
   */
348
907
  tnow = time(NULL);
349
907
  tm = localtime(&tnow);
350
907
  if (!tm)
351
0
    return NULL;     /* give up if localtime is broken... */
352
907
  thisyear = tm->tm_year + 1900;
353
354
907
  t = build_time_t(thisyear, 1, 15);
355
356
  /*
357
   * Round back to GMT midnight Thursday.  This depends on the knowledge
358
   * that the time_t origin is Thu Jan 01 1970.  (With a different origin
359
   * we'd be probing some other day of the week, but it wouldn't matter
360
   * anyway unless localtime() had DST-transition bugs.)
361
   */
362
907
  t -= (t % T_WEEK);
363
364
907
  tt.n_test_times = 0;
365
907
  tt.test_times[tt.n_test_times++] = t;
366
367
907
  t = build_time_t(thisyear, 7, 15);
368
907
  t -= (t % T_WEEK);
369
370
907
  tt.test_times[tt.n_test_times++] = t;
371
372
4.71M
  while (tt.n_test_times < MAX_TEST_TIMES)
373
4.71M
  {
374
4.71M
    t -= T_WEEK;
375
4.71M
    tt.test_times[tt.n_test_times++] = t;
376
4.71M
  }
377
378
  /* Search for the best-matching timezone file */
379
907
  strlcpy(tmptzdir, pg_TZDIR(), sizeof(tmptzdir));
380
907
  bestscore = -1;
381
907
  resultbuf[0] = '\0';
382
907
  scan_available_timezones(tmptzdir, tmptzdir + strlen(tmptzdir) + 1,
383
907
               &tt,
384
907
               &bestscore, resultbuf);
385
907
  if (bestscore > 0)
386
907
  {
387
    /* Ignore Olson's rather silly "Factory" zone; use GMT instead */
388
907
    if (strcmp(resultbuf, "Factory") == 0)
389
0
      return NULL;
390
907
    return resultbuf;
391
907
  }
392
393
  /*
394
   * Couldn't find a match in the database, so next we try constructed zone
395
   * names (like "PST8PDT").
396
   *
397
   * First we need to determine the names of the local standard and daylight
398
   * zones.  The idea here is to scan forward from today until we have seen
399
   * both zones, if both are in use.
400
   */
401
0
  memset(std_zone_name, 0, sizeof(std_zone_name));
402
0
  memset(dst_zone_name, 0, sizeof(dst_zone_name));
403
0
  std_ofs = 0;
404
405
0
  tnow = time(NULL);
406
407
  /*
408
   * Round back to a GMT midnight so results don't depend on local time of
409
   * day
410
   */
411
0
  tnow -= (tnow % T_DAY);
412
413
  /*
414
   * We have to look a little further ahead than one year, in case today is
415
   * just past a DST boundary that falls earlier in the year than the next
416
   * similar boundary.  Arbitrarily scan up to 14 months.
417
   */
418
0
  for (t = tnow; t <= tnow + T_MONTH * 14; t += T_MONTH)
419
0
  {
420
0
    tm = localtime(&t);
421
0
    if (!tm)
422
0
      continue;
423
0
    if (tm->tm_isdst < 0)
424
0
      continue;
425
0
    if (tm->tm_isdst == 0 && std_zone_name[0] == '\0')
426
0
    {
427
      /* found STD zone */
428
0
      memset(cbuf, 0, sizeof(cbuf));
429
0
      strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
430
0
      strcpy(std_zone_name, cbuf);
431
0
      std_ofs = get_timezone_offset(tm);
432
0
    }
433
0
    if (tm->tm_isdst > 0 && dst_zone_name[0] == '\0')
434
0
    {
435
      /* found DST zone */
436
0
      memset(cbuf, 0, sizeof(cbuf));
437
0
      strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
438
0
      strcpy(dst_zone_name, cbuf);
439
0
    }
440
    /* Done if found both */
441
0
    if (std_zone_name[0] && dst_zone_name[0])
442
0
      break;
443
0
  }
444
445
  /* We should have found a STD zone name by now... */
446
0
  if (std_zone_name[0] == '\0')
447
0
  {
448
#ifdef DEBUG_IDENTIFY_TIMEZONE
449
    fprintf(stderr, "could not determine system time zone\n");
450
#endif
451
0
    return NULL;      /* go to GMT */
452
0
  }
453
454
  /* If we found DST then try STD<ofs>DST */
455
0
  if (dst_zone_name[0] != '\0')
456
0
  {
457
0
    snprintf(resultbuf, sizeof(resultbuf), "%s%d%s",
458
0
         std_zone_name, -std_ofs / 3600, dst_zone_name);
459
0
    if (score_timezone(resultbuf, &tt) > 0)
460
0
      return resultbuf;
461
0
  }
462
463
  /* Try just the STD timezone (works for GMT at least) */
464
0
  strcpy(resultbuf, std_zone_name);
465
0
  if (score_timezone(resultbuf, &tt) > 0)
466
0
    return resultbuf;
467
468
  /* Try STD<ofs> */
469
0
  snprintf(resultbuf, sizeof(resultbuf), "%s%d",
470
0
       std_zone_name, -std_ofs / 3600);
471
0
  if (score_timezone(resultbuf, &tt) > 0)
472
0
    return resultbuf;
473
474
  /*
475
   * Did not find the timezone.  Fallback to use a GMT zone.  Note that the
476
   * Olson timezone database names the GMT-offset zones in POSIX style: plus
477
   * is west of Greenwich.  It's unfortunate that this is opposite of SQL
478
   * conventions.  Should we therefore change the names? Probably not...
479
   */
480
0
  snprintf(resultbuf, sizeof(resultbuf), "Etc/GMT%s%d",
481
0
       (-std_ofs > 0) ? "+" : "", -std_ofs / 3600);
482
483
#ifdef DEBUG_IDENTIFY_TIMEZONE
484
  fprintf(stderr, "could not recognize system time zone, using \"%s\"\n",
485
      resultbuf);
486
#endif
487
0
  return resultbuf;
488
0
}
489
490
/*
491
 * Recursively scan the timezone database looking for the best match to
492
 * the system timezone behavior.
493
 *
494
 * tzdir points to a buffer of size MAXPGPATH.  On entry, it holds the
495
 * pathname of a directory containing TZ files.  We internally modify it
496
 * to hold pathnames of sub-directories and files, but must restore it
497
 * to its original contents before exit.
498
 *
499
 * tzdirsub points to the part of tzdir that represents the subfile name
500
 * (ie, tzdir + the original directory name length, plus one for the
501
 * first added '/').
502
 *
503
 * tt tells about the system timezone behavior we need to match.
504
 *
505
 * *bestscore and *bestzonename on entry hold the best score found so far
506
 * and the name of the best zone.  We overwrite them if we find a better
507
 * score.  bestzonename must be a buffer of length TZ_STRLEN_MAX + 1.
508
 */
509
static void
510
scan_available_timezones(char *tzdir, char *tzdirsub, struct tztry *tt,
511
             int *bestscore, char *bestzonename)
512
19.0k
{
513
19.0k
  int     tzdir_orig_len = strlen(tzdir);
514
19.0k
  char    **names;
515
19.0k
  char    **namep;
516
517
19.0k
  names = pgfnames(tzdir);
518
19.0k
  if (!names)
519
0
    return;
520
521
575k
  for (namep = names; *namep; namep++)
522
556k
  {
523
556k
    char     *name = *namep;
524
556k
    struct stat statbuf;
525
526
    /* Ignore . and .., plus any other "hidden" files */
527
556k
    if (name[0] == '.')
528
0
      continue;
529
530
556k
    snprintf(tzdir + tzdir_orig_len, MAXPGPATH - tzdir_orig_len,
531
556k
         "/%s", name);
532
533
556k
    if (stat(tzdir, &statbuf) != 0)
534
0
    {
535
#ifdef DEBUG_IDENTIFY_TIMEZONE
536
      fprintf(stderr, "could not stat \"%s\": %s\n",
537
          tzdir, strerror(errno));
538
#endif
539
0
      tzdir[tzdir_orig_len] = '\0';
540
0
      continue;
541
0
    }
542
543
556k
    if (S_ISDIR(statbuf.st_mode))
544
18.1k
    {
545
      /* Recurse into subdirectory */
546
18.1k
      scan_available_timezones(tzdir, tzdirsub, tt,
547
18.1k
                   bestscore, bestzonename);
548
18.1k
    }
549
538k
    else
550
538k
    {
551
      /* Load and test this file */
552
538k
      int     score = score_timezone(tzdirsub, tt);
553
554
538k
      if (score > *bestscore)
555
3.62k
      {
556
3.62k
        *bestscore = score;
557
3.62k
        strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
558
3.62k
      }
559
535k
      else if (score == *bestscore)
560
15.4k
      {
561
        /* Consider how to break a tie */
562
15.4k
        if (strlen(tzdirsub) < strlen(bestzonename) ||
563
12.6k
          (strlen(tzdirsub) == strlen(bestzonename) &&
564
0
           strcmp(tzdirsub, bestzonename) < 0))
565
15.4k
          strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
566
15.4k
      }
567
538k
    }
568
569
    /* Restore tzdir */
570
556k
    tzdir[tzdir_orig_len] = '\0';
571
556k
  }
572
573
19.0k
  pgfnames_cleanup(names);
574
19.0k
}
575
#else             /* WIN32 */
576
577
static const struct
578
{
579
  const char *stdname;    /* Windows name of standard timezone */
580
  const char *dstname;    /* Windows name of daylight timezone */
581
  const char *pgtzname;   /* Name of pgsql timezone to map to */
582
}     win32_tzmap[] =
583
584
{
585
  /*
586
   * This list was built from the contents of the registry at
587
   * HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time
588
   * Zones on Windows 10 and Windows 7.
589
   *
590
   * The zones have been matched to Olson timezones by looking at the cities
591
   * listed in the win32 display name (in the comment here) in most cases.
592
   */
593
  {
594
    "Afghanistan Standard Time", "Afghanistan Daylight Time",
595
    "Asia/Kabul"
596
  },              /* (UTC+04:30) Kabul */
597
  {
598
    "Alaskan Standard Time", "Alaskan Daylight Time",
599
    "US/Alaska"
600
  },              /* (UTC-09:00) Alaska */
601
  {
602
    "Aleutian Standard Time", "Aleutian Daylight Time",
603
    "US/Aleutan"
604
  },              /* (UTC-10:00) Aleutian Islands */
605
  {
606
    "Altai Standard Time", "Altai Daylight Time",
607
    "Asia/Barnaul"
608
  },              /* (UTC+07:00) Barnaul, Gorno-Altaysk */
609
  {
610
    "Arab Standard Time", "Arab Daylight Time",
611
    "Asia/Kuwait"
612
  },              /* (UTC+03:00) Kuwait, Riyadh */
613
  {
614
    "Arabian Standard Time", "Arabian Daylight Time",
615
    "Asia/Muscat"
616
  },              /* (UTC+04:00) Abu Dhabi, Muscat */
617
  {
618
    "Arabic Standard Time", "Arabic Daylight Time",
619
    "Asia/Baghdad"
620
  },              /* (UTC+03:00) Baghdad */
621
  {
622
    "Argentina Standard Time", "Argentina Daylight Time",
623
    "America/Buenos_Aires"
624
  },              /* (UTC-03:00) City of Buenos Aires */
625
  {
626
    "Armenian Standard Time", "Armenian Daylight Time",
627
    "Asia/Yerevan"
628
  },              /* (UTC+04:00) Baku, Tbilisi, Yerevan */
629
  {
630
    "Astrakhan Standard Time", "Astrakhan Daylight Time",
631
    "Europe/Astrakhan"
632
  },              /* (UTC+04:00) Astrakhan, Ulyanovsk */
633
  {
634
    "Atlantic Standard Time", "Atlantic Daylight Time",
635
    "Canada/Atlantic"
636
  },              /* (UTC-04:00) Atlantic Time (Canada) */
637
  {
638
    "AUS Central Standard Time", "AUS Central Daylight Time",
639
    "Australia/Darwin"
640
  },              /* (UTC+09:30) Darwin */
641
  {
642
    "Aus Central W. Standard Time", "Aus Central W. Daylight Time",
643
    "Australia/Eucla"
644
  },              /* (UTC+08:45) Eucla */
645
  {
646
    "AUS Eastern Standard Time", "AUS Eastern Daylight Time",
647
    "Australia/Canberra"
648
  },              /* (UTC+10:00) Canberra, Melbourne, Sydney */
649
  {
650
    "Azerbaijan Standard Time", "Azerbaijan Daylight Time",
651
    "Asia/Baku"
652
  },              /* (UTC+04:00) Baku */
653
  {
654
    "Azores Standard Time", "Azores Daylight Time",
655
    "Atlantic/Azores"
656
  },              /* (UTC-01:00) Azores */
657
  {
658
    "Bahia Standard Time", "Bahia Daylight Time",
659
    "America/Salvador"
660
  },              /* (UTC-03:00) Salvador */
661
  {
662
    "Bangladesh Standard Time", "Bangladesh Daylight Time",
663
    "Asia/Dhaka"
664
  },              /* (UTC+06:00) Dhaka */
665
  {
666
    "Bougainville Standard Time", "Bougainville Daylight Time",
667
    "Pacific/Bougainville"
668
  },              /* (UTC+11:00) Bougainville Island */
669
  {
670
    "Belarus Standard Time", "Belarus Daylight Time",
671
    "Europe/Minsk"
672
  },              /* (UTC+03:00) Minsk */
673
  {
674
    "Cabo Verde Standard Time", "Cabo Verde Daylight Time",
675
    "Atlantic/Cape_Verde"
676
  },              /* (UTC-01:00) Cabo Verde Is. */
677
  {
678
    "Chatham Islands Standard Time", "Chatham Islands Daylight Time",
679
    "Pacific/Chatham"
680
  },              /* (UTC+12:45) Chatham Islands */
681
  {
682
    "Canada Central Standard Time", "Canada Central Daylight Time",
683
    "Canada/Saskatchewan"
684
  },              /* (UTC-06:00) Saskatchewan */
685
  {
686
    "Cape Verde Standard Time", "Cape Verde Daylight Time",
687
    "Atlantic/Cape_Verde"
688
  },              /* (UTC-01:00) Cape Verde Is. */
689
  {
690
    "Caucasus Standard Time", "Caucasus Daylight Time",
691
    "Asia/Baku"
692
  },              /* (UTC+04:00) Yerevan */
693
  {
694
    "Cen. Australia Standard Time", "Cen. Australia Daylight Time",
695
    "Australia/Adelaide"
696
  },              /* (UTC+09:30) Adelaide */
697
  /* Central America (other than Mexico) generally does not observe DST */
698
  {
699
    "Central America Standard Time", "Central America Daylight Time",
700
    "CST6"
701
  },              /* (UTC-06:00) Central America */
702
  {
703
    "Central Asia Standard Time", "Central Asia Daylight Time",
704
    "Asia/Dhaka"
705
  },              /* (UTC+06:00) Astana */
706
  {
707
    "Central Brazilian Standard Time", "Central Brazilian Daylight Time",
708
    "America/Cuiaba"
709
  },              /* (UTC-04:00) Cuiaba */
710
  {
711
    "Central Europe Standard Time", "Central Europe Daylight Time",
712
    "Europe/Belgrade"
713
  },              /* (UTC+01:00) Belgrade, Bratislava, Budapest,
714
                 * Ljubljana, Prague */
715
  {
716
    "Central European Standard Time", "Central European Daylight Time",
717
    "Europe/Sarajevo"
718
  },              /* (UTC+01:00) Sarajevo, Skopje, Warsaw,
719
                 * Zagreb */
720
  {
721
    "Central Pacific Standard Time", "Central Pacific Daylight Time",
722
    "Pacific/Noumea"
723
  },              /* (UTC+11:00) Solomon Is., New Caledonia */
724
  {
725
    "Central Standard Time", "Central Daylight Time",
726
    "US/Central"
727
  },              /* (UTC-06:00) Central Time (US & Canada) */
728
  {
729
    "Central Standard Time (Mexico)", "Central Daylight Time (Mexico)",
730
    "America/Mexico_City"
731
  },              /* (UTC-06:00) Guadalajara, Mexico City,
732
                 * Monterrey */
733
  {
734
    "China Standard Time", "China Daylight Time",
735
    "Asia/Hong_Kong"
736
  },              /* (UTC+08:00) Beijing, Chongqing, Hong Kong,
737
                 * Urumqi */
738
  {
739
    "Cuba Standard Time", "Cuba Daylight Time",
740
    "America/Havana"
741
  },              /* (UTC-05:00) Havana */
742
  {
743
    "Dateline Standard Time", "Dateline Daylight Time",
744
    "Etc/UTC+12"
745
  },              /* (UTC-12:00) International Date Line West */
746
  {
747
    "E. Africa Standard Time", "E. Africa Daylight Time",
748
    "Africa/Nairobi"
749
  },              /* (UTC+03:00) Nairobi */
750
  {
751
    "E. Australia Standard Time", "E. Australia Daylight Time",
752
    "Australia/Brisbane"
753
  },              /* (UTC+10:00) Brisbane */
754
  {
755
    "E. Europe Standard Time", "E. Europe Daylight Time",
756
    "Europe/Bucharest"
757
  },              /* (UTC+02:00) E. Europe */
758
  {
759
    "E. South America Standard Time", "E. South America Daylight Time",
760
    "America/Araguaina"
761
  },              /* (UTC-03:00) Brasilia */
762
  {
763
    "Eastern Standard Time", "Eastern Daylight Time",
764
    "US/Eastern"
765
  },              /* (UTC-05:00) Eastern Time (US & Canada) */
766
  {
767
    "Eastern Standard Time (Mexico)", "Eastern Daylight Time (Mexico)",
768
    "America/Mexico_City"
769
  },              /* (UTC-05:00) Chetumal */
770
  {
771
    "Easter Island Standard Time", "Easter Island Daylight Time",
772
    "Pacific/Easter"
773
  },              /* (UTC-06:00) Easter Island */
774
  {
775
    "Egypt Standard Time", "Egypt Daylight Time",
776
    "Africa/Cairo"
777
  },              /* (UTC+02:00) Cairo */
778
  {
779
    "Ekaterinburg Standard Time (RTZ 4)", "Ekaterinburg Daylight Time",
780
    "Asia/Yekaterinburg"
781
  },              /* (UTC+05:00) Ekaterinburg */
782
  {
783
    "Fiji Standard Time", "Fiji Daylight Time",
784
    "Pacific/Fiji"
785
  },              /* (UTC+12:00) Fiji */
786
  {
787
    "FLE Standard Time", "FLE Daylight Time",
788
    "Europe/Helsinki"
789
  },              /* (UTC+02:00) Helsinki, Kyiv, Riga, Sofia,
790
                 * Tallinn, Vilnius */
791
  {
792
    "Georgian Standard Time", "Georgian Daylight Time",
793
    "Asia/Tbilisi"
794
  },              /* (UTC+04:00) Tbilisi */
795
  {
796
    "GMT Standard Time", "GMT Daylight Time",
797
    "Europe/London"
798
  },              /* (UTC) Dublin, Edinburgh, Lisbon, London */
799
  {
800
    "Greenland Standard Time", "Greenland Daylight Time",
801
    "America/Godthab"
802
  },              /* (UTC-03:00) Greenland */
803
  {
804
    "Greenwich Standard Time", "Greenwich Daylight Time",
805
    "Africa/Casablanca"
806
  },              /* (UTC) Monrovia, Reykjavik */
807
  {
808
    "GTB Standard Time", "GTB Daylight Time",
809
    "Europe/Athens"
810
  },              /* (UTC+02:00) Athens, Bucharest */
811
  {
812
    "Haiti Standard Time", "Haiti Daylight Time",
813
    "US/Eastern"
814
  },              /* (UTC-05:00) Haiti */
815
  {
816
    "Hawaiian Standard Time", "Hawaiian Daylight Time",
817
    "US/Hawaii"
818
  },              /* (UTC-10:00) Hawaii */
819
  {
820
    "India Standard Time", "India Daylight Time",
821
    "Asia/Calcutta"
822
  },              /* (UTC+05:30) Chennai, Kolkata, Mumbai, New
823
                 * Delhi */
824
  {
825
    "Iran Standard Time", "Iran Daylight Time",
826
    "Asia/Tehran"
827
  },              /* (UTC+03:30) Tehran */
828
  {
829
    "Jerusalem Standard Time", "Jerusalem Daylight Time",
830
    "Asia/Jerusalem"
831
  },              /* (UTC+02:00) Jerusalem */
832
  {
833
    "Jordan Standard Time", "Jordan Daylight Time",
834
    "Asia/Amman"
835
  },              /* (UTC+02:00) Amman */
836
  {
837
    "Kamchatka Standard Time", "Kamchatka Daylight Time",
838
    "Asia/Kamchatka"
839
  },              /* (UTC+12:00) Petropavlovsk-Kamchatsky - Old */
840
  {
841
    "Korea Standard Time", "Korea Daylight Time",
842
    "Asia/Seoul"
843
  },              /* (UTC+09:00) Seoul */
844
  {
845
    "Libya Standard Time", "Libya Daylight Time",
846
    "Africa/Tripoli"
847
  },              /* (UTC+02:00) Tripoli */
848
  {
849
    "Line Islands Standard Time", "Line Islands Daylight Time",
850
    "Pacific/Kiritimati"
851
  },              /* (UTC+14:00) Kiritimati Island */
852
  {
853
    "Lord Howe Standard Time", "Lord Howe Daylight Time",
854
    "Australia/Lord_Howe"
855
  },              /* (UTC+10:30) Lord Howe Island */
856
  {
857
    "Magadan Standard Time", "Magadan Daylight Time",
858
    "Asia/Magadan"
859
  },              /* (UTC+10:00) Magadan */
860
  {
861
    "Marquesas Standard Time", "Marquesas Daylight Time",
862
    "Pacific/Marquesas"
863
  },              /* (UTC-09:30) Marquesas Islands */
864
  {
865
    "Mauritius Standard Time", "Mauritius Daylight Time",
866
    "Indian/Mauritius"
867
  },              /* (UTC+04:00) Port Louis */
868
  {
869
    "Mexico Standard Time", "Mexico Daylight Time",
870
    "America/Mexico_City"
871
  },              /* (UTC-06:00) Guadalajara, Mexico City,
872
                 * Monterrey */
873
  {
874
    "Mexico Standard Time 2", "Mexico Daylight Time 2",
875
    "America/Chihuahua"
876
  },              /* (UTC-07:00) Chihuahua, La Paz, Mazatlan */
877
  {
878
    "Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time",
879
    "Atlantic/South_Georgia"
880
  },              /* (UTC-02:00) Mid-Atlantic - Old */
881
  {
882
    "Middle East Standard Time", "Middle East Daylight Time",
883
    "Asia/Beirut"
884
  },              /* (UTC+02:00) Beirut */
885
  {
886
    "Montevideo Standard Time", "Montevideo Daylight Time",
887
    "America/Montevideo"
888
  },              /* (UTC-03:00) Montevideo */
889
  {
890
    "Morocco Standard Time", "Morocco Daylight Time",
891
    "Africa/Casablanca"
892
  },              /* (UTC) Casablanca */
893
  {
894
    "Mountain Standard Time", "Mountain Daylight Time",
895
    "US/Mountain"
896
  },              /* (UTC-07:00) Mountain Time (US & Canada) */
897
  {
898
    "Mountain Standard Time (Mexico)", "Mountain Daylight Time (Mexico)",
899
    "America/Chihuahua"
900
  },              /* (UTC-07:00) Chihuahua, La Paz, Mazatlan */
901
  {
902
    "Myanmar Standard Time", "Myanmar Daylight Time",
903
    "Asia/Rangoon"
904
  },              /* (UTC+06:30) Yangon (Rangoon) */
905
  {
906
    "N. Central Asia Standard Time", "N. Central Asia Daylight Time",
907
    "Asia/Novosibirsk"
908
  },              /* (UTC+06:00) Novosibirsk (RTZ 5) */
909
  {
910
    "Namibia Standard Time", "Namibia Daylight Time",
911
    "Africa/Windhoek"
912
  },              /* (UTC+01:00) Windhoek */
913
  {
914
    "Nepal Standard Time", "Nepal Daylight Time",
915
    "Asia/Katmandu"
916
  },              /* (UTC+05:45) Kathmandu */
917
  {
918
    "New Zealand Standard Time", "New Zealand Daylight Time",
919
    "Pacific/Auckland"
920
  },              /* (UTC+12:00) Auckland, Wellington */
921
  {
922
    "Newfoundland Standard Time", "Newfoundland Daylight Time",
923
    "Canada/Newfoundland"
924
  },              /* (UTC-03:30) Newfoundland */
925
  {
926
    "Norfolk Standard Time", "Norfolk Daylight Time",
927
    "Pacific/Norfolk"
928
  },              /* (UTC+11:00) Norfolk Island */
929
  {
930
    "North Asia East Standard Time", "North Asia East Daylight Time",
931
    "Asia/Irkutsk"
932
  },              /* (UTC+08:00) Irkutsk, Ulaan Bataar */
933
  {
934
    "North Asia Standard Time", "North Asia Daylight Time",
935
    "Asia/Krasnoyarsk"
936
  },              /* (UTC+07:00) Krasnoyarsk */
937
  {
938
    "North Korea Standard Time", "North Korea Daylight Time",
939
    "Asia/Pyongyang"
940
  },              /* (UTC+08:30) Pyongyang */
941
  {
942
    "Pacific SA Standard Time", "Pacific SA Daylight Time",
943
    "America/Santiago"
944
  },              /* (UTC-03:00) Santiago */
945
  {
946
    "Pacific Standard Time", "Pacific Daylight Time",
947
    "US/Pacific"
948
  },              /* (UTC-08:00) Pacific Time (US & Canada) */
949
  {
950
    "Pacific Standard Time (Mexico)", "Pacific Daylight Time (Mexico)",
951
    "America/Tijuana"
952
  },              /* (UTC-08:00) Baja California */
953
  {
954
    "Pakistan Standard Time", "Pakistan Daylight Time",
955
    "Asia/Karachi"
956
  },              /* (UTC+05:00) Islamabad, Karachi */
957
  {
958
    "Paraguay Standard Time", "Paraguay Daylight Time",
959
    "America/Asuncion"
960
  },              /* (UTC-04:00) Asuncion */
961
  {
962
    "Romance Standard Time", "Romance Daylight Time",
963
    "Europe/Brussels"
964
  },              /* (UTC+01:00) Brussels, Copenhagen, Madrid,
965
                 * Paris */
966
  {
967
    "Russia TZ 1 Standard Time", "Russia TZ 1 Daylight Time",
968
    "Europe/Kaliningrad"
969
  },              /* (UTC+02:00) Kaliningrad (RTZ 1) */
970
  {
971
    "Russia TZ 2 Standard Time", "Russia TZ 2 Daylight Time",
972
    "Europe/Moscow"
973
  },              /* (UTC+03:00) Moscow, St. Petersburg,
974
                 * Volgograd (RTZ 2) */
975
  {
976
    "Russia TZ 3 Standard Time", "Russia TZ 3 Daylight Time",
977
    "Europe/Samara"
978
  },              /* (UTC+04:00) Izhevsk, Samara (RTZ 3) */
979
  {
980
    "Russia TZ 4 Standard Time", "Russia TZ 4 Daylight Time",
981
    "Asia/Yekaterinburg"
982
  },              /* (UTC+05:00) Ekaterinburg (RTZ 4) */
983
  {
984
    "Russia TZ 5 Standard Time", "Russia TZ 5 Daylight Time",
985
    "Asia/Novosibirsk"
986
  },              /* (UTC+06:00) Novosibirsk (RTZ 5) */
987
  {
988
    "Russia TZ 6 Standard Time", "Russia TZ 6 Daylight Time",
989
    "Asia/Krasnoyarsk"
990
  },              /* (UTC+07:00) Krasnoyarsk (RTZ 6) */
991
  {
992
    "Russia TZ 7 Standard Time", "Russia TZ 7 Daylight Time",
993
    "Asia/Irkutsk"
994
  },              /* (UTC+08:00) Irkutsk (RTZ 7) */
995
  {
996
    "Russia TZ 8 Standard Time", "Russia TZ 8 Daylight Time",
997
    "Asia/Yakutsk"
998
  },              /* (UTC+09:00) Yakutsk (RTZ 8) */
999
  {
1000
    "Russia TZ 9 Standard Time", "Russia TZ 9 Daylight Time",
1001
    "Asia/Vladivostok"
1002
  },              /* (UTC+10:00) Vladivostok, Magadan (RTZ 9) */
1003
  {
1004
    "Russia TZ 10 Standard Time", "Russia TZ 10 Daylight Time",
1005
    "Asia/Magadan"
1006
  },              /* (UTC+11:00) Chokurdakh (RTZ 10) */
1007
  {
1008
    "Russia TZ 11 Standard Time", "Russia TZ 11 Daylight Time",
1009
    "Asia/Anadyr"
1010
  },              /* (UTC+12:00) Anadyr,
1011
                 * Petropavlovsk-Kamchatsky (RTZ 11) */
1012
  {
1013
    "Russian Standard Time", "Russian Daylight Time",
1014
    "Europe/Moscow"
1015
  },              /* (UTC+03:00) Moscow, St. Petersburg,
1016
                 * Volgograd */
1017
  {
1018
    "SA Eastern Standard Time", "SA Eastern Daylight Time",
1019
    "America/Buenos_Aires"
1020
  },              /* (UTC-03:00) Cayenne, Fortaleza */
1021
  {
1022
    "SA Pacific Standard Time", "SA Pacific Daylight Time",
1023
    "America/Bogota"
1024
  },              /* (UTC-05:00) Bogota, Lima, Quito, Rio Branco */
1025
  {
1026
    "SA Western Standard Time", "SA Western Daylight Time",
1027
    "America/Caracas"
1028
  },              /* (UTC-04:00) Georgetown, La Paz, Manaus, San
1029
                 * Juan */
1030
  {
1031
    "Saint Pierre Standard Time", "Saint Pierre Daylight Time",
1032
    "America/Miquelon"
1033
  },              /* (UTC-03:00) Saint Pierre and Miquelon */
1034
  {
1035
    "Samoa Standard Time", "Samoa Daylight Time",
1036
    "Pacific/Samoa"
1037
  },              /* (UTC+13:00) Samoa */
1038
  {
1039
    "SE Asia Standard Time", "SE Asia Daylight Time",
1040
    "Asia/Bangkok"
1041
  },              /* (UTC+07:00) Bangkok, Hanoi, Jakarta */
1042
  {
1043
    "Malay Peninsula Standard Time", "Malay Peninsula Daylight Time",
1044
    "Asia/Kuala_Lumpur"
1045
  },              /* (UTC+08:00) Kuala Lumpur, Singapore */
1046
  {
1047
    "Sakhalin Standard Time", "Sakhalin Daylight Time",
1048
    "Asia/Sakhalin"
1049
  },              /* (UTC+11:00) Sakhalin */
1050
  {
1051
    "South Africa Standard Time", "South Africa Daylight Time",
1052
    "Africa/Harare"
1053
  },              /* (UTC+02:00) Harare, Pretoria */
1054
  {
1055
    "Sri Lanka Standard Time", "Sri Lanka Daylight Time",
1056
    "Asia/Colombo"
1057
  },              /* (UTC+05:30) Sri Jayawardenepura */
1058
  {
1059
    "Syria Standard Time", "Syria Daylight Time",
1060
    "Asia/Damascus"
1061
  },              /* (UTC+02:00) Damascus */
1062
  {
1063
    "Taipei Standard Time", "Taipei Daylight Time",
1064
    "Asia/Taipei"
1065
  },              /* (UTC+08:00) Taipei */
1066
  {
1067
    "Tasmania Standard Time", "Tasmania Daylight Time",
1068
    "Australia/Hobart"
1069
  },              /* (UTC+10:00) Hobart */
1070
  {
1071
    "Tocantins Standard Time", "Tocantins Daylight Time",
1072
    "America/Araguaina"
1073
  },              /* (UTC-03:00) Araguaina */
1074
  {
1075
    "Tokyo Standard Time", "Tokyo Daylight Time",
1076
    "Asia/Tokyo"
1077
  },              /* (UTC+09:00) Osaka, Sapporo, Tokyo */
1078
  {
1079
    "Tonga Standard Time", "Tonga Daylight Time",
1080
    "Pacific/Tongatapu"
1081
  },              /* (UTC+13:00) Nuku'alofa */
1082
  {
1083
    "Tomsk Standard Time", "Tomsk Daylight Time",
1084
    "Asia/Tomsk"
1085
  },              /* (UTC+07:00) Tomsk */
1086
  {
1087
    "Transbaikal Standard Time", "Transbaikal Daylight Time",
1088
    "Asia/Chita"
1089
  },              /* (UTC+09:00) Chita */
1090
  {
1091
    "Turkey Standard Time", "Turkey Daylight Time",
1092
    "Europe/Istanbul"
1093
  },              /* (UTC+02:00) Istanbul */
1094
  {
1095
    "Turks and Caicos Standard Time", "Turks and Caicos Daylight Time",
1096
    "America/Grand_Turk"
1097
  },              /* (UTC-04:00) Turks and Caicos */
1098
  {
1099
    "Ulaanbaatar Standard Time", "Ulaanbaatar Daylight Time",
1100
    "Asia/Ulaanbaatar",
1101
  },              /* (UTC+08:00) Ulaanbaatar */
1102
  {
1103
    "US Eastern Standard Time", "US Eastern Daylight Time",
1104
    "US/Eastern"
1105
  },              /* (UTC-05:00) Indiana (East) */
1106
  {
1107
    "US Mountain Standard Time", "US Mountain Daylight Time",
1108
    "US/Arizona"
1109
  },              /* (UTC-07:00) Arizona */
1110
  {
1111
    "Coordinated Universal Time", "Coordinated Universal Time",
1112
    "UTC"
1113
  },              /* (UTC) Coordinated Universal Time */
1114
  {
1115
    "UTC+12", "UTC+12",
1116
    "Etc/GMT+12"
1117
  },              /* (UTC+12:00) Coordinated Universal Time+12 */
1118
  {
1119
    "UTC-02", "UTC-02",
1120
    "Etc/GMT-02"
1121
  },              /* (UTC-02:00) Coordinated Universal Time-02 */
1122
  {
1123
    "UTC-08", "UTC-08",
1124
    "Etc/GMT-08"
1125
  },              /* (UTC-08:00) Coordinated Universal Time-08 */
1126
  {
1127
    "UTC-09", "UTC-09",
1128
    "Etc/GMT-09"
1129
  },              /* (UTC-09:00) Coordinated Universal Time-09 */
1130
  {
1131
    "UTC-11", "UTC-11",
1132
    "Etc/GMT-11"
1133
  },              /* (UTC-11:00) Coordinated Universal Time-11 */
1134
  {
1135
    "Venezuela Standard Time", "Venezuela Daylight Time",
1136
    "America/Caracas",
1137
  },              /* (UTC-04:30) Caracas */
1138
  {
1139
    "Vladivostok Standard Time", "Vladivostok Daylight Time",
1140
    "Asia/Vladivostok"
1141
  },              /* (UTC+10:00) Vladivostok (RTZ 9) */
1142
  {
1143
    "W. Australia Standard Time", "W. Australia Daylight Time",
1144
    "Australia/Perth"
1145
  },              /* (UTC+08:00) Perth */
1146
#ifdef NOT_USED
1147
  /* Could not find a match for this one (just a guess). Excluded for now. */
1148
  {
1149
    "W. Central Africa Standard Time", "W. Central Africa Daylight Time",
1150
    "WAT"
1151
  },              /* (UTC+01:00) West Central Africa */
1152
#endif
1153
  {
1154
    "W. Europe Standard Time", "W. Europe Daylight Time",
1155
    "CET"
1156
  },              /* (UTC+01:00) Amsterdam, Berlin, Bern, Rome,
1157
                 * Stockholm, Vienna */
1158
  {
1159
    "W. Mongolia Standard Time", "W. Mongolia Daylight Time",
1160
    "Asia/Hovd"
1161
  },              /* (UTC+07:00) Hovd */
1162
  {
1163
    "West Asia Standard Time", "West Asia Daylight Time",
1164
    "Asia/Karachi"
1165
  },              /* (UTC+05:00) Ashgabat, Tashkent */
1166
  {
1167
    "West Bank Gaza Standard Time", "West Bank Gaza Daylight Time",
1168
    "Asia/Gaza"
1169
  },              /* (UTC+02:00) Gaza, Hebron */
1170
  {
1171
    "West Pacific Standard Time", "West Pacific Daylight Time",
1172
    "Pacific/Guam"
1173
  },              /* (UTC+10:00) Guam, Port Moresby */
1174
  {
1175
    "Yakutsk Standard Time", "Yakutsk Daylight Time",
1176
    "Asia/Yakutsk"
1177
  },              /* (UTC+09:00) Yakutsk */
1178
  {
1179
    NULL, NULL, NULL
1180
  }
1181
};
1182
1183
static const char *
1184
identify_system_timezone(void)
1185
{
1186
  int     i;
1187
  char    tzname[128];
1188
  char    localtzname[256];
1189
  time_t    t = time(NULL);
1190
  struct tm  *tm = localtime(&t);
1191
  HKEY    rootKey;
1192
  int     idx;
1193
1194
  if (!tm)
1195
  {
1196
#ifdef DEBUG_IDENTIFY_TIMEZONE
1197
    fprintf(stderr, "could not identify system time zone: localtime() failed\n");
1198
#endif
1199
    return NULL;      /* go to GMT */
1200
  }
1201
1202
  memset(tzname, 0, sizeof(tzname));
1203
  strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
1204
1205
  for (i = 0; win32_tzmap[i].stdname != NULL; i++)
1206
  {
1207
    if (strcmp(tzname, win32_tzmap[i].stdname) == 0 ||
1208
      strcmp(tzname, win32_tzmap[i].dstname) == 0)
1209
    {
1210
#ifdef DEBUG_IDENTIFY_TIMEZONE
1211
      fprintf(stderr, "TZ \"%s\" matches system time zone \"%s\"\n",
1212
          win32_tzmap[i].pgtzname, tzname);
1213
#endif
1214
      return win32_tzmap[i].pgtzname;
1215
    }
1216
  }
1217
1218
  /*
1219
   * Localized Windows versions return localized names for the timezone.
1220
   * Scan the registry to find the English name, and then try matching
1221
   * against our table again.
1222
   */
1223
  memset(localtzname, 0, sizeof(localtzname));
1224
  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1225
           "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
1226
           0,
1227
           KEY_READ,
1228
           &rootKey) != ERROR_SUCCESS)
1229
  {
1230
#ifdef DEBUG_IDENTIFY_TIMEZONE
1231
    fprintf(stderr, "could not open registry key to identify system time zone: error code %lu\n",
1232
        GetLastError());
1233
#endif
1234
    return NULL;      /* go to GMT */
1235
  }
1236
1237
  for (idx = 0;; idx++)
1238
  {
1239
    char    keyname[256];
1240
    char    zonename[256];
1241
    DWORD   namesize;
1242
    FILETIME  lastwrite;
1243
    HKEY    key;
1244
    LONG    r;
1245
1246
    memset(keyname, 0, sizeof(keyname));
1247
    namesize = sizeof(keyname);
1248
    if ((r = RegEnumKeyEx(rootKey,
1249
                idx,
1250
                keyname,
1251
                &namesize,
1252
                NULL,
1253
                NULL,
1254
                NULL,
1255
                &lastwrite)) != ERROR_SUCCESS)
1256
    {
1257
      if (r == ERROR_NO_MORE_ITEMS)
1258
        break;
1259
#ifdef DEBUG_IDENTIFY_TIMEZONE
1260
      fprintf(stderr, "could not enumerate registry subkeys to identify system time zone: %d\n",
1261
          (int) r);
1262
#endif
1263
      break;
1264
    }
1265
1266
    if ((r = RegOpenKeyEx(rootKey, keyname, 0, KEY_READ, &key)) != ERROR_SUCCESS)
1267
    {
1268
#ifdef DEBUG_IDENTIFY_TIMEZONE
1269
      fprintf(stderr, "could not open registry subkey to identify system time zone: %d\n",
1270
          (int) r);
1271
#endif
1272
      break;
1273
    }
1274
1275
    memset(zonename, 0, sizeof(zonename));
1276
    namesize = sizeof(zonename);
1277
    if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
1278
    {
1279
#ifdef DEBUG_IDENTIFY_TIMEZONE
1280
      fprintf(stderr, "could not query value for key \"std\" to identify system time zone \"%s\": %d\n",
1281
          keyname, (int) r);
1282
#endif
1283
      RegCloseKey(key);
1284
      continue;     /* Proceed to look at the next timezone */
1285
    }
1286
    if (strcmp(tzname, zonename) == 0)
1287
    {
1288
      /* Matched zone */
1289
      strcpy(localtzname, keyname);
1290
      RegCloseKey(key);
1291
      break;
1292
    }
1293
    memset(zonename, 0, sizeof(zonename));
1294
    namesize = sizeof(zonename);
1295
    if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
1296
    {
1297
#ifdef DEBUG_IDENTIFY_TIMEZONE
1298
      fprintf(stderr, "could not query value for key \"dlt\" to identify system time zone \"%s\": %d\n",
1299
          keyname, (int) r);
1300
#endif
1301
      RegCloseKey(key);
1302
      continue;     /* Proceed to look at the next timezone */
1303
    }
1304
    if (strcmp(tzname, zonename) == 0)
1305
    {
1306
      /* Matched DST zone */
1307
      strcpy(localtzname, keyname);
1308
      RegCloseKey(key);
1309
      break;
1310
    }
1311
1312
    RegCloseKey(key);
1313
  }
1314
1315
  RegCloseKey(rootKey);
1316
1317
  if (localtzname[0])
1318
  {
1319
    /* Found a localized name, so scan for that one too */
1320
    for (i = 0; win32_tzmap[i].stdname != NULL; i++)
1321
    {
1322
      if (strcmp(localtzname, win32_tzmap[i].stdname) == 0 ||
1323
        strcmp(localtzname, win32_tzmap[i].dstname) == 0)
1324
      {
1325
#ifdef DEBUG_IDENTIFY_TIMEZONE
1326
        fprintf(stderr, "TZ \"%s\" matches localized system time zone \"%s\" (\"%s\")\n",
1327
            win32_tzmap[i].pgtzname, tzname, localtzname);
1328
#endif
1329
        return win32_tzmap[i].pgtzname;
1330
      }
1331
    }
1332
  }
1333
1334
#ifdef DEBUG_IDENTIFY_TIMEZONE
1335
  fprintf(stderr, "could not find a match for system time zone \"%s\"\n",
1336
      tzname);
1337
#endif
1338
  return NULL;        /* go to GMT */
1339
}
1340
#endif              /* WIN32 */
1341
1342
1343
/*
1344
 * Return true if the given zone name is valid and is an "acceptable" zone.
1345
 */
1346
static bool
1347
validate_zone(const char *tzname)
1348
1.81k
{
1349
1.81k
  pg_tz    *tz;
1350
1351
1.81k
  if (!tzname || !tzname[0])
1352
907
    return false;
1353
1354
907
  tz = pg_load_tz(tzname);
1355
907
  if (!tz)
1356
0
    return false;
1357
1358
907
  if (!pg_tz_acceptable(tz))
1359
0
    return false;
1360
1361
907
  return true;
1362
907
}
1363
1364
/*
1365
 * Identify a suitable default timezone setting based on the environment.
1366
 *
1367
 * The installation share_path must be passed in, as that is the default
1368
 * location for the timezone database directory.
1369
 *
1370
 * We first look to the TZ environment variable.  If not found or not
1371
 * recognized by our own code, we see if we can identify the timezone
1372
 * from the behavior of the system timezone library.  When all else fails,
1373
 * return NULL, indicating that we should default to GMT.
1374
 */
1375
const char *
1376
select_default_timezone(const char *share_path)
1377
907
{
1378
907
  const char *tzname;
1379
1380
  /* Initialize timezone directory path, if needed */
1381
907
#ifndef SYSTEMTZDIR
1382
907
  snprintf(tzdirpath, sizeof(tzdirpath), "%s/timezone", share_path);
1383
907
#endif
1384
1385
  /* Check TZ environment variable */
1386
907
  tzname = getenv("TZ");
1387
907
  if (validate_zone(tzname))
1388
0
    return tzname;
1389
1390
  /* Nope, so try to identify the system timezone */
1391
907
  tzname = identify_system_timezone();
1392
907
  if (validate_zone(tzname))
1393
907
    return tzname;
1394
1395
0
  return NULL;
1396
0
}