1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* gweather-timezone.c - Timezone handling
4
* Copyright 2008, Red Hat, Inc.
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public License
8
* as published by the Free Software Foundation; either version 2.1 of
9
* the License, or (at your option) any later version.
11
* This library is distributed in the hope that it will be useful, but
12
* 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.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, see
18
* <http://www.gnu.org/licenses/>.
27
#define GWEATHER_I_KNOW_THIS_IS_UNSTABLE
28
#include "gweather-timezone.h"
30
#include "weather-priv.h"
33
* SECTION:gweathertimezone
34
* @Title: GWeatherTimezone
38
* There are no public methods for creating timezones; they can only
39
* be created by calling gweather_location_new_world() to parse
40
* Locations.xml, and then calling various #GWeatherLocation methods
41
* to extract relevant timezones from the location hierarchy.
44
struct _GWeatherTimezone {
46
int offset, dst_offset;
52
#define TZ_MAGIC "TZif"
53
#define TZ_HEADER_SIZE 44
54
#define TZ_TIMECNT_OFFSET 32
55
#define TZ_TRANSITIONS_OFFSET 44
57
#define TZ_TTINFO_SIZE 6
58
#define TZ_TTINFO_GMTOFF_OFFSET 0
59
#define TZ_TTINFO_ISDST_OFFSET 4
62
parse_tzdata (const char *tzname, time_t start, time_t end,
63
int *offset, gboolean *has_dst, int *dst_offset)
65
char *filename, *contents;
67
int timecnt, transitions_size, ttinfo_map_size;
68
int initial_transition = -1, second_transition = -1;
70
char *ttinfo_map, *ttinfos;
71
gint32 initial_offset, second_offset;
72
char initial_isdst, second_isdst;
75
filename = g_build_filename (ZONEINFO_DIR, tzname, NULL);
76
if (!g_file_get_contents (filename, &contents, &length, NULL)) {
82
if (length < TZ_HEADER_SIZE ||
83
strncmp (contents, TZ_MAGIC, strlen (TZ_MAGIC)) != 0) {
88
timecnt = GUINT32_FROM_BE (*(guint32 *)(contents + TZ_TIMECNT_OFFSET));
89
transitions = (void *)(contents + TZ_TRANSITIONS_OFFSET);
90
transitions_size = timecnt * sizeof (*transitions);
91
ttinfo_map = (void *)(contents + TZ_TRANSITIONS_OFFSET + transitions_size);
92
ttinfo_map_size = timecnt;
93
ttinfos = (void *)(ttinfo_map + ttinfo_map_size);
95
/* @transitions is an array of @timecnt time_t values. We need to
96
* find the transition into the current offset, which is the last
97
* transition before @start. If the following transition is before
98
* @end, then note that one too, since it presumably means we're
101
for (i = 1; i < timecnt && initial_transition == -1; i++) {
102
if (GINT32_FROM_BE (transitions[i]) > start) {
103
initial_transition = ttinfo_map[i - 1];
104
if (GINT32_FROM_BE (transitions[i]) < end)
105
second_transition = ttinfo_map[i];
108
if (initial_transition == -1) {
110
initial_transition = ttinfo_map[timecnt - 1];
112
initial_transition = 0;
115
/* Copy the data out of the corresponding ttinfo structs */
116
initial_offset = *(gint32 *)(ttinfos +
117
initial_transition * TZ_TTINFO_SIZE +
118
TZ_TTINFO_GMTOFF_OFFSET);
119
initial_offset = GINT32_FROM_BE (initial_offset);
120
initial_isdst = *(ttinfos +
121
initial_transition * TZ_TTINFO_SIZE +
122
TZ_TTINFO_ISDST_OFFSET);
124
if (second_transition != -1) {
125
second_offset = *(gint32 *)(ttinfos +
126
second_transition * TZ_TTINFO_SIZE +
127
TZ_TTINFO_GMTOFF_OFFSET);
128
second_offset = GINT32_FROM_BE (second_offset);
129
second_isdst = *(ttinfos +
130
second_transition * TZ_TTINFO_SIZE +
131
TZ_TTINFO_ISDST_OFFSET);
133
*has_dst = (initial_isdst != second_isdst);
138
*offset = initial_offset / 60;
141
*offset = second_offset / 60;
142
*dst_offset = initial_offset / 60;
144
*offset = initial_offset / 60;
145
*dst_offset = second_offset / 60;
153
static GWeatherTimezone *
154
parse_timezone (GWeatherParser *parser)
156
GWeatherTimezone *zone = NULL;
157
char *id = NULL, *name = NULL;
158
int offset = 0, dst_offset = 0;
159
gboolean has_dst = FALSE;
161
id = (char *) xmlTextReaderGetAttribute (parser->xml, (xmlChar *) "id");
163
xmlTextReaderNext (parser->xml);
167
if (!xmlTextReaderIsEmptyElement (parser->xml)) {
168
if (xmlTextReaderRead (parser->xml) != 1) {
173
while (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_END_ELEMENT) {
174
if (xmlTextReaderNodeType (parser->xml) != XML_READER_TYPE_ELEMENT) {
175
if (xmlTextReaderRead (parser->xml) != 1)
180
if (!strcmp ((const char *) xmlTextReaderConstName (parser->xml), "name"))
181
name = gweather_parser_get_localized_value (parser);
183
if (xmlTextReaderNext (parser->xml) != 1)
189
if (parse_tzdata (id, parser->year_start, parser->year_end,
190
&offset, &has_dst, &dst_offset)) {
191
zone = g_slice_new0 (GWeatherTimezone);
193
zone->id = g_strdup (id);
194
zone->name = g_strdup (name);
195
zone->offset = offset;
196
zone->has_dst = has_dst;
197
zone->dst_offset = dst_offset;
208
gweather_timezones_parse_xml (GWeatherParser *parser)
211
GWeatherTimezone *zone;
215
zones = g_ptr_array_new ();
217
if (xmlTextReaderRead (parser->xml) != 1)
219
while ((tagtype = xmlTextReaderNodeType (parser->xml)) !=
220
XML_READER_TYPE_END_ELEMENT) {
221
if (tagtype != XML_READER_TYPE_ELEMENT) {
222
if (xmlTextReaderRead (parser->xml) != 1)
227
tagname = (const char *) xmlTextReaderConstName (parser->xml);
229
if (!strcmp (tagname, "timezone")) {
230
zone = parse_timezone (parser);
232
g_ptr_array_add (zones, zone);
235
if (xmlTextReaderNext (parser->xml) != 1)
238
if (xmlTextReaderRead (parser->xml) != 1)
241
g_ptr_array_add (zones, NULL);
242
return (GWeatherTimezone **)g_ptr_array_free (zones, FALSE);
245
for (i = 0; i < zones->len; i++)
246
gweather_timezone_unref (zones->pdata[i]);
247
g_ptr_array_free (zones, TRUE);
252
* gweather_timezone_ref:
253
* @zone: a #GWeatherTimezone
255
* Adds 1 to @zone's reference count.
257
* Return value: @zone
260
gweather_timezone_ref (GWeatherTimezone *zone)
262
g_return_val_if_fail (zone != NULL, NULL);
269
* gweather_timezone_unref:
270
* @zone: a #GWeatherTimezone
272
* Subtracts 1 from @zone's reference count and frees it if it reaches 0.
275
gweather_timezone_unref (GWeatherTimezone *zone)
277
g_return_if_fail (zone != NULL);
279
if (!--zone->ref_count) {
282
g_slice_free (GWeatherTimezone, zone);
287
gweather_timezone_get_type (void)
289
static volatile gsize type_volatile = 0;
291
if (g_once_init_enter (&type_volatile)) {
292
GType type = g_boxed_type_register_static (
293
g_intern_static_string ("GWeatherTimezone"),
294
(GBoxedCopyFunc) gweather_timezone_ref,
295
(GBoxedFreeFunc) gweather_timezone_unref);
296
g_once_init_leave (&type_volatile, type);
298
return type_volatile;
302
* gweather_timezone_get_utc:
304
* Gets the UTC timezone.
306
* Return value: a #GWeatherTimezone for UTC, or %NULL on error.
309
gweather_timezone_get_utc (void)
311
GWeatherTimezone *zone = NULL;
313
zone = g_slice_new0 (GWeatherTimezone);
315
zone->id = g_strdup ("GMT");
316
zone->name = g_strdup (_("Greenwich Mean Time"));
318
zone->has_dst = FALSE;
319
zone->dst_offset = 0;
325
* gweather_timezone_get_name:
326
* @zone: a #GWeatherTimezone
328
* Gets @zone's name; a translated, user-presentable string.
330
* Note that the returned name might not be unique among timezones,
331
* and may not make sense to the user unless it is presented along
332
* with the timezone's country's name (or in some context where the
333
* country is obvious).
335
* Return value: @zone's name
338
gweather_timezone_get_name (GWeatherTimezone *zone)
340
g_return_val_if_fail (zone != NULL, NULL);
345
* gweather_timezone_get_tzid:
346
* @zone: a #GWeatherTimezone
348
* Gets @zone's tzdata identifier, eg "America/New_York".
350
* Return value: @zone's tzid
353
gweather_timezone_get_tzid (GWeatherTimezone *zone)
355
g_return_val_if_fail (zone != NULL, NULL);
360
* gweather_timezone_get_offset:
361
* @zone: a #GWeatherTimezone
363
* Gets @zone's standard offset from UTC, in minutes. Eg, a value of
364
* 120 would indicate "GMT+2".
366
* Return value: @zone's standard offset, in minutes
369
gweather_timezone_get_offset (GWeatherTimezone *zone)
371
g_return_val_if_fail (zone != NULL, 0);
376
* gweather_timezone_has_dst:
377
* @zone: a #GWeatherTimezone
379
* Checks if @zone observes daylight/summer time for part of the year.
381
* Return value: %TRUE if @zone observes daylight/summer time.
384
gweather_timezone_has_dst (GWeatherTimezone *zone)
386
g_return_val_if_fail (zone != NULL, FALSE);
387
return zone->has_dst;
391
* gweather_timezone_get_dst_offset:
392
* @zone: a #GWeatherTimezone
394
* Gets @zone's daylight/summer time offset from UTC, in minutes. Eg,
395
* a value of 120 would indicate "GMT+2". This is only meaningful if
396
* gweather_timezone_has_dst() returns %TRUE.
398
* Return value: @zone's daylight/summer time offset, in minutes
401
gweather_timezone_get_dst_offset (GWeatherTimezone *zone)
403
g_return_val_if_fail (zone != NULL, 0);
404
g_return_val_if_fail (zone->has_dst, 0);
405
return zone->dst_offset;