LCOV - code coverage report
Current view: top level - glib/glib - gtimezone.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 649 731 88.8 %
Date: 2024-04-16 05:15:53 Functions: 46 46 100.0 %
Branches: 346 506 68.4 %

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

Generated by: LCOV version 1.14