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