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