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 of the licence, 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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Author: Ryan Lortie <desrt@desrt.ca>
26 #include "gtimezone.h"
32 #include "gmappedfile.h"
33 #include "gtestutils.h"
34 #include "gfileutils.h"
35 #include "gstrfuncs.h"
40 #include "gdatetime.h"
51 * @short_description: a structure representing a time zone
52 * @see_also: #GDateTime
54 * #GTimeZone is a structure that represents a time zone, at no
55 * particular point in time. It is refcounted and immutable.
57 * A time zone contains a number of intervals. Each interval has
58 * an abbreviation to describe it, an offet to UTC and a flag indicating
59 * if the daylight savings time is in effect during that interval. A
60 * time zone always has at least one interval -- interval 0.
62 * Every UTC time is contained within exactly one interval, but a given
63 * local time may be contained within zero, one or two intervals (due to
64 * incontinuities associated with daylight savings time).
66 * An interval may refer to a specific period of time (eg: the duration
67 * of daylight savings time during 2010) or it may refer to many periods
68 * of time that share the same properties (eg: all periods of daylight
69 * savings time). It is also possible (usually for political reasons)
70 * that some properties (like the abbreviation) change between intervals
71 * without other properties changing.
73 * #GTimeZone is available since GLib 2.26.
79 * #GDateTime is an opaque structure whose members cannot be accessed
85 /* zoneinfo file format {{{1 */
88 typedef struct { gchar bytes[8]; } gint64_be;
89 typedef struct { gchar bytes[4]; } gint32_be;
90 typedef struct { gchar bytes[4]; } guint32_be;
92 static inline gint64 gint64_from_be (const gint64_be be) {
93 gint64 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT64_FROM_BE (tmp);
96 static inline gint32 gint32_from_be (const gint32_be be) {
97 gint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT32_FROM_BE (tmp);
100 static inline guint32 guint32_from_be (const guint32_be be) {
101 guint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GUINT32_FROM_BE (tmp);
108 guchar tzh_reserved[15];
110 guint32_be tzh_ttisgmtcnt;
111 guint32_be tzh_ttisstdcnt;
112 guint32_be tzh_leapcnt;
113 guint32_be tzh_timecnt;
114 guint32_be tzh_typecnt;
115 guint32_be tzh_charcnt;
129 gboolean is_standard;
154 /* POSIX Timezone abbreviations are typically 3 or 4 characters, but
155 Microsoft uses 32-character names. We'll use one larger to ensure
156 we have room for the terminating \0.
165 TimeZoneDate dlt_start;
166 TimeZoneDate dlt_end;
167 gchar std_name[NAME_SIZE];
168 gchar dlt_name[NAME_SIZE];
172 /* GTimeZone structure and lifecycle {{{1 */
181 G_LOCK_DEFINE_STATIC (time_zones);
182 static GHashTable/*<string?, GTimeZone>*/ *time_zones;
184 #define MIN_TZYEAR 1900
185 #define MAX_TZYEAR 2038
191 * Decreases the reference count on @tz.
196 g_time_zone_unref (GTimeZone *tz)
201 ref_count = g_atomic_int_get (&tz->ref_count);
203 g_assert (ref_count > 0);
207 if (tz->name != NULL)
211 /* someone else might have grabbed a ref in the meantime */
212 if G_UNLIKELY (g_atomic_int_get (&tz->ref_count) != 1)
214 G_UNLOCK(time_zones);
218 g_hash_table_remove (time_zones, tz->name);
219 G_UNLOCK(time_zones);
222 if (tz->t_info != NULL)
223 g_array_free (tz->t_info, TRUE);
224 if (tz->transitions != NULL)
225 g_array_free (tz->transitions, TRUE);
228 g_slice_free (GTimeZone, tz);
231 else if G_UNLIKELY (!g_atomic_int_compare_and_exchange (&tz->ref_count,
241 * Increases the reference count on @tz.
243 * Returns: a new reference to @tz.
248 g_time_zone_ref (GTimeZone *tz)
250 g_assert (tz->ref_count > 0);
252 g_atomic_int_inc (&tz->ref_count);
257 /* fake zoneinfo creation (for RFC3339/ISO 8601 timezones) {{{1 */
259 * parses strings of the form h or hh[[:]mm[[[:]ss]]] where:
265 parse_time (const gchar *time_,
268 if (*time_ < '0' || '9' < *time_)
271 *offset = 60 * 60 * (*time_++ - '0');
278 if (*time_ < '0' || '9' < *time_)
282 *offset += 60 * 60 * (*time_++ - '0');
284 if (*offset > 23 * 60 * 60)
294 if (*time_ < '0' || '5' < *time_)
297 *offset += 10 * 60 * (*time_++ - '0');
299 if (*time_ < '0' || '9' < *time_)
302 *offset += 60 * (*time_++ - '0');
310 if (*time_ < '0' || '5' < *time_)
313 *offset += 10 * (*time_++ - '0');
315 if (*time_ < '0' || '9' < *time_)
318 *offset += *time_++ - '0';
320 return *time_ == '\0';
324 parse_constant_offset (const gchar *name,
327 if (g_strcmp0 (name, "UTC") == 0)
333 if (*name >= '0' && '9' >= *name)
334 return parse_time (name, offset);
343 return parse_time (name, offset);
346 if (parse_time (name, offset))
358 zone_for_constant_offset (GTimeZone *gtz, const gchar *name)
363 if (name == NULL || !parse_constant_offset (name, &offset))
366 info.gmt_offset = offset;
368 info.is_standard = TRUE;
370 info.abbrev = g_strdup (name);
373 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), 1);
374 g_array_append_val (gtz->t_info, info);
376 /* Constant offset, no transitions */
377 gtz->transitions = NULL;
382 zone_info_unix (const gchar *identifier)
385 GMappedFile *file = NULL;
386 GBytes *zoneinfo = NULL;
388 /* identifier can be a relative or absolute path name;
389 if relative, it is interpreted starting from /usr/share/zoneinfo
390 while the POSIX standard says it should start with :,
391 glibc allows both syntaxes, so we should too */
392 if (identifier != NULL)
396 tzdir = getenv ("TZDIR");
398 tzdir = "/usr/share/zoneinfo";
400 if (*identifier == ':')
403 if (g_path_is_absolute (identifier))
404 filename = g_strdup (identifier);
406 filename = g_build_filename (tzdir, identifier, NULL);
409 filename = g_strdup ("/etc/localtime");
411 file = g_mapped_file_new (filename, FALSE, NULL);
414 zoneinfo = g_bytes_new_with_free_func (g_mapped_file_get_contents (file),
415 g_mapped_file_get_length (file),
416 (GDestroyNotify)g_mapped_file_unref,
417 g_mapped_file_ref (file));
418 g_mapped_file_unref (file);
425 init_zone_from_iana_info (GTimeZone *gtz, GBytes *zoneinfo)
429 guint32 time_count, type_count, leap_count, isgmt_count;
430 guint32 isstd_count, char_count ;
431 gpointer tz_transitions, tz_type_index, tz_ttinfo;
432 gpointer tz_leaps, tz_isgmt, tz_isstd;
434 guint timesize = sizeof (gint32), countsize = sizeof (gint32);
435 const struct tzhead *header = g_bytes_get_data (zoneinfo, &size);
437 g_return_if_fail (size >= sizeof (struct tzhead) &&
438 memcmp (header, "TZif", 4) == 0);
440 if (header->tzh_version == '2')
442 /* Skip ahead to the newer 64-bit data if it's available. */
443 header = (const struct tzhead *)
444 (((const gchar *) (header + 1)) +
445 guint32_from_be(header->tzh_ttisgmtcnt) +
446 guint32_from_be(header->tzh_ttisstdcnt) +
447 8 * guint32_from_be(header->tzh_leapcnt) +
448 5 * guint32_from_be(header->tzh_timecnt) +
449 6 * guint32_from_be(header->tzh_typecnt) +
450 guint32_from_be(header->tzh_charcnt));
451 timesize = sizeof (gint64);
453 time_count = guint32_from_be(header->tzh_timecnt);
454 type_count = guint32_from_be(header->tzh_typecnt);
455 leap_count = guint32_from_be(header->tzh_leapcnt);
456 isgmt_count = guint32_from_be(header->tzh_ttisgmtcnt);
457 isstd_count = guint32_from_be(header->tzh_ttisstdcnt);
458 char_count = guint32_from_be(header->tzh_charcnt);
460 g_assert (type_count == isgmt_count);
461 g_assert (type_count == isstd_count);
463 tz_transitions = (gpointer)(header + 1);
464 tz_type_index = tz_transitions + timesize * time_count;
465 tz_ttinfo = tz_type_index + time_count;
466 tz_abbrs = tz_ttinfo + sizeof (struct ttinfo) * type_count;
467 tz_leaps = tz_abbrs + char_count;
468 tz_isstd = tz_leaps + (timesize + countsize) * leap_count;
469 tz_isgmt = tz_isstd + isstd_count;
471 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo),
473 gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition),
476 for (index = 0; index < type_count; index++)
478 TransitionInfo t_info;
479 struct ttinfo info = ((struct ttinfo*)tz_ttinfo)[index];
480 t_info.gmt_offset = gint32_from_be (info.tt_gmtoff);
481 t_info.is_dst = info.tt_isdst ? TRUE : FALSE;
482 t_info.is_standard = ((guint8*)tz_isstd)[index] ? TRUE : FALSE;
483 t_info.is_gmt = ((guint8*)tz_isgmt)[index] ? TRUE : FALSE;
484 t_info.abbrev = g_strdup (&tz_abbrs[info.tt_abbrind]);
485 g_array_append_val (gtz->t_info, t_info);
488 for (index = 0; index < time_count; index++)
491 if (header->tzh_version == '2')
492 trans.time = gint64_from_be (((gint64_be*)tz_transitions)[index]);
494 trans.time = gint32_from_be (((gint32_be*)tz_transitions)[index]);
495 trans.info_index = ((guint8*)tz_type_index)[index];
496 g_assert (trans.info_index >= 0);
497 g_assert (trans.info_index < gtz->t_info->len);
498 g_array_append_val (gtz->transitions, trans);
500 g_bytes_unref (zoneinfo);
503 #elif defined (G_OS_WIN32)
506 copy_windows_systemtime (SYSTEMTIME *s_time, TimeZoneDate *tzdate)
508 tzdate->sec = s_time->wSecond;
509 tzdate->min = s_time->wMinute;
510 tzdate->hour = s_time->wHour;
511 tzdate->mon = s_time->wMonth;
512 tzdate->year = s_time->wYear;
513 tzdate->wday = s_time->wDayOfWeek ? s_time->wDayOfWeek : 7;
517 tzdate->mday = s_time->wDay;
521 tzdate->week = s_time->wDay;
524 /* UTC = local time + bias while local time = UTC + offset */
526 rule_from_windows_time_zone_info (TimeZoneRule *rule,
527 TIME_ZONE_INFORMATION *tzi)
530 if (tzi->StandardDate.wMonth)
532 rule->std_offset = -(tzi->Bias + tzi->StandardBias) * 60;
533 rule->dlt_offset = -(tzi->Bias + tzi->DaylightBias) * 60;
534 copy_windows_systemtime (&(tzi->DaylightDate), &(rule->dlt_start));
536 rule->dlt_start.isstd = FALSE;
537 rule->dlt_start.isgmt = FALSE;
538 copy_windows_systemtime (&(tzi->StandardDate), &(rule->dlt_end));
540 rule->dlt_end.isstd = FALSE;
541 rule->dlt_end.isgmt = FALSE;
546 rule->std_offset = -tzi->Bias * 60;
547 rule->dlt_start.mon = 0;
549 strncpy (rule->std_name, (gchar*)tzi->StandardName, NAME_SIZE - 1);
550 strncpy (rule->dlt_name, (gchar*)tzi->DaylightName, NAME_SIZE - 1);
554 windows_default_tzname (void)
556 const gchar *subkey =
557 "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
559 gchar *key_name = NULL;
560 if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0,
561 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
564 if (RegQueryValueExA (key, "TimeZoneKeyName", NULL, NULL,
565 NULL, &size) == ERROR_SUCCESS)
567 key_name = g_malloc ((gint)size);
568 if (RegQueryValueExA (key, "TimeZoneKeyName", NULL, NULL,
569 (LPBYTE)key_name, &size) != ERROR_SUCCESS)
585 SYSTEMTIME StandardDate;
586 SYSTEMTIME DaylightDate;
590 system_time_copy (SYSTEMTIME *orig, SYSTEMTIME *target)
592 g_return_if_fail (orig != NULL);
593 g_return_if_fail (target != NULL);
595 target->wYear = orig->wYear;
596 target->wMonth = orig->wMonth;
597 target->wDayOfWeek = orig->wDayOfWeek;
598 target->wDay = orig->wDay;
599 target->wHour = orig->wHour;
600 target->wMinute = orig->wMinute;
601 target->wSecond = orig->wSecond;
602 target->wMilliseconds = orig->wMilliseconds;
606 register_tzi_to_tzi (RegTZI *reg, TIME_ZONE_INFORMATION *tzi)
608 g_return_if_fail (reg != NULL);
609 g_return_if_fail (tzi != NULL);
610 tzi->Bias = reg->Bias;
611 system_time_copy (&(reg->StandardDate), &(tzi->StandardDate));
612 tzi->StandardBias = reg->StandardBias;
613 system_time_copy (&(reg->DaylightDate), &(tzi->DaylightDate));
614 tzi->DaylightBias = reg->DaylightBias;
618 rules_from_windows_time_zone (const gchar *identifier, TimeZoneRule **rules)
621 gchar *subkey, *subkey_dynamic;
622 gchar *key_name = NULL;
623 const gchar *reg_key =
624 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
625 TIME_ZONE_INFORMATION tzi;
628 RegTZI regtzi, regtzi_prev;
634 key_name = windows_default_tzname ();
636 key_name = g_strdup (identifier);
641 subkey = g_strconcat (reg_key, key_name, NULL);
642 subkey_dynamic = g_strconcat (subkey, "\\Dynamic DST", NULL);
644 if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0,
645 KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
647 size = sizeof tzi.StandardName;
648 if (RegQueryValueExA (key, "Std", NULL, NULL,
649 (LPBYTE)&(tzi.StandardName), &size) != ERROR_SUCCESS)
651 if (RegQueryValueExA (key, "Dlt", NULL, NULL,
652 (LPBYTE)&(tzi.DaylightName), &size) != ERROR_SUCCESS)
656 if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey_dynamic, 0,
657 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
664 if (RegQueryValueExA (key, "FirstEntry", NULL, NULL,
665 (LPBYTE) &first, &size) != ERROR_SUCCESS)
669 if (RegQueryValueExA (key, "LastEntry", NULL, NULL,
670 (LPBYTE) &last, &size) != ERROR_SUCCESS)
673 rules_num = last - first + 2;
674 *rules = g_new0 (TimeZoneRule, rules_num);
676 for (year = first, i = 0; year <= last; year++)
678 s = g_strdup_printf ("%d", year);
680 size = sizeof regtzi;
681 if (RegQueryValueExA (key, s, NULL, NULL,
682 (LPBYTE) ®tzi, &size) != ERROR_SUCCESS)
691 if (year > first && memcmp (®tzi_prev, ®tzi, sizeof regtzi) == 0)
694 memcpy (®tzi_prev, ®tzi, sizeof regtzi);
696 register_tzi_to_tzi (®tzi, &tzi);
697 rule_from_windows_time_zone_info (&(*rules)[i], &tzi);
698 (*rules)[i++].start_year = year;
706 else if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0,
707 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
709 size = sizeof regtzi;
710 if (RegQueryValueExA (key, "TZI", NULL, NULL,
711 (LPBYTE) ®tzi, &size) == ERROR_SUCCESS)
714 *rules = g_new0 (TimeZoneRule, 2);
715 register_tzi_to_tzi (®tzi, &tzi);
716 rule_from_windows_time_zone_info (&(*rules)[0], &tzi);
722 g_free (subkey_dynamic);
728 (*rules)[0].start_year = MIN_TZYEAR;
729 if ((*rules)[rules_num - 2].start_year < MAX_TZYEAR)
730 (*rules)[rules_num - 1].start_year = MAX_TZYEAR;
732 (*rules)[rules_num - 1].start_year = (*rules)[rules_num - 2].start_year + 1;
743 find_relative_date (TimeZoneDate *buffer,
751 /* Get last day if last is needed, first day otherwise */
752 dt = g_date_time_new (tz,
754 buffer->mon + (buffer->week < 5? 0 : 1),
755 buffer->week < 5? 1 : 0,
756 buffer->hour, buffer->min, buffer->sec);
758 buffer->wday = g_date_time_get_day_of_week (dt);
759 buffer->mday = g_date_time_get_day_of_month (dt);
761 if (buffer->week < 5)
763 if (wday < buffer->wday)
766 buffer->mday += (buffer->week - 1) * 7;
769 else if (wday > buffer->wday)
772 buffer->mday += wday - buffer->wday;
775 g_date_time_unref (dt);
778 /* Offset is previous offset of local time */
780 boundary_for_year (TimeZoneDate *boundary,
796 else if (boundary->isstd)
799 offset = prev_offset;
801 G_UNLOCK (time_zones);
803 identifier = g_strdup_printf ("%+03d:%02d:%02d",
805 (int) abs (offset / 60) % 60,
806 (int) abs (offset) % 3600);
807 tz = g_time_zone_new (identifier);
810 if (boundary->year == 0)
815 find_relative_date (&buffer, tz);
818 g_assert (buffer.year == year);
820 dt = g_date_time_new (tz,
821 buffer.year, buffer.mon, buffer.mday,
822 buffer.hour, buffer.min, buffer.sec);
823 t = g_date_time_to_unix (dt);
824 g_date_time_unref (dt);
826 g_time_zone_unref (tz);
834 init_zone_from_rules (GTimeZone *gtz,
838 TransitionInfo info[2];
840 gint type_count, trans_count;
847 /* Last rule only contains max year */
848 for (i = 0; i < rules_num - 1; i++)
850 if (rules[i].dlt_start.mon)
853 trans_count += 2 * (rules[i+1].start_year - rules[i].start_year);
862 /* If standard time happens before daylight time in first rule
863 * with daylight, skip first transition so the minimum is in
864 * standard time and the first transition is in daylight time */
865 for (i = 0; i < rules_num - 1 && rules[0].dlt_start.mon == 0; i++);
867 if (i < rules_num -1 && rules[i].dlt_start.mon > 0 &&
868 rules[i].dlt_start.mon > rules[i].dlt_end.mon)
874 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), type_count);
875 gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition), trans_count);
877 last_offset = rules[0].std_offset;
879 for (i = 0; i < rules_num - 1; i++)
881 if (rules[i].dlt_start.mon)
884 info[0].gmt_offset = rules[i].std_offset;
885 info[0].is_dst = FALSE;
886 info[0].is_standard = rules[i].dlt_end.isstd;
887 info[0].is_gmt = rules[i].dlt_end.isgmt;
889 if (rules[i].std_name)
890 info[0].abbrev = g_strdup (rules[i].std_name);
893 info[0].abbrev = g_strdup_printf ("%+03d%02d",
894 (int) rules[i].std_offset / 3600,
895 (int) abs (rules[i].std_offset / 60) % 60);
899 info[1].gmt_offset = rules[i].dlt_offset;
900 info[1].is_dst = TRUE;
901 info[1].is_standard = rules[i].dlt_start.isstd;
902 info[1].is_gmt = rules[i].dlt_start.isgmt;
904 if (rules[i].dlt_name)
905 info[1].abbrev = g_strdup (rules[i].dlt_name);
908 info[1].abbrev = g_strdup_printf ("%+03d%02d",
909 (int) rules[i].dlt_offset / 3600,
910 (int) abs (rules[i].dlt_offset / 60) % 60);
912 if (rules[i].dlt_start.mon < rules[i].dlt_end.mon)
914 g_array_append_val (gtz->t_info, info[1]);
915 g_array_append_val (gtz->t_info, info[0]);
919 g_array_append_val (gtz->t_info, info[0]);
920 g_array_append_val (gtz->t_info, info[1]);
923 /* Transition dates */
924 for (year = rules[i].start_year; year < rules[i+1].start_year; year++)
926 if (rules[i].dlt_start.mon < rules[i].dlt_end.mon)
929 trans.info_index = y;
930 trans.time = boundary_for_year (&rules[i].dlt_start, year,
931 last_offset, rules[i].std_offset);
932 g_array_insert_val (gtz->transitions, x++, trans);
933 last_offset = rules[i].dlt_offset;
936 trans.info_index = y+1;
937 trans.time = boundary_for_year (&rules[i].dlt_end, year,
938 last_offset, rules[i].std_offset);
939 g_array_insert_val (gtz->transitions, x++, trans);
940 last_offset = rules[i].std_offset;
945 trans.info_index = y;
946 trans.time = boundary_for_year (&rules[i].dlt_end, year,
947 last_offset, rules[i].std_offset);
949 g_array_insert_val (gtz->transitions, x++, trans);
952 last_offset = rules[i].std_offset;
955 trans.info_index = y+1;
956 trans.time = boundary_for_year (&rules[i].dlt_start, year,
957 last_offset, rules[i].std_offset);
958 g_array_insert_val (gtz->transitions, x++, trans);
959 last_offset = rules[i].dlt_offset;
968 info[0].gmt_offset = rules[i].std_offset;
969 info[0].is_dst = FALSE;
970 info[0].is_standard = FALSE;
971 info[0].is_gmt = FALSE;
973 if (rules[i].std_name)
974 info[0].abbrev = g_strdup (rules[i].std_name);
977 info[0].abbrev = g_strdup_printf ("%+03d%02d",
978 (int) rules[i].std_offset / 3600,
979 (int) abs (rules[i].std_offset / 60) % 60);
981 g_array_append_val (gtz->t_info, info[0]);
983 last_offset = rules[i].std_offset;
991 * parses date[/time] for parsing TZ environment variable
993 * date is either Mm.w.d, Jn or N
1000 * time is either h or hh[[:]mm[[[:]ss]]]
1006 parse_tz_boundary (const gchar *identifier,
1007 TimeZoneDate *boundary)
1010 gint month, week, day;
1015 if (*pos == 'M') /* Relative date */
1019 if (*pos == '\0' || *pos < '0' || '9' < *pos)
1022 month = *pos++ - '0';
1024 if ((month == 1 && *pos >= '0' && '2' >= *pos) ||
1025 (month == 0 && *pos >= '0' && '9' >= *pos))
1028 month += *pos++ - '0';
1031 if (*pos++ != '.' || month == 0)
1034 if (*pos == '\0' || *pos < '1' || '5' < *pos)
1037 week = *pos++ - '0';
1042 if (*pos == '\0' || *pos < '0' || '6' < *pos)
1051 boundary->mon = month;
1052 boundary->week = week;
1053 boundary->wday = day;
1056 else if (*pos == 'J') /* Julian day */
1061 while (*pos >= '0' && '9' >= *pos)
1064 day += *pos++ - '0';
1067 if (day < 1 || 365 < day)
1070 date = g_date_new_julian (day);
1072 boundary->mon = (int) g_date_get_month (date);
1073 boundary->mday = (int) g_date_get_day (date);
1078 else if (*pos >= '0' && '9' >= *pos) /* Zero-based Julian day */
1081 while (*pos >= '0' && '9' >= *pos)
1084 day += *pos++ - '0';
1087 if (day < 0 || 365 < day)
1090 date = g_date_new_julian (day >= 59? day : day + 1);
1092 boundary->mon = (int) g_date_get_month (date);
1093 boundary->mday = (int) g_date_get_day (date);
1106 boundary->isstd = FALSE;
1107 boundary->isgmt = FALSE;
1113 if (!parse_time (++pos, &offset))
1116 boundary->hour = offset / 3600;
1117 boundary->min = (offset / 60) % 60;
1118 boundary->sec = offset % 3600;
1129 return *pos == '\0';
1134 create_ruleset_from_rule (TimeZoneRule **rules, TimeZoneRule *rule)
1136 *rules = g_new0 (TimeZoneRule, 2);
1138 (*rules)[0].start_year = MIN_TZYEAR;
1139 (*rules)[1].start_year = MAX_TZYEAR;
1141 (*rules)[0].std_offset = -rule->std_offset;
1142 (*rules)[0].dlt_offset = -rule->dlt_offset;
1143 (*rules)[0].dlt_start = rule->dlt_start;
1144 (*rules)[0].dlt_end = rule->dlt_end;
1145 strcpy (rule->std_name, (*rules)[0].std_name);
1146 strcpy (rule->dlt_name, (*rules)[0].dlt_name);
1151 parse_offset (gchar **pos, gint32 *target)
1154 gchar *target_pos = *pos;
1157 while (**pos == '+' || **pos == '-' || **pos == ':' ||
1158 (**pos >= '0' && '9' >= **pos))
1161 buffer = g_strndup (target_pos, *pos - target_pos);
1162 ret = parse_constant_offset (buffer, target);
1169 parse_identifier_boundary (gchar **pos, TimeZoneDate *target)
1172 gchar *target_pos = *pos;
1175 while (**pos != ',' && **pos != '\0')
1177 buffer = g_strndup (target_pos, *pos++ - target_pos);
1178 ret = parse_tz_boundary (buffer, target);
1185 set_tz_name (gchar **pos, gchar *buffer, guint size)
1187 gchar *name_pos = *pos;
1190 /* Name is ASCII alpha (Is this necessarily true?) */
1191 while (g_ascii_isalpha (**pos))
1194 /* Offset for standard required (format 1) */
1198 /* Name should be three or more alphabetic characters */
1199 if (*pos - name_pos < 3)
1202 memset (buffer, 0, NAME_SIZE);
1203 /* name_pos isn't 0-terminated, so we have to limit the length expressly */
1204 len = *pos - name_pos > size - 1 ? size - 1 : *pos - name_pos;
1205 strncpy (buffer, name_pos, len);
1210 parse_identifier_boundaries (gchar **pos, TimeZoneRule *tzr)
1212 /* Default offset is 1 hour less from standard offset */
1213 if (*(*pos++) == ',')
1215 tzr->dlt_offset = tzr->std_offset - 60 * 60;
1218 /* Daylight offset */
1219 if (!parse_offset (pos, &(tzr->dlt_offset)))
1222 /* Start and end required (format 2) */
1223 if (*(*pos++) != ',')
1227 if (!parse_identifier_boundary (pos, &(tzr->dlt_start)) || **pos != ',')
1231 if (!parse_identifier_boundary (pos, &(tzr->dlt_end)))
1237 * Creates an array of TimeZoneRule from a TZ environment variable
1238 * type of identifier. Should free rules afterwards
1241 rules_from_identifier (const gchar *identifier,
1242 TimeZoneRule **rules)
1250 pos = (gchar*)identifier;
1251 memset (&tzr, 0, sizeof (tzr));
1252 /* Standard offset */
1253 if (!(set_tz_name (&pos, tzr.std_name, NAME_SIZE)) ||
1254 !parse_offset (&pos, &(tzr.std_offset)))
1260 if (!(set_tz_name (&pos, tzr.dlt_name, NAME_SIZE)))
1264 /* Start and end required (format 2) */
1271 if (!parse_identifier_boundaries (&pos, &tzr))
1280 /* If doesn't have offset for daylight then it is Windows format */
1281 if (tzr.dlt_offset == 0)
1284 guint rules_num = 0;
1286 /* Use US rules, Windows' default is Pacific Standard Time */
1287 tzr.dlt_offset = tzr.std_offset - 60 * 60;
1289 if ((rules_num = rules_from_windows_time_zone ("Pacific Standard Time",
1292 for (i = 0; i < rules_num - 1; i++)
1294 (*rules)[i].std_offset = - tzr.std_offset;
1295 (*rules)[i].dlt_offset = - tzr.dlt_offset;
1296 strcpy ((*rules)[i].std_name, tzr.std_name);
1297 strcpy ((*rules)[i].dlt_name, tzr.dlt_name);
1307 return create_ruleset_from_rule (rules, &tzr);
1310 /* Construction {{{1 */
1313 * @identifier: (allow-none): a timezone identifier
1315 * Creates a #GTimeZone corresponding to @identifier.
1317 * @identifier can either be an RFC3339/ISO 8601 time offset or
1318 * something that would pass as a valid value for the
1319 * <varname>TZ</varname> environment variable (including %NULL).
1321 * In Windows, @identifier can also be the unlocalized name of a time
1322 * zone for standard time, for example "Pacific Standard Time".
1324 * Valid RFC3339 time offsets are <literal>"Z"</literal> (for UTC) or
1325 * <literal>"±hh:mm"</literal>. ISO 8601 additionally specifies
1326 * <literal>"±hhmm"</literal> and <literal>"±hh"</literal>. Offsets are
1327 * time values to be added to Coordinated Universal Time (UTC) to get
1330 * In Unix, the <varname>TZ</varname> environment variable typically
1331 * corresponds to the name of a file in the zoneinfo database, or
1332 * string in "std offset [dst [offset],start[/time],end[/time]]"
1333 * (POSIX) format. There are no spaces in the specification. The
1334 * name of standard and daylight savings time zone must be three or more
1335 * alphabetic characters. Offsets are time values to be added to local
1336 * time to get Coordinated Universal Time (UTC) and should be
1337 * <literal>"[±]hh[[:]mm[:ss]]"</literal>. Dates are either
1338 * <literal>"Jn"</literal> (Julian day with n between 1 and 365, leap
1339 * years not counted), <literal>"n"</literal> (zero-based Julian day
1340 * with n between 0 and 365) or <literal>"Mm.w.d"</literal> (day d
1341 * (0 <= d <= 6) of week w (1 <= w <= 5) of month m (1 <= m <= 12), day
1342 * 0 is a Sunday). Times are in local wall clock time, the default is
1345 * In Windows, the "tzn[+|–]hh[:mm[:ss]][dzn]" format is used, but also
1346 * accepts POSIX format. The Windows format uses US rules for all time
1347 * zones; daylight savings time is 60 minutes behind the standard time
1348 * with date and time of change taken from Pacific Standard Time.
1349 * Offsets are time values to be added to the local time to get
1350 * Coordinated Universal Time (UTC).
1352 * g_time_zone_new_local() calls this function with the value of the
1353 * <varname>TZ</varname> environment variable. This function itself is
1354 * independent of the value of <varname>TZ</varname>, but if @identifier
1355 * is %NULL then <filename>/etc/localtime</filename> will be consulted
1356 * to discover the correct time zone on Unix and the registry will be
1357 * consulted or GetTimeZoneInformation() will be used to get the local
1358 * time zone on Windows.
1360 * If intervals are not available, only time zone rules from
1361 * <varname>TZ</varname> environment variable or other means, then they
1362 * will be computed from year 1900 to 2037. If the maximum year for the
1363 * rules is available and it is greater than 2037, then it will followed
1367 * url='http://tools.ietf.org/html/rfc3339#section-5.6'>RFC3339
1368 * §5.6</ulink> for a precise definition of valid RFC3339 time offsets
1369 * (the <varname>time-offset</varname> expansion) and ISO 8601 for the
1370 * full list of valid time offsets. See <ulink
1371 * url='http://www.gnu.org/s/libc/manual/html_node/TZ-Variable.html'>The
1372 * GNU C Library manual</ulink> for an explanation of the possible
1373 * values of the <varname>TZ</varname> environment variable. See <ulink
1374 * url='http://msdn.microsoft.com/en-us/library/ms912391%28v=winembedded.11%29.aspx'>
1375 * Microsoft Time Zone Index Values</ulink> for the list of time zones
1378 * You should release the return value by calling g_time_zone_unref()
1379 * when you are done with it.
1381 * Returns: the requested timezone
1386 g_time_zone_new (const gchar *identifier)
1388 GTimeZone *tz = NULL;
1389 TimeZoneRule *rules;
1392 G_LOCK (time_zones);
1393 if (time_zones == NULL)
1394 time_zones = g_hash_table_new (g_str_hash, g_str_equal);
1398 tz = g_hash_table_lookup (time_zones, identifier);
1401 g_atomic_int_inc (&tz->ref_count);
1402 G_UNLOCK (time_zones);
1407 tz = g_slice_new0 (GTimeZone);
1408 tz->name = g_strdup (identifier);
1411 zone_for_constant_offset (tz, identifier);
1413 if (tz->t_info == NULL &&
1414 (rules_num = rules_from_identifier (identifier, &rules)))
1416 init_zone_from_rules (tz, rules, rules_num);
1420 if (tz->t_info == NULL)
1423 GBytes *zoneinfo = zone_info_unix (identifier);
1425 zone_for_constant_offset (tz, "UTC");
1428 init_zone_from_iana_info (tz, zoneinfo);
1429 g_bytes_unref (zoneinfo);
1431 #elif defined (G_OS_WIN32)
1432 if ((rules_num = rules_from_windows_time_zone (identifier, &rules)))
1434 init_zone_from_rules (tz, rules, rules_num);
1439 if (tz->t_info == NULL)
1442 zone_for_constant_offset (tz, "UTC");
1445 TIME_ZONE_INFORMATION tzi;
1447 if (GetTimeZoneInformation (&tzi) != TIME_ZONE_ID_INVALID)
1449 rules = g_new0 (TimeZoneRule, 2);
1451 rule_from_windows_time_zone_info (&rules[0], &tzi);
1453 memset (rules[0].std_name, 0, NAME_SIZE);
1454 memset (rules[0].dlt_name, 0, NAME_SIZE);
1456 rules[0].start_year = MIN_TZYEAR;
1457 rules[1].start_year = MAX_TZYEAR;
1459 init_zone_from_rules (tz, rules, 2);
1467 if (tz->t_info != NULL)
1470 g_hash_table_insert (time_zones, tz->name, tz);
1472 g_atomic_int_inc (&tz->ref_count);
1473 G_UNLOCK (time_zones);
1479 * g_time_zone_new_utc:
1481 * Creates a #GTimeZone corresponding to UTC.
1483 * This is equivalent to calling g_time_zone_new() with a value like
1484 * "Z", "UTC", "+00", etc.
1486 * You should release the return value by calling g_time_zone_unref()
1487 * when you are done with it.
1489 * Returns: the universal timezone
1494 g_time_zone_new_utc (void)
1496 return g_time_zone_new ("UTC");
1500 * g_time_zone_new_local:
1502 * Creates a #GTimeZone corresponding to local time. The local time
1503 * zone may change between invocations to this function; for example,
1504 * if the system administrator changes it.
1506 * This is equivalent to calling g_time_zone_new() with the value of the
1507 * <varname>TZ</varname> environment variable (including the possibility
1510 * You should release the return value by calling g_time_zone_unref()
1511 * when you are done with it.
1513 * Returns: the local timezone
1518 g_time_zone_new_local (void)
1520 return g_time_zone_new (getenv ("TZ"));
1523 #define TRANSITION(n) g_array_index (tz->transitions, Transition, n)
1524 #define TRANSITION_INFO(n) g_array_index (tz->t_info, TransitionInfo, n)
1526 /* Internal helpers {{{1 */
1527 /* Note that interval 0 is *before* the first transition time, so
1528 * interval 1 gets transitions[0].
1530 inline static const TransitionInfo*
1531 interval_info (GTimeZone *tz,
1535 g_return_val_if_fail (tz->t_info != NULL, NULL);
1536 if (interval && tz->transitions && interval <= tz->transitions->len)
1537 index = (TRANSITION(interval - 1)).info_index;
1540 return &(TRANSITION_INFO(index));
1543 inline static gint64
1544 interval_start (GTimeZone *tz,
1547 if (!interval || tz->transitions == NULL || tz->transitions->len == 0)
1549 if (interval > tz->transitions->len)
1550 interval = tz->transitions->len;
1551 return (TRANSITION(interval - 1)).time;
1554 inline static gint64
1555 interval_end (GTimeZone *tz,
1558 if (tz->transitions && interval < tz->transitions->len)
1559 return (TRANSITION(interval)).time - 1;
1563 inline static gint32
1564 interval_offset (GTimeZone *tz,
1567 g_return_val_if_fail (tz->t_info != NULL, 0);
1568 return interval_info (tz, interval)->gmt_offset;
1571 inline static gboolean
1572 interval_isdst (GTimeZone *tz,
1575 g_return_val_if_fail (tz->t_info != NULL, 0);
1576 return interval_info (tz, interval)->is_dst;
1580 inline static gboolean
1581 interval_isgmt (GTimeZone *tz,
1584 g_return_val_if_fail (tz->t_info != NULL, 0);
1585 return interval_info (tz, interval)->is_gmt;
1588 inline static gboolean
1589 interval_isstandard (GTimeZone *tz,
1592 return interval_info (tz, interval)->is_standard;
1595 inline static gchar*
1596 interval_abbrev (GTimeZone *tz,
1599 g_return_val_if_fail (tz->t_info != NULL, 0);
1600 return interval_info (tz, interval)->abbrev;
1603 inline static gint64
1604 interval_local_start (GTimeZone *tz,
1608 return interval_start (tz, interval) + interval_offset (tz, interval);
1613 inline static gint64
1614 interval_local_end (GTimeZone *tz,
1617 if (tz->transitions && interval < tz->transitions->len)
1618 return interval_end (tz, interval) + interval_offset (tz, interval);
1624 interval_valid (GTimeZone *tz,
1627 if ( tz->transitions == NULL)
1628 return interval == 0;
1629 return interval <= tz->transitions->len;
1632 /* g_time_zone_find_interval() {{{1 */
1635 * g_time_zone_adjust_time:
1637 * @type: the #GTimeType of @time_
1638 * @time_: a pointer to a number of seconds since January 1, 1970
1640 * Finds an interval within @tz that corresponds to the given @time_,
1641 * possibly adjusting @time_ if required to fit into an interval.
1642 * The meaning of @time_ depends on @type.
1644 * This function is similar to g_time_zone_find_interval(), with the
1645 * difference that it always succeeds (by making the adjustments
1648 * In any of the cases where g_time_zone_find_interval() succeeds then
1649 * this function returns the same value, without modifying @time_.
1651 * This function may, however, modify @time_ in order to deal with
1652 * non-existent times. If the non-existent local @time_ of 02:30 were
1653 * requested on March 14th 2010 in Toronto then this function would
1654 * adjust @time_ to be 03:00 and return the interval containing the
1657 * Returns: the interval containing @time_, never -1
1662 g_time_zone_adjust_time (GTimeZone *tz,
1669 if (tz->transitions == NULL)
1672 intervals = tz->transitions->len;
1674 /* find the interval containing *time UTC
1675 * TODO: this could be binary searched (or better) */
1676 for (i = 0; i <= intervals; i++)
1677 if (*time_ <= interval_end (tz, i))
1680 g_assert (interval_start (tz, i) <= *time_ && *time_ <= interval_end (tz, i));
1682 if (type != G_TIME_TYPE_UNIVERSAL)
1684 if (*time_ < interval_local_start (tz, i))
1685 /* if time came before the start of this interval... */
1689 /* if it's not in the previous interval... */
1690 if (*time_ > interval_local_end (tz, i))
1692 /* it doesn't exist. fast-forward it. */
1694 *time_ = interval_local_start (tz, i);
1698 else if (*time_ > interval_local_end (tz, i))
1699 /* if time came after the end of this interval... */
1703 /* if it's not in the next interval... */
1704 if (*time_ < interval_local_start (tz, i))
1705 /* it doesn't exist. fast-forward it. */
1706 *time_ = interval_local_start (tz, i);
1709 else if (interval_isdst (tz, i) != type)
1710 /* it's in this interval, but dst flag doesn't match.
1711 * check neighbours for a better fit. */
1713 if (i && *time_ <= interval_local_end (tz, i - 1))
1716 else if (i < intervals &&
1717 *time_ >= interval_local_start (tz, i + 1))
1726 * g_time_zone_find_interval:
1728 * @type: the #GTimeType of @time_
1729 * @time_: a number of seconds since January 1, 1970
1731 * Finds an the interval within @tz that corresponds to the given @time_.
1732 * The meaning of @time_ depends on @type.
1734 * If @type is %G_TIME_TYPE_UNIVERSAL then this function will always
1735 * succeed (since universal time is monotonic and continuous).
1737 * Otherwise @time_ is treated is local time. The distinction between
1738 * %G_TIME_TYPE_STANDARD and %G_TIME_TYPE_DAYLIGHT is ignored except in
1739 * the case that the given @time_ is ambiguous. In Toronto, for example,
1740 * 01:30 on November 7th 2010 occurred twice (once inside of daylight
1741 * savings time and the next, an hour later, outside of daylight savings
1742 * time). In this case, the different value of @type would result in a
1743 * different interval being returned.
1745 * It is still possible for this function to fail. In Toronto, for
1746 * example, 02:00 on March 14th 2010 does not exist (due to the leap
1747 * forward to begin daylight savings time). -1 is returned in that
1750 * Returns: the interval containing @time_, or -1 in case of failure
1755 g_time_zone_find_interval (GTimeZone *tz,
1762 if (tz->transitions == NULL)
1764 intervals = tz->transitions->len;
1765 for (i = 0; i <= intervals; i++)
1766 if (time_ <= interval_end (tz, i))
1769 if (type == G_TIME_TYPE_UNIVERSAL)
1772 if (time_ < interval_local_start (tz, i))
1774 if (time_ > interval_local_end (tz, --i))
1778 else if (time_ > interval_local_end (tz, i))
1780 if (time_ < interval_local_start (tz, ++i))
1784 else if (interval_isdst (tz, i) != type)
1786 if (i && time_ <= interval_local_end (tz, i - 1))
1789 else if (i < intervals && time_ >= interval_local_start (tz, i + 1))
1796 /* Public API accessors {{{1 */
1799 * g_time_zone_get_abbreviation:
1801 * @interval: an interval within the timezone
1803 * Determines the time zone abbreviation to be used during a particular
1804 * @interval of time in the time zone @tz.
1806 * For example, in Toronto this is currently "EST" during the winter
1807 * months and "EDT" during the summer months when daylight savings time
1810 * Returns: the time zone abbreviation, which belongs to @tz
1815 g_time_zone_get_abbreviation (GTimeZone *tz,
1818 g_return_val_if_fail (interval_valid (tz, (guint)interval), NULL);
1820 return interval_abbrev (tz, (guint)interval);
1824 * g_time_zone_get_offset:
1826 * @interval: an interval within the timezone
1828 * Determines the offset to UTC in effect during a particular @interval
1829 * of time in the time zone @tz.
1831 * The offset is the number of seconds that you add to UTC time to
1832 * arrive at local time for @tz (ie: negative numbers for time zones
1833 * west of GMT, positive numbers for east).
1835 * Returns: the number of seconds that should be added to UTC to get the
1841 g_time_zone_get_offset (GTimeZone *tz,
1844 g_return_val_if_fail (interval_valid (tz, (guint)interval), 0);
1846 return interval_offset (tz, (guint)interval);
1850 * g_time_zone_is_dst:
1852 * @interval: an interval within the timezone
1854 * Determines if daylight savings time is in effect during a particular
1855 * @interval of time in the time zone @tz.
1857 * Returns: %TRUE if daylight savings time is in effect
1862 g_time_zone_is_dst (GTimeZone *tz,
1865 g_return_val_if_fail (interval_valid (tz, interval), FALSE);
1867 if (tz->transitions == NULL)
1870 return interval_isdst (tz, (guint)interval);
1874 /* vim:set foldmethod=marker: */