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