Coverage Report

Created: 2025-07-01 06:23

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