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