1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
/* Generic timezone utilities.
4
* Copyright (C) 2000-2001 Ximian, Inc.
6
* Authors: Hans Petter Jansson <hpj@ximian.com>
8
* Largely based on Michael Fulbright's work on Anaconda.
10
* This program is free software; you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License as published by
12
* the Free Software Foundation; either version 2 of the License, or
13
* (at your option) any later version.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
36
/* Forward declarations for private functions */
38
static float convert_pos (gchar *pos, int digits);
39
static int compare_country_names (const void *a, const void *b);
40
static void sort_locations_by_country (GPtrArray *locations);
41
static gchar * tz_data_file_get (void);
42
static void load_backward_tz (TzDB *tz_db);
55
tz_data_file = tz_data_file_get ();
57
g_warning ("Could not get the TimeZone data file name");
60
tzfile = fopen (tz_data_file, "r");
62
g_warning ("Could not open *%s*\n", tz_data_file);
63
g_free (tz_data_file);
67
tz_db = g_new0 (TzDB, 1);
68
tz_db->locations = g_ptr_array_new ();
70
while (fgets (buf, sizeof(buf), tzfile))
73
gchar *latstr, *lngstr, *p;
76
if (*buf == '#') continue;
79
tmpstrarr = g_strsplit(buf,"\t", 6);
81
latstr = g_strdup (tmpstrarr[1]);
83
while (*p != '-' && *p != '+') p++;
84
lngstr = g_strdup (p);
87
loc = g_new0 (TzLocation, 1);
88
loc->country = g_strdup (tmpstrarr[0]);
89
loc->zone = g_strdup (tmpstrarr[2]);
90
loc->latitude = convert_pos (latstr, 2);
91
loc->longitude = convert_pos (lngstr, 3);
94
if (tmpstrarr[3] && *tmpstrarr[3] == '-' && tmpstrarr[4])
95
loc->comment = g_strdup (tmpstrarr[4]);
97
if (tmpstrarr[3] && *tmpstrarr[3] != '-' && !islower(loc->zone)) {
100
/* duplicate entry */
101
locgrp = g_new0 (TzLocation, 1);
102
locgrp->country = g_strdup (tmpstrarr[0]);
103
locgrp->zone = g_strdup (tmpstrarr[3]);
104
locgrp->latitude = convert_pos (latstr, 2);
105
locgrp->longitude = convert_pos (lngstr, 3);
106
locgrp->comment = (tmpstrarr[4]) ? g_strdup (tmpstrarr[4]) : NULL;
108
g_ptr_array_add (tz_db->locations, (gpointer) locgrp);
111
loc->comment = (tmpstrarr[3]) ? g_strdup(tmpstrarr[3]) : NULL;
114
g_ptr_array_add (tz_db->locations, (gpointer) loc);
118
g_strfreev (tmpstrarr);
123
/* now sort by country */
124
sort_locations_by_country (tz_db->locations);
126
g_free (tz_data_file);
128
/* Load up the hashtable of backward links */
129
load_backward_tz (tz_db);
135
tz_location_free (TzLocation *loc)
137
g_free (loc->country);
139
g_free (loc->comment);
145
tz_db_free (TzDB *db)
147
g_ptr_array_foreach (db->locations, (GFunc) tz_location_free, NULL);
148
g_ptr_array_free (db->locations, TRUE);
149
g_hash_table_destroy (db->backward);
154
tz_get_locations (TzDB *db)
156
return db->locations;
161
tz_location_get_country (TzLocation *loc)
168
tz_location_get_zone (TzLocation *loc)
175
tz_location_get_comment (TzLocation *loc)
182
tz_location_get_position (TzLocation *loc, double *longitude, double *latitude)
184
*longitude = loc->longitude;
185
*latitude = loc->latitude;
189
tz_location_get_utc_offset (TzLocation *loc)
194
tz_info = tz_info_from_location (loc);
195
offset = tz_info->utc_offset;
196
tz_info_free (tz_info);
201
tz_info_from_location (TzLocation *loc)
207
g_return_val_if_fail (loc != NULL, NULL);
208
g_return_val_if_fail (loc->zone != NULL, NULL);
210
setenv ("TZ", loc->zone, 1);
215
tzinfo = g_new0 (TzInfo, 1);
217
curtime = time (NULL);
218
curzone = localtime (&curtime);
221
/* Currently this solution doesnt seem to work - I get that */
222
/* America/Phoenix uses daylight savings, which is wrong */
223
tzinfo->tzname_normal = g_strdup (curzone->tm_zone);
224
if (curzone->tm_isdst)
225
tzinfo->tzname_daylight =
226
g_strdup (&curzone->tm_zone[curzone->tm_isdst]);
228
tzinfo->tzname_daylight = NULL;
230
tzinfo->utc_offset = curzone->tm_gmtoff;
232
tzinfo->tzname_normal = NULL;
233
tzinfo->tzname_daylight = NULL;
234
tzinfo->utc_offset = 0;
237
tzinfo->daylight = curzone->tm_isdst;
239
setenv ("TZ", "", 1);
246
tz_info_free (TzInfo *tzinfo)
248
g_return_if_fail (tzinfo != NULL);
250
if (tzinfo->tzname_normal) g_free (tzinfo->tzname_normal);
251
if (tzinfo->tzname_daylight) g_free (tzinfo->tzname_daylight);
259
{ "Asia/Istanbul", "Europe/Istanbul" }, /* Istanbul is in both Europe and Asia */
260
{ "Europe/Nicosia", "Asia/Nicosia" }, /* Ditto */
261
{ "EET", "Europe/Istanbul" }, /* Same tz as the 2 above */
262
{ "HST", "Pacific/Honolulu" },
263
{ "WET", "Europe/Brussels" }, /* Other name for the mainland Europe tz */
264
{ "CET", "Europe/Brussels" }, /* ditto */
265
{ "MET", "Europe/Brussels" },
266
{ "Etc/Zulu", "Etc/GMT" },
267
{ "Etc/UTC", "Etc/GMT" },
268
{ "GMT", "Etc/GMT" },
269
{ "Greenwich", "Etc/GMT" },
270
{ "Etc/UCT", "Etc/GMT" },
271
{ "Etc/GMT0", "Etc/GMT" },
272
{ "Etc/GMT+0", "Etc/GMT" },
273
{ "Etc/GMT-0", "Etc/GMT" },
274
{ "Etc/Universal", "Etc/GMT" },
275
{ "PST8PDT", "America/Los_Angeles" }, /* Other name for the Atlantic tz */
276
{ "EST", "America/New_York" }, /* Other name for the Eastern tz */
277
{ "EST5EDT", "America/New_York" }, /* ditto */
278
{ "CST6CDT", "America/Chicago" }, /* Other name for the Central tz */
279
{ "MST", "America/Denver" }, /* Other name for the mountain tz */
280
{ "MST7MDT", "America/Denver" }, /* ditto */
284
compare_timezones (const char *a,
287
if (g_str_equal (a, b))
289
if (strchr (b, '/') == NULL) {
292
prefixed = g_strdup_printf ("/%s", b);
293
if (g_str_has_suffix (a, prefixed)) {
304
tz_info_get_clean_name (TzDB *tz_db,
308
const char *timezone;
312
/* Remove useless prefixes */
313
if (g_str_has_prefix (tz, "right/"))
314
tz = tz + strlen ("right/");
315
else if (g_str_has_prefix (tz, "posix/"))
316
tz = tz + strlen ("posix/");
318
/* Here start the crazies */
321
for (i = 0; i < G_N_ELEMENTS (aliases); i++) {
322
if (compare_timezones (tz, aliases[i].orig)) {
324
timezone = aliases[i].dest;
331
/* Ignore crazy solar times from the '80s */
332
if (g_str_has_prefix (tz, "Asia/Riyadh") ||
333
g_str_has_prefix (tz, "Mideast/Riyadh")) {
334
timezone = "Asia/Riyadh";
342
ret = g_hash_table_lookup (tz_db->backward, timezone);
344
return g_strdup (timezone);
345
return g_strdup (ret);
348
/* ----------------- *
349
* Private functions *
350
* ----------------- */
353
tz_data_file_get (void)
357
file = g_strdup (TZ_DATA_FILE);
363
convert_pos (gchar *pos, int digits)
370
if (!pos || strlen(pos) < 4 || digits > 9) return 0.0;
372
for (i = 0; i < digits + 1; i++) whole[i] = pos[i];
374
fraction = pos + digits + 1;
376
t1 = g_strtod (whole, NULL);
377
t2 = g_strtod (fraction, NULL);
379
if (t1 >= 0.0) return t1 + t2/pow (10.0, strlen(fraction));
380
else return t1 - t2/pow (10.0, strlen(fraction));
386
/* Currently not working */
388
free_tzdata (TzLocation *tz)
404
compare_country_names (const void *a, const void *b)
406
const TzLocation *tza = * (TzLocation **) a;
407
const TzLocation *tzb = * (TzLocation **) b;
409
return strcmp (tza->zone, tzb->zone);
414
sort_locations_by_country (GPtrArray *locations)
416
qsort (locations->pdata, locations->len, sizeof (gpointer),
417
compare_country_names);
421
load_backward_tz (TzDB *tz_db)
423
GError *error = NULL;
424
char **lines, *contents;
427
tz_db->backward = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
429
if (g_file_get_contents (GNOMECC_DATA_DIR "/datetime/backward", &contents, NULL, &error) == FALSE)
431
g_warning ("Failed to load 'backward' file: %s", error->message);
434
lines = g_strsplit (contents, "\n", -1);
436
for (i = 0; lines[i] != NULL; i++)
442
if (g_ascii_strncasecmp (lines[i], "Link\t", 5) != 0)
445
items = g_strsplit (lines[i], "\t", -1);
448
/* Skip the "Link<tab>" part */
449
for (j = 1; items[j] != NULL; j++)
451
if (items[j][0] == '\0')
462
if (real == NULL || alias == NULL)
463
g_warning ("Could not parse line: %s", lines[i]);
465
/* We don't need more than one name for it */
466
if (g_str_equal (real, "Etc/UTC") ||
467
g_str_equal (real, "Etc/UCT"))
470
g_hash_table_insert (tz_db->backward, g_strdup (alias), g_strdup (real));