2 * Copyright © 2010 Codethink Limited
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.
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.
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/>.
17 * Author: Ryan Lortie <desrt@desrt.ca>
24 #include "gtimezone.h"
30 #include "gmappedfile.h"
31 #include "gtestutils.h"
32 #include "gfileutils.h"
33 #include "gstrfuncs.h"
38 #include "gdatetime.h"
52 * @short_description: a structure representing a time zone
53 * @see_also: #GDateTime
55 * #GTimeZone is a structure that represents a time zone, at no
56 * particular point in time. It is refcounted and immutable.
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().
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().
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).
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.
81 * #GTimeZone is available since GLib 2.26.
87 * #GTimeZone is an opaque structure whose members cannot be accessed
93 /* IANA zoneinfo file format {{{1 */
96 typedef struct { gchar bytes[8]; } gint64_be;
97 typedef struct { gchar bytes[4]; } gint32_be;
98 typedef struct { gchar bytes[4]; } guint32_be;
100 static inline gint64 gint64_from_be (const gint64_be be) {
101 gint64 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT64_FROM_BE (tmp);
104 static inline gint32 gint32_from_be (const gint32_be be) {
105 gint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT32_FROM_BE (tmp);
108 static inline guint32 guint32_from_be (const guint32_be be) {
109 guint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GUINT32_FROM_BE (tmp);
112 /* The layout of an IANA timezone file header */
117 guchar tzh_reserved[15];
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;
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.
145 gint32 offset; /* hour*3600 + min*60 + sec; can be negative. */
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.
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.
163 TimeZoneDate dlt_start;
164 TimeZoneDate dlt_end;
165 gchar std_name[NAME_SIZE];
166 gchar dlt_name[NAME_SIZE];
169 /* GTimeZone's internal representation of a Daylight Savings (Summer)
179 /* GTimeZone's representation of a transition time to or from Daylight
180 Savings (Summer) time and Standard time for the zone. */
187 /* GTimeZone structure */
191 GArray *t_info; /* Array of TransitionInfo */
192 GArray *transitions; /* Array of Transition */
196 G_LOCK_DEFINE_STATIC (time_zones);
197 static GHashTable/*<string?, GTimeZone>*/ *time_zones;
198 G_LOCK_DEFINE_STATIC (tz_local);
199 static gchar *tzenv_cached = NULL;
200 static GTimeZone *tz_local = NULL;
202 #define MIN_TZYEAR 1916 /* Daylight Savings started in WWI */
203 #define MAX_TZYEAR 2999 /* And it's not likely ever to go away, but
204 there's no point in getting carried
208 static GTimeZone *parse_footertz (const gchar *, size_t);
215 * Decreases the reference count on @tz.
220 g_time_zone_unref (GTimeZone *tz)
225 ref_count = g_atomic_int_get (&tz->ref_count);
227 g_assert (ref_count > 0);
231 if (tz->name != NULL)
235 /* someone else might have grabbed a ref in the meantime */
236 if G_UNLIKELY (g_atomic_int_get (&tz->ref_count) != 1)
238 G_UNLOCK(time_zones);
242 g_hash_table_remove (time_zones, tz->name);
243 G_UNLOCK(time_zones);
246 if (tz->t_info != NULL)
249 for (idx = 0; idx < tz->t_info->len; idx++)
251 TransitionInfo *info = &g_array_index (tz->t_info, TransitionInfo, idx);
252 g_free (info->abbrev);
254 g_array_free (tz->t_info, TRUE);
256 if (tz->transitions != NULL)
257 g_array_free (tz->transitions, TRUE);
260 g_slice_free (GTimeZone, tz);
263 else if G_UNLIKELY (!g_atomic_int_compare_and_exchange (&tz->ref_count,
273 * Increases the reference count on @tz.
275 * Returns: a new reference to @tz.
280 g_time_zone_ref (GTimeZone *tz)
282 g_assert (tz->ref_count > 0);
284 g_atomic_int_inc (&tz->ref_count);
289 /* fake zoneinfo creation (for RFC3339/ISO 8601 timezones) {{{1 */
291 * parses strings of the form h or hh[[:]mm[[[:]ss]]] where:
295 * If RFC8536, TIME_ is a transition time sans sign,
296 * so colons are required before mm and ss, and hh can be up to 167.
297 * See Internet RFC 8536 section 3.3.1:
298 * https://tools.ietf.org/html/rfc8536#section-3.3.1
299 * and POSIX Base Definitions 8.3 TZ rule time:
300 * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
303 parse_time (const gchar *time_,
307 if (*time_ < '0' || '9' < *time_)
310 *offset = 60 * 60 * (*time_++ - '0');
317 if (*time_ < '0' || '9' < *time_)
321 *offset += 60 * 60 * (*time_++ - '0');
325 /* Internet RFC 8536 section 3.3.1 and POSIX 8.3 TZ together say
326 that a transition time must be of the form [+-]hh[:mm[:ss]] where
327 the hours part can range from -167 to 167. */
328 if ('0' <= *time_ && *time_ <= '9')
331 *offset += 60 * 60 * (*time_++ - '0');
333 if (*offset > 167 * 60 * 60)
336 else if (*offset > 24 * 60 * 60)
348 if (*time_ < '0' || '5' < *time_)
351 *offset += 10 * 60 * (*time_++ - '0');
353 if (*time_ < '0' || '9' < *time_)
356 *offset += 60 * (*time_++ - '0');
366 if (*time_ < '0' || '5' < *time_)
369 *offset += 10 * (*time_++ - '0');
371 if (*time_ < '0' || '9' < *time_)
374 *offset += *time_++ - '0';
376 return *time_ == '\0';
380 parse_constant_offset (const gchar *name,
384 /* Internet RFC 8536 section 3.3.1 and POSIX 8.3 TZ together say
385 that a transition time must be numeric. */
386 if (!rfc8536 && g_strcmp0 (name, "UTC") == 0)
392 if (*name >= '0' && '9' >= *name)
393 return parse_time (name, offset, rfc8536);
399 /* Internet RFC 8536 section 3.3.1 requires a numeric zone. */
400 return !rfc8536 && !*name;
403 return parse_time (name, offset, rfc8536);
406 if (parse_time (name, offset, rfc8536))
420 zone_for_constant_offset (GTimeZone *gtz, const gchar *name)
425 if (name == NULL || !parse_constant_offset (name, &offset, FALSE))
428 info.gmt_offset = offset;
430 info.abbrev = g_strdup (name);
432 gtz->name = g_strdup (name);
433 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), 1);
434 g_array_append_val (gtz->t_info, info);
436 /* Constant offset, no transitions */
437 gtz->transitions = NULL;
442 zone_info_unix (const gchar *identifier,
443 gchar **out_identifier)
446 GMappedFile *file = NULL;
447 GBytes *zoneinfo = NULL;
448 gchar *resolved_identifier = NULL;
451 tzdir = g_getenv ("TZDIR");
453 tzdir = "/usr/share/zoneinfo";
455 /* identifier can be a relative or absolute path name;
456 if relative, it is interpreted starting from /usr/share/zoneinfo
457 while the POSIX standard says it should start with :,
458 glibc allows both syntaxes, so we should too */
459 if (identifier != NULL)
461 resolved_identifier = g_strdup (identifier);
463 if (*identifier == ':')
466 if (g_path_is_absolute (identifier))
467 filename = g_strdup (identifier);
469 filename = g_build_filename (tzdir, identifier, NULL);
473 gsize prefix_len = 0;
474 gchar *canonical_path = NULL;
475 GError *read_link_err = NULL;
477 filename = g_strdup ("/etc/localtime");
479 /* Resolve the actual timezone pointed to by /etc/localtime. */
480 resolved_identifier = g_file_read_link (filename, &read_link_err);
481 if (resolved_identifier == NULL)
483 gboolean not_a_symlink = g_error_matches (read_link_err,
486 g_clear_error (&read_link_err);
488 /* Fallback to the content of /var/db/zoneinfo or /etc/timezone
489 * if /etc/localtime is not a symlink. /var/db/zoneinfo is
490 * where 'tzsetup' program on FreeBSD and DragonflyBSD stores
491 * the timezone chosen by the user. /etc/timezone is where user
492 * choice is expressed on Gentoo OpenRC and others. */
493 if (not_a_symlink && (g_file_get_contents ("/var/db/zoneinfo",
494 &resolved_identifier,
496 g_file_get_contents ("/etc/timezone",
497 &resolved_identifier,
499 g_strchomp (resolved_identifier);
503 g_assert (resolved_identifier == NULL);
509 /* Resolve relative path */
510 canonical_path = g_canonicalize_filename (resolved_identifier, "/etc");
511 g_free (resolved_identifier);
512 resolved_identifier = g_steal_pointer (&canonical_path);
515 /* Strip the prefix and slashes if possible. */
516 if (g_str_has_prefix (resolved_identifier, tzdir))
518 prefix_len = strlen (tzdir);
519 while (*(resolved_identifier + prefix_len) == '/')
524 memmove (resolved_identifier, resolved_identifier + prefix_len,
525 strlen (resolved_identifier) - prefix_len + 1 /* nul terminator */);
527 g_free (canonical_path);
530 file = g_mapped_file_new (filename, FALSE, NULL);
533 zoneinfo = g_bytes_new_with_free_func (g_mapped_file_get_contents (file),
534 g_mapped_file_get_length (file),
535 (GDestroyNotify)g_mapped_file_unref,
536 g_mapped_file_ref (file));
537 g_mapped_file_unref (file);
540 g_assert (resolved_identifier != NULL);
543 if (out_identifier != NULL)
544 *out_identifier = g_steal_pointer (&resolved_identifier);
546 g_free (resolved_identifier);
553 init_zone_from_iana_info (GTimeZone *gtz,
555 gchar *identifier /* (transfer full) */)
559 guint32 time_count, type_count;
560 guint8 *tz_transitions, *tz_type_index, *tz_ttinfo;
562 gsize timesize = sizeof (gint32);
563 gconstpointer header_data = g_bytes_get_data (zoneinfo, &size);
564 const gchar *data = header_data;
565 const struct tzhead *header = header_data;
566 GTimeZone *footertz = NULL;
567 guint extra_time_count = 0, extra_type_count = 0;
568 gint64 last_explicit_transition_time;
570 g_return_if_fail (size >= sizeof (struct tzhead) &&
571 memcmp (header, "TZif", 4) == 0);
573 /* FIXME: Handle invalid TZif files better (Issue#1088). */
575 if (header->tzh_version >= '2')
577 /* Skip ahead to the newer 64-bit data if it's available. */
578 header = (const struct tzhead *)
579 (((const gchar *) (header + 1)) +
580 guint32_from_be(header->tzh_ttisgmtcnt) +
581 guint32_from_be(header->tzh_ttisstdcnt) +
582 8 * guint32_from_be(header->tzh_leapcnt) +
583 5 * guint32_from_be(header->tzh_timecnt) +
584 6 * guint32_from_be(header->tzh_typecnt) +
585 guint32_from_be(header->tzh_charcnt));
586 timesize = sizeof (gint64);
588 time_count = guint32_from_be(header->tzh_timecnt);
589 type_count = guint32_from_be(header->tzh_typecnt);
591 if (header->tzh_version >= '2')
593 const gchar *footer = (((const gchar *) (header + 1))
594 + guint32_from_be(header->tzh_ttisgmtcnt)
595 + guint32_from_be(header->tzh_ttisstdcnt)
596 + 12 * guint32_from_be(header->tzh_leapcnt)
599 + guint32_from_be(header->tzh_charcnt));
600 const gchar *footerlast;
602 g_return_if_fail (footer <= data + size - 2 && footer[0] == '\n');
603 footerlast = memchr (footer + 1, '\n', data + size - (footer + 1));
604 g_return_if_fail (footerlast);
605 footerlen = footerlast + 1 - footer;
608 footertz = parse_footertz (footer, footerlen);
609 g_return_if_fail (footertz);
610 extra_type_count = footertz->t_info->len;
611 extra_time_count = footertz->transitions->len;
615 tz_transitions = ((guint8 *) (header) + sizeof (*header));
616 tz_type_index = tz_transitions + timesize * time_count;
617 tz_ttinfo = tz_type_index + time_count;
618 tz_abbrs = tz_ttinfo + sizeof (struct ttinfo) * type_count;
620 gtz->name = g_steal_pointer (&identifier);
621 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo),
622 type_count + extra_type_count);
623 gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition),
624 time_count + extra_time_count);
626 for (index = 0; index < type_count; index++)
628 TransitionInfo t_info;
629 struct ttinfo info = ((struct ttinfo*)tz_ttinfo)[index];
630 t_info.gmt_offset = gint32_from_be (info.tt_gmtoff);
631 t_info.is_dst = info.tt_isdst ? TRUE : FALSE;
632 t_info.abbrev = g_strdup ((gchar *) &tz_abbrs[info.tt_abbrind]);
633 g_array_append_val (gtz->t_info, t_info);
636 for (index = 0; index < time_count; index++)
639 if (header->tzh_version >= '2')
640 trans.time = gint64_from_be (((gint64_be*)tz_transitions)[index]);
642 trans.time = gint32_from_be (((gint32_be*)tz_transitions)[index]);
643 last_explicit_transition_time = trans.time;
644 trans.info_index = tz_type_index[index];
645 g_assert (trans.info_index >= 0);
646 g_assert ((guint) trans.info_index < gtz->t_info->len);
647 g_array_append_val (gtz->transitions, trans);
652 /* Append footer time types. Don't bother to coalesce
653 duplicates with existing time types. */
654 for (index = 0; index < extra_type_count; index++)
656 TransitionInfo t_info;
657 TransitionInfo *footer_t_info
658 = &g_array_index (footertz->t_info, TransitionInfo, index);
659 t_info.gmt_offset = footer_t_info->gmt_offset;
660 t_info.is_dst = footer_t_info->is_dst;
661 t_info.abbrev = g_steal_pointer (&footer_t_info->abbrev);
662 g_array_append_val (gtz->t_info, t_info);
665 /* Append footer transitions that follow the last explicit
667 for (index = 0; index < extra_time_count; index++)
669 Transition *footer_transition
670 = &g_array_index (footertz->transitions, Transition, index);
672 || last_explicit_transition_time < footer_transition->time)
675 trans.time = footer_transition->time;
676 trans.info_index = type_count + footer_transition->info_index;
677 g_array_append_val (gtz->transitions, trans);
681 g_time_zone_unref (footertz);
685 #elif defined (G_OS_WIN32)
688 copy_windows_systemtime (SYSTEMTIME *s_time, TimeZoneDate *tzdate)
691 = s_time->wHour * 3600 + s_time->wMinute * 60 + s_time->wSecond;
692 tzdate->mon = s_time->wMonth;
693 tzdate->year = s_time->wYear;
694 tzdate->wday = s_time->wDayOfWeek ? s_time->wDayOfWeek : 7;
698 tzdate->mday = s_time->wDay;
702 tzdate->week = s_time->wDay;
705 /* UTC = local time + bias while local time = UTC + offset */
707 rule_from_windows_time_zone_info (TimeZoneRule *rule,
708 TIME_ZONE_INFORMATION *tzi)
710 gchar *std_name, *dlt_name;
712 std_name = g_utf16_to_utf8 ((gunichar2 *)tzi->StandardName, -1, NULL, NULL, NULL);
713 if (std_name == NULL)
716 dlt_name = g_utf16_to_utf8 ((gunichar2 *)tzi->DaylightName, -1, NULL, NULL, NULL);
717 if (dlt_name == NULL)
724 if (tzi->StandardDate.wMonth)
726 rule->std_offset = -(tzi->Bias + tzi->StandardBias) * 60;
727 rule->dlt_offset = -(tzi->Bias + tzi->DaylightBias) * 60;
728 copy_windows_systemtime (&(tzi->DaylightDate), &(rule->dlt_start));
730 copy_windows_systemtime (&(tzi->StandardDate), &(rule->dlt_end));
735 rule->std_offset = -tzi->Bias * 60;
736 rule->dlt_start.mon = 0;
738 strncpy (rule->std_name, std_name, NAME_SIZE - 1);
739 strncpy (rule->dlt_name, dlt_name, NAME_SIZE - 1);
748 windows_default_tzname (void)
750 const gunichar2 *subkey =
751 L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
753 gchar *key_name = NULL;
754 gunichar2 *key_name_w = NULL;
755 if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey, 0,
756 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
759 if (RegQueryValueExW (key, L"TimeZoneKeyName", NULL, NULL,
760 NULL, &size) == ERROR_SUCCESS)
762 key_name_w = g_malloc ((gint)size);
764 if (key_name_w == NULL ||
765 RegQueryValueExW (key, L"TimeZoneKeyName", NULL, NULL,
766 (LPBYTE)key_name_w, &size) != ERROR_SUCCESS)
772 key_name = g_utf16_to_utf8 (key_name_w, -1, NULL, NULL, NULL);
784 SYSTEMTIME StandardDate;
785 SYSTEMTIME DaylightDate;
789 system_time_copy (SYSTEMTIME *orig, SYSTEMTIME *target)
791 g_return_if_fail (orig != NULL);
792 g_return_if_fail (target != NULL);
794 target->wYear = orig->wYear;
795 target->wMonth = orig->wMonth;
796 target->wDayOfWeek = orig->wDayOfWeek;
797 target->wDay = orig->wDay;
798 target->wHour = orig->wHour;
799 target->wMinute = orig->wMinute;
800 target->wSecond = orig->wSecond;
801 target->wMilliseconds = orig->wMilliseconds;
805 register_tzi_to_tzi (RegTZI *reg, TIME_ZONE_INFORMATION *tzi)
807 g_return_if_fail (reg != NULL);
808 g_return_if_fail (tzi != NULL);
809 tzi->Bias = reg->Bias;
810 system_time_copy (&(reg->StandardDate), &(tzi->StandardDate));
811 tzi->StandardBias = reg->StandardBias;
812 system_time_copy (&(reg->DaylightDate), &(tzi->DaylightDate));
813 tzi->DaylightBias = reg->DaylightBias;
817 rules_from_windows_time_zone (const gchar *identifier,
818 gchar **out_identifier,
819 TimeZoneRule **rules,
820 gboolean copy_identifier)
823 gchar *subkey = NULL;
824 gchar *subkey_dynamic = NULL;
825 gchar *key_name = NULL;
826 const gchar *reg_key =
827 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
828 TIME_ZONE_INFORMATION tzi;
831 RegTZI regtzi, regtzi_prev;
832 WCHAR winsyspath[MAX_PATH];
833 gunichar2 *subkey_w, *subkey_dynamic_w;
835 subkey_dynamic_w = NULL;
837 if (GetSystemDirectoryW (winsyspath, MAX_PATH) == 0)
840 g_assert (copy_identifier == FALSE || out_identifier != NULL);
841 g_assert (rules != NULL);
844 *out_identifier = NULL;
850 key_name = windows_default_tzname ();
852 key_name = g_strdup (identifier);
857 subkey = g_strconcat (reg_key, key_name, NULL);
858 subkey_w = g_utf8_to_utf16 (subkey, -1, NULL, NULL, NULL);
859 if (subkey_w == NULL)
860 goto utf16_conv_failed;
862 subkey_dynamic = g_strconcat (subkey, "\\Dynamic DST", NULL);
863 subkey_dynamic_w = g_utf8_to_utf16 (subkey_dynamic, -1, NULL, NULL, NULL);
864 if (subkey_dynamic_w == NULL)
865 goto utf16_conv_failed;
867 if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_w, 0,
868 KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
869 goto utf16_conv_failed;
871 size = sizeof tzi.StandardName;
873 /* use RegLoadMUIStringW() to query MUI_Std from the registry if possible, otherwise
874 fallback to querying Std */
875 if (RegLoadMUIStringW (key, L"MUI_Std", tzi.StandardName,
876 size, &size, 0, winsyspath) != ERROR_SUCCESS)
878 size = sizeof tzi.StandardName;
879 if (RegQueryValueExW (key, L"Std", NULL, NULL,
880 (LPBYTE)&(tzi.StandardName), &size) != ERROR_SUCCESS)
881 goto registry_failed;
884 size = sizeof tzi.DaylightName;
886 /* use RegLoadMUIStringW() to query MUI_Dlt from the registry if possible, otherwise
887 fallback to querying Dlt */
888 if (RegLoadMUIStringW (key, L"MUI_Dlt", tzi.DaylightName,
889 size, &size, 0, winsyspath) != ERROR_SUCCESS)
891 size = sizeof tzi.DaylightName;
892 if (RegQueryValueExW (key, L"Dlt", NULL, NULL,
893 (LPBYTE)&(tzi.DaylightName), &size) != ERROR_SUCCESS)
894 goto registry_failed;
898 if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_dynamic_w, 0,
899 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
906 if (RegQueryValueExW (key, L"FirstEntry", NULL, NULL,
907 (LPBYTE) &first, &size) != ERROR_SUCCESS)
908 goto registry_failed;
911 if (RegQueryValueExW (key, L"LastEntry", NULL, NULL,
912 (LPBYTE) &last, &size) != ERROR_SUCCESS)
913 goto registry_failed;
915 rules_num = last - first + 2;
916 *rules = g_new0 (TimeZoneRule, rules_num);
918 for (year = first, i = 0; *rules != NULL && year <= last; year++)
920 gboolean failed = FALSE;
921 swprintf_s (s, 11, L"%d", year);
925 size = sizeof regtzi;
926 if (RegQueryValueExW (key, s, NULL, NULL,
927 (LPBYTE) ®tzi, &size) != ERROR_SUCCESS)
938 if (year > first && memcmp (®tzi_prev, ®tzi, sizeof regtzi) == 0)
941 memcpy (®tzi_prev, ®tzi, sizeof regtzi);
943 register_tzi_to_tzi (®tzi, &tzi);
945 if (!rule_from_windows_time_zone_info (&(*rules)[i], &tzi))
952 (*rules)[i++].start_year = year;
960 else if (RegOpenKeyExW (HKEY_LOCAL_MACHINE, subkey_w, 0,
961 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
963 size = sizeof regtzi;
964 if (RegQueryValueExW (key, L"TZI", NULL, NULL,
965 (LPBYTE) ®tzi, &size) == ERROR_SUCCESS)
968 *rules = g_new0 (TimeZoneRule, 2);
969 register_tzi_to_tzi (®tzi, &tzi);
971 if (!rule_from_windows_time_zone_info (&(*rules)[0], &tzi))
982 g_free (subkey_dynamic_w);
983 g_free (subkey_dynamic);
989 (*rules)[0].start_year = MIN_TZYEAR;
990 if ((*rules)[rules_num - 2].start_year < MAX_TZYEAR)
991 (*rules)[rules_num - 1].start_year = MAX_TZYEAR;
993 (*rules)[rules_num - 1].start_year = (*rules)[rules_num - 2].start_year + 1;
996 *out_identifier = g_steal_pointer (&key_name);
1011 find_relative_date (TimeZoneDate *buffer)
1015 g_date_clear (&date, 1);
1016 wday = buffer->wday;
1018 /* Get last day if last is needed, first day otherwise */
1019 if (buffer->mon == 13 || buffer->mon == 14) /* Julian Date */
1021 g_date_set_dmy (&date, 1, 1, buffer->year);
1022 if (wday >= 59 && buffer->mon == 13 && g_date_is_leap_year (buffer->year))
1023 g_date_add_days (&date, wday);
1025 g_date_add_days (&date, wday - 1);
1026 buffer->mon = (int) g_date_get_month (&date);
1027 buffer->mday = (int) g_date_get_day (&date);
1033 guint days_in_month = g_date_get_days_in_month (buffer->mon, buffer->year);
1034 GDateWeekday first_wday;
1036 g_date_set_dmy (&date, 1, buffer->mon, buffer->year);
1037 first_wday = g_date_get_weekday (&date);
1039 if (first_wday > wday)
1041 /* week is 1 <= w <= 5, we need 0-based */
1042 days = 7 * (buffer->week - 1) + wday - first_wday;
1044 while (days > days_in_month)
1047 g_date_add_days (&date, days);
1049 buffer->mday = g_date_get_day (&date);
1053 /* Offset is previous offset of local time. Returns 0 if month is 0 */
1055 boundary_for_year (TimeZoneDate *boundary,
1059 TimeZoneDate buffer;
1061 const guint64 unix_epoch_start = 719163L;
1062 const guint64 seconds_per_day = 86400L;
1068 if (boundary->year == 0)
1073 find_relative_date (&buffer);
1076 g_assert (buffer.year == year);
1077 g_date_clear (&date, 1);
1078 g_date_set_dmy (&date, buffer.mday, buffer.mon, buffer.year);
1079 return ((g_date_get_julian (&date) - unix_epoch_start) * seconds_per_day +
1080 buffer.offset - offset);
1084 fill_transition_info_from_rule (TransitionInfo *info,
1088 gint offset = is_dst ? rule->dlt_offset : rule->std_offset;
1089 gchar *name = is_dst ? rule->dlt_name : rule->std_name;
1091 info->gmt_offset = offset;
1092 info->is_dst = is_dst;
1095 info->abbrev = g_strdup (name);
1098 info->abbrev = g_strdup_printf ("%+03d%02d",
1099 (int) offset / 3600,
1100 (int) abs (offset / 60) % 60);
1104 init_zone_from_rules (GTimeZone *gtz,
1105 TimeZoneRule *rules,
1107 gchar *identifier /* (transfer full) */)
1109 guint type_count = 0, trans_count = 0, info_index = 0;
1110 guint ri; /* rule index */
1111 gboolean skip_first_std_trans = TRUE;
1117 /* Last rule only contains max year */
1118 for (ri = 0; ri < rules_num - 1; ri++)
1120 if (rules[ri].dlt_start.mon || rules[ri].dlt_end.mon)
1122 guint rulespan = (rules[ri + 1].start_year - rules[ri].start_year);
1123 guint transitions = rules[ri].dlt_start.mon > 0 ? 1 : 0;
1124 transitions += rules[ri].dlt_end.mon > 0 ? 1 : 0;
1125 type_count += rules[ri].dlt_start.mon > 0 ? 2 : 1;
1126 trans_count += transitions * rulespan;
1132 gtz->name = g_steal_pointer (&identifier);
1133 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), type_count);
1134 gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition), trans_count);
1136 last_offset = rules[0].std_offset;
1138 for (ri = 0; ri < rules_num - 1; ri++)
1140 if ((rules[ri].std_offset || rules[ri].dlt_offset) &&
1141 rules[ri].dlt_start.mon == 0 && rules[ri].dlt_end.mon == 0)
1143 TransitionInfo std_info;
1145 fill_transition_info_from_rule (&std_info, &(rules[ri]), FALSE);
1146 g_array_append_val (gtz->t_info, std_info);
1149 ((rules[ri - 1].dlt_start.mon > 12 &&
1150 rules[ri - 1].dlt_start.wday > rules[ri - 1].dlt_end.wday) ||
1151 rules[ri - 1].dlt_start.mon > rules[ri - 1].dlt_end.mon))
1153 /* The previous rule was a southern hemisphere rule that
1154 starts the year with DST, so we need to add a
1155 transition to return to standard time */
1156 guint year = rules[ri].start_year;
1157 gint64 std_time = boundary_for_year (&rules[ri].dlt_end,
1159 Transition std_trans = {std_time, info_index};
1160 g_array_append_val (gtz->transitions, std_trans);
1163 last_offset = rules[ri].std_offset;
1165 skip_first_std_trans = TRUE;
1169 const guint start_year = rules[ri].start_year;
1170 const guint end_year = rules[ri + 1].start_year;
1173 TransitionInfo std_info, dlt_info;
1174 if (rules[ri].dlt_start.mon > 12)
1175 dlt_first = rules[ri].dlt_start.wday > rules[ri].dlt_end.wday;
1177 dlt_first = rules[ri].dlt_start.mon > rules[ri].dlt_end.mon;
1178 /* Standard rules are always even, because before the first
1179 transition is always standard time, and 0 is even. */
1180 fill_transition_info_from_rule (&std_info, &(rules[ri]), FALSE);
1181 fill_transition_info_from_rule (&dlt_info, &(rules[ri]), TRUE);
1183 g_array_append_val (gtz->t_info, std_info);
1184 g_array_append_val (gtz->t_info, dlt_info);
1186 /* Transition dates. We hope that a year which ends daylight
1187 time in a southern-hemisphere country (i.e., one that
1188 begins the year in daylight time) will include a rule
1189 which has only a dlt_end. */
1190 for (year = start_year; year < end_year; year++)
1192 gint32 dlt_offset = (dlt_first ? last_offset :
1193 rules[ri].dlt_offset);
1194 gint32 std_offset = (dlt_first ? rules[ri].std_offset :
1196 /* NB: boundary_for_year returns 0 if mon == 0 */
1197 gint64 std_time = boundary_for_year (&rules[ri].dlt_end,
1199 gint64 dlt_time = boundary_for_year (&rules[ri].dlt_start,
1201 Transition std_trans = {std_time, info_index};
1202 Transition dlt_trans = {dlt_time, info_index + 1};
1203 last_offset = (dlt_first ? rules[ri].dlt_offset :
1204 rules[ri].std_offset);
1207 if (skip_first_std_trans)
1208 skip_first_std_trans = FALSE;
1210 g_array_append_val (gtz->transitions, std_trans);
1212 g_array_append_val (gtz->transitions, dlt_trans);
1217 g_array_append_val (gtz->transitions, dlt_trans);
1219 g_array_append_val (gtz->transitions, std_trans);
1227 ((rules[ri - 1].dlt_start.mon > 12 &&
1228 rules[ri - 1].dlt_start.wday > rules[ri - 1].dlt_end.wday) ||
1229 rules[ri - 1].dlt_start.mon > rules[ri - 1].dlt_end.mon))
1231 /* The previous rule was a southern hemisphere rule that
1232 starts the year with DST, so we need to add a
1233 transition to return to standard time */
1234 TransitionInfo info;
1235 guint year = rules[ri].start_year;
1237 fill_transition_info_from_rule (&info, &(rules[ri - 1]), FALSE);
1238 g_array_append_val (gtz->t_info, info);
1239 trans.time = boundary_for_year (&rules[ri - 1].dlt_end,
1241 trans.info_index = info_index;
1242 g_array_append_val (gtz->transitions, trans);
1247 * parses date[/time] for parsing TZ environment variable
1249 * date is either Mm.w.d, Jn or N
1256 * time is either h or hh[[:]mm[[[:]ss]]]
1262 parse_mwd_boundary (gchar **pos, TimeZoneDate *boundary)
1264 gint month, week, day;
1266 if (**pos == '\0' || **pos < '0' || '9' < **pos)
1269 month = *(*pos)++ - '0';
1271 if ((month == 1 && **pos >= '0' && '2' >= **pos) ||
1272 (month == 0 && **pos >= '0' && '9' >= **pos))
1275 month += *(*pos)++ - '0';
1278 if (*(*pos)++ != '.' || month == 0)
1281 if (**pos == '\0' || **pos < '1' || '5' < **pos)
1284 week = *(*pos)++ - '0';
1286 if (*(*pos)++ != '.')
1289 if (**pos == '\0' || **pos < '0' || '6' < **pos)
1292 day = *(*pos)++ - '0';
1298 boundary->mon = month;
1299 boundary->week = week;
1300 boundary->wday = day;
1305 * This parses two slightly different ways of specifying
1308 * - ignore_leap == TRUE
1310 * Jn This specifies the Julian day with n between 1 and 365. Leap days
1311 * are not counted. In this format, February 29 can't be represented;
1312 * February 28 is day 59, and March 1 is always day 60.
1314 * - ignore_leap == FALSE
1316 * n This specifies the zero-based Julian day with n between 0 and 365.
1317 * February 29 is counted in leap years.
1320 parse_julian_boundary (gchar** pos, TimeZoneDate *boundary,
1321 gboolean ignore_leap)
1326 while (**pos >= '0' && '9' >= **pos)
1329 day += *(*pos)++ - '0';
1334 if (day < 1 || 365 < day)
1341 if (day < 0 || 365 < day)
1343 /* GDate wants day in range 1->366 */
1347 g_date_clear (&date, 1);
1348 g_date_set_julian (&date, day);
1350 boundary->mon = (int) g_date_get_month (&date);
1351 boundary->mday = (int) g_date_get_day (&date);
1358 parse_tz_boundary (const gchar *identifier,
1359 TimeZoneDate *boundary)
1363 pos = (gchar*)identifier;
1364 /* Month-week-weekday */
1368 if (!parse_mwd_boundary (&pos, boundary))
1371 /* Julian date which ignores Feb 29 in leap years */
1372 else if (*pos == 'J')
1375 if (!parse_julian_boundary (&pos, boundary, TRUE))
1378 /* Julian date which counts Feb 29 in leap years */
1379 else if (*pos >= '0' && '9' >= *pos)
1381 if (!parse_julian_boundary (&pos, boundary, FALSE))
1390 return parse_constant_offset (pos + 1, &boundary->offset, TRUE);
1393 boundary->offset = 2 * 60 * 60;
1394 return *pos == '\0';
1399 create_ruleset_from_rule (TimeZoneRule **rules, TimeZoneRule *rule)
1401 *rules = g_new0 (TimeZoneRule, 2);
1403 (*rules)[0].start_year = MIN_TZYEAR;
1404 (*rules)[1].start_year = MAX_TZYEAR;
1406 (*rules)[0].std_offset = -rule->std_offset;
1407 (*rules)[0].dlt_offset = -rule->dlt_offset;
1408 (*rules)[0].dlt_start = rule->dlt_start;
1409 (*rules)[0].dlt_end = rule->dlt_end;
1410 strcpy ((*rules)[0].std_name, rule->std_name);
1411 strcpy ((*rules)[0].dlt_name, rule->dlt_name);
1416 parse_offset (gchar **pos, gint32 *target)
1419 gchar *target_pos = *pos;
1422 while (**pos == '+' || **pos == '-' || **pos == ':' ||
1423 (**pos >= '0' && '9' >= **pos))
1426 buffer = g_strndup (target_pos, *pos - target_pos);
1427 ret = parse_constant_offset (buffer, target, FALSE);
1434 parse_identifier_boundary (gchar **pos, TimeZoneDate *target)
1437 gchar *target_pos = *pos;
1440 while (**pos != ',' && **pos != '\0')
1442 buffer = g_strndup (target_pos, *pos - target_pos);
1443 ret = parse_tz_boundary (buffer, target);
1450 set_tz_name (gchar **pos, gchar *buffer, guint size)
1452 gboolean quoted = **pos == '<';
1453 gchar *name_pos = *pos;
1461 while (g_ascii_isalnum (**pos) || **pos == '-' || **pos == '+');
1466 while (g_ascii_isalpha (**pos))
1469 /* Name should be three or more characters */
1470 /* FIXME: Should return FALSE if the name is too long.
1471 This should simplify code later in this function. */
1472 if (*pos - name_pos < 3)
1475 memset (buffer, 0, size);
1476 /* name_pos isn't 0-terminated, so we have to limit the length expressly */
1477 len = *pos - name_pos > size - 1 ? size - 1 : *pos - name_pos;
1478 strncpy (buffer, name_pos, len);
1484 parse_identifier_boundaries (gchar **pos, TimeZoneRule *tzr)
1486 if (*(*pos)++ != ',')
1490 if (!parse_identifier_boundary (pos, &(tzr->dlt_start)) || *(*pos)++ != ',')
1494 if (!parse_identifier_boundary (pos, &(tzr->dlt_end)))
1500 * Creates an array of TimeZoneRule from a TZ environment variable
1501 * type of identifier. Should free rules afterwards
1504 rules_from_identifier (const gchar *identifier,
1505 gchar **out_identifier,
1506 TimeZoneRule **rules)
1511 g_assert (out_identifier != NULL);
1512 g_assert (rules != NULL);
1514 *out_identifier = NULL;
1520 pos = (gchar*)identifier;
1521 memset (&tzr, 0, sizeof (tzr));
1522 /* Standard offset */
1523 if (!(set_tz_name (&pos, tzr.std_name, NAME_SIZE)) ||
1524 !parse_offset (&pos, &(tzr.std_offset)))
1529 *out_identifier = g_strdup (identifier);
1530 return create_ruleset_from_rule (rules, &tzr);
1534 if (!(set_tz_name (&pos, tzr.dlt_name, NAME_SIZE)))
1536 parse_offset (&pos, &(tzr.dlt_offset));
1537 if (tzr.dlt_offset == 0) /* No daylight offset given, assume it's 1
1538 hour earlier that standard */
1539 tzr.dlt_offset = tzr.std_offset - 3600;
1542 /* Windows allows us to use the US DST boundaries if they're not given */
1545 guint rules_num = 0;
1547 /* Use US rules, Windows' default is Pacific Standard Time */
1548 if ((rules_num = rules_from_windows_time_zone ("Pacific Standard Time",
1553 /* We don't want to hardcode our identifier here as
1554 * "Pacific Standard Time", use what was passed in
1556 *out_identifier = g_strdup (identifier);
1558 for (i = 0; i < rules_num - 1; i++)
1560 (*rules)[i].std_offset = - tzr.std_offset;
1561 (*rules)[i].dlt_offset = - tzr.dlt_offset;
1562 strcpy ((*rules)[i].std_name, tzr.std_name);
1563 strcpy ((*rules)[i].dlt_name, tzr.dlt_name);
1574 /* Start and end required (format 2) */
1575 if (!parse_identifier_boundaries (&pos, &tzr))
1578 *out_identifier = g_strdup (identifier);
1579 return create_ruleset_from_rule (rules, &tzr);
1584 parse_footertz (const gchar *footer, size_t footerlen)
1586 gchar *tzstring = g_strndup (footer + 1, footerlen - 2);
1587 GTimeZone *footertz = NULL;
1589 /* FIXME: it might make sense to modify rules_from_identifier to
1590 allow NULL to be passed instead of &ident, saving the strdup/free
1591 pair. The allocation for tzstring could also be avoided by
1592 passing a gsize identifier_len argument to rules_from_identifier
1593 and changing the code in that function to stop assuming that
1594 identifier is nul-terminated. */
1596 TimeZoneRule *rules;
1597 guint rules_num = rules_from_identifier (tzstring, &ident, &rules);
1603 footertz = g_slice_new0 (GTimeZone);
1604 init_zone_from_rules (footertz, rules, rules_num, NULL);
1605 footertz->ref_count++;
1612 /* Construction {{{1 */
1615 * @identifier: (nullable): a timezone identifier
1617 * Creates a #GTimeZone corresponding to @identifier.
1619 * @identifier can either be an RFC3339/ISO 8601 time offset or
1620 * something that would pass as a valid value for the `TZ` environment
1621 * variable (including %NULL).
1623 * In Windows, @identifier can also be the unlocalized name of a time
1624 * zone for standard time, for example "Pacific Standard Time".
1626 * Valid RFC3339 time offsets are `"Z"` (for UTC) or
1627 * `"±hh:mm"`. ISO 8601 additionally specifies
1628 * `"±hhmm"` and `"±hh"`. Offsets are
1629 * time values to be added to Coordinated Universal Time (UTC) to get
1632 * In UNIX, the `TZ` environment variable typically corresponds
1633 * to the name of a file in the zoneinfo database, an absolute path to a file
1634 * somewhere else, or a string in
1635 * "std offset [dst [offset],start[/time],end[/time]]" (POSIX) format.
1636 * There are no spaces in the specification. The name of standard
1637 * and daylight savings time zone must be three or more alphabetic
1638 * characters. Offsets are time values to be added to local time to
1639 * get Coordinated Universal Time (UTC) and should be
1640 * `"[±]hh[[:]mm[:ss]]"`. Dates are either
1641 * `"Jn"` (Julian day with n between 1 and 365, leap
1642 * years not counted), `"n"` (zero-based Julian day
1643 * with n between 0 and 365) or `"Mm.w.d"` (day d
1644 * (0 <= d <= 6) of week w (1 <= w <= 5) of month m (1 <= m <= 12), day
1645 * 0 is a Sunday). Times are in local wall clock time, the default is
1648 * In Windows, the "tzn[+|–]hh[:mm[:ss]][dzn]" format is used, but also
1649 * accepts POSIX format. The Windows format uses US rules for all time
1650 * zones; daylight savings time is 60 minutes behind the standard time
1651 * with date and time of change taken from Pacific Standard Time.
1652 * Offsets are time values to be added to the local time to get
1653 * Coordinated Universal Time (UTC).
1655 * g_time_zone_new_local() calls this function with the value of the
1656 * `TZ` environment variable. This function itself is independent of
1657 * the value of `TZ`, but if @identifier is %NULL then `/etc/localtime`
1658 * will be consulted to discover the correct time zone on UNIX and the
1659 * registry will be consulted or GetTimeZoneInformation() will be used
1660 * to get the local time zone on Windows.
1662 * If intervals are not available, only time zone rules from `TZ`
1663 * environment variable or other means, then they will be computed
1664 * from year 1900 to 2037. If the maximum year for the rules is
1665 * available and it is greater than 2037, then it will followed
1669 * [RFC3339 §5.6](http://tools.ietf.org/html/rfc3339#section-5.6)
1670 * for a precise definition of valid RFC3339 time offsets
1671 * (the `time-offset` expansion) and ISO 8601 for the
1672 * full list of valid time offsets. See
1673 * [The GNU C Library manual](http://www.gnu.org/s/libc/manual/html_node/TZ-Variable.html)
1674 * for an explanation of the possible
1675 * values of the `TZ` environment variable. See
1676 * [Microsoft Time Zone Index Values](http://msdn.microsoft.com/en-us/library/ms912391%28v=winembedded.11%29.aspx)
1677 * for the list of time zones on Windows.
1679 * You should release the return value by calling g_time_zone_unref()
1680 * when you are done with it.
1682 * Returns: the requested timezone
1687 g_time_zone_new (const gchar *identifier)
1689 GTimeZone *tz = NULL;
1690 TimeZoneRule *rules;
1692 gchar *resolved_identifier = NULL;
1694 G_LOCK (time_zones);
1695 if (time_zones == NULL)
1696 time_zones = g_hash_table_new (g_str_hash, g_str_equal);
1700 tz = g_hash_table_lookup (time_zones, identifier);
1703 g_atomic_int_inc (&tz->ref_count);
1704 G_UNLOCK (time_zones);
1709 tz = g_slice_new0 (GTimeZone);
1712 zone_for_constant_offset (tz, identifier);
1714 if (tz->t_info == NULL &&
1715 (rules_num = rules_from_identifier (identifier, &resolved_identifier, &rules)))
1717 init_zone_from_rules (tz, rules, rules_num, g_steal_pointer (&resolved_identifier));
1721 if (tz->t_info == NULL)
1724 GBytes *zoneinfo = zone_info_unix (identifier, &resolved_identifier);
1725 if (zoneinfo != NULL)
1727 init_zone_from_iana_info (tz, zoneinfo, g_steal_pointer (&resolved_identifier));
1728 g_bytes_unref (zoneinfo);
1730 #elif defined (G_OS_WIN32)
1731 if ((rules_num = rules_from_windows_time_zone (identifier,
1732 &resolved_identifier,
1736 init_zone_from_rules (tz, rules, rules_num, g_steal_pointer (&resolved_identifier));
1742 #if defined (G_OS_WIN32)
1743 if (tz->t_info == NULL)
1745 if (identifier == NULL)
1747 TIME_ZONE_INFORMATION tzi;
1749 if (GetTimeZoneInformation (&tzi) != TIME_ZONE_ID_INVALID)
1751 rules = g_new0 (TimeZoneRule, 2);
1753 if (rule_from_windows_time_zone_info (&rules[0], &tzi))
1755 memset (rules[0].std_name, 0, NAME_SIZE);
1756 memset (rules[0].dlt_name, 0, NAME_SIZE);
1758 rules[0].start_year = MIN_TZYEAR;
1759 rules[1].start_year = MAX_TZYEAR;
1761 init_zone_from_rules (tz, rules, 2, windows_default_tzname ());
1770 g_free (resolved_identifier);
1772 /* Always fall back to UTC. */
1773 if (tz->t_info == NULL)
1774 zone_for_constant_offset (tz, "UTC");
1776 g_assert (tz->name != NULL);
1777 g_assert (tz->t_info != NULL);
1779 if (tz->t_info != NULL)
1782 g_hash_table_insert (time_zones, tz->name, tz);
1784 g_atomic_int_inc (&tz->ref_count);
1785 G_UNLOCK (time_zones);
1791 * g_time_zone_new_utc:
1793 * Creates a #GTimeZone corresponding to UTC.
1795 * This is equivalent to calling g_time_zone_new() with a value like
1796 * "Z", "UTC", "+00", etc.
1798 * You should release the return value by calling g_time_zone_unref()
1799 * when you are done with it.
1801 * Returns: the universal timezone
1806 g_time_zone_new_utc (void)
1808 static GTimeZone *utc = NULL;
1809 static gsize initialised;
1811 if (g_once_init_enter (&initialised))
1813 utc = g_time_zone_new ("UTC");
1814 g_once_init_leave (&initialised, TRUE);
1817 return g_time_zone_ref (utc);
1821 * g_time_zone_new_local:
1823 * Creates a #GTimeZone corresponding to local time. The local time
1824 * zone may change between invocations to this function; for example,
1825 * if the system administrator changes it.
1827 * This is equivalent to calling g_time_zone_new() with the value of
1828 * the `TZ` environment variable (including the possibility of %NULL).
1830 * You should release the return value by calling g_time_zone_unref()
1831 * when you are done with it.
1833 * Returns: the local timezone
1838 g_time_zone_new_local (void)
1840 const gchar *tzenv = g_getenv ("TZ");
1845 /* Is time zone changed and must be flushed? */
1846 if (tz_local && g_strcmp0 (tzenv, tzenv_cached) != 0)
1848 g_clear_pointer (&tz_local, g_time_zone_unref);
1849 g_clear_pointer (&tzenv_cached, g_free);
1852 if (tz_local == NULL)
1854 tz_local = g_time_zone_new (tzenv);
1855 tzenv_cached = g_strdup (tzenv);
1858 tz = g_time_zone_ref (tz_local);
1860 G_UNLOCK (tz_local);
1866 * g_time_zone_new_offset:
1867 * @seconds: offset to UTC, in seconds
1869 * Creates a #GTimeZone corresponding to the given constant offset from UTC,
1872 * This is equivalent to calling g_time_zone_new() with a string in the form
1873 * `[+|-]hh[:mm[:ss]]`.
1875 * Returns: (transfer full): a timezone at the given offset from UTC
1879 g_time_zone_new_offset (gint32 seconds)
1881 GTimeZone *tz = NULL;
1882 gchar *identifier = NULL;
1884 /* Seemingly, we should be using @seconds directly to set the
1885 * #TransitionInfo.gmt_offset to avoid all this string building and parsing.
1886 * However, we always need to set the #GTimeZone.name to a constructed
1887 * string anyway, so we might as well reuse its code. */
1888 identifier = g_strdup_printf ("%c%02u:%02u:%02u",
1889 (seconds >= 0) ? '+' : '-',
1890 (ABS (seconds) / 60) / 60,
1891 (ABS (seconds) / 60) % 60,
1892 ABS (seconds) % 60);
1893 tz = g_time_zone_new (identifier);
1894 g_free (identifier);
1896 g_assert (g_time_zone_get_offset (tz, 0) == seconds);
1901 #define TRANSITION(n) g_array_index (tz->transitions, Transition, n)
1902 #define TRANSITION_INFO(n) g_array_index (tz->t_info, TransitionInfo, n)
1904 /* Internal helpers {{{1 */
1905 /* NB: Interval 0 is before the first transition, so there's no
1906 * transition structure to point to which TransitionInfo to
1907 * use. Rule-based zones are set up so that TI 0 is always standard
1908 * time (which is what's in effect before Daylight time got started
1909 * in the early 20th century), but IANA tzfiles don't follow that
1910 * convention. The tzfile documentation says to use the first
1911 * standard-time (i.e., non-DST) tinfo, so that's what we do.
1913 inline static const TransitionInfo*
1914 interval_info (GTimeZone *tz,
1918 g_return_val_if_fail (tz->t_info != NULL, NULL);
1919 if (interval && tz->transitions && interval <= tz->transitions->len)
1920 index = (TRANSITION(interval - 1)).info_index;
1923 for (index = 0; index < tz->t_info->len; index++)
1925 TransitionInfo *tzinfo = &(TRANSITION_INFO(index));
1926 if (!tzinfo->is_dst)
1932 return &(TRANSITION_INFO(index));
1935 inline static gint64
1936 interval_start (GTimeZone *tz,
1939 if (!interval || tz->transitions == NULL || tz->transitions->len == 0)
1941 if (interval > tz->transitions->len)
1942 interval = tz->transitions->len;
1943 return (TRANSITION(interval - 1)).time;
1946 inline static gint64
1947 interval_end (GTimeZone *tz,
1950 if (tz->transitions && interval < tz->transitions->len)
1952 gint64 lim = (TRANSITION(interval)).time;
1953 return lim - (lim != G_MININT64);
1958 inline static gint32
1959 interval_offset (GTimeZone *tz,
1962 g_return_val_if_fail (tz->t_info != NULL, 0);
1963 return interval_info (tz, interval)->gmt_offset;
1966 inline static gboolean
1967 interval_isdst (GTimeZone *tz,
1970 g_return_val_if_fail (tz->t_info != NULL, 0);
1971 return interval_info (tz, interval)->is_dst;
1975 inline static gchar*
1976 interval_abbrev (GTimeZone *tz,
1979 g_return_val_if_fail (tz->t_info != NULL, 0);
1980 return interval_info (tz, interval)->abbrev;
1983 inline static gint64
1984 interval_local_start (GTimeZone *tz,
1988 return interval_start (tz, interval) + interval_offset (tz, interval);
1993 inline static gint64
1994 interval_local_end (GTimeZone *tz,
1997 if (tz->transitions && interval < tz->transitions->len)
1998 return interval_end (tz, interval) + interval_offset (tz, interval);
2004 interval_valid (GTimeZone *tz,
2007 if ( tz->transitions == NULL)
2008 return interval == 0;
2009 return interval <= tz->transitions->len;
2012 /* g_time_zone_find_interval() {{{1 */
2015 * g_time_zone_adjust_time:
2017 * @type: the #GTimeType of @time_
2018 * @time_: a pointer to a number of seconds since January 1, 1970
2020 * Finds an interval within @tz that corresponds to the given @time_,
2021 * possibly adjusting @time_ if required to fit into an interval.
2022 * The meaning of @time_ depends on @type.
2024 * This function is similar to g_time_zone_find_interval(), with the
2025 * difference that it always succeeds (by making the adjustments
2028 * In any of the cases where g_time_zone_find_interval() succeeds then
2029 * this function returns the same value, without modifying @time_.
2031 * This function may, however, modify @time_ in order to deal with
2032 * non-existent times. If the non-existent local @time_ of 02:30 were
2033 * requested on March 14th 2010 in Toronto then this function would
2034 * adjust @time_ to be 03:00 and return the interval containing the
2037 * Returns: the interval containing @time_, never -1
2042 g_time_zone_adjust_time (GTimeZone *tz,
2047 gboolean interval_is_dst;
2049 if (tz->transitions == NULL)
2052 intervals = tz->transitions->len;
2054 /* find the interval containing *time UTC
2055 * TODO: this could be binary searched (or better) */
2056 for (i = 0; i <= intervals; i++)
2057 if (*time_ <= interval_end (tz, i))
2060 g_assert (interval_start (tz, i) <= *time_ && *time_ <= interval_end (tz, i));
2062 if (type != G_TIME_TYPE_UNIVERSAL)
2064 if (*time_ < interval_local_start (tz, i))
2065 /* if time came before the start of this interval... */
2069 /* if it's not in the previous interval... */
2070 if (*time_ > interval_local_end (tz, i))
2072 /* it doesn't exist. fast-forward it. */
2074 *time_ = interval_local_start (tz, i);
2078 else if (*time_ > interval_local_end (tz, i))
2079 /* if time came after the end of this interval... */
2083 /* if it's not in the next interval... */
2084 if (*time_ < interval_local_start (tz, i))
2085 /* it doesn't exist. fast-forward it. */
2086 *time_ = interval_local_start (tz, i);
2091 interval_is_dst = interval_isdst (tz, i);
2092 if ((interval_is_dst && type != G_TIME_TYPE_DAYLIGHT) ||
2093 (!interval_is_dst && type == G_TIME_TYPE_DAYLIGHT))
2095 /* it's in this interval, but dst flag doesn't match.
2096 * check neighbours for a better fit. */
2097 if (i && *time_ <= interval_local_end (tz, i - 1))
2100 else if (i < intervals &&
2101 *time_ >= interval_local_start (tz, i + 1))
2111 * g_time_zone_find_interval:
2113 * @type: the #GTimeType of @time_
2114 * @time_: a number of seconds since January 1, 1970
2116 * Finds an interval within @tz that corresponds to the given @time_.
2117 * The meaning of @time_ depends on @type.
2119 * If @type is %G_TIME_TYPE_UNIVERSAL then this function will always
2120 * succeed (since universal time is monotonic and continuous).
2122 * Otherwise @time_ is treated as local time. The distinction between
2123 * %G_TIME_TYPE_STANDARD and %G_TIME_TYPE_DAYLIGHT is ignored except in
2124 * the case that the given @time_ is ambiguous. In Toronto, for example,
2125 * 01:30 on November 7th 2010 occurred twice (once inside of daylight
2126 * savings time and the next, an hour later, outside of daylight savings
2127 * time). In this case, the different value of @type would result in a
2128 * different interval being returned.
2130 * It is still possible for this function to fail. In Toronto, for
2131 * example, 02:00 on March 14th 2010 does not exist (due to the leap
2132 * forward to begin daylight savings time). -1 is returned in that
2135 * Returns: the interval containing @time_, or -1 in case of failure
2140 g_time_zone_find_interval (GTimeZone *tz,
2145 gboolean interval_is_dst;
2147 if (tz->transitions == NULL)
2149 intervals = tz->transitions->len;
2150 for (i = 0; i <= intervals; i++)
2151 if (time_ <= interval_end (tz, i))
2154 if (type == G_TIME_TYPE_UNIVERSAL)
2157 if (time_ < interval_local_start (tz, i))
2159 if (time_ > interval_local_end (tz, --i))
2163 else if (time_ > interval_local_end (tz, i))
2165 if (time_ < interval_local_start (tz, ++i))
2171 interval_is_dst = interval_isdst (tz, i);
2172 if ((interval_is_dst && type != G_TIME_TYPE_DAYLIGHT) ||
2173 (!interval_is_dst && type == G_TIME_TYPE_DAYLIGHT))
2175 if (i && time_ <= interval_local_end (tz, i - 1))
2178 else if (i < intervals && time_ >= interval_local_start (tz, i + 1))
2186 /* Public API accessors {{{1 */
2189 * g_time_zone_get_abbreviation:
2191 * @interval: an interval within the timezone
2193 * Determines the time zone abbreviation to be used during a particular
2194 * @interval of time in the time zone @tz.
2196 * For example, in Toronto this is currently "EST" during the winter
2197 * months and "EDT" during the summer months when daylight savings time
2200 * Returns: the time zone abbreviation, which belongs to @tz
2205 g_time_zone_get_abbreviation (GTimeZone *tz,
2208 g_return_val_if_fail (interval_valid (tz, (guint)interval), NULL);
2210 return interval_abbrev (tz, (guint)interval);
2214 * g_time_zone_get_offset:
2216 * @interval: an interval within the timezone
2218 * Determines the offset to UTC in effect during a particular @interval
2219 * of time in the time zone @tz.
2221 * The offset is the number of seconds that you add to UTC time to
2222 * arrive at local time for @tz (ie: negative numbers for time zones
2223 * west of GMT, positive numbers for east).
2225 * Returns: the number of seconds that should be added to UTC to get the
2231 g_time_zone_get_offset (GTimeZone *tz,
2234 g_return_val_if_fail (interval_valid (tz, (guint)interval), 0);
2236 return interval_offset (tz, (guint)interval);
2240 * g_time_zone_is_dst:
2242 * @interval: an interval within the timezone
2244 * Determines if daylight savings time is in effect during a particular
2245 * @interval of time in the time zone @tz.
2247 * Returns: %TRUE if daylight savings time is in effect
2252 g_time_zone_is_dst (GTimeZone *tz,
2255 g_return_val_if_fail (interval_valid (tz, interval), FALSE);
2257 if (tz->transitions == NULL)
2260 return interval_isdst (tz, (guint)interval);
2264 * g_time_zone_get_identifier:
2267 * Get the identifier of this #GTimeZone, as passed to g_time_zone_new().
2268 * If the identifier passed at construction time was not recognised, `UTC` will
2269 * be returned. If it was %NULL, the identifier of the local timezone at
2270 * construction time will be returned.
2272 * The identifier will be returned in the same format as provided at
2273 * construction time: if provided as a time offset, that will be returned by
2276 * Returns: identifier for this timezone
2280 g_time_zone_get_identifier (GTimeZone *tz)
2282 g_return_val_if_fail (tz != NULL, NULL);
2288 /* vim:set foldmethod=marker: */