LCOV - code coverage report
Current view: top level - glib - gtimezone.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 88.9 % 732 651
Test Date: 2024-11-05 05:22:59 Functions: 100.0 % 46 46
Branches: - 0 0

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

Generated by: LCOV version 2.0-1