From 86b7f1a83d7db9c912f32b29c32e1124c0a6454d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 16:51:55 +0200 Subject: Adding upstream version 3.38.2. Signed-off-by: Daniel Baumann --- plugins/datetime/backward | 118 ++++++++ plugins/datetime/gsd-datetime-manager.c | 226 ++++++++++++++ plugins/datetime/gsd-datetime-manager.h | 37 +++ plugins/datetime/gsd-timezone-monitor.c | 472 +++++++++++++++++++++++++++++ plugins/datetime/gsd-timezone-monitor.h | 55 ++++ plugins/datetime/main.c | 7 + plugins/datetime/meson.build | 40 +++ plugins/datetime/timedated1-interface.xml | 28 ++ plugins/datetime/tz.c | 482 ++++++++++++++++++++++++++++++ plugins/datetime/tz.h | 89 ++++++ plugins/datetime/weather-tz.c | 122 ++++++++ plugins/datetime/weather-tz.h | 27 ++ 12 files changed, 1703 insertions(+) create mode 100644 plugins/datetime/backward create mode 100644 plugins/datetime/gsd-datetime-manager.c create mode 100644 plugins/datetime/gsd-datetime-manager.h create mode 100644 plugins/datetime/gsd-timezone-monitor.c create mode 100644 plugins/datetime/gsd-timezone-monitor.h create mode 100644 plugins/datetime/main.c create mode 100644 plugins/datetime/meson.build create mode 100644 plugins/datetime/timedated1-interface.xml create mode 100644 plugins/datetime/tz.c create mode 100644 plugins/datetime/tz.h create mode 100644 plugins/datetime/weather-tz.c create mode 100644 plugins/datetime/weather-tz.h (limited to 'plugins/datetime') diff --git a/plugins/datetime/backward b/plugins/datetime/backward new file mode 100644 index 0000000..f1f95a8 --- /dev/null +++ b/plugins/datetime/backward @@ -0,0 +1,118 @@ +#
+# @(#)backward	8.9
+# This file is in the public domain, so clarified as of
+# 2009-05-17 by Arthur David Olson.
+
+# This file provides links between current names for time zones
+# and their old names.  Many names changed in late 1993.
+
+Link	Africa/Asmara		Africa/Asmera
+Link	Africa/Bamako		Africa/Timbuktu
+Link	America/Argentina/Catamarca	America/Argentina/ComodRivadavia
+Link	America/Adak		America/Atka
+Link	America/Argentina/Buenos_Aires	America/Buenos_Aires
+Link	America/Argentina/Catamarca	America/Catamarca
+Link	America/Atikokan	America/Coral_Harbour
+Link	America/Argentina/Cordoba	America/Cordoba
+Link	America/Tijuana		America/Ensenada
+Link	America/Indiana/Indianapolis	America/Fort_Wayne
+Link	America/Indiana/Indianapolis	America/Indianapolis
+Link	America/Argentina/Jujuy	America/Jujuy
+Link	America/Indiana/Knox	America/Knox_IN
+Link	America/Kentucky/Louisville	America/Louisville
+Link	America/Argentina/Mendoza	America/Mendoza
+Link	America/Rio_Branco	America/Porto_Acre
+Link	America/Argentina/Cordoba	America/Rosario
+Link	America/St_Thomas	America/Virgin
+Link	Asia/Ashgabat		Asia/Ashkhabad
+Link	Asia/Chongqing		Asia/Chungking
+Link	Asia/Dhaka		Asia/Dacca
+Link	Asia/Kathmandu		Asia/Katmandu
+Link	Asia/Kolkata		Asia/Calcutta
+Link	Asia/Macau		Asia/Macao
+Link	Asia/Jerusalem		Asia/Tel_Aviv
+Link	Asia/Ho_Chi_Minh	Asia/Saigon
+Link	Asia/Thimphu		Asia/Thimbu
+Link	Asia/Makassar		Asia/Ujung_Pandang
+Link	Asia/Ulaanbaatar	Asia/Ulan_Bator
+Link	Atlantic/Faroe		Atlantic/Faeroe
+Link	Europe/Oslo		Atlantic/Jan_Mayen
+Link	Australia/Sydney	Australia/ACT
+Link	Australia/Sydney	Australia/Canberra
+Link	Australia/Lord_Howe	Australia/LHI
+Link	Australia/Sydney	Australia/NSW
+Link	Australia/Darwin	Australia/North
+Link	Australia/Brisbane	Australia/Queensland
+Link	Australia/Adelaide	Australia/South
+Link	Australia/Hobart	Australia/Tasmania
+Link	Australia/Melbourne	Australia/Victoria
+Link	Australia/Perth		Australia/West
+Link	Australia/Broken_Hill	Australia/Yancowinna
+Link	America/Rio_Branco	Brazil/Acre
+Link	America/Noronha		Brazil/DeNoronha
+Link	America/Sao_Paulo	Brazil/East
+Link	America/Manaus		Brazil/West
+Link	America/Halifax		Canada/Atlantic
+Link	America/Winnipeg	Canada/Central
+Link	America/Regina		Canada/East-Saskatchewan
+Link	America/Toronto		Canada/Eastern
+Link	America/Edmonton	Canada/Mountain
+Link	America/St_Johns	Canada/Newfoundland
+Link	America/Vancouver	Canada/Pacific
+Link	America/Regina		Canada/Saskatchewan
+Link	America/Whitehorse	Canada/Yukon
+Link	America/Santiago	Chile/Continental
+Link	Pacific/Easter		Chile/EasterIsland
+Link	America/Havana		Cuba
+Link	Africa/Cairo		Egypt
+Link	Europe/Dublin		Eire
+Link	Europe/London		Europe/Belfast
+Link	Europe/Chisinau		Europe/Tiraspol
+Link	Europe/London		GB
+Link	Europe/London		GB-Eire
+Link	Etc/GMT			GMT+0
+Link	Etc/GMT			GMT-0
+Link	Etc/GMT			GMT0
+Link	Etc/GMT			Greenwich
+Link	Asia/Hong_Kong		Hongkong
+Link	Atlantic/Reykjavik	Iceland
+Link	Asia/Tehran		Iran
+Link	Asia/Jerusalem		Israel
+Link	America/Jamaica		Jamaica
+Link	Asia/Tokyo		Japan
+Link	Pacific/Kwajalein	Kwajalein
+Link	Africa/Tripoli		Libya
+Link	America/Tijuana		Mexico/BajaNorte
+Link	America/Mazatlan	Mexico/BajaSur
+Link	America/Mexico_City	Mexico/General
+Link	Pacific/Auckland	NZ
+Link	Pacific/Chatham		NZ-CHAT
+Link	America/Denver		Navajo
+Link	Asia/Shanghai		PRC
+Link	Pacific/Pago_Pago	Pacific/Samoa
+Link	Pacific/Chuuk		Pacific/Yap
+Link	Pacific/Chuuk		Pacific/Truk
+Link	Pacific/Pohnpei		Pacific/Ponape
+Link	Europe/Warsaw		Poland
+Link	Europe/Lisbon		Portugal
+Link	Asia/Taipei		ROC
+Link	Asia/Seoul		ROK
+Link	Asia/Singapore		Singapore
+Link	Europe/Istanbul		Turkey
+Link	Etc/UCT			UCT
+Link	America/Anchorage	US/Alaska
+Link	America/Adak		US/Aleutian
+Link	America/Phoenix		US/Arizona
+Link	America/Chicago		US/Central
+Link	America/Indiana/Indianapolis	US/East-Indiana
+Link	America/New_York	US/Eastern
+Link	Pacific/Honolulu	US/Hawaii
+Link	America/Indiana/Knox	US/Indiana-Starke
+Link	America/Detroit		US/Michigan
+Link	America/Denver		US/Mountain
+Link	America/Los_Angeles	US/Pacific
+Link	Pacific/Pago_Pago	US/Samoa
+Link	Etc/UTC			UTC
+Link	Etc/UTC			Universal
+Link	Europe/Moscow		W-SU
+Link	Etc/UTC			Zulu
diff --git a/plugins/datetime/gsd-datetime-manager.c b/plugins/datetime/gsd-datetime-manager.c
new file mode 100644
index 0000000..dcd9f8c
--- /dev/null
+++ b/plugins/datetime/gsd-datetime-manager.c
@@ -0,0 +1,226 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ *
+ */
+
+#include "config.h"
+
+#include 
+#include 
+#include 
+
+#include "gsd-datetime-manager.h"
+#include "gsd-timezone-monitor.h"
+#include "gnome-settings-profile.h"
+
+#define DATETIME_SCHEMA "org.gnome.desktop.datetime"
+#define AUTO_TIMEZONE_KEY "automatic-timezone"
+
+struct _GsdDatetimeManager
+{
+        GObject parent;
+
+        GSettings *settings;
+        GsdTimezoneMonitor *timezone_monitor;
+        NotifyNotification *notification;
+};
+
+static void gsd_datetime_manager_class_init (GsdDatetimeManagerClass *klass);
+static void gsd_datetime_manager_init (GsdDatetimeManager *manager);
+static void gsd_datetime_manager_finalize (GObject *object);
+
+G_DEFINE_TYPE (GsdDatetimeManager, gsd_datetime_manager, G_TYPE_OBJECT)
+
+static gpointer manager_object = NULL;
+
+static void
+notification_closed_cb (NotifyNotification *n,
+                        GsdDatetimeManager *self)
+{
+        g_clear_object (&self->notification);
+}
+
+static void
+open_settings_cb (NotifyNotification *n,
+                  const char         *action,
+                  const char         *path)
+{
+        const gchar *argv[] = { "gnome-control-center", "datetime", NULL };
+
+        g_debug ("Running gnome-control-center datetime");
+        g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
+                       NULL, NULL, NULL, NULL);
+
+        notify_notification_close (n, NULL);
+}
+
+static void
+timezone_changed_cb (GsdTimezoneMonitor *timezone_monitor,
+                     const gchar        *timezone_id,
+                     GsdDatetimeManager *self)
+{
+        GDateTime *datetime;
+        GTimeZone *tz;
+        gchar *notification_summary;
+        gchar *timezone_name;
+        gchar *utc_offset;
+
+        tz = g_time_zone_new (timezone_id);
+        datetime = g_date_time_new_now (tz);
+        g_time_zone_unref (tz);
+
+        /* Translators: UTC here means the Coordinated Universal Time.
+         * %:::z will be replaced by the offset from UTC e.g. UTC+02 */
+        utc_offset = g_date_time_format (datetime, _("UTC%:::z"));
+        timezone_name = g_strdup (g_date_time_get_timezone_abbreviation (datetime));
+        g_date_time_unref (datetime);
+
+        notification_summary = g_strdup_printf (_("Time Zone Updated to %s (%s)"),
+                                                timezone_name,
+                                                utc_offset);
+        g_free (timezone_name);
+        g_free (utc_offset);
+
+        if (self->notification == NULL) {
+                self->notification = notify_notification_new (notification_summary, NULL,
+                                                                    "preferences-system-time-symbolic");
+                g_signal_connect (self->notification,
+                                  "closed",
+                                  G_CALLBACK (notification_closed_cb),
+                                  self);
+
+                notify_notification_add_action (self->notification,
+                                                "settings",
+                                                _("Settings"),
+                                                (NotifyActionCallback) open_settings_cb,
+                                                NULL, NULL);
+        } else {
+                notify_notification_update (self->notification,
+                                            notification_summary, NULL,
+                                            "preferences-system-time-symbolic");
+        }
+        g_free (notification_summary);
+
+        notify_notification_set_app_name (self->notification, _("Date & Time Settings"));
+        notify_notification_set_hint_string (self->notification, "desktop-entry", "gnome-datetime-panel");
+        notify_notification_set_urgency (self->notification, NOTIFY_URGENCY_NORMAL);
+        notify_notification_set_timeout (self->notification, NOTIFY_EXPIRES_NEVER);
+
+        if (!notify_notification_show (self->notification, NULL)) {
+                g_warning ("Failed to send timezone notification");
+        }
+}
+
+static void
+auto_timezone_settings_changed_cb (GSettings          *settings,
+                                   const char         *key,
+                                   GsdDatetimeManager *self)
+{
+        gboolean enabled;
+
+        enabled = g_settings_get_boolean (settings, key);
+        if (enabled && self->timezone_monitor == NULL) {
+                g_debug ("Automatic timezone enabled");
+                self->timezone_monitor = gsd_timezone_monitor_new ();
+
+                g_signal_connect (self->timezone_monitor, "timezone-changed",
+                                  G_CALLBACK (timezone_changed_cb), self);
+        } else {
+                g_debug ("Automatic timezone disabled");
+                g_clear_object (&self->timezone_monitor);
+        }
+}
+
+gboolean
+gsd_datetime_manager_start (GsdDatetimeManager *self,
+                            GError            **error)
+{
+        g_debug ("Starting datetime manager");
+        gnome_settings_profile_start (NULL);
+
+        self->settings = g_settings_new (DATETIME_SCHEMA);
+
+        g_signal_connect (self->settings, "changed::" AUTO_TIMEZONE_KEY,
+                          G_CALLBACK (auto_timezone_settings_changed_cb), self);
+        auto_timezone_settings_changed_cb (self->settings, AUTO_TIMEZONE_KEY, self);
+
+        gnome_settings_profile_end (NULL);
+
+        return TRUE;
+}
+
+void
+gsd_datetime_manager_stop (GsdDatetimeManager *self)
+{
+        g_debug ("Stopping datetime manager");
+
+        g_clear_object (&self->settings);
+        g_clear_object (&self->timezone_monitor);
+
+        if (self->notification != NULL) {
+                g_signal_handlers_disconnect_by_func (self->notification,
+                                                      G_CALLBACK (notification_closed_cb),
+                                                      self);
+                g_clear_object (&self->notification);
+        }
+}
+
+static void
+gsd_datetime_manager_class_init (GsdDatetimeManagerClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gsd_datetime_manager_finalize;
+
+        notify_init ("gnome-settings-daemon");
+}
+
+static void
+gsd_datetime_manager_init (GsdDatetimeManager *manager)
+{
+}
+
+static void
+gsd_datetime_manager_finalize (GObject *object)
+{
+        GsdDatetimeManager *manager;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSD_IS_DATETIME_MANAGER (object));
+
+        manager = GSD_DATETIME_MANAGER (object);
+
+        g_return_if_fail (manager != NULL);
+
+        gsd_datetime_manager_stop (manager);
+
+        G_OBJECT_CLASS (gsd_datetime_manager_parent_class)->finalize (object);
+}
+
+GsdDatetimeManager *
+gsd_datetime_manager_new (void)
+{
+        if (manager_object != NULL) {
+                g_object_ref (manager_object);
+        } else {
+                manager_object = g_object_new (GSD_TYPE_DATETIME_MANAGER, NULL);
+                g_object_add_weak_pointer (manager_object,
+                                           (gpointer *) &manager_object);
+        }
+
+        return GSD_DATETIME_MANAGER (manager_object);
+}
diff --git a/plugins/datetime/gsd-datetime-manager.h b/plugins/datetime/gsd-datetime-manager.h
new file mode 100644
index 0000000..5478145
--- /dev/null
+++ b/plugins/datetime/gsd-datetime-manager.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ *
+ */
+
+#ifndef __GSD_DATETIME_MANAGER_H
+#define __GSD_DATETIME_MANAGER_H
+
+#include 
+#include 
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_DATETIME_MANAGER         (gsd_datetime_manager_get_type ())
+G_DECLARE_FINAL_TYPE (GsdDatetimeManager, gsd_datetime_manager, GSD, DATETIME_MANAGER, GObject)
+
+GsdDatetimeManager *gsd_datetime_manager_new (void);
+gboolean gsd_datetime_manager_start (GsdDatetimeManager *manager, GError **error);
+void gsd_datetime_manager_stop (GsdDatetimeManager *manager);
+
+G_END_DECLS
+
+#endif /* __GSD_DATETIME_MANAGER_H */
diff --git a/plugins/datetime/gsd-timezone-monitor.c b/plugins/datetime/gsd-timezone-monitor.c
new file mode 100644
index 0000000..a6e8a48
--- /dev/null
+++ b/plugins/datetime/gsd-timezone-monitor.c
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ *
+ */
+
+#include "config.h"
+
+#include "gsd-timezone-monitor.h"
+
+#include "timedated.h"
+#include "tz.h"
+#include "weather-tz.h"
+
+#include 
+#include 
+#include 
+
+#define DESKTOP_ID "gnome-datetime-panel"
+#define SET_TIMEZONE_PERMISSION "org.freedesktop.timedate1.set-timezone"
+
+enum {
+        TIMEZONE_CHANGED,
+        LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL] = { 0 };
+
+typedef struct
+{
+        GCancellable *cancellable;
+        GPermission *permission;
+        Timedate1 *dtm;
+
+        GClueClient *geoclue_client;
+        GClueSimple *geoclue_simple;
+        GCancellable *geoclue_cancellable;
+
+        gchar *current_timezone;
+
+        GSettings *location_settings;
+} GsdTimezoneMonitorPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsdTimezoneMonitor, gsd_timezone_monitor, G_TYPE_OBJECT)
+
+static void
+set_timezone_cb (GObject      *source,
+                 GAsyncResult *res,
+                 gpointer      user_data)
+{
+        GsdTimezoneMonitorPrivate *priv;
+        GError *error = NULL;
+
+        if (!timedate1_call_set_timezone_finish (TIMEDATE1 (source),
+                                                 res,
+                                                 &error)) {
+                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                        g_warning ("Could not set system timezone: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        priv = gsd_timezone_monitor_get_instance_private (user_data);
+        g_signal_emit (G_OBJECT (user_data),
+                       signals[TIMEZONE_CHANGED],
+                       0, priv->current_timezone);
+
+        g_debug ("Successfully changed timezone to '%s'",
+                 priv->current_timezone);
+}
+
+static void
+queue_set_timezone (GsdTimezoneMonitor *self,
+                    const gchar        *new_timezone)
+{
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+        g_debug ("Changing timezone to '%s'", new_timezone);
+
+        timedate1_call_set_timezone (priv->dtm,
+                                     new_timezone,
+                                     TRUE,
+                                     priv->cancellable,
+                                     set_timezone_cb,
+                                     self);
+
+        g_free (priv->current_timezone);
+        priv->current_timezone = g_strdup (new_timezone);
+}
+
+static gint
+compare_locations (TzLocation *a,
+                   TzLocation *b)
+{
+        if (a->dist > b->dist)
+                return 1;
+
+        if (a->dist < b->dist)
+                return -1;
+
+        return 0;
+}
+
+static GList *
+sort_by_closest_to (GList           *locations,
+                    GeocodeLocation *location)
+{
+        GList *l;
+
+        for (l = locations; l; l = l->next) {
+                GeocodeLocation *loc;
+                TzLocation *tz_location = l->data;
+
+                loc = geocode_location_new (tz_location->latitude,
+                                            tz_location->longitude,
+                                            GEOCODE_LOCATION_ACCURACY_UNKNOWN);
+                tz_location->dist = geocode_location_get_distance_from (loc, location);
+                g_object_unref (loc);
+        }
+
+        return g_list_sort (locations, (GCompareFunc) compare_locations);
+}
+
+static GList *
+ptr_array_to_list (GPtrArray *array)
+{
+        GList *l = NULL;
+        gint i;
+
+        for (i = 0; i < array->len; i++)
+                l = g_list_prepend (l, g_ptr_array_index (array, i));
+
+        return l;
+}
+
+static GList *
+find_by_country (GList       *locations,
+                 const gchar *country_code)
+{
+        GList *l, *found = NULL;
+        gchar *c1;
+        gchar *c2;
+
+        c1 = g_ascii_strdown (country_code, -1);
+
+        for (l = locations; l; l = l->next) {
+                TzLocation *loc = l->data;
+
+                c2 = g_ascii_strdown (loc->country, -1);
+                if (g_strcmp0 (c1, c2) == 0)
+                        found = g_list_prepend (found, loc);
+                g_free (c2);
+        }
+        g_free (c1);
+
+        return found;
+}
+
+static gchar *
+find_timezone (GsdTimezoneMonitor *self,
+               GeocodeLocation    *location,
+               const gchar        *country_code)
+{
+        TzDB *tzdb;
+        gchar *res;
+        GList *filtered;
+        GList *weather_locations;
+        GList *locations;
+        TzLocation *closest_tz_location;
+
+        tzdb = tz_load_db ();
+
+        /* First load locations from Olson DB */
+        locations = ptr_array_to_list (tz_get_locations (tzdb));
+        g_return_val_if_fail (locations != NULL, NULL);
+
+        /* ... and then add libgweather's locations as well */
+        weather_locations = weather_tz_db_get_locations (country_code);
+        locations = g_list_concat (locations,
+                                   g_list_copy (weather_locations));
+
+        /* Filter tz locations by country */
+        filtered = find_by_country (locations, country_code);
+        if (filtered != NULL) {
+                g_list_free (locations);
+                locations = filtered;
+        } else {
+                g_debug ("No match for country code '%s' in tzdb", country_code);
+        }
+
+        /* Find the closest tz location */
+        locations = sort_by_closest_to (locations, location);
+        closest_tz_location = (TzLocation *) locations->data;
+
+        res = g_strdup (closest_tz_location->zone);
+
+        g_list_free (locations);
+        g_list_free_full (weather_locations, (GDestroyNotify) tz_location_free);
+        tz_db_free (tzdb);
+
+        return res;
+}
+
+static void
+process_location (GsdTimezoneMonitor *self,
+                  GeocodePlace       *place)
+{
+        GeocodeLocation *location;
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+        const gchar *country_code;
+        g_autofree gchar *new_timezone = NULL;
+
+        country_code = geocode_place_get_country_code (place);
+        location = geocode_place_get_location (place);
+
+        new_timezone = find_timezone (self, location, country_code);
+
+        if (g_strcmp0 (priv->current_timezone, new_timezone) != 0) {
+                g_debug ("Found updated timezone '%s' for country '%s'",
+                         new_timezone, country_code);
+                queue_set_timezone (self, new_timezone);
+        } else {
+                g_debug ("Timezone didn't change from '%s' for country '%s'",
+                         new_timezone, country_code);
+        }
+}
+
+static void
+on_reverse_geocoding_ready (GObject      *source_object,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+        GeocodePlace *place;
+        GError *error = NULL;
+
+        place = geocode_reverse_resolve_finish (GEOCODE_REVERSE (source_object),
+                                                res,
+                                                &error);
+        if (error != NULL) {
+                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                        g_debug ("Reverse geocoding failed: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+        g_debug ("Geocode lookup resolved country to '%s'",
+                 geocode_place_get_country (place));
+
+        process_location (user_data, place);
+        g_object_unref (place);
+}
+
+static void
+start_reverse_geocoding (GsdTimezoneMonitor *self,
+                         gdouble             latitude,
+                         gdouble             longitude)
+{
+        GeocodeLocation *location;
+        GeocodeReverse *reverse;
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+        location = geocode_location_new (latitude,
+                                         longitude,
+                                         GEOCODE_LOCATION_ACCURACY_CITY);
+
+        reverse = geocode_reverse_new_for_location (location);
+        geocode_reverse_resolve_async (reverse,
+                                       priv->geoclue_cancellable,
+                                       on_reverse_geocoding_ready,
+                                       self);
+
+        g_object_unref (location);
+        g_object_unref (reverse);
+}
+
+static void
+on_location_notify (GClueSimple *simple,
+                    GParamSpec  *pspec,
+                    gpointer     user_data)
+{
+        GsdTimezoneMonitor *self = user_data;
+        GClueLocation *location;
+        gdouble latitude, longitude;
+
+        location = gclue_simple_get_location (simple);
+
+        latitude = gclue_location_get_latitude (location);
+        longitude = gclue_location_get_longitude (location);
+
+        g_debug ("Got location %lf,%lf", latitude, longitude);
+
+        start_reverse_geocoding (self, latitude, longitude);
+}
+
+static void
+on_geoclue_simple_ready (GObject      *source_object,
+                         GAsyncResult *res,
+                         gpointer      user_data)
+{
+        GError *error = NULL;
+        GsdTimezoneMonitorPrivate *priv;
+        GClueSimple *geoclue_simple;
+
+        geoclue_simple = gclue_simple_new_finish (res, &error);
+        if (geoclue_simple == NULL) {
+                if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                        g_warning ("Failed to connect to GeoClue2 service: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        g_debug ("Geoclue now available");
+
+        priv = gsd_timezone_monitor_get_instance_private (user_data);
+        priv->geoclue_simple = geoclue_simple;
+        priv->geoclue_client = gclue_simple_get_client (priv->geoclue_simple);
+        gclue_client_set_distance_threshold (priv->geoclue_client,
+                                             GEOCODE_LOCATION_ACCURACY_CITY);
+
+        g_signal_connect (priv->geoclue_simple, "notify::location",
+                          G_CALLBACK (on_location_notify), user_data);
+
+        on_location_notify (priv->geoclue_simple, NULL, user_data);
+}
+
+static void
+start_geoclue (GsdTimezoneMonitor *self)
+{
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+        g_debug ("Timezone monitor enabled, starting geoclue");
+
+        priv->geoclue_cancellable = g_cancellable_new ();
+        gclue_simple_new (DESKTOP_ID,
+                          GCLUE_ACCURACY_LEVEL_CITY,
+                          priv->geoclue_cancellable,
+                          on_geoclue_simple_ready,
+                          self);
+
+}
+
+static void
+stop_geoclue (GsdTimezoneMonitor *self)
+{
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+        g_debug ("Timezone monitor disabled, stopping geoclue");
+
+        g_cancellable_cancel (priv->geoclue_cancellable);
+        g_clear_object (&priv->geoclue_cancellable);
+
+        if (priv->geoclue_client) {
+                gclue_client_call_stop (priv->geoclue_client, NULL, NULL, NULL);
+                priv->geoclue_client = NULL;
+        }
+
+        g_clear_object (&priv->geoclue_simple);
+}
+
+GsdTimezoneMonitor *
+gsd_timezone_monitor_new (void)
+{
+        return g_object_new (GSD_TYPE_TIMEZONE_MONITOR, NULL);
+}
+
+static void
+gsd_timezone_monitor_finalize (GObject *obj)
+{
+        GsdTimezoneMonitor *monitor = GSD_TIMEZONE_MONITOR (obj);
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (monitor);
+
+        g_debug ("Stopping timezone monitor");
+
+        stop_geoclue (monitor);
+
+        if (priv->cancellable) {
+                g_cancellable_cancel (priv->cancellable);
+                g_clear_object (&priv->cancellable);
+        }
+
+        g_clear_object (&priv->dtm);
+        g_clear_object (&priv->permission);
+        g_clear_pointer (&priv->current_timezone, g_free);
+
+        g_clear_object (&priv->location_settings);
+
+        G_OBJECT_CLASS (gsd_timezone_monitor_parent_class)->finalize (obj);
+}
+
+static void
+gsd_timezone_monitor_class_init (GsdTimezoneMonitorClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gsd_timezone_monitor_finalize;
+
+        signals[TIMEZONE_CHANGED] =
+                g_signal_new ("timezone-changed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsdTimezoneMonitorClass, timezone_changed),
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+static void
+check_location_settings (GsdTimezoneMonitor *self)
+{
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+        if (g_settings_get_boolean (priv->location_settings, "enabled"))
+                start_geoclue (self);
+        else
+                stop_geoclue (self);
+}
+
+static void
+gsd_timezone_monitor_init (GsdTimezoneMonitor *self)
+{
+        GError *error = NULL;
+        GsdTimezoneMonitorPrivate *priv = gsd_timezone_monitor_get_instance_private (self);
+
+        g_debug ("Starting timezone monitor");
+
+        priv->permission = polkit_permission_new_sync (SET_TIMEZONE_PERMISSION,
+                                                       NULL, NULL,
+                                                       &error);
+        if (priv->permission == NULL) {
+                g_warning ("Could not get '%s' permission: %s",
+                           SET_TIMEZONE_PERMISSION,
+                           error->message);
+                g_error_free (error);
+                return;
+        }
+
+        if (!g_permission_get_allowed (priv->permission)) {
+                g_debug ("No permission to set timezone");
+                return;
+        }
+
+        priv->cancellable = g_cancellable_new ();
+        priv->dtm = timedate1_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                      G_DBUS_PROXY_FLAGS_NONE,
+                                                      "org.freedesktop.timedate1",
+                                                      "/org/freedesktop/timedate1",
+                                                      priv->cancellable,
+                                                      &error);
+        if (priv->dtm == NULL) {
+                g_warning ("Could not get proxy for DateTimeMechanism: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        priv->current_timezone = timedate1_dup_timezone (priv->dtm);
+
+        priv->location_settings = g_settings_new ("org.gnome.system.location");
+        g_signal_connect_swapped (priv->location_settings, "changed::enabled",
+                                  G_CALLBACK (check_location_settings), self);
+        check_location_settings (self);
+}
diff --git a/plugins/datetime/gsd-timezone-monitor.h b/plugins/datetime/gsd-timezone-monitor.h
new file mode 100644
index 0000000..da2bcf8
--- /dev/null
+++ b/plugins/datetime/gsd-timezone-monitor.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ *
+ */
+
+#ifndef __GSD_TIMEZONE_MONITOR_H
+#define __GSD_TIMEZONE_MONITOR_H
+
+#include 
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_TIMEZONE_MONITOR                  (gsd_timezone_monitor_get_type ())
+#define GSD_TIMEZONE_MONITOR(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_TIMEZONE_MONITOR, GsdTimezoneMonitor))
+#define GSD_IS_TIMEZONE_MONITOR(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_TIMEZONE_MONITOR))
+#define GSD_TIMEZONE_MONITOR_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_TIMEZONE_MONITOR, GsdTimezoneMonitorClass))
+#define GSD_IS_TIMEZONE_MONITOR_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_TIMEZONE_MONITOR))
+#define GSD_TIMEZONE_MONITOR_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GSD_TYPE_TIMEZONE_MONITOR, GsdTimezoneMonitorClass))
+
+typedef struct _GsdTimezoneMonitor        GsdTimezoneMonitor;
+typedef struct _GsdTimezoneMonitorClass   GsdTimezoneMonitorClass;
+
+struct _GsdTimezoneMonitor
+{
+	GObject parent_instance;
+};
+
+struct _GsdTimezoneMonitorClass
+{
+	GObjectClass parent_class;
+
+	void (*timezone_changed) (GsdTimezoneMonitor *monitor, gchar *timezone_id);
+};
+
+GType gsd_timezone_monitor_get_type (void) G_GNUC_CONST;
+
+GsdTimezoneMonitor *gsd_timezone_monitor_new (void);
+
+G_END_DECLS
+
+#endif /* __GSD_TIMEZONE_MONITOR_H */
diff --git a/plugins/datetime/main.c b/plugins/datetime/main.c
new file mode 100644
index 0000000..8950ab0
--- /dev/null
+++ b/plugins/datetime/main.c
@@ -0,0 +1,7 @@
+#define NEW gsd_datetime_manager_new
+#define START gsd_datetime_manager_start
+#define STOP gsd_datetime_manager_stop
+#define MANAGER GsdDatetimeManager
+#include "gsd-datetime-manager.h"
+
+#include "daemon-skeleton.h"
diff --git a/plugins/datetime/meson.build b/plugins/datetime/meson.build
new file mode 100644
index 0000000..ed2d433
--- /dev/null
+++ b/plugins/datetime/meson.build
@@ -0,0 +1,40 @@
+install_data(
+  'backward',
+  install_dir: join_paths(gsd_pkgdatadir, 'datetime')
+)
+
+sources = files(
+  'gsd-datetime-manager.c',
+  'gsd-timezone-monitor.c',
+  'main.c',
+  'tz.c',
+  'weather-tz.c'
+)
+
+sources += gnome.gdbus_codegen(
+  'timedated',
+  'timedated1-interface.xml',
+  interface_prefix: 'org.freedesktop.'
+)
+
+deps = plugins_deps + [
+  geocode_glib_dep,
+  gweather_dep,
+  libgeoclue_dep,
+  libnotify_dep,
+  m_dep,
+  polkit_gobject_dep
+]
+
+cflags += ['-DGNOMECC_DATA_DIR="@0@"'.format(gsd_pkgdatadir)]
+
+executable(
+  'gsd-' + plugin_name,
+  sources,
+  include_directories: [top_inc, common_inc],
+  dependencies: deps,
+  c_args: cflags,
+  install: true,
+  install_rpath: gsd_pkglibdir,
+  install_dir: gsd_libexecdir
+)
diff --git a/plugins/datetime/timedated1-interface.xml b/plugins/datetime/timedated1-interface.xml
new file mode 100644
index 0000000..3370e0e
--- /dev/null
+++ b/plugins/datetime/timedated1-interface.xml
@@ -0,0 +1,28 @@
+
+
+ 
+  
+  
+  
+  
+  
+   
+   
+   
+  
+  
+   
+   
+  
+  
+   
+   
+   
+  
+  
+   
+   
+  
+ 
+
diff --git a/plugins/datetime/tz.c b/plugins/datetime/tz.c
new file mode 100644
index 0000000..034d63d
--- /dev/null
+++ b/plugins/datetime/tz.c
@@ -0,0 +1,482 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Generic timezone utilities.
+ *
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Authors: Hans Petter Jansson 
+ * 
+ * Largely based on Michael Fulbright's work on Anaconda.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "tz.h"
+
+
+/* Forward declarations for private functions */
+
+static float convert_pos (gchar *pos, int digits);
+static int compare_country_names (const void *a, const void *b);
+static void sort_locations_by_country (GPtrArray *locations);
+static gchar * tz_data_file_get (void);
+static void load_backward_tz (TzDB *tz_db);
+
+/* ---------------- *
+ * Public interface *
+ * ---------------- */
+TzDB *
+tz_load_db (void)
+{
+	gchar *tz_data_file;
+	TzDB *tz_db;
+	FILE *tzfile;
+	char buf[4096];
+
+	tz_data_file = tz_data_file_get ();
+	if (!tz_data_file) {
+		g_warning ("Could not get the TimeZone data file name");
+		return NULL;
+	}
+	tzfile = fopen (tz_data_file, "r");
+	if (!tzfile) {
+		g_warning ("Could not open *%s*\n", tz_data_file);
+		g_free (tz_data_file);
+		return NULL;
+	}
+
+	tz_db = g_new0 (TzDB, 1);
+	tz_db->locations = g_ptr_array_new ();
+
+	while (fgets (buf, sizeof(buf), tzfile))
+	{
+		gchar **tmpstrarr;
+		gchar *latstr, *lngstr, *p;
+		TzLocation *loc;
+
+		if (*buf == '#') continue;
+
+		g_strchomp(buf);
+		tmpstrarr = g_strsplit(buf,"\t", 6);
+		
+		latstr = g_strdup (tmpstrarr[1]);
+		p = latstr + 1;
+		while (*p != '-' && *p != '+') p++;
+		lngstr = g_strdup (p);
+		*p = '\0';
+		
+		loc = g_new0 (TzLocation, 1);
+		loc->country = g_strdup (tmpstrarr[0]);
+		loc->zone = g_strdup (tmpstrarr[2]);
+		loc->latitude  = convert_pos (latstr, 2);
+		loc->longitude = convert_pos (lngstr, 3);
+		
+#ifdef __sun
+		if (tmpstrarr[3] && *tmpstrarr[3] == '-' && tmpstrarr[4])
+			loc->comment = g_strdup (tmpstrarr[4]);
+
+		if (tmpstrarr[3] && *tmpstrarr[3] != '-' && !islower(loc->zone)) {
+			TzLocation *locgrp;
+
+			/* duplicate entry */
+			locgrp = g_new0 (TzLocation, 1);
+			locgrp->country = g_strdup (tmpstrarr[0]);
+			locgrp->zone = g_strdup (tmpstrarr[3]);
+			locgrp->latitude  = convert_pos (latstr, 2);
+			locgrp->longitude = convert_pos (lngstr, 3);
+			locgrp->comment = (tmpstrarr[4]) ? g_strdup (tmpstrarr[4]) : NULL;
+
+			g_ptr_array_add (tz_db->locations, (gpointer) locgrp);
+		}
+#else
+		loc->comment = (tmpstrarr[3]) ? g_strdup(tmpstrarr[3]) : NULL;
+#endif
+
+		g_ptr_array_add (tz_db->locations, (gpointer) loc);
+
+		g_free (latstr);
+		g_free (lngstr);
+		g_strfreev (tmpstrarr);
+	}
+	
+	fclose (tzfile);
+	
+	/* now sort by country */
+	sort_locations_by_country (tz_db->locations);
+	
+	g_free (tz_data_file);
+
+	/* Load up the hashtable of backward links */
+	load_backward_tz (tz_db);
+
+	return tz_db;
+}
+
+void
+tz_location_free (TzLocation *loc)
+{
+	g_free (loc->country);
+	g_free (loc->zone);
+	g_free (loc->comment);
+
+	g_free (loc);
+}
+
+void
+tz_db_free (TzDB *db)
+{
+	g_ptr_array_foreach (db->locations, (GFunc) tz_location_free, NULL);
+	g_ptr_array_free (db->locations, TRUE);
+	g_hash_table_destroy (db->backward);
+	g_free (db);
+}
+
+GPtrArray *
+tz_get_locations (TzDB *db)
+{
+	return db->locations;
+}
+
+
+gchar *
+tz_location_get_country (TzLocation *loc)
+{
+	return loc->country;
+}
+
+
+gchar *
+tz_location_get_zone (TzLocation *loc)
+{
+	return loc->zone;
+}
+
+
+gchar *
+tz_location_get_comment (TzLocation *loc)
+{
+	return loc->comment;
+}
+
+
+void
+tz_location_get_position (TzLocation *loc, double *longitude, double *latitude)
+{
+	*longitude = loc->longitude;
+	*latitude = loc->latitude;
+}
+
+glong
+tz_location_get_utc_offset (TzLocation *loc)
+{
+	TzInfo *tz_info;
+	glong offset;
+
+	tz_info = tz_info_from_location (loc);
+	offset = tz_info->utc_offset;
+	tz_info_free (tz_info);
+	return offset;
+}
+
+TzInfo *
+tz_info_from_location (TzLocation *loc)
+{
+	TzInfo *tzinfo;
+	time_t curtime;
+	struct tm *curzone;
+	gchar *tz_env_value;
+	
+	g_return_val_if_fail (loc != NULL, NULL);
+	g_return_val_if_fail (loc->zone != NULL, NULL);
+	
+	tz_env_value = g_strdup (getenv ("TZ"));
+	setenv ("TZ", loc->zone, 1);
+	
+#if 0
+	tzset ();
+#endif
+	tzinfo = g_new0 (TzInfo, 1);
+
+	curtime = time (NULL);
+	curzone = localtime (&curtime);
+
+#ifndef __sun
+	/* Currently this solution doesnt seem to work - I get that */
+	/* America/Phoenix uses daylight savings, which is wrong    */
+	tzinfo->tzname_normal = g_strdup (curzone->tm_zone);
+	if (curzone->tm_isdst) 
+		tzinfo->tzname_daylight =
+			g_strdup (&curzone->tm_zone[curzone->tm_isdst]);
+	else
+		tzinfo->tzname_daylight = NULL;
+
+	tzinfo->utc_offset = curzone->tm_gmtoff;
+#else
+	tzinfo->tzname_normal = NULL;
+	tzinfo->tzname_daylight = NULL;
+	tzinfo->utc_offset = 0;
+#endif
+
+	tzinfo->daylight = curzone->tm_isdst;
+
+	if (tz_env_value)
+		setenv ("TZ", tz_env_value, 1);
+	else
+		unsetenv ("TZ");
+
+	g_free (tz_env_value);
+	
+	return tzinfo;
+}
+
+
+void
+tz_info_free (TzInfo *tzinfo)
+{
+	g_return_if_fail (tzinfo != NULL);
+	
+	if (tzinfo->tzname_normal) g_free (tzinfo->tzname_normal);
+	if (tzinfo->tzname_daylight) g_free (tzinfo->tzname_daylight);
+	g_free (tzinfo);
+}
+
+struct {
+	const char *orig;
+	const char *dest;
+} aliases[] = {
+	{ "Asia/Istanbul",  "Europe/Istanbul" },	/* Istanbul is in both Europe and Asia */
+	{ "Europe/Nicosia", "Asia/Nicosia" },		/* Ditto */
+	{ "EET",            "Europe/Istanbul" },	/* Same tz as the 2 above */
+	{ "HST",            "Pacific/Honolulu" },
+	{ "WET",            "Europe/Brussels" },	/* Other name for the mainland Europe tz */
+	{ "CET",            "Europe/Brussels" },	/* ditto */
+	{ "MET",            "Europe/Brussels" },
+	{ "Etc/Zulu",       "Etc/GMT" },
+	{ "Etc/UTC",        "Etc/GMT" },
+	{ "GMT",            "Etc/GMT" },
+	{ "Greenwich",      "Etc/GMT" },
+	{ "Etc/UCT",        "Etc/GMT" },
+	{ "Etc/GMT0",       "Etc/GMT" },
+	{ "Etc/GMT+0",      "Etc/GMT" },
+	{ "Etc/GMT-0",      "Etc/GMT" },
+	{ "Etc/Universal",  "Etc/GMT" },
+	{ "PST8PDT",        "America/Los_Angeles" },	/* Other name for the Atlantic tz */
+	{ "EST",            "America/New_York" },	/* Other name for the Eastern tz */
+	{ "EST5EDT",        "America/New_York" },	/* ditto */
+	{ "CST6CDT",        "America/Chicago" },	/* Other name for the Central tz */
+	{ "MST",            "America/Denver" },		/* Other name for the mountain tz */
+	{ "MST7MDT",        "America/Denver" },		/* ditto */
+};
+
+static gboolean
+compare_timezones (const char *a,
+		   const char *b)
+{
+	if (g_str_equal (a, b))
+		return TRUE;
+	if (strchr (b, '/') == NULL) {
+		char *prefixed;
+
+		prefixed = g_strdup_printf ("/%s", b);
+		if (g_str_has_suffix (a, prefixed)) {
+			g_free (prefixed);
+			return TRUE;
+		}
+		g_free (prefixed);
+	}
+
+	return FALSE;
+}
+
+char *
+tz_info_get_clean_name (TzDB *tz_db,
+			const char *tz)
+{
+	char *ret;
+	const char *timezone;
+	guint i;
+	gboolean replaced;
+
+	/* Remove useless prefixes */
+	if (g_str_has_prefix (tz, "right/"))
+		tz = tz + strlen ("right/");
+	else if (g_str_has_prefix (tz, "posix/"))
+		tz = tz + strlen ("posix/");
+
+	/* Here start the crazies */
+	replaced = FALSE;
+
+	for (i = 0; i < G_N_ELEMENTS (aliases); i++) {
+		if (compare_timezones (tz, aliases[i].orig)) {
+			replaced = TRUE;
+			timezone = aliases[i].dest;
+			break;
+		}
+	}
+
+	/* Try again! */
+	if (!replaced) {
+		/* Ignore crazy solar times from the '80s */
+		if (g_str_has_prefix (tz, "Asia/Riyadh") ||
+		    g_str_has_prefix (tz, "Mideast/Riyadh")) {
+			timezone = "Asia/Riyadh";
+			replaced = TRUE;
+		}
+	}
+
+	if (!replaced)
+		timezone = tz;
+
+	ret = g_hash_table_lookup (tz_db->backward, timezone);
+	if (ret == NULL)
+		return g_strdup (timezone);
+	return g_strdup (ret);
+}
+
+/* ----------------- *
+ * Private functions *
+ * ----------------- */
+
+static gchar *
+tz_data_file_get (void)
+{
+	gchar *file;
+
+	file = g_strdup (TZ_DATA_FILE);
+
+	return file;
+}
+
+static float
+convert_pos (gchar *pos, int digits)
+{
+	gchar whole[10];
+	gchar *fraction;
+	gint i;
+	float t1, t2;
+	
+	if (!pos || strlen(pos) < 4 || digits > 9) return 0.0;
+	
+	for (i = 0; i < digits + 1; i++) whole[i] = pos[i];
+	whole[i] = '\0';
+	fraction = pos + digits + 1;
+
+	t1 = g_strtod (whole, NULL);
+	t2 = g_strtod (fraction, NULL);
+
+	if (t1 >= 0.0) return t1 + t2/pow (10.0, strlen(fraction));
+	else return t1 - t2/pow (10.0, strlen(fraction));
+}
+
+
+#if 0
+
+/* Currently not working */
+static void
+free_tzdata (TzLocation *tz)
+{
+	
+	if (tz->country)
+	  g_free(tz->country);
+	if (tz->zone)
+	  g_free(tz->zone);
+	if (tz->comment)
+	  g_free(tz->comment);
+	
+	g_free(tz);
+}
+#endif
+
+
+static int
+compare_country_names (const void *a, const void *b)
+{
+	const TzLocation *tza = * (TzLocation **) a;
+	const TzLocation *tzb = * (TzLocation **) b;
+	
+	return strcmp (tza->zone, tzb->zone);
+}
+
+
+static void
+sort_locations_by_country (GPtrArray *locations)
+{
+	qsort (locations->pdata, locations->len, sizeof (gpointer),
+	       compare_country_names);
+}
+
+static void
+load_backward_tz (TzDB *tz_db)
+{
+  GError *error = NULL;
+  char **lines, *contents;
+  guint i;
+
+  tz_db->backward = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+  if (g_file_get_contents (GNOMECC_DATA_DIR "/datetime/backward", &contents, NULL, &error) == FALSE)
+    {
+      g_warning ("Failed to load 'backward' file: %s", error->message);
+      return;
+    }
+  lines = g_strsplit (contents, "\n", -1);
+  g_free (contents);
+  for (i = 0; lines[i] != NULL; i++)
+    {
+      char **items;
+      guint j;
+      char *real, *alias;
+
+      if (g_ascii_strncasecmp (lines[i], "Link\t", 5) != 0)
+        continue;
+
+      items = g_strsplit (lines[i], "\t", -1);
+      real = NULL;
+      alias = NULL;
+      /* Skip the "Link" part */
+      for (j = 1; items[j] != NULL; j++)
+        {
+          if (items[j][0] == '\0')
+            continue;
+          if (real == NULL)
+            {
+              real = items[j];
+              continue;
+            }
+          alias = items[j];
+          break;
+        }
+
+      if (real == NULL || alias == NULL)
+        g_warning ("Could not parse line: %s", lines[i]);
+
+      /* We don't need more than one name for it */
+      if (g_str_equal (real, "Etc/UTC") ||
+          g_str_equal (real, "Etc/UCT"))
+        real = "Etc/GMT";
+
+      g_hash_table_insert (tz_db->backward, g_strdup (alias), g_strdup (real));
+      g_strfreev (items);
+    }
+  g_strfreev (lines);
+}
+
diff --git a/plugins/datetime/tz.h b/plugins/datetime/tz.h
new file mode 100644
index 0000000..ab5535c
--- /dev/null
+++ b/plugins/datetime/tz.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Generic timezone utilities.
+ *
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Authors: Hans Petter Jansson 
+ * 
+ * Largely based on Michael Fulbright's work on Anaconda.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+
+#ifndef _E_TZ_H
+#define _E_TZ_H
+
+#include 
+
+#ifndef __sun
+#  define TZ_DATA_FILE "/usr/share/zoneinfo/zone.tab"
+#else
+#  define TZ_DATA_FILE "/usr/share/lib/zoneinfo/tab/zone_sun.tab"
+#endif
+
+typedef struct _TzDB TzDB;
+typedef struct _TzLocation TzLocation;
+typedef struct _TzInfo TzInfo;
+
+
+struct _TzDB
+{
+	GPtrArray  *locations;
+	GHashTable *backward;
+};
+
+struct _TzLocation
+{
+	gchar *country;
+	gdouble latitude;
+	gdouble longitude;
+	gchar *zone;
+	gchar *comment;
+
+	gdouble dist; /* distance to clicked point for comparison */
+};
+
+/* see the glibc info page information on time zone information */
+/*  tzname_normal    is the default name for the timezone */
+/*  tzname_daylight  is the name of the zone when in daylight savings */
+/*  utc_offset       is offset in seconds from utc */
+/*  daylight         if non-zero then location obeys daylight savings */
+
+struct _TzInfo
+{
+	gchar *tzname_normal;
+	gchar *tzname_daylight;
+	glong utc_offset;
+	gint daylight;
+};
+
+
+TzDB      *tz_load_db                 (void);
+void       tz_db_free                 (TzDB *db);
+char *     tz_info_get_clean_name     (TzDB *tz_db,
+				       const char *tz);
+GPtrArray *tz_get_locations           (TzDB *db);
+void       tz_location_get_position   (TzLocation *loc,
+				       double *longitude, double *latitude);
+void       tz_location_free           (TzLocation *loc);
+char      *tz_location_get_country    (TzLocation *loc);
+gchar     *tz_location_get_zone       (TzLocation *loc);
+gchar     *tz_location_get_comment    (TzLocation *loc);
+glong      tz_location_get_utc_offset (TzLocation *loc);
+gint       tz_location_set_locally    (TzLocation *loc);
+TzInfo    *tz_info_from_location      (TzLocation *loc);
+void       tz_info_free               (TzInfo *tz_info);
+
+#endif
diff --git a/plugins/datetime/weather-tz.c b/plugins/datetime/weather-tz.c
new file mode 100644
index 0000000..f1de2b9
--- /dev/null
+++ b/plugins/datetime/weather-tz.c
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ *
+ */
+
+#include "config.h"
+
+#include "weather-tz.h"
+#include "tz.h"
+
+#define GWEATHER_I_KNOW_THIS_IS_UNSTABLE
+#include 
+
+static GList *
+location_get_cities (GWeatherLocation *parent_location)
+{
+        GList *cities = NULL;
+        GWeatherLocation **children;
+        gint i;
+
+        children = gweather_location_get_children (parent_location);
+        for (i = 0; children[i]; i++) {
+                if (gweather_location_get_level (children[i]) == GWEATHER_LOCATION_CITY) {
+                        cities = g_list_prepend (cities,
+                                                 children[i]);
+                } else {
+                        cities = g_list_concat (cities,
+                                                location_get_cities (children[i]));
+                }
+        }
+
+        return cities;
+}
+
+static gboolean
+weather_location_has_timezone (GWeatherLocation *loc)
+{
+        return gweather_location_get_timezone (loc) != NULL;
+}
+
+/**
+ * load_timezones:
+ * @cities: a list of #GWeatherLocation
+ *
+ * Returns: a list of #TzLocation
+ */
+static GList *
+load_timezones (GList *cities)
+{
+        GList *l;
+        GList *tz_locations = NULL;
+
+        for (l = cities; l; l = l->next) {
+                TzLocation *loc;
+                const gchar *country;
+                const gchar *timezone_id;
+                gdouble latitude;
+                gdouble longitude;
+
+                if (!gweather_location_has_coords (l->data) ||
+                    !weather_location_has_timezone (l->data)) {
+                        g_debug ("Incomplete GWeather location entry: (%s) %s",
+                                 gweather_location_get_country (l->data),
+                                 gweather_location_get_city_name (l->data));
+                        continue;
+                }
+
+                country = gweather_location_get_country (l->data);
+                timezone_id = gweather_timezone_get_tzid (gweather_location_get_timezone (l->data));
+                gweather_location_get_coords (l->data,
+                                              &latitude,
+                                              &longitude);
+
+                loc = g_new0 (TzLocation, 1);
+                loc->country = g_strdup (country);
+                loc->latitude = latitude;
+                loc->longitude = longitude;
+                loc->zone = g_strdup (timezone_id);
+                loc->comment = NULL;
+
+                tz_locations = g_list_prepend (tz_locations, loc);
+        }
+
+        return tz_locations;
+}
+
+GList *
+weather_tz_db_get_locations (const gchar *country_code)
+{
+        GList *cities;
+        GList *tz_locations;
+        GWeatherLocation *world;
+        GWeatherLocation *country;
+
+        world = gweather_location_get_world ();
+
+        country = gweather_location_find_by_country_code (world, country_code);
+
+        if (!country)
+                return NULL;
+
+        cities = location_get_cities (country);
+        tz_locations = load_timezones (cities);
+
+        g_list_free (cities);
+
+        return tz_locations;
+}
diff --git a/plugins/datetime/weather-tz.h b/plugins/datetime/weather-tz.h
new file mode 100644
index 0000000..15b1571
--- /dev/null
+++ b/plugins/datetime/weather-tz.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Kalev Lember 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ *
+ */
+
+#ifndef __WEATHER_TZ_H
+#define __WEATHER_TZ_H
+
+#include 
+
+GList           *weather_tz_db_get_locations    (const char *country);
+
+#endif /* __WEATHER_TZ_H */
-- 
cgit v1.2.3