[glib] GTimeZone: Add initialization functions for rules



commit 9a4a53c03daf20b7a2f61db87af3b698dac80784
Author: Arnel A. Borja <kyoushuu yahoo com>
Date:   Sun Oct 21 11:26:21 2012 +0800

    GTimeZone: Add initialization functions for rules
    
    Add functions to initialize a GTimeZone from rules.

 glib/gtimezone.c |  277 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 277 insertions(+), 0 deletions(-)
---
diff --git a/glib/gtimezone.c b/glib/gtimezone.c
index 74c0a27..87d602f 100644
--- a/glib/gtimezone.c
+++ b/glib/gtimezone.c
@@ -37,6 +37,7 @@
 #include "gthread.h"
 #include "gbytes.h"
 #include "gslice.h"
+#include "gdatetime.h"
 
 /**
  * SECTION:timezone
@@ -130,6 +131,31 @@ typedef struct
   gint   info_index;
 } Transition;
 
+typedef struct
+{
+  gint     year;
+  gint     mon;
+  gint     mday;
+  gint     wday;
+  gint     week;
+  gint     hour;
+  gint     min;
+  gint     sec;
+  gboolean isstd;
+  gboolean isgmt;
+} TimeZoneDate;
+
+typedef struct
+{
+  gint         start_year;
+  gint32       std_offset;
+  gint32       dlt_offset;
+  TimeZoneDate dlt_start;
+  TimeZoneDate dlt_end;
+  const gchar *std_name;
+  const gchar *dlt_name;
+} TimeZoneRule;
+
 
 /* GTimeZone structure and lifecycle {{{1 */
 struct _GTimeZone
@@ -143,6 +169,9 @@ struct _GTimeZone
 G_LOCK_DEFINE_STATIC (time_zones);
 static GHashTable/*<string?, GTimeZone>*/ *time_zones;
 
+#define MIN_TZYEAR 1900
+#define MAX_TZYEAR 2038
+
 /**
  * g_time_zone_unref:
  * @tz: a #GTimeZone
@@ -460,6 +489,254 @@ init_zone_from_iana_info (GTimeZone *gtz, GBytes *zoneinfo)
 
 #endif
 
+static void
+find_relative_date (TimeZoneDate *buffer,
+                    GTimeZone    *tz)
+{
+  GDateTime *dt;
+  gint wday;
+
+  wday = buffer->wday;
+
+  /* Get last day if last is needed, first day otherwise */
+  dt = g_date_time_new (tz,
+                        buffer->year,
+                        buffer->mon + (buffer->week < 5? 0 : 1),
+                        buffer->week < 5? 1 : 0,
+                        buffer->hour, buffer->min, buffer->sec);
+
+  buffer->wday = g_date_time_get_day_of_week (dt);
+  buffer->mday = g_date_time_get_day_of_month (dt);
+
+  if (buffer->week < 5)
+    {
+      if (wday < buffer->wday)
+        buffer->wday -= 7;
+
+      buffer->mday += (buffer->week - 1) * 7;
+    }
+
+  else if (wday > buffer->wday)
+    buffer->wday += 7;
+
+  buffer->mday += wday - buffer->wday;
+  buffer->wday = wday;
+
+  g_date_time_unref (dt);
+}
+
+/* Offset is previous offset of local time */
+static gint64
+boundary_for_year (TimeZoneDate *boundary,
+                   gint          year,
+                   gint32        prev_offset,
+                   gint32        std_offset)
+{
+  TimeZoneDate buffer;
+  GDateTime *dt;
+  GTimeZone *tz;
+  gint64 t;
+  gint32 offset;
+  gchar *identifier;
+
+  buffer = *boundary;
+
+  if (boundary->isgmt)
+    offset = 0;
+  else if (boundary->isstd)
+    offset = std_offset;
+  else
+    offset = prev_offset;
+
+  G_UNLOCK (time_zones);
+
+  identifier = g_strdup_printf ("%+03d:%02d:%02d",
+                                (int) offset / 3600,
+                                (int) abs (offset / 60) % 60,
+                                (int) abs (offset) % 3600);
+  tz = g_time_zone_new (identifier);
+  g_free (identifier);
+
+  if (boundary->year == 0)
+    {
+      buffer.year = year;
+
+      if (buffer.wday)
+        find_relative_date (&buffer, tz);
+    }
+
+  g_assert (buffer.year == year);
+
+  dt = g_date_time_new (tz,
+                        buffer.year, buffer.mon, buffer.mday,
+                        buffer.hour, buffer.min, buffer.sec);
+  t = g_date_time_to_unix (dt);
+  g_date_time_unref (dt);
+
+  g_time_zone_unref (tz);
+
+  G_LOCK (time_zones);
+
+  return t;
+}
+
+static void
+init_zone_from_rules (GTimeZone    *gtz,
+                      TimeZoneRule *rules,
+                      gint          rules_num)
+{
+  TransitionInfo info[2];
+  Transition trans;
+  gint type_count, trans_count;
+  gint year, i, x, y;
+  gint32 last_offset;
+
+  type_count = 0;
+  trans_count = 0;
+
+  /* Last rule only contains max year */
+  for (i = 0; i < rules_num - 1; i++)
+    {
+      if (rules[i].dlt_start.mon)
+        {
+          type_count += 2;
+          trans_count += 2 * (rules[i+1].start_year - rules[i].start_year);
+        }
+      else
+        type_count++;
+    }
+
+  x = 0;
+  y = 0;
+
+  /* If standard time happens before daylight time in first rule
+   * with daylight, skip first transition so the minimum is in
+   * standard time and the first transition is in daylight time */
+  for (i = 0; i < rules_num - 1 && rules[0].dlt_start.mon == 0; i++);
+
+  if (i < rules_num -1 && rules[i].dlt_start.mon > 0 &&
+      rules[i].dlt_start.mon > rules[i].dlt_end.mon)
+    {
+      trans_count--;
+      x = -1;
+    }
+
+  gtz->t_info = g_array_sized_new (FALSE, TRUE, sizeof (TransitionInfo), type_count);
+  gtz->transitions = g_array_sized_new (FALSE, TRUE, sizeof (Transition), trans_count);
+
+  last_offset = rules[0].std_offset;
+
+  for (i = 0; i < rules_num - 1; i++)
+    {
+      if (rules[i].dlt_start.mon)
+        {
+          /* Standard */
+          info[0].gmt_offset = rules[i].std_offset;
+          info[0].is_dst = FALSE;
+          info[0].is_standard = rules[i].dlt_end.isstd;
+          info[0].is_gmt = rules[i].dlt_end.isgmt;
+
+          if (rules[i].std_name)
+            info[0].abbrev = g_strdup (rules[i].std_name);
+
+          else
+            info[0].abbrev = g_strdup_printf ("%+03d%02d",
+                                              (int) rules[i].std_offset / 3600,
+                                              (int) abs (rules[i].std_offset / 60) % 60);
+
+
+          /* Daylight */
+          info[1].gmt_offset = rules[i].dlt_offset;
+          info[1].is_dst = TRUE;
+          info[1].is_standard = rules[i].dlt_start.isstd;
+          info[1].is_gmt = rules[i].dlt_start.isgmt;
+
+          if (rules[i].dlt_name)
+            info[1].abbrev = g_strdup (rules[i].dlt_name);
+
+          else
+            info[1].abbrev = g_strdup_printf ("%+03d%02d",
+                                              (int) rules[i].dlt_offset / 3600,
+                                              (int) abs (rules[i].dlt_offset / 60) % 60);
+
+          if (rules[i].dlt_start.mon < rules[i].dlt_end.mon)
+            {
+              g_array_append_val (gtz->t_info, info[1]);
+              g_array_append_val (gtz->t_info, info[0]);
+            }
+          else
+            {
+              g_array_append_val (gtz->t_info, info[0]);
+              g_array_append_val (gtz->t_info, info[1]);
+            }
+
+          /* Transition dates */
+          for (year = rules[i].start_year; year < rules[i+1].start_year; year++)
+            {
+              if (rules[i].dlt_start.mon < rules[i].dlt_end.mon)
+                {
+                  /* Daylight Data */
+                  trans.info_index = y;
+                  trans.time = boundary_for_year (&rules[i].dlt_start, year,
+                                                  last_offset, rules[i].std_offset);
+                  g_array_insert_val (gtz->transitions, x++, trans);
+                  last_offset = rules[i].dlt_offset;
+
+                  /* Standard Data */
+                  trans.info_index = y+1;
+                  trans.time = boundary_for_year (&rules[i].dlt_end, year,
+                                                  last_offset, rules[i].std_offset);
+                  g_array_insert_val (gtz->transitions, x++, trans);
+                  last_offset = rules[i].std_offset;
+                }
+              else
+                {
+                  /* Standard Data */
+                  trans.info_index = y;
+                  trans.time = boundary_for_year (&rules[i].dlt_end, year,
+                                                  last_offset, rules[i].std_offset);
+                  if (x >= 0)
+                    g_array_insert_val (gtz->transitions, x++, trans);
+                  else
+                    x++;
+                  last_offset = rules[i].std_offset;
+
+                  /* Daylight Data */
+                  trans.info_index = y+1;
+                  trans.time = boundary_for_year (&rules[i].dlt_start, year,
+                                                  last_offset, rules[i].std_offset);
+                  g_array_insert_val (gtz->transitions, x++, trans);
+                  last_offset = rules[i].dlt_offset;
+                }
+            }
+
+          y += 2;
+        }
+      else
+        {
+          /* Standard */
+          info[0].gmt_offset = rules[i].std_offset;
+          info[0].is_dst = FALSE;
+          info[0].is_standard = FALSE;
+          info[0].is_gmt = FALSE;
+
+          if (rules[i].std_name)
+            info[0].abbrev = g_strdup (rules[i].std_name);
+
+          else
+            info[0].abbrev = g_strdup_printf ("%+03d%02d",
+                                              (int) rules[i].std_offset / 3600,
+                                              (int) abs (rules[i].std_offset / 60) % 60);
+
+          g_array_append_val (gtz->t_info, info[0]);
+
+          last_offset = rules[i].std_offset;
+
+          y++;
+        }
+    }
+}
+
 /* Construction {{{1 */
 /**
  * g_time_zone_new:



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]