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