LCOV - code coverage report
Current view: top level - glib - gtimezone.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 83.5 % 1031 861
Test Date: 2026-07-14 05:12:09 Functions: 100.0 % 53 53
Branches: - 0 0

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

Generated by: LCOV version 2.0-1