Imported Upstream version 2.61.2
[platform/upstream/glib.git] / glib / gtimezone.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Ryan Lortie <desrt@desrt.ca>
18  */
19
20 /* Prologue {{{1 */
21
22 #include "config.h"
23
24 #include "gtimezone.h"
25
26 #include <string.h>
27 #include <stdlib.h>
28 #include <signal.h>
29
30 #include "gmappedfile.h"
31 #include "gtestutils.h"
32 #include "gfileutils.h"
33 #include "gstrfuncs.h"
34 #include "ghash.h"
35 #include "gthread.h"
36 #include "gbytes.h"
37 #include "gslice.h"
38 #include "gdatetime.h"
39 #include "gdate.h"
40
41 #ifdef G_OS_WIN32
42
43 #define STRICT
44 #include <windows.h>
45 #include <wchar.h>
46 #endif
47
48 /**
49  * SECTION:timezone
50  * @title: GTimeZone
51  * @short_description: a structure representing a time zone
52  * @see_also: #GDateTime
53  *
54  * #GTimeZone is a structure that represents a time zone, at no
55  * particular point in time.  It is refcounted and immutable.
56  *
57  * Each time zone has an identifier (for example, ‘Europe/London’) which is
58  * platform dependent. See g_time_zone_new() for information on the identifier
59  * formats. The identifier of a time zone can be retrieved using
60  * g_time_zone_get_identifier().
61  *
62  * A time zone contains a number of intervals.  Each interval has
63  * an abbreviation to describe it (for example, ‘PDT’), an offet to UTC and a
64  * flag indicating if the daylight savings time is in effect during that
65  * interval.  A time zone always has at least one interval — interval 0. Note
66  * that interval abbreviations are not the same as time zone identifiers
67  * (apart from ‘UTC’), and cannot be passed to g_time_zone_new().
68  *
69  * Every UTC time is contained within exactly one interval, but a given
70  * local time may be contained within zero, one or two intervals (due to
71  * incontinuities associated with daylight savings time).
72  *
73  * An interval may refer to a specific period of time (eg: the duration
74  * of daylight savings time during 2010) or it may refer to many periods
75  * of time that share the same properties (eg: all periods of daylight
76  * savings time).  It is also possible (usually for political reasons)
77  * that some properties (like the abbreviation) change between intervals
78  * without other properties changing.
79  *
80  * #GTimeZone is available since GLib 2.26.
81  */
82
83 /**
84  * GTimeZone:
85  *
86  * #GTimeZone is an opaque structure whose members cannot be accessed
87  * directly.
88  *
89  * Since: 2.26
90  **/
91
92 /* IANA zoneinfo file format {{{1 */
93
94 /* unaligned */
95 typedef struct { gchar bytes[8]; } gint64_be;
96 typedef struct { gchar bytes[4]; } gint32_be;
97 typedef struct { gchar bytes[4]; } guint32_be;
98
99 static inline gint64 gint64_from_be (const gint64_be be) {
100   gint64 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT64_FROM_BE (tmp);
101 }
102
103 static inline gint32 gint32_from_be (const gint32_be be) {
104   gint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT32_FROM_BE (tmp);
105 }
106
107 static inline guint32 guint32_from_be (const guint32_be be) {
108   guint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GUINT32_FROM_BE (tmp);
109 }
110
111 /* The layout of an IANA timezone file header */
112 struct tzhead
113 {
114   gchar      tzh_magic[4];
115   gchar      tzh_version;
116   guchar     tzh_reserved[15];
117
118   guint32_be tzh_ttisgmtcnt;
119   guint32_be tzh_ttisstdcnt;
120   guint32_be tzh_leapcnt;
121   guint32_be tzh_timecnt;
122   guint32_be tzh_typecnt;
123   guint32_be tzh_charcnt;
124 };
125
126 struct ttinfo
127 {
128   gint32_be tt_gmtoff;
129   guint8    tt_isdst;
130   guint8    tt_abbrind;
131 };
132
133 /* A Transition Date structure for TZ Rules, an intermediate structure
134    for parsing MSWindows and Environment-variable time zones. It
135    Generalizes MSWindows's SYSTEMTIME struct.
136  */
137 typedef struct
138 {
139   gint     year;
140   gint     mon;
141   gint     mday;
142   gint     wday;
143   gint     week;
144   gint     hour;
145   gint     min;
146   gint     sec;
147 } TimeZoneDate;
148
149 /* POSIX Timezone abbreviations are typically 3 or 4 characters, but
150    Microsoft uses 32-character names. We'll use one larger to ensure
151    we have room for the terminating \0.
152  */
153 #define NAME_SIZE 33
154
155 /* A MSWindows-style time zone transition rule. Generalizes the
156    MSWindows TIME_ZONE_INFORMATION struct. Also used to compose time
157    zones from tzset-style identifiers.
158  */
159 typedef struct
160 {
161   gint         start_year;
162   gint32       std_offset;
163   gint32       dlt_offset;
164   TimeZoneDate dlt_start;
165   TimeZoneDate dlt_end;
166   gchar std_name[NAME_SIZE];
167   gchar dlt_name[NAME_SIZE];
168 } TimeZoneRule;
169
170 /* GTimeZone's internal representation of a Daylight Savings (Summer)
171    time interval.
172  */
173 typedef struct
174 {
175   gint32     gmt_offset;
176   gboolean   is_dst;
177   gchar     *abbrev;
178 } TransitionInfo;
179
180 /* GTimeZone's representation of a transition time to or from Daylight
181    Savings (Summer) time and Standard time for the zone. */
182 typedef struct
183 {
184   gint64 time;
185   gint   info_index;
186 } Transition;
187
188 /* GTimeZone structure */
189 struct _GTimeZone
190 {
191   gchar   *name;
192   GArray  *t_info;         /* Array of TransitionInfo */
193   GArray  *transitions;    /* Array of Transition */
194   gint     ref_count;
195 };
196
197 G_LOCK_DEFINE_STATIC (time_zones);
198 static GHashTable/*<string?, GTimeZone>*/ *time_zones;
199
200 #define MIN_TZYEAR 1916 /* Daylight Savings started in WWI */
201 #define MAX_TZYEAR 2999 /* And it's not likely ever to go away, but
202                            there's no point in getting carried
203                            away. */
204
205 /**
206  * g_time_zone_unref:
207  * @tz: a #GTimeZone
208  *
209  * Decreases the reference count on @tz.
210  *
211  * Since: 2.26
212  **/
213 void
214 g_time_zone_unref (GTimeZone *tz)
215 {
216   int ref_count;
217
218 again:
219   ref_count = g_atomic_int_get (&tz->ref_count);
220
221   g_assert (ref_count > 0);
222
223   if (ref_count == 1)
224     {
225       if (tz->name != NULL)
226         {
227           G_LOCK(time_zones);
228
229           /* someone else might have grabbed a ref in the meantime */
230           if G_UNLIKELY (g_atomic_int_get (&tz->ref_count) != 1)
231             {
232               G_UNLOCK(time_zones);
233               goto again;
234             }
235
236           g_hash_table_remove (time_zones, tz->name);
237           G_UNLOCK(time_zones);
238         }
239
240       if (tz->t_info != NULL)
241         {
242           guint idx;
243           for (idx = 0; idx < tz->t_info->len; idx++)
244             {
245               TransitionInfo *info = &g_array_index (tz->t_info, TransitionInfo, idx);
246               g_free (info->abbrev);
247             }
248           g_array_free (tz->t_info, TRUE);
249         }
250       if (tz->transitions != NULL)
251         g_array_free (tz->transitions, TRUE);
252       g_free (tz->name);
253
254       g_slice_free (GTimeZone, tz);
255     }
256
257   else if G_UNLIKELY (!g_atomic_int_compare_and_exchange (&tz->ref_count,
258                                                           ref_count,
259                                                           ref_count - 1))
260     goto again;
261 }
262
263 /**
264  * g_time_zone_ref:
265  * @tz: a #GTimeZone
266  *
267  * Increases the reference count on @tz.
268  *
269  * Returns: a new reference to @tz.
270  *
271  * Since: 2.26
272  **/
273 GTimeZone *
274 g_time_zone_ref (GTimeZone *tz)
275 {
276   g_assert (tz->ref_count > 0);
277
278   g_atomic_int_inc (&tz->ref_count);
279
280   return tz;
281 }
282
283 /* fake zoneinfo creation (for RFC3339/ISO 8601 timezones) {{{1 */
284 /*
285  * parses strings of the form h or hh[[:]mm[[[:]ss]]] where:
286  *  - h[h] is 0 to 23
287  *  - mm is 00 to 59
288  *  - ss is 00 to 59
289  */
290 static gboolean
291 parse_time (const gchar *time_,
292             gint32      *offset)
293 {
294   if (*time_ < '0' || '9' < *time_)
295     return FALSE;
296
297   *offset = 60 * 60 * (*time_++ - '0');
298
299   if (*time_ == '\0')
300     return TRUE;
301
302   if (*time_ != ':')
303     {
304       if (*time_ < '0' || '9' < *time_)
305         return FALSE;
306
307       *offset *= 10;
308       *offset += 60 * 60 * (*time_++ - '0');
309
310       if (*offset > 23 * 60 * 60)
311         return FALSE;
312
313       if (*time_ == '\0')
314         return TRUE;
315     }
316
317   if (*time_ == ':')
318     time_++;
319
320   if (*time_ < '0' || '5' < *time_)
321     return FALSE;
322
323   *offset += 10 * 60 * (*time_++ - '0');
324
325   if (*time_ < '0' || '9' < *time_)
326     return FALSE;
327
328   *offset += 60 * (*time_++ - '0');
329
330   if (*time_ == '\0')
331     return TRUE;
332
333   if (*time_ == ':')
334     time_++;
335
336   if (*time_ < '0' || '5' < *time_)
337     return FALSE;
338
339   *offset += 10 * (*time_++ - '0');
340
341   if (*time_ < '0' || '9' < *time_)
342     return FALSE;
343
344   *offset += *time_++ - '0';
345
346   return *time_ == '\0';
347 }
348
349 static gboolean
350 parse_constant_offset (const gchar *name,
351                        gint32      *offset)
352 {
353   if (g_strcmp0 (name, "UTC") == 0)
354     {
355       *offset = 0;
356       return TRUE;
357     }
358
359   if (*name >= '0' && '9' >= *name)
360     return parse_time (name, offset);
361
362   switch (*name++)
363     {
364     case 'Z':
365       *offset = 0;
366       return !*name;
367
368     case '+':
369       return parse_time (name, offset);
370
371     case '-':
372       if (parse_time (name, offset))
373         {
374           *offset = -*offset;
375           return TRUE;
376         }
377       else
378         return FALSE;
379
380     default:
381       return FALSE;
382     }
383 }
384
385 static void
386 zone_for_constant_offset (GTimeZone *gtz, const gchar *name)
387 {
388   gint32 offset;
389   TransitionInfo info;
390
391   if (name == NULL || !parse_constant_offset (name, &offset))
392     return;
393
394   info.gmt_offset = offset;
395   info.is_dst = FALSE;
396   info.abbrev =  g_strdup (name);
397
398   gtz->name = g_strdup (name);
399   gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), 1);
400   g_array_append_val (gtz->t_info, info);
401
402   /* Constant offset, no transitions */
403   gtz->transitions = NULL;
404 }
405
406 #ifdef G_OS_UNIX
407 static GBytes*
408 zone_info_unix (const gchar  *identifier,
409                 gchar       **out_identifier)
410 {
411   gchar *filename;
412   GMappedFile *file = NULL;
413   GBytes *zoneinfo = NULL;
414   gchar *resolved_identifier = NULL;
415   const gchar *tzdir;
416
417   tzdir = getenv ("TZDIR");
418   if (tzdir == NULL)
419     tzdir = "/usr/share/zoneinfo";
420
421   /* identifier can be a relative or absolute path name;
422      if relative, it is interpreted starting from /usr/share/zoneinfo
423      while the POSIX standard says it should start with :,
424      glibc allows both syntaxes, so we should too */
425   if (identifier != NULL)
426     {
427       resolved_identifier = g_strdup (identifier);
428
429       if (*identifier == ':')
430         identifier ++;
431
432       if (g_path_is_absolute (identifier))
433         filename = g_strdup (identifier);
434       else
435         filename = g_build_filename (tzdir, identifier, NULL);
436     }
437   else
438     {
439       gsize prefix_len = 0;
440       gchar *canonical_path = NULL;
441       GError *read_link_err = NULL;
442
443       filename = g_strdup ("/etc/localtime");
444
445       /* Resolve the actual timezone pointed to by /etc/localtime. */
446       resolved_identifier = g_file_read_link (filename, &read_link_err);
447       if (resolved_identifier == NULL)
448         {
449           gboolean not_a_symlink = g_error_matches (read_link_err,
450                                                     G_FILE_ERROR,
451                                                     G_FILE_ERROR_INVAL);
452           g_clear_error (&read_link_err);
453
454           /* Fallback to the content of /var/db/zoneinfo or /etc/timezone
455            * if /etc/localtime is not a symlink. /var/db/zoneinfo is
456            * where 'tzsetup' program on FreeBSD and DragonflyBSD stores
457            * the timezone chosen by the user. /etc/timezone is where user
458            * choice is expressed on Gentoo OpenRC and others. */
459           if (not_a_symlink && (g_file_get_contents ("/var/db/zoneinfo",
460                                                      &resolved_identifier,
461                                                      NULL, NULL) ||
462                                 g_file_get_contents ("/etc/timezone",
463                                                      &resolved_identifier,
464                                                      NULL, NULL)))
465             g_strchomp (resolved_identifier);
466           else
467             {
468               /* Error */
469               g_assert (resolved_identifier == NULL);
470               goto out;
471             }
472         }
473       else
474         {
475           /* Resolve relative path */
476           canonical_path = g_canonicalize_filename (resolved_identifier, "/etc");
477           g_free (resolved_identifier);
478           resolved_identifier = g_steal_pointer (&canonical_path);
479         }
480
481       /* Strip the prefix and slashes if possible. */
482       if (g_str_has_prefix (resolved_identifier, tzdir))
483         {
484           prefix_len = strlen (tzdir);
485           while (*(resolved_identifier + prefix_len) == '/')
486             prefix_len++;
487         }
488
489       if (prefix_len > 0)
490         memmove (resolved_identifier, resolved_identifier + prefix_len,
491                  strlen (resolved_identifier) - prefix_len + 1  /* nul terminator */);
492
493       g_free (canonical_path);
494     }
495
496   file = g_mapped_file_new (filename, FALSE, NULL);
497   if (file != NULL)
498     {
499       zoneinfo = g_bytes_new_with_free_func (g_mapped_file_get_contents (file),
500                                              g_mapped_file_get_length (file),
501                                              (GDestroyNotify)g_mapped_file_unref,
502                                              g_mapped_file_ref (file));
503       g_mapped_file_unref (file);
504     }
505
506   g_assert (resolved_identifier != NULL);
507
508 out:
509   if (out_identifier != NULL)
510     *out_identifier = g_steal_pointer (&resolved_identifier);
511
512   g_free (resolved_identifier);
513   g_free (filename);
514
515   return zoneinfo;
516 }
517
518 static void
519 init_zone_from_iana_info (GTimeZone *gtz,
520                           GBytes    *zoneinfo,
521                           gchar     *identifier  /* (transfer full) */)
522 {
523   gsize size;
524   guint index;
525   guint32 time_count, type_count;
526   guint8 *tz_transitions, *tz_type_index, *tz_ttinfo;
527   guint8 *tz_abbrs;
528   gsize timesize = sizeof (gint32);
529   const struct tzhead *header = g_bytes_get_data (zoneinfo, &size);
530
531   g_return_if_fail (size >= sizeof (struct tzhead) &&
532                     memcmp (header, "TZif", 4) == 0);
533
534   if (header->tzh_version == '2')
535       {
536         /* Skip ahead to the newer 64-bit data if it's available. */
537         header = (const struct tzhead *)
538           (((const gchar *) (header + 1)) +
539            guint32_from_be(header->tzh_ttisgmtcnt) +
540            guint32_from_be(header->tzh_ttisstdcnt) +
541            8 * guint32_from_be(header->tzh_leapcnt) +
542            5 * guint32_from_be(header->tzh_timecnt) +
543            6 * guint32_from_be(header->tzh_typecnt) +
544            guint32_from_be(header->tzh_charcnt));
545         timesize = sizeof (gint64);
546       }
547   time_count = guint32_from_be(header->tzh_timecnt);
548   type_count = guint32_from_be(header->tzh_typecnt);
549
550   tz_transitions = ((guint8 *) (header) + sizeof (*header));
551   tz_type_index = tz_transitions + timesize * time_count;
552   tz_ttinfo = tz_type_index + time_count;
553   tz_abbrs = tz_ttinfo + sizeof (struct ttinfo) * type_count;
554
555   gtz->name = g_steal_pointer (&identifier);
556   gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo),
557                                    type_count);
558   gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition),
559                                         time_count);
560
561   for (index = 0; index < type_count; index++)
562     {
563       TransitionInfo t_info;
564       struct ttinfo info = ((struct ttinfo*)tz_ttinfo)[index];
565       t_info.gmt_offset = gint32_from_be (info.tt_gmtoff);
566       t_info.is_dst = info.tt_isdst ? TRUE : FALSE;
567       t_info.abbrev = g_strdup ((gchar *) &tz_abbrs[info.tt_abbrind]);
568       g_array_append_val (gtz->t_info, t_info);
569     }
570
571   for (index = 0; index < time_count; index++)
572     {
573       Transition trans;
574       if (header->tzh_version == '2')
575         trans.time = gint64_from_be (((gint64_be*)tz_transitions)[index]);
576       else
577         trans.time = gint32_from_be (((gint32_be*)tz_transitions)[index]);
578       trans.info_index = tz_type_index[index];
579       g_assert (trans.info_index >= 0);
580       g_assert ((guint) trans.info_index < gtz->t_info->len);
581       g_array_append_val (gtz->transitions, trans);
582     }
583 }
584
585 #elif defined (G_OS_WIN32)
586
587 static void
588 copy_windows_systemtime (SYSTEMTIME *s_time, TimeZoneDate *tzdate)
589 {
590   tzdate->sec = s_time->wSecond;
591   tzdate->min = s_time->wMinute;
592   tzdate->hour = s_time->wHour;
593   tzdate->mon = s_time->wMonth;
594   tzdate->year = s_time->wYear;
595   tzdate->wday = s_time->wDayOfWeek ? s_time->wDayOfWeek : 7;
596
597   if (s_time->wYear)
598     {
599       tzdate->mday = s_time->wDay;
600       tzdate->wday = 0;
601     }
602   else
603     tzdate->week = s_time->wDay;
604 }
605
606 /* UTC = local time + bias while local time = UTC + offset */
607 static gboolean
608 rule_from_windows_time_zone_info (TimeZoneRule *rule,
609                                   TIME_ZONE_INFORMATION *tzi)
610 {
611   gchar *std_name, *dlt_name;
612
613   std_name = g_utf16_to_utf8 ((gunichar2 *)tzi->StandardName, -1, NULL, NULL, NULL);
614   if (std_name == NULL)
615     return FALSE;
616
617   dlt_name = g_utf16_to_utf8 ((gunichar2 *)tzi->DaylightName, -1, NULL, NULL, NULL);
618   if (dlt_name == NULL)
619     {
620       g_free (std_name);
621       return FALSE;
622     }
623
624   /* Set offset */
625   if (tzi->StandardDate.wMonth)
626     {
627       rule->std_offset = -(tzi->Bias + tzi->StandardBias) * 60;
628       rule->dlt_offset = -(tzi->Bias + tzi->DaylightBias) * 60;
629       copy_windows_systemtime (&(tzi->DaylightDate), &(rule->dlt_start));
630
631       copy_windows_systemtime (&(tzi->StandardDate), &(rule->dlt_end));
632     }
633
634   else
635     {
636       rule->std_offset = -tzi->Bias * 60;
637       rule->dlt_start.mon = 0;
638     }
639   strncpy (rule->std_name, std_name, NAME_SIZE - 1);
640   strncpy (rule->dlt_name, dlt_name, NAME_SIZE - 1);
641
642   g_free (std_name);
643   g_free (dlt_name);
644
645   return TRUE;
646 }
647
648 static gchar*
649 windows_default_tzname (void)
650 {
651   const gunichar2 *subkey =
652     L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
653   HKEY key;
654   gchar *key_name = NULL;
655   gunichar2 *key_name_w = NULL;
656   if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey, 0,
657                      KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
658     {
659       DWORD size = 0;
660       if (RegQueryValueExW (key, L"TimeZoneKeyName", NULL, NULL,
661                             NULL, &size) == ERROR_SUCCESS)
662         {
663           key_name_w = g_malloc ((gint)size);
664
665           if (key_name_w == NULL ||
666               RegQueryValueExW (key, L"TimeZoneKeyName", NULL, NULL,
667                                 (LPBYTE)key_name_w, &size) != ERROR_SUCCESS)
668             {
669               g_free (key_name_w);
670               key_name = NULL;
671             }
672           else
673             key_name = g_utf16_to_utf8 (key_name_w, -1, NULL, NULL, NULL);
674         }
675       RegCloseKey (key);
676     }
677   return key_name;
678 }
679
680 typedef   struct
681 {
682   LONG Bias;
683   LONG StandardBias;
684   LONG DaylightBias;
685   SYSTEMTIME StandardDate;
686   SYSTEMTIME DaylightDate;
687 } RegTZI;
688
689 static void
690 system_time_copy (SYSTEMTIME *orig, SYSTEMTIME *target)
691 {
692   g_return_if_fail (orig != NULL);
693   g_return_if_fail (target != NULL);
694
695   target->wYear = orig->wYear;
696   target->wMonth = orig->wMonth;
697   target->wDayOfWeek = orig->wDayOfWeek;
698   target->wDay = orig->wDay;
699   target->wHour = orig->wHour;
700   target->wMinute = orig->wMinute;
701   target->wSecond = orig->wSecond;
702   target->wMilliseconds = orig->wMilliseconds;
703 }
704
705 static void
706 register_tzi_to_tzi (RegTZI *reg, TIME_ZONE_INFORMATION *tzi)
707 {
708   g_return_if_fail (reg != NULL);
709   g_return_if_fail (tzi != NULL);
710   tzi->Bias = reg->Bias;
711   system_time_copy (&(reg->StandardDate), &(tzi->StandardDate));
712   tzi->StandardBias = reg->StandardBias;
713   system_time_copy (&(reg->DaylightDate), &(tzi->DaylightDate));
714   tzi->DaylightBias = reg->DaylightBias;
715 }
716
717 static guint
718 rules_from_windows_time_zone (const gchar   *identifier,
719                               gchar        **out_identifier,
720                               TimeZoneRule **rules,
721                               gboolean       copy_identifier)
722 {
723   HKEY key;
724   gchar *subkey = NULL;
725   gchar *subkey_dynamic = NULL;
726   gchar *key_name = NULL;
727   const gchar *reg_key =
728     "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
729   TIME_ZONE_INFORMATION tzi;
730   DWORD size;
731   guint rules_num = 0;
732   RegTZI regtzi, regtzi_prev;
733   WCHAR winsyspath[MAX_PATH];
734   gunichar2 *subkey_w, *subkey_dynamic_w;
735
736   if (GetSystemDirectoryW (winsyspath, MAX_PATH) == 0)
737     return 0;
738
739   g_assert (copy_identifier == FALSE || out_identifier != NULL);
740   g_assert (rules != NULL);
741
742   if (copy_identifier)
743     *out_identifier = NULL;
744
745   *rules = NULL;
746   key_name = NULL;
747
748   if (!identifier)
749     key_name = windows_default_tzname ();
750   else
751     key_name = g_strdup (identifier);
752
753   if (!key_name)
754     return 0;
755
756   subkey = g_strconcat (reg_key, key_name, NULL);
757   subkey_w = g_utf8_to_utf16 (subkey, -1, NULL, NULL, NULL);
758   if (subkey_w == NULL)
759     goto utf16_conv_failed;
760
761   subkey_dynamic = g_strconcat (subkey, "\\Dynamic DST", NULL);
762   subkey_dynamic_w = g_utf8_to_utf16 (subkey_dynamic, -1, NULL, NULL, NULL);
763   if (subkey_dynamic_w == NULL)
764     goto utf16_conv_failed;
765
766   if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_w, 0,
767                      KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
768       goto utf16_conv_failed;
769
770   size = sizeof tzi.StandardName;
771
772   /* use RegLoadMUIStringW() to query MUI_Std from the registry if possible, otherwise
773      fallback to querying Std */
774   if (RegLoadMUIStringW (key, L"MUI_Std", tzi.StandardName,
775                          size, &size, 0, winsyspath) != ERROR_SUCCESS)
776     {
777       size = sizeof tzi.StandardName;
778       if (RegQueryValueExW (key, L"Std", NULL, NULL,
779                             (LPBYTE)&(tzi.StandardName), &size) != ERROR_SUCCESS)
780         goto registry_failed;
781     }
782
783   size = sizeof tzi.DaylightName;
784
785   /* use RegLoadMUIStringW() to query MUI_Dlt from the registry if possible, otherwise
786      fallback to querying Dlt */
787   if (RegLoadMUIStringW (key, L"MUI_Dlt", tzi.DaylightName,
788                          size, &size, 0, winsyspath) != ERROR_SUCCESS)
789     {
790       size = sizeof tzi.DaylightName;
791       if (RegQueryValueExW (key, L"Dlt", NULL, NULL,
792                             (LPBYTE)&(tzi.DaylightName), &size) != ERROR_SUCCESS)
793         goto registry_failed;
794     }
795
796   RegCloseKey (key);
797   if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_dynamic_w, 0,
798                      KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
799     {
800       DWORD first, last;
801       int year, i;
802       wchar_t s[12];
803
804       size = sizeof first;
805       if (RegQueryValueExW (key, L"FirstEntry", NULL, NULL,
806                             (LPBYTE) &first, &size) != ERROR_SUCCESS)
807         goto registry_failed;
808
809       size = sizeof last;
810       if (RegQueryValueExW (key, L"LastEntry", NULL, NULL,
811                             (LPBYTE) &last, &size) != ERROR_SUCCESS)
812         goto registry_failed;
813
814       rules_num = last - first + 2;
815       *rules = g_new0 (TimeZoneRule, rules_num);
816
817       for (year = first, i = 0; *rules != NULL && year <= last; year++)
818         {
819           gboolean failed = FALSE;
820           swprintf_s (s, 11, L"%d", year);
821
822           if (!failed)
823             {
824               size = sizeof regtzi;
825               if (RegQueryValueExW (key, s, NULL, NULL,
826                                     (LPBYTE) &regtzi, &size) != ERROR_SUCCESS)
827                 failed = TRUE;
828             }
829
830           if (failed)
831             {
832               g_free (*rules);
833               *rules = NULL;
834               break;
835             }
836
837           if (year > first && memcmp (&regtzi_prev, &regtzi, sizeof regtzi) == 0)
838               continue;
839           else
840             memcpy (&regtzi_prev, &regtzi, sizeof regtzi);
841
842           register_tzi_to_tzi (&regtzi, &tzi);
843
844           if (!rule_from_windows_time_zone_info (&(*rules)[i], &tzi))
845             {
846               g_free (*rules);
847               *rules = NULL;
848               break;
849             }
850
851           (*rules)[i++].start_year = year;
852         }
853
854       rules_num = i + 1;
855
856 registry_failed:
857       RegCloseKey (key);
858     }
859   else if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_w, 0,
860                           KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
861     {
862       size = sizeof regtzi;
863       if (RegQueryValueExW (key, L"TZI", NULL, NULL,
864                             (LPBYTE) &regtzi, &size) == ERROR_SUCCESS)
865         {
866           rules_num = 2;
867           *rules = g_new0 (TimeZoneRule, 2);
868           register_tzi_to_tzi (&regtzi, &tzi);
869
870           if (!rule_from_windows_time_zone_info (&(*rules)[0], &tzi))
871             {
872               g_free (*rules);
873               *rules = NULL;
874             }
875         }
876
877       RegCloseKey (key);
878     }
879
880 utf16_conv_failed:
881   g_free (subkey_dynamic_w);
882   g_free (subkey_dynamic);
883   g_free (subkey_w);
884   g_free (subkey);
885
886   if (*rules)
887     {
888       (*rules)[0].start_year = MIN_TZYEAR;
889       if ((*rules)[rules_num - 2].start_year < MAX_TZYEAR)
890         (*rules)[rules_num - 1].start_year = MAX_TZYEAR;
891       else
892         (*rules)[rules_num - 1].start_year = (*rules)[rules_num - 2].start_year + 1;
893
894       if (copy_identifier)
895         *out_identifier = g_steal_pointer (&key_name);
896       else
897         g_free (key_name);
898
899       return rules_num;
900     }
901
902   g_free (key_name);
903
904   return 0;
905 }
906
907 #endif
908
909 static void
910 find_relative_date (TimeZoneDate *buffer)
911 {
912   guint wday;
913   GDate date;
914   g_date_clear (&date, 1);
915   wday = buffer->wday;
916
917   /* Get last day if last is needed, first day otherwise */
918   if (buffer->mon == 13 || buffer->mon == 14) /* Julian Date */
919     {
920       g_date_set_dmy (&date, 1, 1, buffer->year);
921       if (wday >= 59 && buffer->mon == 13 && g_date_is_leap_year (buffer->year))
922         g_date_add_days (&date, wday);
923       else
924         g_date_add_days (&date, wday - 1);
925       buffer->mon = (int) g_date_get_month (&date);
926       buffer->mday = (int) g_date_get_day (&date);
927       buffer->wday = 0;
928     }
929   else /* M.W.D */
930     {
931       guint days;
932       guint days_in_month = g_date_get_days_in_month (buffer->mon, buffer->year);
933       GDateWeekday first_wday;
934
935       g_date_set_dmy (&date, 1, buffer->mon, buffer->year);
936       first_wday = g_date_get_weekday (&date);
937
938       if (first_wday > wday)
939         ++(buffer->week);
940       /* week is 1 <= w <= 5, we need 0-based */
941       days = 7 * (buffer->week - 1) + wday - first_wday;
942
943       while (days > days_in_month)
944         days -= 7;
945
946       g_date_add_days (&date, days);
947
948       buffer->mday = g_date_get_day (&date);
949     }
950 }
951
952 /* Offset is previous offset of local time. Returns 0 if month is 0 */
953 static gint64
954 boundary_for_year (TimeZoneDate *boundary,
955                    gint          year,
956                    gint32        offset)
957 {
958   TimeZoneDate buffer;
959   GDate date;
960   const guint64 unix_epoch_start = 719163L;
961   const guint64 seconds_per_day = 86400L;
962
963   if (!boundary->mon)
964     return 0;
965   buffer = *boundary;
966
967   if (boundary->year == 0)
968     {
969       buffer.year = year;
970
971       if (buffer.wday)
972         find_relative_date (&buffer);
973     }
974
975   g_assert (buffer.year == year);
976   g_date_clear (&date, 1);
977   g_date_set_dmy (&date, buffer.mday, buffer.mon, buffer.year);
978   return ((g_date_get_julian (&date) - unix_epoch_start) * seconds_per_day +
979           buffer.hour * 3600 + buffer.min * 60 + buffer.sec - offset);
980 }
981
982 static void
983 fill_transition_info_from_rule (TransitionInfo *info,
984                                 TimeZoneRule   *rule,
985                                 gboolean        is_dst)
986 {
987   gint offset = is_dst ? rule->dlt_offset : rule->std_offset;
988   gchar *name = is_dst ? rule->dlt_name : rule->std_name;
989
990   info->gmt_offset = offset;
991   info->is_dst = is_dst;
992
993   if (name)
994     info->abbrev = g_strdup (name);
995
996   else
997     info->abbrev = g_strdup_printf ("%+03d%02d",
998                                       (int) offset / 3600,
999                                       (int) abs (offset / 60) % 60);
1000 }
1001
1002 static void
1003 init_zone_from_rules (GTimeZone    *gtz,
1004                       TimeZoneRule *rules,
1005                       guint         rules_num,
1006                       gchar        *identifier  /* (transfer full) */)
1007 {
1008   guint type_count = 0, trans_count = 0, info_index = 0;
1009   guint ri; /* rule index */
1010   gboolean skip_first_std_trans = TRUE;
1011   gint32 last_offset;
1012
1013   type_count = 0;
1014   trans_count = 0;
1015
1016   /* Last rule only contains max year */
1017   for (ri = 0; ri < rules_num - 1; ri++)
1018     {
1019       if (rules[ri].dlt_start.mon || rules[ri].dlt_end.mon)
1020         {
1021           guint rulespan = (rules[ri + 1].start_year - rules[ri].start_year);
1022           guint transitions = rules[ri].dlt_start.mon > 0 ? 1 : 0;
1023           transitions += rules[ri].dlt_end.mon > 0 ? 1 : 0;
1024           type_count += rules[ri].dlt_start.mon > 0 ? 2 : 1;
1025           trans_count += transitions * rulespan;
1026         }
1027       else
1028         type_count++;
1029     }
1030
1031   gtz->name = g_steal_pointer (&identifier);
1032   gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), type_count);
1033   gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition), trans_count);
1034
1035   last_offset = rules[0].std_offset;
1036
1037   for (ri = 0; ri < rules_num - 1; ri++)
1038     {
1039       if ((rules[ri].std_offset || rules[ri].dlt_offset) &&
1040           rules[ri].dlt_start.mon == 0 && rules[ri].dlt_end.mon == 0)
1041         {
1042           TransitionInfo std_info;
1043           /* Standard */
1044           fill_transition_info_from_rule (&std_info, &(rules[ri]), FALSE);
1045           g_array_append_val (gtz->t_info, std_info);
1046
1047           if (ri > 0 &&
1048               ((rules[ri - 1].dlt_start.mon > 12 &&
1049                 rules[ri - 1].dlt_start.wday > rules[ri - 1].dlt_end.wday) ||
1050                 rules[ri - 1].dlt_start.mon > rules[ri - 1].dlt_end.mon))
1051             {
1052               /* The previous rule was a southern hemisphere rule that
1053                  starts the year with DST, so we need to add a
1054                  transition to return to standard time */
1055               guint year = rules[ri].start_year;
1056               gint64 std_time =  boundary_for_year (&rules[ri].dlt_end,
1057                                                     year, last_offset);
1058               Transition std_trans = {std_time, info_index};
1059               g_array_append_val (gtz->transitions, std_trans);
1060
1061             }
1062           last_offset = rules[ri].std_offset;
1063           ++info_index;
1064           skip_first_std_trans = TRUE;
1065          }
1066       else
1067         {
1068           const guint start_year = rules[ri].start_year;
1069           const guint end_year = rules[ri + 1].start_year;
1070           gboolean dlt_first;
1071           guint year;
1072           TransitionInfo std_info, dlt_info;
1073           if (rules[ri].dlt_start.mon > 12)
1074             dlt_first = rules[ri].dlt_start.wday > rules[ri].dlt_end.wday;
1075           else
1076             dlt_first = rules[ri].dlt_start.mon > rules[ri].dlt_end.mon;
1077           /* Standard rules are always even, because before the first
1078              transition is always standard time, and 0 is even. */
1079           fill_transition_info_from_rule (&std_info, &(rules[ri]), FALSE);
1080           fill_transition_info_from_rule (&dlt_info, &(rules[ri]), TRUE);
1081
1082           g_array_append_val (gtz->t_info, std_info);
1083           g_array_append_val (gtz->t_info, dlt_info);
1084
1085           /* Transition dates. We hope that a year which ends daylight
1086              time in a southern-hemisphere country (i.e., one that
1087              begins the year in daylight time) will include a rule
1088              which has only a dlt_end. */
1089           for (year = start_year; year < end_year; year++)
1090             {
1091               gint32 dlt_offset = (dlt_first ? last_offset :
1092                                    rules[ri].dlt_offset);
1093               gint32 std_offset = (dlt_first ? rules[ri].std_offset :
1094                                    last_offset);
1095               /* NB: boundary_for_year returns 0 if mon == 0 */
1096               gint64 std_time =  boundary_for_year (&rules[ri].dlt_end,
1097                                                     year, dlt_offset);
1098               gint64 dlt_time = boundary_for_year (&rules[ri].dlt_start,
1099                                                    year, std_offset);
1100               Transition std_trans = {std_time, info_index};
1101               Transition dlt_trans = {dlt_time, info_index + 1};
1102               last_offset = (dlt_first ? rules[ri].dlt_offset :
1103                              rules[ri].std_offset);
1104               if (dlt_first)
1105                 {
1106                   if (skip_first_std_trans)
1107                     skip_first_std_trans = FALSE;
1108                   else if (std_time)
1109                     g_array_append_val (gtz->transitions, std_trans);
1110                   if (dlt_time)
1111                     g_array_append_val (gtz->transitions, dlt_trans);
1112                 }
1113               else
1114                 {
1115                   if (dlt_time)
1116                     g_array_append_val (gtz->transitions, dlt_trans);
1117                   if (std_time)
1118                     g_array_append_val (gtz->transitions, std_trans);
1119                 }
1120             }
1121
1122           info_index += 2;
1123         }
1124     }
1125   if (ri > 0 &&
1126       ((rules[ri - 1].dlt_start.mon > 12 &&
1127         rules[ri - 1].dlt_start.wday > rules[ri - 1].dlt_end.wday) ||
1128        rules[ri - 1].dlt_start.mon > rules[ri - 1].dlt_end.mon))
1129     {
1130       /* The previous rule was a southern hemisphere rule that
1131          starts the year with DST, so we need to add a
1132          transition to return to standard time */
1133       TransitionInfo info;
1134       guint year = rules[ri].start_year;
1135       Transition trans;
1136       fill_transition_info_from_rule (&info, &(rules[ri - 1]), FALSE);
1137       g_array_append_val (gtz->t_info, info);
1138       trans.time = boundary_for_year (&rules[ri - 1].dlt_end,
1139                                       year, last_offset);
1140       trans.info_index = info_index;
1141       g_array_append_val (gtz->transitions, trans);
1142      }
1143 }
1144
1145 /*
1146  * parses date[/time] for parsing TZ environment variable
1147  *
1148  * date is either Mm.w.d, Jn or N
1149  * - m is 1 to 12
1150  * - w is 1 to 5
1151  * - d is 0 to 6
1152  * - n is 1 to 365
1153  * - N is 0 to 365
1154  *
1155  * time is either h or hh[[:]mm[[[:]ss]]]
1156  *  - h[h] is 0 to 23
1157  *  - mm is 00 to 59
1158  *  - ss is 00 to 59
1159  */
1160 static gboolean
1161 parse_mwd_boundary (gchar **pos, TimeZoneDate *boundary)
1162 {
1163   gint month, week, day;
1164
1165   if (**pos == '\0' || **pos < '0' || '9' < **pos)
1166     return FALSE;
1167
1168   month = *(*pos)++ - '0';
1169
1170   if ((month == 1 && **pos >= '0' && '2' >= **pos) ||
1171       (month == 0 && **pos >= '0' && '9' >= **pos))
1172     {
1173       month *= 10;
1174       month += *(*pos)++ - '0';
1175     }
1176
1177   if (*(*pos)++ != '.' || month == 0)
1178     return FALSE;
1179
1180   if (**pos == '\0' || **pos < '1' || '5' < **pos)
1181     return FALSE;
1182
1183   week = *(*pos)++ - '0';
1184
1185   if (*(*pos)++ != '.')
1186     return FALSE;
1187
1188   if (**pos == '\0' || **pos < '0' || '6' < **pos)
1189     return FALSE;
1190
1191   day = *(*pos)++ - '0';
1192
1193   if (!day)
1194     day += 7;
1195
1196   boundary->year = 0;
1197   boundary->mon = month;
1198   boundary->week = week;
1199   boundary->wday = day;
1200   return TRUE;
1201 }
1202
1203 /* Different implementations of tzset interpret the Julian day field
1204    differently. For example, Linux specifies that it should be 1-based
1205    (1 Jan is JD 1) for both Jn and n formats, while zOS and BSD
1206    specify that a Jn JD is 1-based while an n JD is 0-based. Rather
1207    than trying to follow different specs, we will follow GDate's
1208    practice thatIn order to keep it simple, we will follow Linux's
1209    practice. */
1210
1211 static gboolean
1212 parse_julian_boundary (gchar** pos, TimeZoneDate *boundary,
1213                        gboolean ignore_leap)
1214 {
1215   gint day = 0;
1216   GDate date;
1217
1218   while (**pos >= '0' && '9' >= **pos)
1219     {
1220       day *= 10;
1221       day += *(*pos)++ - '0';
1222     }
1223
1224   if (day < 1 || 365 < day)
1225     return FALSE;
1226
1227   g_date_clear (&date, 1);
1228   g_date_set_julian (&date, day);
1229   boundary->year = 0;
1230   boundary->mon = (int) g_date_get_month (&date);
1231   boundary->mday = (int) g_date_get_day (&date);
1232   boundary->wday = 0;
1233
1234   if (!ignore_leap && day >= 59)
1235     boundary->mday++;
1236
1237   return TRUE;
1238 }
1239
1240 static gboolean
1241 parse_tz_boundary (const gchar  *identifier,
1242                    TimeZoneDate *boundary)
1243 {
1244   gchar *pos;
1245
1246   pos = (gchar*)identifier;
1247   /* Month-week-weekday */
1248   if (*pos == 'M')
1249     {
1250       ++pos;
1251       if (!parse_mwd_boundary (&pos, boundary))
1252         return FALSE;
1253     }
1254   /* Julian date which ignores Feb 29 in leap years */
1255   else if (*pos == 'J')
1256     {
1257       ++pos;
1258       if (!parse_julian_boundary (&pos, boundary, FALSE))
1259         return FALSE ;
1260     }
1261   /* Julian date which counts Feb 29 in leap years */
1262   else if (*pos >= '0' && '9' >= *pos)
1263     {
1264       if (!parse_julian_boundary (&pos, boundary, TRUE))
1265         return FALSE;
1266     }
1267   else
1268     return FALSE;
1269
1270   /* Time */
1271
1272   if (*pos == '/')
1273     {
1274       gint32 offset;
1275
1276       if (!parse_time (++pos, &offset))
1277         return FALSE;
1278
1279       boundary->hour = offset / 3600;
1280       boundary->min = (offset / 60) % 60;
1281       boundary->sec = offset % 3600;
1282
1283       return TRUE;
1284     }
1285
1286   else
1287     {
1288       boundary->hour = 2;
1289       boundary->min = 0;
1290       boundary->sec = 0;
1291
1292       return *pos == '\0';
1293     }
1294 }
1295
1296 static guint
1297 create_ruleset_from_rule (TimeZoneRule **rules, TimeZoneRule *rule)
1298 {
1299   *rules = g_new0 (TimeZoneRule, 2);
1300
1301   (*rules)[0].start_year = MIN_TZYEAR;
1302   (*rules)[1].start_year = MAX_TZYEAR;
1303
1304   (*rules)[0].std_offset = -rule->std_offset;
1305   (*rules)[0].dlt_offset = -rule->dlt_offset;
1306   (*rules)[0].dlt_start  = rule->dlt_start;
1307   (*rules)[0].dlt_end = rule->dlt_end;
1308   strcpy ((*rules)[0].std_name, rule->std_name);
1309   strcpy ((*rules)[0].dlt_name, rule->dlt_name);
1310   return 2;
1311 }
1312
1313 static gboolean
1314 parse_offset (gchar **pos, gint32 *target)
1315 {
1316   gchar *buffer;
1317   gchar *target_pos = *pos;
1318   gboolean ret;
1319
1320   while (**pos == '+' || **pos == '-' || **pos == ':' ||
1321          (**pos >= '0' && '9' >= **pos))
1322     ++(*pos);
1323
1324   buffer = g_strndup (target_pos, *pos - target_pos);
1325   ret = parse_constant_offset (buffer, target);
1326   g_free (buffer);
1327
1328   return ret;
1329 }
1330
1331 static gboolean
1332 parse_identifier_boundary (gchar **pos, TimeZoneDate *target)
1333 {
1334   gchar *buffer;
1335   gchar *target_pos = *pos;
1336   gboolean ret;
1337
1338   while (**pos != ',' && **pos != '\0')
1339     ++(*pos);
1340   buffer = g_strndup (target_pos, *pos - target_pos);
1341   ret = parse_tz_boundary (buffer, target);
1342   g_free (buffer);
1343
1344   return ret;
1345 }
1346
1347 static gboolean
1348 set_tz_name (gchar **pos, gchar *buffer, guint size)
1349 {
1350   gchar *name_pos = *pos;
1351   guint len;
1352
1353   /* Name is ASCII alpha (Is this necessarily true?) */
1354   while (g_ascii_isalpha (**pos))
1355     ++(*pos);
1356
1357   /* Name should be three or more alphabetic characters */
1358   if (*pos - name_pos < 3)
1359     return FALSE;
1360
1361   memset (buffer, 0, NAME_SIZE);
1362   /* name_pos isn't 0-terminated, so we have to limit the length expressly */
1363   len = *pos - name_pos > size - 1 ? size - 1 : *pos - name_pos;
1364   strncpy (buffer, name_pos, len);
1365   return TRUE;
1366 }
1367
1368 static gboolean
1369 parse_identifier_boundaries (gchar **pos, TimeZoneRule *tzr)
1370 {
1371   if (*(*pos)++ != ',')
1372     return FALSE;
1373
1374   /* Start date */
1375   if (!parse_identifier_boundary (pos, &(tzr->dlt_start)) || *(*pos)++ != ',')
1376     return FALSE;
1377
1378   /* End date */
1379   if (!parse_identifier_boundary (pos, &(tzr->dlt_end)))
1380     return FALSE;
1381   return TRUE;
1382 }
1383
1384 /*
1385  * Creates an array of TimeZoneRule from a TZ environment variable
1386  * type of identifier.  Should free rules afterwards
1387  */
1388 static guint
1389 rules_from_identifier (const gchar   *identifier,
1390                        gchar        **out_identifier,
1391                        TimeZoneRule **rules)
1392 {
1393   gchar *pos;
1394   TimeZoneRule tzr;
1395
1396   g_assert (out_identifier != NULL);
1397   g_assert (rules != NULL);
1398
1399   *out_identifier = NULL;
1400   *rules = NULL;
1401
1402   if (!identifier)
1403     return 0;
1404
1405   pos = (gchar*)identifier;
1406   memset (&tzr, 0, sizeof (tzr));
1407   /* Standard offset */
1408   if (!(set_tz_name (&pos, tzr.std_name, NAME_SIZE)) ||
1409       !parse_offset (&pos, &(tzr.std_offset)))
1410     return 0;
1411
1412   if (*pos == 0)
1413     {
1414       *out_identifier = g_strdup (identifier);
1415       return create_ruleset_from_rule (rules, &tzr);
1416     }
1417
1418   /* Format 2 */
1419   if (!(set_tz_name (&pos, tzr.dlt_name, NAME_SIZE)))
1420     return 0;
1421   parse_offset (&pos, &(tzr.dlt_offset));
1422   if (tzr.dlt_offset == 0) /* No daylight offset given, assume it's 1
1423                               hour earlier that standard */
1424     tzr.dlt_offset = tzr.std_offset - 3600;
1425   if (*pos == '\0')
1426 #ifdef G_OS_WIN32
1427     /* Windows allows us to use the US DST boundaries if they're not given */
1428     {
1429       int i;
1430       guint rules_num = 0;
1431
1432       /* Use US rules, Windows' default is Pacific Standard Time */
1433       if ((rules_num = rules_from_windows_time_zone ("Pacific Standard Time",
1434                                                      NULL,
1435                                                      rules,
1436                                                      FALSE)))
1437         {
1438           /* We don't want to hardcode our identifier here as
1439            * "Pacific Standard Time", use what was passed in
1440            */
1441           *out_identifier = g_strdup (identifier);
1442
1443           for (i = 0; i < rules_num - 1; i++)
1444             {
1445               (*rules)[i].std_offset = - tzr.std_offset;
1446               (*rules)[i].dlt_offset = - tzr.dlt_offset;
1447               strcpy ((*rules)[i].std_name, tzr.std_name);
1448               strcpy ((*rules)[i].dlt_name, tzr.dlt_name);
1449             }
1450
1451           return rules_num;
1452         }
1453       else
1454         return 0;
1455     }
1456 #else
1457   return 0;
1458 #endif
1459   /* Start and end required (format 2) */
1460   if (!parse_identifier_boundaries (&pos, &tzr))
1461     return 0;
1462
1463   *out_identifier = g_strdup (identifier);
1464   return create_ruleset_from_rule (rules, &tzr);
1465 }
1466
1467 /* Construction {{{1 */
1468 /**
1469  * g_time_zone_new:
1470  * @identifier: (nullable): a timezone identifier
1471  *
1472  * Creates a #GTimeZone corresponding to @identifier.
1473  *
1474  * @identifier can either be an RFC3339/ISO 8601 time offset or
1475  * something that would pass as a valid value for the `TZ` environment
1476  * variable (including %NULL).
1477  *
1478  * In Windows, @identifier can also be the unlocalized name of a time
1479  * zone for standard time, for example "Pacific Standard Time".
1480  *
1481  * Valid RFC3339 time offsets are `"Z"` (for UTC) or
1482  * `"±hh:mm"`.  ISO 8601 additionally specifies
1483  * `"±hhmm"` and `"±hh"`.  Offsets are
1484  * time values to be added to Coordinated Universal Time (UTC) to get
1485  * the local time.
1486  *
1487  * In UNIX, the `TZ` environment variable typically corresponds
1488  * to the name of a file in the zoneinfo database, or string in
1489  * "std offset [dst [offset],start[/time],end[/time]]" (POSIX) format.
1490  * There  are  no spaces in the specification. The name of standard
1491  * and daylight savings time zone must be three or more alphabetic
1492  * characters. Offsets are time values to be added to local time to
1493  * get Coordinated Universal Time (UTC) and should be
1494  * `"[±]hh[[:]mm[:ss]]"`.  Dates are either
1495  * `"Jn"` (Julian day with n between 1 and 365, leap
1496  * years not counted), `"n"` (zero-based Julian day
1497  * with n between 0 and 365) or `"Mm.w.d"` (day d
1498  * (0 <= d <= 6) of week w (1 <= w <= 5) of month m (1 <= m <= 12), day
1499  * 0 is a Sunday).  Times are in local wall clock time, the default is
1500  * 02:00:00.
1501  *
1502  * In Windows, the "tzn[+|–]hh[:mm[:ss]][dzn]" format is used, but also
1503  * accepts POSIX format.  The Windows format uses US rules for all time
1504  * zones; daylight savings time is 60 minutes behind the standard time
1505  * with date and time of change taken from Pacific Standard Time.
1506  * Offsets are time values to be added to the local time to get
1507  * Coordinated Universal Time (UTC).
1508  *
1509  * g_time_zone_new_local() calls this function with the value of the
1510  * `TZ` environment variable. This function itself is independent of
1511  * the value of `TZ`, but if @identifier is %NULL then `/etc/localtime`
1512  * will be consulted to discover the correct time zone on UNIX and the
1513  * registry will be consulted or GetTimeZoneInformation() will be used
1514  * to get the local time zone on Windows.
1515  *
1516  * If intervals are not available, only time zone rules from `TZ`
1517  * environment variable or other means, then they will be computed
1518  * from year 1900 to 2037.  If the maximum year for the rules is
1519  * available and it is greater than 2037, then it will followed
1520  * instead.
1521  *
1522  * See
1523  * [RFC3339 §5.6](http://tools.ietf.org/html/rfc3339#section-5.6)
1524  * for a precise definition of valid RFC3339 time offsets
1525  * (the `time-offset` expansion) and ISO 8601 for the
1526  * full list of valid time offsets.  See
1527  * [The GNU C Library manual](http://www.gnu.org/s/libc/manual/html_node/TZ-Variable.html)
1528  * for an explanation of the possible
1529  * values of the `TZ` environment variable. See
1530  * [Microsoft Time Zone Index Values](http://msdn.microsoft.com/en-us/library/ms912391%28v=winembedded.11%29.aspx)
1531  * for the list of time zones on Windows.
1532  *
1533  * You should release the return value by calling g_time_zone_unref()
1534  * when you are done with it.
1535  *
1536  * Returns: the requested timezone
1537  *
1538  * Since: 2.26
1539  **/
1540 GTimeZone *
1541 g_time_zone_new (const gchar *identifier)
1542 {
1543   GTimeZone *tz = NULL;
1544   TimeZoneRule *rules;
1545   gint rules_num;
1546   gchar *resolved_identifier = NULL;
1547
1548   G_LOCK (time_zones);
1549   if (time_zones == NULL)
1550     time_zones = g_hash_table_new (g_str_hash, g_str_equal);
1551
1552   if (identifier)
1553     {
1554       tz = g_hash_table_lookup (time_zones, identifier);
1555       if (tz)
1556         {
1557           g_atomic_int_inc (&tz->ref_count);
1558           G_UNLOCK (time_zones);
1559           return tz;
1560         }
1561     }
1562
1563   tz = g_slice_new0 (GTimeZone);
1564   tz->ref_count = 0;
1565
1566   zone_for_constant_offset (tz, identifier);
1567
1568   if (tz->t_info == NULL &&
1569       (rules_num = rules_from_identifier (identifier, &resolved_identifier, &rules)))
1570     {
1571       init_zone_from_rules (tz, rules, rules_num, g_steal_pointer (&resolved_identifier));
1572       g_free (rules);
1573     }
1574
1575   if (tz->t_info == NULL)
1576     {
1577 #ifdef G_OS_UNIX
1578       GBytes *zoneinfo = zone_info_unix (identifier, &resolved_identifier);
1579       if (zoneinfo != NULL)
1580         {
1581           init_zone_from_iana_info (tz, zoneinfo, g_steal_pointer (&resolved_identifier));
1582           g_bytes_unref (zoneinfo);
1583         }
1584 #elif defined (G_OS_WIN32)
1585       if ((rules_num = rules_from_windows_time_zone (identifier,
1586                                                      &resolved_identifier,
1587                                                      &rules,
1588                                                      TRUE)))
1589         {
1590           init_zone_from_rules (tz, rules, rules_num, g_steal_pointer (&resolved_identifier));
1591           g_free (rules);
1592         }
1593 #endif
1594     }
1595
1596 #if defined (G_OS_WIN32)
1597   if (tz->t_info == NULL)
1598     {
1599       if (identifier == NULL)
1600         {
1601           TIME_ZONE_INFORMATION tzi;
1602
1603           if (GetTimeZoneInformation (&tzi) != TIME_ZONE_ID_INVALID)
1604             {
1605               rules = g_new0 (TimeZoneRule, 2);
1606
1607               if (rule_from_windows_time_zone_info (&rules[0], &tzi))
1608                 {
1609                   memset (rules[0].std_name, 0, NAME_SIZE);
1610                   memset (rules[0].dlt_name, 0, NAME_SIZE);
1611
1612                   rules[0].start_year = MIN_TZYEAR;
1613                   rules[1].start_year = MAX_TZYEAR;
1614
1615                   init_zone_from_rules (tz, rules, 2, windows_default_tzname ());
1616                 }
1617
1618               g_free (rules);
1619             }
1620         }
1621     }
1622 #endif
1623
1624   g_free (resolved_identifier);
1625
1626   /* Always fall back to UTC. */
1627   if (tz->t_info == NULL)
1628     zone_for_constant_offset (tz, "UTC");
1629
1630   g_assert (tz->name != NULL);
1631   g_assert (tz->t_info != NULL);
1632
1633   if (tz->t_info != NULL)
1634     {
1635       if (identifier)
1636         g_hash_table_insert (time_zones, tz->name, tz);
1637     }
1638   g_atomic_int_inc (&tz->ref_count);
1639   G_UNLOCK (time_zones);
1640
1641   return tz;
1642 }
1643
1644 /**
1645  * g_time_zone_new_utc:
1646  *
1647  * Creates a #GTimeZone corresponding to UTC.
1648  *
1649  * This is equivalent to calling g_time_zone_new() with a value like
1650  * "Z", "UTC", "+00", etc.
1651  *
1652  * You should release the return value by calling g_time_zone_unref()
1653  * when you are done with it.
1654  *
1655  * Returns: the universal timezone
1656  *
1657  * Since: 2.26
1658  **/
1659 GTimeZone *
1660 g_time_zone_new_utc (void)
1661 {
1662   return g_time_zone_new ("UTC");
1663 }
1664
1665 /**
1666  * g_time_zone_new_local:
1667  *
1668  * Creates a #GTimeZone corresponding to local time.  The local time
1669  * zone may change between invocations to this function; for example,
1670  * if the system administrator changes it.
1671  *
1672  * This is equivalent to calling g_time_zone_new() with the value of
1673  * the `TZ` environment variable (including the possibility of %NULL).
1674  *
1675  * You should release the return value by calling g_time_zone_unref()
1676  * when you are done with it.
1677  *
1678  * Returns: the local timezone
1679  *
1680  * Since: 2.26
1681  **/
1682 GTimeZone *
1683 g_time_zone_new_local (void)
1684 {
1685   return g_time_zone_new (getenv ("TZ"));
1686 }
1687
1688 /**
1689  * g_time_zone_new_offset:
1690  * @seconds: offset to UTC, in seconds
1691  *
1692  * Creates a #GTimeZone corresponding to the given constant offset from UTC,
1693  * in seconds.
1694  *
1695  * This is equivalent to calling g_time_zone_new() with a string in the form
1696  * `[+|-]hh[:mm[:ss]]`.
1697  *
1698  * Returns: (transfer full): a timezone at the given offset from UTC
1699  * Since: 2.58
1700  */
1701 GTimeZone *
1702 g_time_zone_new_offset (gint32 seconds)
1703 {
1704   GTimeZone *tz = NULL;
1705   gchar *identifier = NULL;
1706
1707   /* Seemingly, we should be using @seconds directly to set the
1708    * #TransitionInfo.gmt_offset to avoid all this string building and parsing.
1709    * However, we always need to set the #GTimeZone.name to a constructed
1710    * string anyway, so we might as well reuse its code. */
1711   identifier = g_strdup_printf ("%c%02u:%02u:%02u",
1712                                 (seconds >= 0) ? '+' : '-',
1713                                 (ABS (seconds) / 60) / 60,
1714                                 (ABS (seconds) / 60) % 60,
1715                                 ABS (seconds) % 60);
1716   tz = g_time_zone_new (identifier);
1717   g_free (identifier);
1718
1719   g_assert (g_time_zone_get_offset (tz, 0) == seconds);
1720
1721   return tz;
1722 }
1723
1724 #define TRANSITION(n)         g_array_index (tz->transitions, Transition, n)
1725 #define TRANSITION_INFO(n)    g_array_index (tz->t_info, TransitionInfo, n)
1726
1727 /* Internal helpers {{{1 */
1728 /* NB: Interval 0 is before the first transition, so there's no
1729  * transition structure to point to which TransitionInfo to
1730  * use. Rule-based zones are set up so that TI 0 is always standard
1731  * time (which is what's in effect before Daylight time got started
1732  * in the early 20th century), but IANA tzfiles don't follow that
1733  * convention. The tzfile documentation says to use the first
1734  * standard-time (i.e., non-DST) tinfo, so that's what we do.
1735  */
1736 inline static const TransitionInfo*
1737 interval_info (GTimeZone *tz,
1738                guint      interval)
1739 {
1740   guint index;
1741   g_return_val_if_fail (tz->t_info != NULL, NULL);
1742   if (interval && tz->transitions && interval <= tz->transitions->len)
1743     index = (TRANSITION(interval - 1)).info_index;
1744   else
1745     {
1746       for (index = 0; index < tz->t_info->len; index++)
1747         {
1748           TransitionInfo *tzinfo = &(TRANSITION_INFO(index));
1749           if (!tzinfo->is_dst)
1750             return tzinfo;
1751         }
1752       index = 0;
1753     }
1754
1755   return &(TRANSITION_INFO(index));
1756 }
1757
1758 inline static gint64
1759 interval_start (GTimeZone *tz,
1760                 guint      interval)
1761 {
1762   if (!interval || tz->transitions == NULL || tz->transitions->len == 0)
1763     return G_MININT64;
1764   if (interval > tz->transitions->len)
1765     interval = tz->transitions->len;
1766   return (TRANSITION(interval - 1)).time;
1767 }
1768
1769 inline static gint64
1770 interval_end (GTimeZone *tz,
1771               guint      interval)
1772 {
1773   if (tz->transitions && interval < tz->transitions->len)
1774     {
1775       gint64 lim = (TRANSITION(interval)).time;
1776       return lim - (lim != G_MININT64);
1777     }
1778   return G_MAXINT64;
1779 }
1780
1781 inline static gint32
1782 interval_offset (GTimeZone *tz,
1783                  guint      interval)
1784 {
1785   g_return_val_if_fail (tz->t_info != NULL, 0);
1786   return interval_info (tz, interval)->gmt_offset;
1787 }
1788
1789 inline static gboolean
1790 interval_isdst (GTimeZone *tz,
1791                 guint      interval)
1792 {
1793   g_return_val_if_fail (tz->t_info != NULL, 0);
1794   return interval_info (tz, interval)->is_dst;
1795 }
1796
1797
1798 inline static gchar*
1799 interval_abbrev (GTimeZone *tz,
1800                   guint      interval)
1801 {
1802   g_return_val_if_fail (tz->t_info != NULL, 0);
1803   return interval_info (tz, interval)->abbrev;
1804 }
1805
1806 inline static gint64
1807 interval_local_start (GTimeZone *tz,
1808                       guint      interval)
1809 {
1810   if (interval)
1811     return interval_start (tz, interval) + interval_offset (tz, interval);
1812
1813   return G_MININT64;
1814 }
1815
1816 inline static gint64
1817 interval_local_end (GTimeZone *tz,
1818                     guint      interval)
1819 {
1820   if (tz->transitions && interval < tz->transitions->len)
1821     return interval_end (tz, interval) + interval_offset (tz, interval);
1822
1823   return G_MAXINT64;
1824 }
1825
1826 static gboolean
1827 interval_valid (GTimeZone *tz,
1828                 guint      interval)
1829 {
1830   if ( tz->transitions == NULL)
1831     return interval == 0;
1832   return interval <= tz->transitions->len;
1833 }
1834
1835 /* g_time_zone_find_interval() {{{1 */
1836
1837 /**
1838  * g_time_zone_adjust_time:
1839  * @tz: a #GTimeZone
1840  * @type: the #GTimeType of @time_
1841  * @time_: a pointer to a number of seconds since January 1, 1970
1842  *
1843  * Finds an interval within @tz that corresponds to the given @time_,
1844  * possibly adjusting @time_ if required to fit into an interval.
1845  * The meaning of @time_ depends on @type.
1846  *
1847  * This function is similar to g_time_zone_find_interval(), with the
1848  * difference that it always succeeds (by making the adjustments
1849  * described below).
1850  *
1851  * In any of the cases where g_time_zone_find_interval() succeeds then
1852  * this function returns the same value, without modifying @time_.
1853  *
1854  * This function may, however, modify @time_ in order to deal with
1855  * non-existent times.  If the non-existent local @time_ of 02:30 were
1856  * requested on March 14th 2010 in Toronto then this function would
1857  * adjust @time_ to be 03:00 and return the interval containing the
1858  * adjusted time.
1859  *
1860  * Returns: the interval containing @time_, never -1
1861  *
1862  * Since: 2.26
1863  **/
1864 gint
1865 g_time_zone_adjust_time (GTimeZone *tz,
1866                          GTimeType  type,
1867                          gint64    *time_)
1868 {
1869   guint i, intervals;
1870   gboolean interval_is_dst;
1871
1872   if (tz->transitions == NULL)
1873     return 0;
1874
1875   intervals = tz->transitions->len;
1876
1877   /* find the interval containing *time UTC
1878    * TODO: this could be binary searched (or better) */
1879   for (i = 0; i <= intervals; i++)
1880     if (*time_ <= interval_end (tz, i))
1881       break;
1882
1883   g_assert (interval_start (tz, i) <= *time_ && *time_ <= interval_end (tz, i));
1884
1885   if (type != G_TIME_TYPE_UNIVERSAL)
1886     {
1887       if (*time_ < interval_local_start (tz, i))
1888         /* if time came before the start of this interval... */
1889         {
1890           i--;
1891
1892           /* if it's not in the previous interval... */
1893           if (*time_ > interval_local_end (tz, i))
1894             {
1895               /* it doesn't exist.  fast-forward it. */
1896               i++;
1897               *time_ = interval_local_start (tz, i);
1898             }
1899         }
1900
1901       else if (*time_ > interval_local_end (tz, i))
1902         /* if time came after the end of this interval... */
1903         {
1904           i++;
1905
1906           /* if it's not in the next interval... */
1907           if (*time_ < interval_local_start (tz, i))
1908             /* it doesn't exist.  fast-forward it. */
1909             *time_ = interval_local_start (tz, i);
1910         }
1911
1912       else
1913         {
1914           interval_is_dst = interval_isdst (tz, i);
1915           if ((interval_is_dst && type != G_TIME_TYPE_DAYLIGHT) ||
1916               (!interval_is_dst && type == G_TIME_TYPE_DAYLIGHT))
1917             {
1918               /* it's in this interval, but dst flag doesn't match.
1919                * check neighbours for a better fit. */
1920               if (i && *time_ <= interval_local_end (tz, i - 1))
1921                 i--;
1922
1923               else if (i < intervals &&
1924                        *time_ >= interval_local_start (tz, i + 1))
1925                 i++;
1926             }
1927         }
1928     }
1929
1930   return i;
1931 }
1932
1933 /**
1934  * g_time_zone_find_interval:
1935  * @tz: a #GTimeZone
1936  * @type: the #GTimeType of @time_
1937  * @time_: a number of seconds since January 1, 1970
1938  *
1939  * Finds an the interval within @tz that corresponds to the given @time_.
1940  * The meaning of @time_ depends on @type.
1941  *
1942  * If @type is %G_TIME_TYPE_UNIVERSAL then this function will always
1943  * succeed (since universal time is monotonic and continuous).
1944  *
1945  * Otherwise @time_ is treated as local time.  The distinction between
1946  * %G_TIME_TYPE_STANDARD and %G_TIME_TYPE_DAYLIGHT is ignored except in
1947  * the case that the given @time_ is ambiguous.  In Toronto, for example,
1948  * 01:30 on November 7th 2010 occurred twice (once inside of daylight
1949  * savings time and the next, an hour later, outside of daylight savings
1950  * time).  In this case, the different value of @type would result in a
1951  * different interval being returned.
1952  *
1953  * It is still possible for this function to fail.  In Toronto, for
1954  * example, 02:00 on March 14th 2010 does not exist (due to the leap
1955  * forward to begin daylight savings time).  -1 is returned in that
1956  * case.
1957  *
1958  * Returns: the interval containing @time_, or -1 in case of failure
1959  *
1960  * Since: 2.26
1961  */
1962 gint
1963 g_time_zone_find_interval (GTimeZone *tz,
1964                            GTimeType  type,
1965                            gint64     time_)
1966 {
1967   guint i, intervals;
1968   gboolean interval_is_dst;
1969
1970   if (tz->transitions == NULL)
1971     return 0;
1972   intervals = tz->transitions->len;
1973   for (i = 0; i <= intervals; i++)
1974     if (time_ <= interval_end (tz, i))
1975       break;
1976
1977   if (type == G_TIME_TYPE_UNIVERSAL)
1978     return i;
1979
1980   if (time_ < interval_local_start (tz, i))
1981     {
1982       if (time_ > interval_local_end (tz, --i))
1983         return -1;
1984     }
1985
1986   else if (time_ > interval_local_end (tz, i))
1987     {
1988       if (time_ < interval_local_start (tz, ++i))
1989         return -1;
1990     }
1991
1992   else
1993     {
1994       interval_is_dst = interval_isdst (tz, i);
1995       if  ((interval_is_dst && type != G_TIME_TYPE_DAYLIGHT) ||
1996            (!interval_is_dst && type == G_TIME_TYPE_DAYLIGHT))
1997         {
1998           if (i && time_ <= interval_local_end (tz, i - 1))
1999             i--;
2000
2001           else if (i < intervals && time_ >= interval_local_start (tz, i + 1))
2002             i++;
2003         }
2004     }
2005
2006   return i;
2007 }
2008
2009 /* Public API accessors {{{1 */
2010
2011 /**
2012  * g_time_zone_get_abbreviation:
2013  * @tz: a #GTimeZone
2014  * @interval: an interval within the timezone
2015  *
2016  * Determines the time zone abbreviation to be used during a particular
2017  * @interval of time in the time zone @tz.
2018  *
2019  * For example, in Toronto this is currently "EST" during the winter
2020  * months and "EDT" during the summer months when daylight savings time
2021  * is in effect.
2022  *
2023  * Returns: the time zone abbreviation, which belongs to @tz
2024  *
2025  * Since: 2.26
2026  **/
2027 const gchar *
2028 g_time_zone_get_abbreviation (GTimeZone *tz,
2029                               gint       interval)
2030 {
2031   g_return_val_if_fail (interval_valid (tz, (guint)interval), NULL);
2032
2033   return interval_abbrev (tz, (guint)interval);
2034 }
2035
2036 /**
2037  * g_time_zone_get_offset:
2038  * @tz: a #GTimeZone
2039  * @interval: an interval within the timezone
2040  *
2041  * Determines the offset to UTC in effect during a particular @interval
2042  * of time in the time zone @tz.
2043  *
2044  * The offset is the number of seconds that you add to UTC time to
2045  * arrive at local time for @tz (ie: negative numbers for time zones
2046  * west of GMT, positive numbers for east).
2047  *
2048  * Returns: the number of seconds that should be added to UTC to get the
2049  *          local time in @tz
2050  *
2051  * Since: 2.26
2052  **/
2053 gint32
2054 g_time_zone_get_offset (GTimeZone *tz,
2055                         gint       interval)
2056 {
2057   g_return_val_if_fail (interval_valid (tz, (guint)interval), 0);
2058
2059   return interval_offset (tz, (guint)interval);
2060 }
2061
2062 /**
2063  * g_time_zone_is_dst:
2064  * @tz: a #GTimeZone
2065  * @interval: an interval within the timezone
2066  *
2067  * Determines if daylight savings time is in effect during a particular
2068  * @interval of time in the time zone @tz.
2069  *
2070  * Returns: %TRUE if daylight savings time is in effect
2071  *
2072  * Since: 2.26
2073  **/
2074 gboolean
2075 g_time_zone_is_dst (GTimeZone *tz,
2076                     gint       interval)
2077 {
2078   g_return_val_if_fail (interval_valid (tz, interval), FALSE);
2079
2080   if (tz->transitions == NULL)
2081     return FALSE;
2082
2083   return interval_isdst (tz, (guint)interval);
2084 }
2085
2086 /**
2087  * g_time_zone_get_identifier:
2088  * @tz: a #GTimeZone
2089  *
2090  * Get the identifier of this #GTimeZone, as passed to g_time_zone_new().
2091  * If the identifier passed at construction time was not recognised, `UTC` will
2092  * be returned. If it was %NULL, the identifier of the local timezone at
2093  * construction time will be returned.
2094  *
2095  * The identifier will be returned in the same format as provided at
2096  * construction time: if provided as a time offset, that will be returned by
2097  * this function.
2098  *
2099  * Returns: identifier for this timezone
2100  * Since: 2.58
2101  */
2102 const gchar *
2103 g_time_zone_get_identifier (GTimeZone *tz)
2104 {
2105   g_return_val_if_fail (tz != NULL, NULL);
2106
2107   return tz->name;
2108 }
2109
2110 /* Epilogue {{{1 */
2111 /* vim:set foldmethod=marker: */