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;
159 TimeZoneDate dlt_start;
160 TimeZoneDate dlt_end;
161 const gchar *std_name;
162 const gchar *dlt_name;
166 /* GTimeZone structure and lifecycle {{{1 */
175 G_LOCK_DEFINE_STATIC (time_zones);
176 static GHashTable/*<string?, GTimeZone>*/ *time_zones;
178 #define MIN_TZYEAR 1900
179 #define MAX_TZYEAR 2038
185 * Decreases the reference count on @tz.
190 g_time_zone_unref (GTimeZone *tz)
195 ref_count = g_atomic_int_get (&tz->ref_count);
197 g_assert (ref_count > 0);
201 if (tz->name != NULL)
205 /* someone else might have grabbed a ref in the meantime */
206 if G_UNLIKELY (g_atomic_int_get (&tz->ref_count) != 1)
208 G_UNLOCK(time_zones);
212 g_hash_table_remove (time_zones, tz->name);
213 G_UNLOCK(time_zones);
216 g_array_free (tz->t_info, TRUE);
217 if (tz->transitions != NULL)
218 g_array_free (tz->transitions, TRUE);
221 g_slice_free (GTimeZone, tz);
224 else if G_UNLIKELY (!g_atomic_int_compare_and_exchange (&tz->ref_count,
234 * Increases the reference count on @tz.
236 * Returns: a new reference to @tz.
241 g_time_zone_ref (GTimeZone *tz)
243 g_assert (tz->ref_count > 0);
245 g_atomic_int_inc (&tz->ref_count);
250 /* fake zoneinfo creation (for RFC3339/ISO 8601 timezones) {{{1 */
252 * parses strings of the form h or hh[[:]mm[[[:]ss]]] where:
258 parse_time (const gchar *time_,
261 if (*time_ < '0' || '9' < *time_)
264 *offset = 60 * 60 * (*time_++ - '0');
271 if (*time_ < '0' || '9' < *time_)
275 *offset += 60 * 60 * (*time_++ - '0');
277 if (*offset > 23 * 60 * 60)
287 if (*time_ < '0' || '5' < *time_)
290 *offset += 10 * 60 * (*time_++ - '0');
292 if (*time_ < '0' || '9' < *time_)
295 *offset += 60 * (*time_++ - '0');
303 if (*time_ < '0' || '5' < *time_)
306 *offset += 10 * (*time_++ - '0');
308 if (*time_ < '0' || '9' < *time_)
311 *offset += *time_++ - '0';
313 return *time_ == '\0';
317 parse_constant_offset (const gchar *name,
320 if (g_strcmp0 (name, "UTC") == 0)
326 if (*name >= '0' && '9' >= *name)
327 return parse_time (name, offset);
336 return parse_time (name, offset);
339 if (parse_time (name, offset))
351 zone_for_constant_offset (GTimeZone *gtz, const gchar *name)
356 if (name == NULL || !parse_constant_offset (name, &offset))
359 info.gmt_offset = offset;
361 info.is_standard = TRUE;
363 info.abbrev = g_strdup (name);
366 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), 1);
367 g_array_append_val (gtz->t_info, info);
369 /* Constant offset, no transitions */
370 gtz->transitions = NULL;
375 zone_info_unix (const gchar *identifier)
378 GMappedFile *file = NULL;
379 GBytes *zoneinfo = NULL;
381 /* identifier can be a relative or absolute path name;
382 if relative, it is interpreted starting from /usr/share/zoneinfo
383 while the POSIX standard says it should start with :,
384 glibc allows both syntaxes, so we should too */
385 if (identifier != NULL)
389 tzdir = getenv ("TZDIR");
391 tzdir = "/usr/share/zoneinfo";
393 if (*identifier == ':')
396 if (g_path_is_absolute (identifier))
397 filename = g_strdup (identifier);
399 filename = g_build_filename (tzdir, identifier, NULL);
402 filename = g_strdup ("/etc/localtime");
404 file = g_mapped_file_new (filename, FALSE, NULL);
407 zoneinfo = g_bytes_new_with_free_func (g_mapped_file_get_contents (file),
408 g_mapped_file_get_length (file),
409 (GDestroyNotify)g_mapped_file_unref,
410 g_mapped_file_ref (file));
411 g_mapped_file_unref (file);
418 init_zone_from_iana_info (GTimeZone *gtz, GBytes *zoneinfo)
422 guint32 time_count, type_count, leap_count, isgmt_count;
423 guint32 isstd_count, char_count ;
424 gpointer tz_transitions, tz_type_index, tz_ttinfo;
425 gpointer tz_leaps, tz_isgmt, tz_isstd;
427 guint timesize = sizeof (gint32), countsize = sizeof (gint32);
428 const struct tzhead *header = g_bytes_get_data (zoneinfo, &size);
430 g_return_if_fail (size >= sizeof (struct tzhead) &&
431 memcmp (header, "TZif", 4) == 0);
433 if (header->tzh_version == '2')
435 /* Skip ahead to the newer 64-bit data if it's available. */
436 header = (const struct tzhead *)
437 (((const gchar *) (header + 1)) +
438 guint32_from_be(header->tzh_ttisgmtcnt) +
439 guint32_from_be(header->tzh_ttisstdcnt) +
440 8 * guint32_from_be(header->tzh_leapcnt) +
441 5 * guint32_from_be(header->tzh_timecnt) +
442 6 * guint32_from_be(header->tzh_typecnt) +
443 guint32_from_be(header->tzh_charcnt));
444 timesize = sizeof (gint64);
446 time_count = guint32_from_be(header->tzh_timecnt);
447 type_count = guint32_from_be(header->tzh_typecnt);
448 leap_count = guint32_from_be(header->tzh_leapcnt);
449 isgmt_count = guint32_from_be(header->tzh_ttisgmtcnt);
450 isstd_count = guint32_from_be(header->tzh_ttisstdcnt);
451 char_count = guint32_from_be(header->tzh_charcnt);
453 g_assert (type_count == isgmt_count);
454 g_assert (type_count == isstd_count);
456 tz_transitions = (gpointer)(header + 1);
457 tz_type_index = tz_transitions + timesize * time_count;
458 tz_ttinfo = tz_type_index + time_count;
459 tz_abbrs = tz_ttinfo + sizeof (struct ttinfo) * type_count;
460 tz_leaps = tz_abbrs + char_count;
461 tz_isstd = tz_leaps + (timesize + countsize) * leap_count;
462 tz_isgmt = tz_isstd + isstd_count;
464 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo),
466 gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition),
469 for (index = 0; index < type_count; index++)
471 TransitionInfo t_info;
472 struct ttinfo info = ((struct ttinfo*)tz_ttinfo)[index];
473 t_info.gmt_offset = gint32_from_be (info.tt_gmtoff);
474 t_info.is_dst = info.tt_isdst ? TRUE : FALSE;
475 t_info.is_standard = ((guint8*)tz_isstd)[index] ? TRUE : FALSE;
476 t_info.is_gmt = ((guint8*)tz_isgmt)[index] ? TRUE : FALSE;
477 t_info.abbrev = g_strdup (&tz_abbrs[info.tt_abbrind]);
478 g_array_append_val (gtz->t_info, t_info);
481 for (index = 0; index < time_count; index++)
484 if (header->tzh_version == '2')
485 trans.time = gint64_from_be (((gint64_be*)tz_transitions)[index]);
487 trans.time = gint32_from_be (((gint32_be*)tz_transitions)[index]);
488 trans.info_index = ((guint8*)tz_type_index)[index];
489 g_assert (trans.info_index >= 0);
490 g_assert (trans.info_index < gtz->t_info->len);
491 g_array_append_val (gtz->transitions, trans);
493 g_bytes_unref (zoneinfo);
496 #elif defined (G_OS_WIN32)
498 /* UTC = local time + bias while local time = UTC + offset */
500 rule_from_windows_time_zone_info (TimeZoneRule *rules,
504 SYSTEMTIME StandardDate,
505 SYSTEMTIME DaylightDate)
508 if (StandardDate.wMonth)
510 rules->std_offset = -(Bias + StandardBias) * 60;
511 rules->dlt_offset = -(Bias + DaylightBias) * 60;
513 rules->dlt_start.sec = DaylightDate.wSecond;
514 rules->dlt_start.min = DaylightDate.wMinute;
515 rules->dlt_start.hour = DaylightDate.wHour;
516 rules->dlt_start.mon = DaylightDate.wMonth;
517 rules->dlt_start.year = DaylightDate.wYear;
518 rules->dlt_start.wday = DaylightDate.wDayOfWeek? DaylightDate.wDayOfWeek : 7;
520 if (DaylightDate.wYear)
522 rules->dlt_start.mday = DaylightDate.wDay;
523 rules->dlt_start.wday = 0;
526 rules->dlt_start.week = DaylightDate.wDay;
528 rules->dlt_start.isstd = FALSE;
529 rules->dlt_start.isgmt = FALSE;
531 rules->dlt_end.sec = StandardDate.wSecond;
532 rules->dlt_end.min = StandardDate.wMinute;
533 rules->dlt_end.hour = StandardDate.wHour;
534 rules->dlt_end.mday = StandardDate.wDay;
535 rules->dlt_end.mon = StandardDate.wMonth;
536 rules->dlt_end.year = StandardDate.wYear;
537 rules->dlt_end.wday = StandardDate.wDayOfWeek? StandardDate.wDayOfWeek : 7;
539 if (StandardDate.wYear)
541 rules->dlt_end.mday = StandardDate.wDay;
542 rules->dlt_end.wday = 0;
545 rules->dlt_end.week = StandardDate.wDay;
547 rules->dlt_end.isstd = FALSE;
548 rules->dlt_end.isgmt = FALSE;
553 rules->std_offset = -Bias * 60;
555 rules->dlt_start.mon = 0;
560 rules_from_windows_time_zone (const gchar *identifier,
561 TimeZoneRule **rules,
567 gchar *subkey, *subkey_dynamic;
575 SYSTEMTIME StandardDate;
576 SYSTEMTIME DaylightDate;
589 subkey = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
591 if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0,
592 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
595 if (RegQueryValueExA (key, "TimeZoneKeyName", NULL, NULL,
596 NULL, &size) == ERROR_SUCCESS)
598 key_name = g_malloc (size);
600 if (RegQueryValueExA (key, "TimeZoneKeyName", NULL, NULL,
601 (LPBYTE) key_name, &size) != ERROR_SUCCESS)
612 key_name = g_strdup (identifier);
617 subkey = g_strconcat ("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\",
621 subkey_dynamic = g_strconcat (subkey, "\\Dynamic DST", NULL);
623 if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey_dynamic, 0,
624 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
631 if (RegQueryValueExA (key, "FirstEntry", NULL, NULL,
632 (LPBYTE) &first, &size) != ERROR_SUCCESS)
636 if (RegQueryValueExA (key, "LastEntry", NULL, NULL,
637 (LPBYTE) &last, &size) != ERROR_SUCCESS)
640 *rules_num = last - first + 2;
641 *rules = g_new0 (TimeZoneRule, *rules_num);
643 for (year = first, i = 0; year <= last; year++)
645 s = g_strdup_printf ("%d", year);
648 if (RegQueryValueExA (key, s, NULL, NULL,
649 (LPBYTE) &tzi, &size) != ERROR_SUCCESS)
658 if (year > first && memcmp (&tzi_prev, &tzi, sizeof tzi) == 0)
661 memcpy (&tzi_prev, &tzi, sizeof tzi);
663 rule_from_windows_time_zone_info (&(*rules)[i], tzi.Bias,
664 tzi.StandardBias, tzi.DaylightBias,
665 tzi.StandardDate, tzi.DaylightDate);
667 (*rules)[i++].start_year = year;
675 else if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, subkey, 0,
676 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
679 if (RegQueryValueExA (key, "TZI", NULL, NULL,
680 (LPBYTE) &tzi, &size) == ERROR_SUCCESS)
683 *rules = g_new0 (TimeZoneRule, 2);
685 rule_from_windows_time_zone_info (&(*rules)[0], tzi.Bias,
686 tzi.StandardBias, tzi.DaylightBias,
687 tzi.StandardDate, tzi.DaylightDate);
693 g_free (subkey_dynamic);
699 (*rules)[0].start_year = MIN_TZYEAR;
700 if ((*rules)[*rules_num - 2].start_year < MAX_TZYEAR)
701 (*rules)[*rules_num - 1].start_year = MAX_TZYEAR;
703 (*rules)[*rules_num - 1].start_year = (*rules)[*rules_num - 2].start_year + 1;
714 find_relative_date (TimeZoneDate *buffer,
722 /* Get last day if last is needed, first day otherwise */
723 dt = g_date_time_new (tz,
725 buffer->mon + (buffer->week < 5? 0 : 1),
726 buffer->week < 5? 1 : 0,
727 buffer->hour, buffer->min, buffer->sec);
729 buffer->wday = g_date_time_get_day_of_week (dt);
730 buffer->mday = g_date_time_get_day_of_month (dt);
732 if (buffer->week < 5)
734 if (wday < buffer->wday)
737 buffer->mday += (buffer->week - 1) * 7;
740 else if (wday > buffer->wday)
743 buffer->mday += wday - buffer->wday;
746 g_date_time_unref (dt);
749 /* Offset is previous offset of local time */
751 boundary_for_year (TimeZoneDate *boundary,
767 else if (boundary->isstd)
770 offset = prev_offset;
772 G_UNLOCK (time_zones);
774 identifier = g_strdup_printf ("%+03d:%02d:%02d",
776 (int) abs (offset / 60) % 60,
777 (int) abs (offset) % 3600);
778 tz = g_time_zone_new (identifier);
781 if (boundary->year == 0)
786 find_relative_date (&buffer, tz);
789 g_assert (buffer.year == year);
791 dt = g_date_time_new (tz,
792 buffer.year, buffer.mon, buffer.mday,
793 buffer.hour, buffer.min, buffer.sec);
794 t = g_date_time_to_unix (dt);
795 g_date_time_unref (dt);
797 g_time_zone_unref (tz);
805 init_zone_from_rules (GTimeZone *gtz,
809 TransitionInfo info[2];
811 gint type_count, trans_count;
818 /* Last rule only contains max year */
819 for (i = 0; i < rules_num - 1; i++)
821 if (rules[i].dlt_start.mon)
824 trans_count += 2 * (rules[i+1].start_year - rules[i].start_year);
833 /* If standard time happens before daylight time in first rule
834 * with daylight, skip first transition so the minimum is in
835 * standard time and the first transition is in daylight time */
836 for (i = 0; i < rules_num - 1 && rules[0].dlt_start.mon == 0; i++);
838 if (i < rules_num -1 && rules[i].dlt_start.mon > 0 &&
839 rules[i].dlt_start.mon > rules[i].dlt_end.mon)
845 gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), type_count);
846 gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition), trans_count);
848 last_offset = rules[0].std_offset;
850 for (i = 0; i < rules_num - 1; i++)
852 if (rules[i].dlt_start.mon)
855 info[0].gmt_offset = rules[i].std_offset;
856 info[0].is_dst = FALSE;
857 info[0].is_standard = rules[i].dlt_end.isstd;
858 info[0].is_gmt = rules[i].dlt_end.isgmt;
860 if (rules[i].std_name)
861 info[0].abbrev = g_strdup (rules[i].std_name);
864 info[0].abbrev = g_strdup_printf ("%+03d%02d",
865 (int) rules[i].std_offset / 3600,
866 (int) abs (rules[i].std_offset / 60) % 60);
870 info[1].gmt_offset = rules[i].dlt_offset;
871 info[1].is_dst = TRUE;
872 info[1].is_standard = rules[i].dlt_start.isstd;
873 info[1].is_gmt = rules[i].dlt_start.isgmt;
875 if (rules[i].dlt_name)
876 info[1].abbrev = g_strdup (rules[i].dlt_name);
879 info[1].abbrev = g_strdup_printf ("%+03d%02d",
880 (int) rules[i].dlt_offset / 3600,
881 (int) abs (rules[i].dlt_offset / 60) % 60);
883 if (rules[i].dlt_start.mon < rules[i].dlt_end.mon)
885 g_array_append_val (gtz->t_info, info[1]);
886 g_array_append_val (gtz->t_info, info[0]);
890 g_array_append_val (gtz->t_info, info[0]);
891 g_array_append_val (gtz->t_info, info[1]);
894 /* Transition dates */
895 for (year = rules[i].start_year; year < rules[i+1].start_year; year++)
897 if (rules[i].dlt_start.mon < rules[i].dlt_end.mon)
900 trans.info_index = y;
901 trans.time = boundary_for_year (&rules[i].dlt_start, year,
902 last_offset, rules[i].std_offset);
903 g_array_insert_val (gtz->transitions, x++, trans);
904 last_offset = rules[i].dlt_offset;
907 trans.info_index = y+1;
908 trans.time = boundary_for_year (&rules[i].dlt_end, year,
909 last_offset, rules[i].std_offset);
910 g_array_insert_val (gtz->transitions, x++, trans);
911 last_offset = rules[i].std_offset;
916 trans.info_index = y;
917 trans.time = boundary_for_year (&rules[i].dlt_end, year,
918 last_offset, rules[i].std_offset);
920 g_array_insert_val (gtz->transitions, x++, trans);
923 last_offset = rules[i].std_offset;
926 trans.info_index = y+1;
927 trans.time = boundary_for_year (&rules[i].dlt_start, year,
928 last_offset, rules[i].std_offset);
929 g_array_insert_val (gtz->transitions, x++, trans);
930 last_offset = rules[i].dlt_offset;
939 info[0].gmt_offset = rules[i].std_offset;
940 info[0].is_dst = FALSE;
941 info[0].is_standard = FALSE;
942 info[0].is_gmt = FALSE;
944 if (rules[i].std_name)
945 info[0].abbrev = g_strdup (rules[i].std_name);
948 info[0].abbrev = g_strdup_printf ("%+03d%02d",
949 (int) rules[i].std_offset / 3600,
950 (int) abs (rules[i].std_offset / 60) % 60);
952 g_array_append_val (gtz->t_info, info[0]);
954 last_offset = rules[i].std_offset;
962 * parses date[/time] for parsing TZ environment variable
964 * date is either Mm.w.d, Jn or N
971 * time is either h or hh[[:]mm[[[:]ss]]]
977 parse_tz_boundary (const gchar *identifier,
978 TimeZoneDate *boundary)
981 gint month, week, day;
986 if (*pos == 'M') /* Relative date */
990 if (*pos == '\0' || *pos < '0' || '9' < *pos)
993 month = *pos++ - '0';
995 if ((month == 1 && *pos >= '0' && '2' >= *pos) ||
996 (month == 0 && *pos >= '0' && '9' >= *pos))
999 month += *pos++ - '0';
1002 if (*pos++ != '.' || month == 0)
1005 if (*pos == '\0' || *pos < '1' || '5' < *pos)
1008 week = *pos++ - '0';
1013 if (*pos == '\0' || *pos < '0' || '6' < *pos)
1022 boundary->mon = month;
1023 boundary->week = week;
1024 boundary->wday = day;
1027 else if (*pos == 'J') /* Julian day */
1032 while (*pos >= '0' && '9' >= *pos)
1035 day += *pos++ - '0';
1038 if (day < 1 || 365 < day)
1041 date = g_date_new_julian (day);
1043 boundary->mon = (int) g_date_get_month (date);
1044 boundary->mday = (int) g_date_get_day (date);
1049 else if (*pos >= '0' && '9' >= *pos) /* Zero-based Julian day */
1052 while (*pos >= '0' && '9' >= *pos)
1055 day += *pos++ - '0';
1058 if (day < 0 || 365 < day)
1061 date = g_date_new_julian (day >= 59? day : day + 1);
1063 boundary->mon = (int) g_date_get_month (date);
1064 boundary->mday = (int) g_date_get_day (date);
1077 boundary->isstd = FALSE;
1078 boundary->isgmt = FALSE;
1084 if (!parse_time (++pos, &offset))
1087 boundary->hour = offset / 3600;
1088 boundary->min = (offset / 60) % 60;
1089 boundary->sec = offset % 3600;
1100 return *pos == '\0';
1105 * Creates an array of TimeZoneRule from a TZ environment variable
1106 * type of identifier. Should free rules, std_name and dlt_name
1110 rules_from_identifier (const gchar *identifier,
1111 TimeZoneRule **rules,
1116 const gchar *std_name_pos, *std_offset_pos;
1117 const gchar *dlt_name_pos, *dlt_offset_pos;
1119 const gchar *start_date_pos, *end_date_pos;
1125 gint32 std_offset, dlt_offset;
1126 TimeZoneDate dlt_start, dlt_end;
1136 while ((*pos >= 'a' && 'z' >= *pos) || (*pos >= 'A' && 'Z' >= *pos))
1139 /* Offset for standard required (format 1) */
1143 /* Name should be three or more alphabetic characters */
1144 if (pos - identifier < 3)
1147 std_offset_pos = pos;
1149 /* Standard offset */
1150 while (*pos == '+' || *pos == '-' || *pos == ':' || (*pos >= '0' && '9' >= *pos))
1153 buffer = g_strndup (std_offset_pos, pos - std_offset_pos);
1154 ret = parse_constant_offset (buffer, &std_offset);
1161 dlt_offset_pos = NULL;
1167 while ((*pos >= 'a' && 'z' >= *pos) || (*pos >= 'A' && 'Z' >= *pos))
1170 /* Name should be three or more alphabetic characters */
1171 if (pos - identifier < 3)
1174 dlt_offset_pos = pos;
1177 /* Start and end required (format 2) */
1185 /* Default offset is 1 hour less from standard offset */
1187 dlt_offset = std_offset - 60 * 60;
1191 /* Daylight offset */
1192 while (*pos == '+' || *pos == '-' || *pos == ':' || (*pos >= '0' && '9' >= *pos))
1195 buffer = g_strndup (dlt_offset_pos, pos - dlt_offset_pos);
1196 ret = parse_constant_offset (buffer, &dlt_offset);
1202 /* Start and end required (format 2) */
1208 start_date_pos = pos;
1210 while (*pos != ',' && *pos != '\0')
1213 /* End required (format 2) */
1217 buffer = g_strndup (start_date_pos, pos++ - start_date_pos);
1218 ret = parse_tz_boundary (buffer, &dlt_start);
1227 while (*pos != '\0')
1230 buffer = g_strndup (end_date_pos, pos - end_date_pos);
1231 ret = parse_tz_boundary (buffer, &dlt_end);
1243 *std_name = g_strndup (std_name_pos, std_offset_pos - std_name_pos);
1245 if (dlt_name_pos != pos)
1246 *dlt_name = g_strndup (dlt_name_pos, dlt_offset_pos - dlt_name_pos);
1255 /* If doesn't have offset for daylight then it is Windows format */
1256 if (dlt_offset_pos == pos)
1260 /* Use US rules, Windows' default is Pacific Standard Time */
1261 dlt_offset = std_offset - 60 * 60;
1263 if (rules_from_windows_time_zone ("Pacific Standard Time", rules, rules_num, NULL, NULL))
1265 for (i = 0; i < *rules_num - 1; i++)
1267 (*rules)[i].std_offset = -std_offset;
1268 (*rules)[i].dlt_offset = -dlt_offset;
1269 (*rules)[i].std_name = *std_name;
1270 (*rules)[i].dlt_name = *dlt_name;
1282 *rules = g_new0 (TimeZoneRule, 2);
1284 (*rules)[0].start_year = MIN_TZYEAR;
1285 (*rules)[1].start_year = MAX_TZYEAR;
1287 (*rules)[0].std_offset = -std_offset;
1288 (*rules)[0].dlt_offset = -dlt_offset;
1289 (*rules)[0].dlt_start = dlt_start;
1290 (*rules)[0].dlt_end = dlt_end;
1291 (*rules)[0].std_name = *std_name;
1292 (*rules)[0].dlt_name = *dlt_name;
1297 /* Construction {{{1 */
1300 * @identifier: (allow-none): a timezone identifier
1302 * Creates a #GTimeZone corresponding to @identifier.
1304 * @identifier can either be an RFC3339/ISO 8601 time offset or
1305 * something that would pass as a valid value for the
1306 * <varname>TZ</varname> environment variable (including %NULL).
1308 * Valid RFC3339 time offsets are <literal>"Z"</literal> (for UTC) or
1309 * <literal>"±hh:mm"</literal>. ISO 8601 additionally specifies
1310 * <literal>"±hhmm"</literal> and <literal>"±hh"</literal>. Offsets are
1311 * time values to be added to Coordinated Universal Time (UTC) to get
1314 * In Unix, the <varname>TZ</varname> environment variable typically
1315 * corresponds to the name of a file in the zoneinfo database, or
1316 * string in "std offset [dst [offset],start[/time],end[/time]]"
1317 * (POSIX) format. There are no spaces in the specification. The
1318 * name of standard and daylight savings time zone must be three or more
1319 * alphabetic characters. Offsets are time values to be added to local
1320 * time to get Coordinated Universal Time (UTC) and should be
1321 * <literal>"[±]hh[[:]mm[:ss]]"</literal>. Dates are either
1322 * <literal>"Jn"</literal> (Julian day with n between 1 and 365, leap
1323 * years not counted), <literal>"n"</literal> (zero-based Julian day
1324 * with n between 0 and 365) or <literal>"Mm.w.d"</literal> (day d
1325 * (0 <= d <= 6) of week w (1 <= w <= 5) of month m (1 <= m <= 12), day
1326 * 0 is a Sunday). Times are in local wall clock time, the default is
1329 * In Windows, the "tzn[+|–]hh[:mm[:ss]][dzn]" format is used, but also
1330 * accepts POSIX format. The Windows format uses US rules for all time
1331 * zones; daylight savings time is 60 minutes behind the standard time
1332 * with date and time of change taken from Pacific Standard Time.
1333 * Offsets are time values to be added to the local time to get
1334 * Coordinated Universal Time (UTC).
1336 * g_time_zone_new_local() calls this function with the value of the
1337 * <varname>TZ</varname> environment variable. This function itself is
1338 * independent of the value of <varname>TZ</varname>, but if @identifier
1339 * is %NULL then <filename>/etc/localtime</filename> will be consulted
1340 * to discover the correct timezone.
1342 * If intervals are not available, only time zone rules from
1343 * <varname>TZ</varname> environment variable or other means, then they
1344 * will be computed from year 1900 to 2037. If the maximum year for the
1345 * rules is available and it is greater than 2037, then it will followed
1349 * url='http://tools.ietf.org/html/rfc3339#section-5.6'>RFC3339
1350 * §5.6</ulink> for a precise definition of valid RFC3339 time offsets
1351 * (the <varname>time-offset</varname> expansion) and ISO 8601 for the
1352 * full list of valid time offsets. See <ulink
1353 * url='http://www.gnu.org/s/libc/manual/html_node/TZ-Variable.html'>The
1354 * GNU C Library manual</ulink> for an explanation of the possible
1355 * values of the <varname>TZ</varname> environment variable.
1357 * You should release the return value by calling g_time_zone_unref()
1358 * when you are done with it.
1360 * Returns: the requested timezone
1365 g_time_zone_new (const gchar *identifier)
1367 GTimeZone *tz = NULL;
1368 TimeZoneRule *rules;
1370 gchar *std_name, *dlt_name;
1372 G_LOCK (time_zones);
1373 if (time_zones == NULL)
1374 time_zones = g_hash_table_new (g_str_hash, g_str_equal);
1378 tz = g_hash_table_lookup (time_zones, identifier);
1381 g_atomic_int_inc (&tz->ref_count);
1382 G_UNLOCK (time_zones);
1387 tz = g_slice_new0 (GTimeZone);
1388 tz->name = g_strdup (identifier);
1391 zone_for_constant_offset (tz, identifier);
1393 if (tz->t_info == NULL &&
1394 rules_from_identifier (identifier,
1396 &std_name, &dlt_name))
1398 init_zone_from_rules (tz, rules, rules_num);
1404 if (tz->t_info == NULL)
1407 GBytes *zoneinfo = zone_info_unix (identifier);
1409 zone_for_constant_offset (tz, "UTC");
1412 init_zone_from_iana_info (tz, zoneinfo);
1413 g_bytes_unref (zoneinfo);
1415 #elif defined G_OS_WIN32
1419 if (tz->t_info != NULL)
1422 g_hash_table_insert (time_zones, tz->name, tz);
1424 g_atomic_int_inc (&tz->ref_count);
1425 G_UNLOCK (time_zones);
1431 * g_time_zone_new_utc:
1433 * Creates a #GTimeZone corresponding to UTC.
1435 * This is equivalent to calling g_time_zone_new() with a value like
1436 * "Z", "UTC", "+00", etc.
1438 * You should release the return value by calling g_time_zone_unref()
1439 * when you are done with it.
1441 * Returns: the universal timezone
1446 g_time_zone_new_utc (void)
1448 return g_time_zone_new ("UTC");
1452 * g_time_zone_new_local:
1454 * Creates a #GTimeZone corresponding to local time. The local time
1455 * zone may change between invocations to this function; for example,
1456 * if the system administrator changes it.
1458 * This is equivalent to calling g_time_zone_new() with the value of the
1459 * <varname>TZ</varname> environment variable (including the possibility
1462 * You should release the return value by calling g_time_zone_unref()
1463 * when you are done with it.
1465 * Returns: the local timezone
1470 g_time_zone_new_local (void)
1472 return g_time_zone_new (getenv ("TZ"));
1475 #define TRANSITION(n) g_array_index (tz->transitions, Transition, n)
1476 #define TRANSITION_INFO(n) g_array_index (tz->t_info, TransitionInfo, n)
1478 /* Internal helpers {{{1 */
1479 /* Note that interval 0 is *before* the first transition time, so
1480 * interval 1 gets transitions[0].
1482 inline static const TransitionInfo*
1483 interval_info (GTimeZone *tz,
1487 g_return_val_if_fail (tz->t_info != NULL, NULL);
1488 if (interval && tz->transitions && interval <= tz->transitions->len)
1489 index = (TRANSITION(interval - 1)).info_index;
1492 return &(TRANSITION_INFO(index));
1495 inline static gint64
1496 interval_start (GTimeZone *tz,
1499 if (!interval || tz->transitions == NULL || tz->transitions->len == 0)
1501 if (interval > tz->transitions->len)
1502 interval = tz->transitions->len;
1503 return (TRANSITION(interval - 1)).time;
1506 inline static gint64
1507 interval_end (GTimeZone *tz,
1510 if (tz->transitions && interval < tz->transitions->len)
1511 return (TRANSITION(interval)).time - 1;
1515 inline static gint32
1516 interval_offset (GTimeZone *tz,
1519 g_return_val_if_fail (tz->t_info != NULL, 0);
1520 return interval_info (tz, interval)->gmt_offset;
1523 inline static gboolean
1524 interval_isdst (GTimeZone *tz,
1527 g_return_val_if_fail (tz->t_info != NULL, 0);
1528 return interval_info (tz, interval)->is_dst;
1532 inline static gboolean
1533 interval_isgmt (GTimeZone *tz,
1536 g_return_val_if_fail (tz->t_info != NULL, 0);
1537 return interval_info (tz, interval)->is_gmt;
1540 inline static gboolean
1541 interval_isstandard (GTimeZone *tz,
1544 return interval_info (tz, interval)->is_standard;
1547 inline static gchar*
1548 interval_abbrev (GTimeZone *tz,
1551 g_return_val_if_fail (tz->t_info != NULL, 0);
1552 return interval_info (tz, interval)->abbrev;
1555 inline static gint64
1556 interval_local_start (GTimeZone *tz,
1560 return interval_start (tz, interval) + interval_offset (tz, interval);
1565 inline static gint64
1566 interval_local_end (GTimeZone *tz,
1569 if (tz->transitions && interval < tz->transitions->len)
1570 return interval_end (tz, interval) + interval_offset (tz, interval);
1576 interval_valid (GTimeZone *tz,
1579 if ( tz->transitions == NULL)
1580 return interval == 0;
1581 return interval <= tz->transitions->len;
1584 /* g_time_zone_find_interval() {{{1 */
1587 * g_time_zone_adjust_time:
1589 * @type: the #GTimeType of @time_
1590 * @time_: a pointer to a number of seconds since January 1, 1970
1592 * Finds an interval within @tz that corresponds to the given @time_,
1593 * possibly adjusting @time_ if required to fit into an interval.
1594 * The meaning of @time_ depends on @type.
1596 * This function is similar to g_time_zone_find_interval(), with the
1597 * difference that it always succeeds (by making the adjustments
1600 * In any of the cases where g_time_zone_find_interval() succeeds then
1601 * this function returns the same value, without modifying @time_.
1603 * This function may, however, modify @time_ in order to deal with
1604 * non-existent times. If the non-existent local @time_ of 02:30 were
1605 * requested on March 14th 2010 in Toronto then this function would
1606 * adjust @time_ to be 03:00 and return the interval containing the
1609 * Returns: the interval containing @time_, never -1
1614 g_time_zone_adjust_time (GTimeZone *tz,
1621 if (tz->transitions == NULL)
1624 intervals = tz->transitions->len;
1626 /* find the interval containing *time UTC
1627 * TODO: this could be binary searched (or better) */
1628 for (i = 0; i <= intervals; i++)
1629 if (*time_ <= interval_end (tz, i))
1632 g_assert (interval_start (tz, i) <= *time_ && *time_ <= interval_end (tz, i));
1634 if (type != G_TIME_TYPE_UNIVERSAL)
1636 if (*time_ < interval_local_start (tz, i))
1637 /* if time came before the start of this interval... */
1641 /* if it's not in the previous interval... */
1642 if (*time_ > interval_local_end (tz, i))
1644 /* it doesn't exist. fast-forward it. */
1646 *time_ = interval_local_start (tz, i);
1650 else if (*time_ > interval_local_end (tz, i))
1651 /* if time came after the end of this interval... */
1655 /* if it's not in the next interval... */
1656 if (*time_ < interval_local_start (tz, i))
1657 /* it doesn't exist. fast-forward it. */
1658 *time_ = interval_local_start (tz, i);
1661 else if (interval_isdst (tz, i) != type)
1662 /* it's in this interval, but dst flag doesn't match.
1663 * check neighbours for a better fit. */
1665 if (i && *time_ <= interval_local_end (tz, i - 1))
1668 else if (i < intervals &&
1669 *time_ >= interval_local_start (tz, i + 1))
1678 * g_time_zone_find_interval:
1680 * @type: the #GTimeType of @time_
1681 * @time_: a number of seconds since January 1, 1970
1683 * Finds an the interval within @tz that corresponds to the given @time_.
1684 * The meaning of @time_ depends on @type.
1686 * If @type is %G_TIME_TYPE_UNIVERSAL then this function will always
1687 * succeed (since universal time is monotonic and continuous).
1689 * Otherwise @time_ is treated is local time. The distinction between
1690 * %G_TIME_TYPE_STANDARD and %G_TIME_TYPE_DAYLIGHT is ignored except in
1691 * the case that the given @time_ is ambiguous. In Toronto, for example,
1692 * 01:30 on November 7th 2010 occurred twice (once inside of daylight
1693 * savings time and the next, an hour later, outside of daylight savings
1694 * time). In this case, the different value of @type would result in a
1695 * different interval being returned.
1697 * It is still possible for this function to fail. In Toronto, for
1698 * example, 02:00 on March 14th 2010 does not exist (due to the leap
1699 * forward to begin daylight savings time). -1 is returned in that
1702 * Returns: the interval containing @time_, or -1 in case of failure
1707 g_time_zone_find_interval (GTimeZone *tz,
1714 if (tz->transitions == NULL)
1716 intervals = tz->transitions->len;
1717 for (i = 0; i <= intervals; i++)
1718 if (time_ <= interval_end (tz, i))
1721 if (type == G_TIME_TYPE_UNIVERSAL)
1724 if (time_ < interval_local_start (tz, i))
1726 if (time_ > interval_local_end (tz, --i))
1730 else if (time_ > interval_local_end (tz, i))
1732 if (time_ < interval_local_start (tz, ++i))
1736 else if (interval_isdst (tz, i) != type)
1738 if (i && time_ <= interval_local_end (tz, i - 1))
1741 else if (i < intervals && time_ >= interval_local_start (tz, i + 1))
1748 /* Public API accessors {{{1 */
1751 * g_time_zone_get_abbreviation:
1753 * @interval: an interval within the timezone
1755 * Determines the time zone abbreviation to be used during a particular
1756 * @interval of time in the time zone @tz.
1758 * For example, in Toronto this is currently "EST" during the winter
1759 * months and "EDT" during the summer months when daylight savings time
1762 * Returns: the time zone abbreviation, which belongs to @tz
1767 g_time_zone_get_abbreviation (GTimeZone *tz,
1770 g_return_val_if_fail (interval_valid (tz, (guint)interval), NULL);
1772 return interval_abbrev (tz, (guint)interval);
1776 * g_time_zone_get_offset:
1778 * @interval: an interval within the timezone
1780 * Determines the offset to UTC in effect during a particular @interval
1781 * of time in the time zone @tz.
1783 * The offset is the number of seconds that you add to UTC time to
1784 * arrive at local time for @tz (ie: negative numbers for time zones
1785 * west of GMT, positive numbers for east).
1787 * Returns: the number of seconds that should be added to UTC to get the
1793 g_time_zone_get_offset (GTimeZone *tz,
1796 g_return_val_if_fail (interval_valid (tz, (guint)interval), 0);
1798 return interval_offset (tz, (guint)interval);
1802 * g_time_zone_is_dst:
1804 * @interval: an interval within the timezone
1806 * Determines if daylight savings time is in effect during a particular
1807 * @interval of time in the time zone @tz.
1809 * Returns: %TRUE if daylight savings time is in effect
1814 g_time_zone_is_dst (GTimeZone *tz,
1817 g_return_val_if_fail (interval_valid (tz, interval), FALSE);
1819 if (tz->transitions == NULL)
1822 return interval_isdst (tz, (guint)interval);
1826 /* vim:set foldmethod=marker: */