Coverage Report

Created: 2025-08-29 06:48

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