aboutsummaryrefslogtreecommitdiffstats
path: root/libical/src
diff options
context:
space:
mode:
authorDamon Chaplin <damon@ximian.com>2001-07-03 12:11:38 +0800
committerDamon Chaplin <damon@src.gnome.org>2001-07-03 12:11:38 +0800
commit0e27010e0b193b787e93cbf50c08a6d477e33dee (patch)
treea3d0cf7c5cfc7e8297e7a2a06694b93b6c459ca6 /libical/src
parent0c39016124cbe7895093434351b83087438cc92a (diff)
downloadgsoc2013-evolution-0e27010e0b193b787e93cbf50c08a6d477e33dee.tar
gsoc2013-evolution-0e27010e0b193b787e93cbf50c08a6d477e33dee.tar.gz
gsoc2013-evolution-0e27010e0b193b787e93cbf50c08a6d477e33dee.tar.bz2
gsoc2013-evolution-0e27010e0b193b787e93cbf50c08a6d477e33dee.tar.lz
gsoc2013-evolution-0e27010e0b193b787e93cbf50c08a6d477e33dee.tar.xz
gsoc2013-evolution-0e27010e0b193b787e93cbf50c08a6d477e33dee.tar.zst
gsoc2013-evolution-0e27010e0b193b787e93cbf50c08a6d477e33dee.zip
more timezone stuff.
2001-07-03 Damon Chaplin <damon@ximian.com> * src/libical/icaltimezone.c (icaltimezone_get_builtin_timezone_from_tzid): * src/libical/icaltime.c: * src/libical/icalcomponent.c: more timezone stuff. svn path=/trunk/; revision=10727
Diffstat (limited to 'libical/src')
-rw-r--r--libical/src/libical/icalcomponent.c410
-rw-r--r--libical/src/libical/icalcomponent.h24
-rw-r--r--libical/src/libical/icaltime.c222
-rw-r--r--libical/src/libical/icaltime.h28
-rw-r--r--libical/src/libical/icaltimezone.c439
-rw-r--r--libical/src/libical/icaltimezone.h41
6 files changed, 856 insertions, 308 deletions
diff --git a/libical/src/libical/icalcomponent.c b/libical/src/libical/icalcomponent.c
index ee4ec54f30..8c5295b79f 100644
--- a/libical/src/libical/icalcomponent.c
+++ b/libical/src/libical/icalcomponent.c
@@ -44,6 +44,7 @@
#include <errno.h>
#include <assert.h>
#include <stdio.h> /* for fprintf */
+#include <string.h> /* for strdup */
#define MAX_TMP 1024
@@ -68,8 +69,22 @@ void icalcomponent_add_children(struct icalcomponent_impl *impl,va_list args);
icalcomponent* icalcomponent_new_impl (icalcomponent_kind kind);
int icalcomponent_property_sorter(void *a, void *b);
+static void icalcomponent_merge_vtimezone (icalcomponent *comp,
+ icalcomponent *vtimezone,
+ icalarray *tzids_to_rename);
+static void icalcomponent_handle_conflicting_vtimezones (icalcomponent *comp,
+ icalcomponent *vtimezone,
+ icalproperty *tzid_prop,
+ const char *tzid,
+ icalarray *tzids_to_rename);
+static int icalcomponent_get_tzid_prefix_len (const char *tzid);
+static void icalcomponent_rename_tzids(icalcomponent* comp,
+ icalarray* rename_table);
static void icalcomponent_rename_tzids_callback(icalparameter *param,
void *data);
+static int icalcomponent_compare_vtimezones (icalcomponent *vtimezone1,
+ icalcomponent *vtimezone2);
+
void icalcomponent_add_children(struct icalcomponent_impl *impl,va_list args)
{
@@ -704,58 +719,6 @@ icalcomponent* icalcomponent_get_first_real_component(icalcomponent *c)
return 0;
}
-time_t icalcomponent_convert_time(icalproperty *p)
-{
- struct icaltimetype sict;
- time_t convt;
- icalproperty *tzp;
-
-
- /* Though it says _dtstart, it will work for dtend too */
- sict = icalproperty_get_dtstart(p);
-
- tzp = icalproperty_get_first_parameter(p,ICAL_TZID_PARAMETER);
-
- if (sict.is_utc == 1 && tzp != 0){
- icalerror_warn("icalcomponent_get_span: component has a UTC DTSTART with a timezone specified ");
- icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
- return 0;
- }
-
- if(sict.is_utc == 1){
- /* _as_timet will use gmtime() to do the conversion */
- convt = icaltime_as_timet(sict);
-
-#ifdef TEST_CONVERT_TIME
- printf("convert time: use as_timet:\n %s\n %s",
- icalproperty_as_ical_string(p), ctime(&convt));
-#endif
-
- } else if (sict.is_utc == 0 && tzp == 0 ) {
- time_t offset;
-
- /* _as_timet will use localtime() to do the conversion */
- convt = icaltime_as_timet(sict);
- offset = icaltime_utc_offset(sict,0);
- convt += offset;
-
-#ifdef TEST_CONVERT_TIME
- printf("convert time: use as_timet and adjust:\n %s\n %s",
- icalproperty_as_ical_string(p), ctime(&convt));
-#endif
- } else {
- /* Convert the time to UTC for the named timezone*/
- const char* timezone = icalparameter_get_tzid(tzp);
- convt = icaltime_as_timet(icaltime_as_utc(sict,timezone));
-
-#ifdef TEST_CONVERT_TIME
- printf("convert time: use _as_utc:\n %s\n %s",
- icalproperty_as_ical_string(p), ctime(&convt));
-#endif
- }
-
- return convt;
-}
struct icaltime_span icalcomponent_get_span(icalcomponent* comp)
{
icalcomponent *inner;
@@ -822,7 +785,10 @@ struct icaltime_span icalcomponent_get_span(icalcomponent* comp)
icalerror_clear_errno();
+ /* FIXME: Needs updating to new icaltimezone functions. */
+#if 0
span.start = icalcomponent_convert_time(p);
+#endif
#ifdef TEST_CONVERT_TIME
printf("convert time:\n %s %s",
@@ -846,7 +812,10 @@ struct icaltime_span icalcomponent_get_span(icalcomponent* comp)
}
if (p!=0){
+ /* FIXME: Needs updating to new icaltimezone functions. */
+#if 0
span.end = icalcomponent_convert_time(p);
+#endif
} else if (start.is_date == 1) {
/* Duration is all day */
span.end = span.start + 60*60*24;
@@ -1514,47 +1483,220 @@ icalcomponent* icalcomponent_new_xdaylight()
return icalcomponent_new(ICAL_XDAYLIGHT_COMPONENT);
}
-/* Calls the given function for each TZID parameter found in the component. */
-void icalcomponent_foreach_tzid(icalcomponent* comp,
- void (*callback)(icalparameter *param, void *data),
- void *callback_data)
-{
- icalproperty *prop;
- icalproperty_kind kind;
- icalparameter *param;
- icalcomponent *child;
- /* First rename any TZID parameters used in this component. */
- prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
- while (prop) {
- kind = icalproperty_isa (prop);
-
- /* These are the only properties that can have a TZID. Note that
- COMPLETED, CREATED, DTSTAMP & LASTMODIFIED must be in UTC. */
- if (kind == ICAL_DTSTART_PROPERTY || kind == ICAL_DTEND_PROPERTY
- || kind == ICAL_DUE_PROPERTY || kind == ICAL_EXDATE_PROPERTY
- || kind == ICAL_RDATE_PROPERTY) {
- param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
- if (param)
- (*callback) (param, callback_data);
+/*
+ * Timezone stuff.
+ */
+
+/* This takes 2 VCALENDAR components and merges the second one into the first,
+ resolving any problems with conflicting TZIDs. comp_to_merge will no
+ longer exist after calling this function. */
+void icalcomponent_merge_component(icalcomponent* comp,
+ icalcomponent* comp_to_merge)
+{
+ icalcomponent *subcomp, *next_subcomp;
+ icalarray *tzids_to_rename;
+ int i;
+
+ /* Check that both components are VCALENDAR components. */
+ assert (icalcomponent_isa(comp) == ICAL_VCALENDAR_COMPONENT);
+ assert (icalcomponent_isa(comp_to_merge) != ICAL_VCALENDAR_COMPONENT);
+
+ /* Step through each subcomponent of comp_to_merge, looking for VTIMEZONEs.
+ For each VTIMEZONE found, check if we need to add it to comp and if we
+ need to rename it and all TZID references to it. */
+ tzids_to_rename = icalarray_new (sizeof (char*), 16);
+ subcomp = icalcomponent_get_first_component (comp_to_merge,
+ ICAL_VTIMEZONE_COMPONENT);
+ while (subcomp) {
+ next_subcomp = icalcomponent_get_next_component (comp_to_merge,
+ ICAL_VTIMEZONE_COMPONENT);
+ /* This will add the VTIMEZONE to comp, if necessary, and also update
+ the array of TZIDs we need to rename. */
+ icalcomponent_merge_vtimezone (comp, subcomp, tzids_to_rename);
+ /* FIXME: Handle possible NEWFAILED error. */
+
+ subcomp = next_subcomp;
+ }
+
+ /* If we need to do any renaming of TZIDs, do it now. */
+ if (tzids_to_rename->num_elements != 0) {
+ icalcomponent_rename_tzids (comp_to_merge, tzids_to_rename);
+
+ /* Now free the tzids_to_rename array. */
+ for (i = 0; i < tzids_to_rename->num_elements; i++) {
+ free (icalarray_element_at (tzids_to_rename, i));
+ }
+ icalarray_free (tzids_to_rename);
+ }
+
+ /* Now move all the components from comp_to_merge to comp, excluding
+ VTIMEZONE components. */
+ subcomp = icalcomponent_get_first_component (comp_to_merge,
+ ICAL_ANY_COMPONENT);
+ while (subcomp) {
+ next_subcomp = icalcomponent_get_next_component (comp_to_merge,
+ ICAL_ANY_COMPONENT);
+ if (icalcomponent_isa(subcomp) != ICAL_VTIMEZONE_COMPONENT) {
+ icalcomponent_remove_component (comp_to_merge, subcomp);
+ icalcomponent_add_component (comp, subcomp);
+ }
+ subcomp = next_subcomp;
+ }
+
+ /* Free comp_to_merge. We have moved most of the subcomponents over to
+ comp now. */
+ icalcomponent_free (comp_to_merge);
+}
+
+
+static void icalcomponent_merge_vtimezone (icalcomponent *comp,
+ icalcomponent *vtimezone,
+ icalarray *tzids_to_rename)
+{
+ icalproperty *tzid_prop;
+ const char *tzid;
+ icaltimezone *existing_vtimezone;
+
+ /* Get the TZID of the VTIMEZONE. */
+ tzid_prop = icalcomponent_get_first_property (comp, ICAL_TZID_PROPERTY);
+ if (!tzid_prop)
+ return;
+
+ tzid = icalproperty_get_tzid (tzid_prop);
+ if (!tzid)
+ return;
+
+ /* See if there is already a VTIMEZONE in comp with the same TZID. */
+ existing_vtimezone = icalcomponent_get_timezone (comp, tzid);
+
+ /* If there is no existing VTIMEZONE with the same TZID, we can just move
+ the VTIMEZONE to comp and return. */
+ if (!existing_vtimezone) {
+ icalcomponent_remove_component (icalcomponent_get_parent (vtimezone),
+ vtimezone);
+ icalcomponent_add_component (comp, vtimezone);
+ return;
+ }
+
+ /* If the TZID has a '/' prefix, then we don't have to worry about the
+ clashing TZIDs, as they are supposed to be exactly the same VTIMEZONE. */
+ if (tzid[0] == '/')
+ return;
+
+ /* Now we have two VTIMEZONEs with the same TZID (which isn't a globally
+ unique one), so we compare the VTIMEZONE components to see if they are
+ the same. If they are, we don't need to do anything. */
+ if (icalcomponent_compare_vtimezones (existing_vtimezone, vtimezone))
+ return;
+ /* FIXME: Handle possible NEWFAILED error. */
+
+ /* Now we have two different VTIMEZONEs with the same TZID. */
+ icalcomponent_handle_conflicting_vtimezones (comp, vtimezone, tzid_prop,
+ tzid, tzids_to_rename);
+}
+
+
+static void
+icalcomponent_handle_conflicting_vtimezones (icalcomponent *comp,
+ icalcomponent *vtimezone,
+ icalproperty *tzid_prop,
+ const char *tzid,
+ icalarray *tzids_to_rename)
+{
+ struct icalcomponent_impl *impl = (struct icalcomponent_impl*)comp;
+ int tzid_len, i, suffix, max_suffix = 0;
+ char *tzid_copy, *new_tzid, suffix_buf[32];
+
+ /* Find the length of the TZID without any trailing digits. */
+ tzid_len = icalcomponent_get_tzid_prefix_len (tzid);
+
+ /* Step through each of the VTIMEZONEs in comp. We may already have the
+ clashing VTIMEZONE in the calendar, but it may have been renamed
+ (i.e. a unique number added on the end of the TZID, e.g. 'London2').
+ So we compare the new VTIMEZONE with any VTIMEZONEs that have the
+ same prefix (e.g. 'London'). If it matches any of those, we have to
+ rename the TZIDs to that TZID, else we rename to a new TZID, using
+ the biggest numeric suffix found + 1. */
+ for (i = 0; i < impl->timezones->num_elements; i++) {
+ icaltimezone *zone;
+ char *existing_tzid, *existing_tzid_copy;
+ int existing_tzid_len;
+
+ zone = icalarray_element_at (impl->timezones, i);
+ existing_tzid = icaltimezone_get_tzid (zone);
+
+ /* Find the length of the TZID without any trailing digits. */
+ existing_tzid_len = icalcomponent_get_tzid_prefix_len (existing_tzid);
+
+ /* Check if we have the same prefix. */
+ if (tzid_len == existing_tzid_len
+ && !strncmp (tzid, existing_tzid, tzid_len)) {
+ /* Compare the VTIMEZONEs. */
+ if (icalcomponent_compare_vtimezones (icaltimezone_get_component (zone),
+ vtimezone)) {
+ /* The VTIMEZONEs match, so we can use the existing VTIMEZONE. But
+ we have to rename TZIDs to this TZID. */
+ tzid_copy = strdup (tzid);
+ existing_tzid_copy = strdup (existing_tzid);
+ if (!tzid_copy || !existing_tzid_copy) {
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ } else {
+ icalarray_append (tzids_to_rename, tzid_copy);
+ icalarray_append (tzids_to_rename, existing_tzid_copy);
+ }
+ return;
+ } else {
+ /* FIXME: Handle possible NEWFAILED error. */
+
+ /* Convert the suffix to an integer and remember the maximum numeric
+ suffix found. */
+ suffix = atoi (existing_tzid + existing_tzid_len);
+ if (max_suffix < suffix)
+ max_suffix = suffix;
}
-
- prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY);
}
+ }
- /* Now recursively rename child components. */
- child = icalcomponent_get_first_component (comp, ICAL_ANY_COMPONENT);
- while (child) {
- icalcomponent_foreach_tzid (child, callback, callback_data);
- child = icalcomponent_get_next_component (comp, ICAL_ANY_COMPONENT);
- }
+ /* We didn't find a VTIMEZONE that matched, so we have to rename the TZID,
+ using the maximum numerical suffix found + 1. */
+ tzid_copy = strdup (tzid);
+ sprintf (suffix_buf, "%i", max_suffix + 1);
+ new_tzid = malloc (tzid_len + strlen (suffix_buf) + 1);
+ if (!new_tzid || !tzid_copy) {
+ icalerror_set_errno(ICAL_NEWFAILED_ERROR);
+ return;
+ }
+
+ strncpy (new_tzid, tzid, tzid_len);
+ strcpy (new_tzid + tzid_len, suffix_buf);
+ icalarray_append (tzids_to_rename, tzid_copy);
+ icalarray_append (tzids_to_rename, new_tzid);
+}
+
+
+/* Returns the length of the TZID, without any trailing digits. */
+static int icalcomponent_get_tzid_prefix_len (const char *tzid)
+{
+ int len;
+ const char *p;
+
+ len = strlen (tzid);
+ p = tzid + len - 1;
+ while (len > 0 && *p >= '0' && *p <= '9') {
+ p--;
+ len--;
+ }
+
+ return len;
}
/* Renames all references to the given TZIDs to a new name. rename_table
contains pairs of strings - a current TZID, and the new TZID to rename it
to. */
-void icalcomponent_rename_tzids(icalcomponent* comp, icalarray* rename_table)
+static void icalcomponent_rename_tzids(icalcomponent* comp,
+ icalarray* rename_table)
{
icalcomponent_foreach_tzid (comp, icalcomponent_rename_tzids_callback,
rename_table);
@@ -1580,6 +1722,44 @@ static void icalcomponent_rename_tzids_callback(icalparameter *param, void *data
}
+/* Calls the given function for each TZID parameter found in the component. */
+void icalcomponent_foreach_tzid(icalcomponent* comp,
+ void (*callback)(icalparameter *param, void *data),
+ void *callback_data)
+{
+ icalproperty *prop;
+ icalproperty_kind kind;
+ icalparameter *param;
+ icalcomponent *subcomp;
+
+ /* First look for any TZID parameters used in this component itself. */
+ prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
+ while (prop) {
+ kind = icalproperty_isa (prop);
+
+ /* These are the only properties that can have a TZID. Note that
+ COMPLETED, CREATED, DTSTAMP & LASTMODIFIED must be in UTC. */
+ if (kind == ICAL_DTSTART_PROPERTY || kind == ICAL_DTEND_PROPERTY
+ || kind == ICAL_DUE_PROPERTY || kind == ICAL_EXDATE_PROPERTY
+ || kind == ICAL_RDATE_PROPERTY) {
+ param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+ if (param)
+ (*callback) (param, callback_data);
+ }
+
+ prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY);
+ }
+
+ /* Now recursively check child components. */
+ subcomp = icalcomponent_get_first_component (comp, ICAL_ANY_COMPONENT);
+ while (subcomp) {
+ icalcomponent_foreach_tzid (subcomp, callback, callback_data);
+ subcomp = icalcomponent_get_next_component (comp, ICAL_ANY_COMPONENT);
+ }
+}
+
+
+
/* Returns the icaltimezone from the component corresponding to the given
TZID, or NULL if the component does not have a corresponding VTIMEZONE. */
icaltimezone* icalcomponent_get_timezone(icalcomponent* comp, const char *tzid)
@@ -1609,3 +1789,67 @@ icaltimezone* icalcomponent_get_timezone(icalcomponent* comp, const char *tzid)
return NULL;
}
+
+/* Compares 2 VTIMEZONE components to see if they match, ignoring their TZIDs.
+ It returns 1 if they match, 0 if they don't, or -1 on error. */
+static int icalcomponent_compare_vtimezones (icalcomponent *vtimezone1,
+ icalcomponent *vtimezone2)
+{
+ icalproperty *prop1, *prop2;
+ const char *tzid1, *tzid2;
+ char *tzid2_copy, *string1, *string2;
+ int cmp;
+
+ /* Get the TZID property of the first VTIMEZONE. */
+ prop1 = icalcomponent_get_first_property (vtimezone1, ICAL_TZID_PROPERTY);
+ if (!prop1)
+ return -1;
+
+ tzid1 = icalproperty_get_tzid (prop1);
+ if (!tzid1)
+ return -1;
+
+ /* Get the TZID property of the second VTIMEZONE. */
+ prop2 = icalcomponent_get_first_property (vtimezone1, ICAL_TZID_PROPERTY);
+ if (!prop2)
+ return -1;
+
+ tzid2 = icalproperty_get_tzid (prop2);
+ if (!tzid2)
+ return -1;
+
+ /* Copy the second TZID, and set the property to the same as the first
+ TZID, since we don't care if these match of not. */
+ tzid2_copy = strdup (tzid2);
+ if (!tzid2_copy) {
+ icalerror_set_errno (ICAL_NEWFAILED_ERROR);
+ return 0;
+ }
+
+ icalproperty_set_tzid (prop2, tzid1);
+
+ /* Now convert both VTIMEZONEs to strings and compare them. */
+ string1 = icalcomponent_as_ical_string (vtimezone1);
+ if (!string1) {
+ free (tzid2_copy);
+ return -1;
+ }
+
+ string2 = icalcomponent_as_ical_string (vtimezone2);
+ if (!string2) {
+ free (string1);
+ free (tzid2_copy);
+ return -1;
+ }
+
+ cmp = strcmp (string1, string2);
+
+ free (string1);
+ free (string2);
+
+ /* Now reset the second TZID. */
+ icalproperty_set_tzid (prop2, tzid2_copy);
+ free (tzid2_copy);
+
+ return (cmp == 0) ? 1 : 0;
+}
diff --git a/libical/src/libical/icalcomponent.h b/libical/src/libical/icalcomponent.h
index 6046bbee1e..55c0592bb2 100644
--- a/libical/src/libical/icalcomponent.h
+++ b/libical/src/libical/icalcomponent.h
@@ -32,6 +32,14 @@
typedef void icalcomponent;
+/* An opaque struct representing a timezone. We declare this here to avoid
+ a circular dependancy. */
+#ifndef ICALTIMEONE_DEFINED
+#define ICALTIMEONE_DEFINED
+typedef struct _icaltimezone icaltimezone;
+#endif
+
+
/* This is exposed so that callers will not have to allocate and
deallocate iterators. Pretend that you can't see it. */
typedef struct icalcompiter
@@ -97,6 +105,13 @@ void icalcomponent_remove_component(icalcomponent* parent,
int icalcomponent_count_components(icalcomponent* component,
icalcomponent_kind kind);
+/* This takes 2 VCALENDAR components and merges the second one into the first,
+ resolving any problems with conflicting TZIDs. comp_to_merge will no
+ longer exist after calling this function. */
+void icalcomponent_merge_component(icalcomponent* comp,
+ icalcomponent* comp_to_merge);
+
+
/* Iteration Routines. There are two forms of iterators, internal and
external. The internal ones came first, and are almost completely
sufficient, but they fail badly when you want to construct a loop that
@@ -217,7 +232,16 @@ int icalcomponent_remove_attendee(icalcomponent *comp, char* cuid);
struct icalattendeetype icalcomponent_get_attendee(icalcomponent *comp,
int index);
+/* Calls the given function for each TZID parameter found in the component,
+ and any subcomponents. */
+void icalcomponent_foreach_tzid(icalcomponent* comp,
+ void (*callback)(icalparameter *param, void *data),
+ void *callback_data);
+/* Returns the icaltimezone in the component corresponding to the TZID, or NULL
+ if it can't be found. */
+icaltimezone* icalcomponent_get_timezone(icalcomponent* comp,
+ const char *tzid);
/*************** Type Specific routines ***************/
diff --git a/libical/src/libical/icaltime.c b/libical/src/libical/icaltime.c
index 6a3859c094..6f6ed5748f 100644
--- a/libical/src/libical/icaltime.c
+++ b/libical/src/libical/icaltime.c
@@ -44,6 +44,7 @@
#include "icalmemory.h"
#endif
+#include "icaltimezone.h"
@@ -73,6 +74,41 @@ icaltime_from_timet(time_t tm, int is_date)
return tt;
}
+struct icaltimetype
+icaltime_from_timet_with_zone(time_t tm, int is_date, icaltimezone *zone)
+{
+ struct icaltimetype tt = icaltime_null_time();
+ struct tm t;
+ icaltimezone *utc_zone;
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+
+ /* Convert the time_t to a struct tm in UTC time. We can trust gmtime
+ for this. */
+ t = *(gmtime(&tm));
+
+ tt.year = t.tm_year + 1900;
+ tt.month = t.tm_mon + 1;
+ tt.day = t.tm_mday;
+ tt.hour = t.tm_hour;
+ tt.minute = t.tm_min;
+ tt.second = t.tm_sec;
+
+ tt.is_utc = (zone == utc_zone) ? 1 : 0;
+ tt.is_date = is_date;
+ tt.is_daylight = 0;
+
+ /* Use our timezone functions to convert to the required timezone. */
+ icaltimezone_convert_time (&tt, utc_zone, zone);
+
+ if (is_date) {
+ /* FIXME: is_daylight may need to be changed. */
+ tt.second = tt.minute = tt.hour = 0;
+ }
+
+ return tt;
+}
+
/* Structure used by set_tz to hold an old value of TZ, and the new
value, which is in memory we will have to free in unset_tz */
/* This will hold the last "TZ=XXX" string we used with putenv(). After we
@@ -177,6 +213,42 @@ time_t icaltime_as_timet(struct icaltimetype tt)
}
+time_t icaltime_as_timet_with_zone(struct icaltimetype tt, icaltimezone *zone)
+{
+ icaltimezone *utc_zone;
+ struct tm stm;
+ time_t t;
+ char *old_tz;
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+
+ /* If the time is the special null time, return 0. */
+ if (icaltime_is_null_time(tt)) {
+ return 0;
+ }
+
+ /* Use our timezone functions to convert to UTC. */
+ icaltimezone_convert_time (&tt, zone, utc_zone);
+
+ /* Copy the icaltimetype to a struct tm. */
+ memset (&stm, 0, sizeof (struct tm));
+
+ stm.tm_sec = tt.second;
+ stm.tm_min = tt.minute;
+ stm.tm_hour = tt.hour;
+ stm.tm_mday = tt.day;
+ stm.tm_mon = tt.month-1;
+ stm.tm_year = tt.year-1900;
+ stm.tm_isdst = -1;
+
+ /* Set TZ to UTC and use mktime to convert to a time_t. */
+ old_tz = set_tz ("UTC");
+ t = mktime (&stm);
+ unset_tz (old_tz);
+
+ return t;
+}
+
char* icaltime_as_ical_string(struct icaltimetype tt)
{
size_t size = 17;
@@ -202,101 +274,12 @@ char* icaltime_as_ical_string(struct icaltimetype tt)
}
-/* convert tt, of timezone tzid, into a utc time */
-struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid)
-{
- int tzid_offset;
-
- if(tt.is_utc == 1 || tt.is_date == 1){
- return tt;
- }
-
- tzid_offset = icaltime_utc_offset(tt,tzid);
-
- tt.second -= tzid_offset;
-
- tt.is_utc = 1;
-
- return icaltime_normalize(tt);
-}
-
-/* convert tt, a time in UTC, into a time in timezone tzid */
-struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid)
-{
- int tzid_offset;
-
- tzid_offset = icaltime_utc_offset(tt,tzid);
-
- tt.second += tzid_offset;
-
- tt.is_utc = 0;
-
- return icaltime_normalize(tt);
-
-}
-
-
-/* Return the offset of the named zone as seconds. tt is a time
- indicating the date for which you want the offset */
-int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid)
-{
-
- time_t tt = icaltime_as_timet(ictt);
- time_t offset_tt;
- struct tm gtm;
-
- char *tz_str = 0;
-
- if(tzid != 0){
- tz_str = set_tz(tzid);
- }
-
- /* Mis-interpret a UTC broken out time as local time */
- gtm = *(gmtime(&tt));
- gtm.tm_isdst = localtime(&tt)->tm_isdst;
- offset_tt = mktime(&gtm);
-
- if(tzid != 0){
- unset_tz(tz_str);
- }
-
- return tt-offset_tt;
-}
-
-
-
-/* Normalize by converting from localtime to utc and back to local
- time. This uses localtime because localtime and mktime are inverses
- of each other */
+/* Normalize the icaltime, so that all fields are within the normal range. */
struct icaltimetype icaltime_normalize(struct icaltimetype tt)
{
- struct tm stm;
- time_t tut;
-
- memset(&stm,0,sizeof( struct tm));
-
- stm.tm_sec = tt.second;
- stm.tm_min = tt.minute;
- stm.tm_hour = tt.hour;
- stm.tm_mday = tt.day;
- stm.tm_mon = tt.month-1;
- stm.tm_year = tt.year-1900;
- stm.tm_isdst = -1; /* prevents mktime from changing hour based on
- daylight savings */
-
- tut = mktime(&stm);
-
- stm = *(localtime(&tut));
-
- tt.second = stm.tm_sec;
- tt.minute = stm.tm_min;
- tt.hour = stm.tm_hour;
- tt.day = stm.tm_mday;
- tt.month = stm.tm_mon +1;
- tt.year = stm.tm_year+1900;
-
- return tt;
+ icaltime_adjust (&tt, 0, 0, 0, 0);
+ return tt;
}
@@ -404,50 +387,69 @@ short icaltime_day_of_week(struct icaltimetype t){
return stm.tm_wday + 1;
}
-/* Day of the year that the first day of the week (Sunday) is on */
+/* Day of the year that the first day of the week (Sunday) is on.
+ FIXME: Doesn't take into account different week start days. */
short icaltime_start_doy_of_week(struct icaltimetype t){
- time_t tt = icaltime_as_timet(t);
- time_t start_tt;
- struct tm *stm;
- int syear;
+ struct tm stm;
- stm = gmtime(&tt);
- syear = stm->tm_year;
+ stm.tm_year = t.year - 1900;
+ stm.tm_mon = t.month - 1;
+ stm.tm_mday = t.day;
+ stm.tm_hour = 0;
+ stm.tm_min = 0;
+ stm.tm_sec = 0;
+ stm.tm_isdst = -1;
- start_tt = tt - stm->tm_wday*(60*60*24);
+ mktime (&stm);
- stm = gmtime(&start_tt);
-
- if(syear == stm->tm_year){
- return stm->tm_yday+1;
+ /* Move back to the start of the week. */
+ stm.tm_mday -= stm.tm_wday;
+
+ mktime (&stm);
+
+ /* If we are still in the same year as the original date, we just return
+ the day of the year. */
+ if (t.year - 1900 == stm.tm_year){
+ return stm.tm_yday+1;
} else {
/* return negative to indicate that start of week is in
previous year. */
int is_leap = 0;
- int year = stm->tm_year;
+ int year = stm.tm_year;
if( (year % 4 == 0 && year % 100 != 0) ||
year % 400 == 0){
is_leap =1;
}
- return (stm->tm_yday+1)-(365+is_leap);
+ return (stm.tm_yday+1)-(365+is_leap);
}
}
+/* FIXME: Doesn't take into account the start day of the week. strftime assumes
+ that weeks start on Monday. */
short icaltime_week_number(struct icaltimetype ictt)
{
- char str[5];
- time_t t = icaltime_as_timet(ictt);
+ struct tm stm;
int week_no;
+ char str[8];
- strftime(str,5,"%V", gmtime(&t));
+ stm.tm_year = ictt.year - 1900;
+ stm.tm_mon = ictt.month - 1;
+ stm.tm_mday = ictt.day;
+ stm.tm_hour = 0;
+ stm.tm_min = 0;
+ stm.tm_sec = 0;
+ stm.tm_isdst = -1;
+
+ mktime (&stm);
+
+ strftime(str,5,"%V", &stm);
week_no = atoi(str);
return week_no;
-
}
diff --git a/libical/src/libical/icaltime.h b/libical/src/libical/icaltime.h
index afe8fd4df2..ef3d65d030 100644
--- a/libical/src/libical/icaltime.h
+++ b/libical/src/libical/icaltime.h
@@ -31,6 +31,13 @@
#include <time.h>
+/* An opaque struct representing a timezone. We declare this here to avoid
+ a circular dependancy. */
+#ifndef ICALTIMEONE_DEFINED
+#define ICALTIMEONE_DEFINED
+typedef struct _icaltimezone icaltimezone;
+#endif
+
/* icaltime_span is returned by icalcomponent_get_span() */
struct icaltime_span {
time_t start; /* in UTC */
@@ -60,9 +67,16 @@ struct icaltimetype
/* Convert seconds past UNIX epoch to a timetype*/
struct icaltimetype icaltime_from_timet(time_t v, int is_date);
+/* Newer version of above, using timezones. */
+struct icaltimetype icaltime_from_timet_with_zone(time_t tm, int is_date,
+ icaltimezone *zone);
+
/* Return the time as seconds past the UNIX epoch */
time_t icaltime_as_timet(struct icaltimetype);
+/* Newer version of above, using timezones. */
+time_t icaltime_as_timet_with_zone(struct icaltimetype tt, icaltimezone *zone);
+
/* Return a string represention of the time, in RFC2445 format. The
string is owned by libical */
char* icaltime_as_ical_string(struct icaltimetype tt);
@@ -77,20 +91,6 @@ int icaltime_as_int(struct icaltimetype);
/* create a time from an ISO format string */
struct icaltimetype icaltime_from_string(const char* str);
-/* Routines for handling timezones */
-/* Return the offset of the named zone as seconds. tt is a time
- indicating the date for which you want the offset */
-int icaltime_utc_offset(struct icaltimetype tt, const char* tzid);
-
-/* convert tt, of timezone tzid, into a utc time. Does nothing if the
- time is already UTC. */
-struct icaltimetype icaltime_as_utc(struct icaltimetype tt,
- const char* tzid);
-
-/* convert tt, a time in UTC, into a time in timezone tzid */
-struct icaltimetype icaltime_as_zone(struct icaltimetype tt,
- const char* tzid);
-
/* Return a null time, which indicates no time has been set. This time represent the beginning of the epoch */
struct icaltimetype icaltime_null_time(void);
diff --git a/libical/src/libical/icaltimezone.c b/libical/src/libical/icaltimezone.c
index 796e01a3ea..def5c1a6fc 100644
--- a/libical/src/libical/icaltimezone.c
+++ b/libical/src/libical/icaltimezone.c
@@ -30,15 +30,19 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "icalproperty.h"
#include "icalarray.h"
#include "icalerror.h"
#include "icalparser.h"
#include "icaltimezone.h"
-
/* This is the toplevel directory where the timezone data is installed in. */
#define ZONEINFO_DIRECTORY PACKAGE_DATA_DIR "/zoneinfo"
+/* The prefix we use to uniquely identify TZIDs. */
+#define TZID_PREFIX "/softwarestudio.org/"
+#define TZID_PREFIX_LEN 20
+
/* This is the filename of the file containing the city names and coordinates
of all the builtin timezones. */
#define ZONES_TAB_FILENAME "zones.tab"
@@ -49,7 +53,7 @@
/* This is the maximum year we will expand to. time_t values only go up to
somewhere around 2037. */
-#define ICALTIMEZONE_MAX_YEAR 2032
+#define ICALTIMEZONE_MAX_YEAR 2035
struct _icaltimezone {
@@ -130,6 +134,9 @@ icaltimezone utc_timezone = { 0 };
+static void icaltimezone_reset (icaltimezone *zone);
+static char* icaltimezone_get_location_from_vtimezone (icalcomponent *component);
+static char* icaltimezone_get_tznames_from_vtimezone (icalcomponent *component);
static void icaltimezone_expand_changes (icaltimezone *zone,
int end_year);
static void icaltimezone_expand_vtimezone (icalcomponent *comp,
@@ -149,14 +156,14 @@ static void icaltimezone_adjust_change (icaltimezonechange *tt,
static void icaltimezone_init (icaltimezone *zone);
-/* Initializes an icaltimezone from the given VTIMEZONE component. It gets
- the TZID from the VTIMEZONE component. It returns 1 on success, or 0 if
- the TZID can't be found. */
-static int icaltimezone_init_from_vtimezone (icaltimezone *zone,
- icalcomponent *component);
+/* Gets the TZID, LOCATION/X-LIC-LOCATION, and TZNAME properties from the
+ VTIMEZONE component and places them in the icaltimezone. It returns 1 on
+ success, or 0 if the TZID can't be found. */
+static int icaltimezone_get_vtimezone_properties (icaltimezone *zone,
+ icalcomponent *component);
-static void icaltimezone_load (icaltimezone *zone);
+static void icaltimezone_load_builtin_timezone (icaltimezone *zone);
static void icaltimezone_ensure_coverage (icaltimezone *zone,
int end_year);
@@ -174,8 +181,53 @@ static void format_utc_offset (int utc_offset,
char *buffer);
+/* Creates a new icaltimezone. */
+icaltimezone*
+icaltimezone_new (void)
+{
+ icaltimezone *zone;
+
+ zone = (icaltimezone*) malloc (sizeof (icaltimezone));
+ if (!zone) {
+ icalerror_set_errno (ICAL_NEWFAILED_ERROR);
+ return;
+ }
+
+ icaltimezone_init (zone);
-/* Initializes an icaltimezone with the given TZID. */
+ return zone;
+}
+
+
+/* Frees all memory used for the icaltimezone. */
+void
+icaltimezone_free (icaltimezone *zone)
+{
+ icaltimezone_reset (zone);
+ free (zone);
+}
+
+
+/* Resets the icaltimezone to the initial state, freeing most of the fields. */
+static void
+icaltimezone_reset (icaltimezone *zone)
+{
+ if (zone->tzid)
+ free (zone->tzid);
+ if (zone->location)
+ free (zone->location);
+ if (zone->tznames)
+ free (zone->tznames);
+ if (zone->component)
+ icalcomponent_free (zone->component);
+ if (zone->changes)
+ icalarray_free (zone->changes);
+
+ icaltimezone_init (zone);
+}
+
+
+/* Initializes an icaltimezone. */
static void
icaltimezone_init (icaltimezone *zone)
{
@@ -191,31 +243,171 @@ icaltimezone_init (icaltimezone *zone)
}
-/* Initializes an icaltimezone from the given VTIMEZONE component. It gets
- the TZID from the VTIMEZONE component. It returns 1 on success, or 0 if
- the TZID can't be found. */
+/* Gets the TZID, LOCATION/X-LIC-LOCATION and TZNAME properties of the
+ VTIMEZONE component and stores them in the icaltimezone.
+ It returns 1 on success, or 0 if the TZID can't be found.
+ Note that it expects the zone to be initialized or reset - it doesn't free
+ any old values. */
static int
-icaltimezone_init_from_vtimezone (icaltimezone *zone,
+icaltimezone_get_vtimezone_properties (icaltimezone *zone,
icalcomponent *component)
{
icalproperty *prop;
- const char *tzid;
-
+ const char *tzid, *location;
+
prop = icalcomponent_get_first_property (component, ICAL_TZID_PROPERTY);
if (!prop)
return 0;
+ /* A VTIMEZONE MUST have a TZID, or a lot of our code won't work. */
tzid = icalproperty_get_tzid (prop);
if (!tzid)
return 0;
- icaltimezone_init (zone);
zone->tzid = strdup (tzid);
zone->component = component;
+ zone->location = icaltimezone_get_location_from_vtimezone (component);
+ zone->tznames = icaltimezone_get_tznames_from_vtimezone (component);
return 1;
}
+/* Gets the LOCATION or X-LIC-LOCATION property from a VTIMEZONE. */
+static char*
+icaltimezone_get_location_from_vtimezone (icalcomponent *component)
+{
+ icalproperty *prop;
+ const char *location;
+ char *name;
+ int found_location = 0;
+
+ prop = icalcomponent_get_first_property (component,
+ ICAL_LOCATION_PROPERTY);
+ if (prop) {
+ location = icalproperty_get_location (prop);
+ if (location)
+ return strdup (location);
+ }
+
+ prop = icalcomponent_get_first_property (component, ICAL_X_PROPERTY);
+ while (prop) {
+ name = icalproperty_get_x_name (prop);
+ if (name && !strcmp (name, "X-LIC-LOCATION")) {
+ location = icalproperty_get_x (prop);
+ if (location)
+ return strdup (location);
+ }
+ prop = icalcomponent_get_next_property (component,
+ ICAL_X_PROPERTY);
+ }
+}
+
+
+/* Gets the TZNAMEs used for the last STANDARD & DAYLIGHT components in a
+ VTIMEZONE. If both STANDARD and DAYLIGHT components use the same TZNAME,
+ it returns that. If they use different TZNAMEs, it formats them like
+ "EST/EDT". The returned string should be freed by the caller. */
+static char*
+icaltimezone_get_tznames_from_vtimezone (icalcomponent *component)
+{
+ icalcomponent *comp;
+ icalcomponent_kind type;
+ icalproperty *prop;
+ struct icaltimetype dtstart;
+ struct icaldatetimeperiodtype rdate;
+ const char *current_tzname;
+ const char *standard_tzname = NULL, *daylight_tzname = NULL;
+ struct icaltimetype standard_max_date, daylight_max_date;
+ struct icaltimetype current_max_date;
+
+ /* Step through the STANDARD & DAYLIGHT subcomponents. */
+ comp = icalcomponent_get_first_component (component, ICAL_ANY_COMPONENT);
+ while (comp) {
+ type = icalcomponent_isa (comp);
+ if (type == ICAL_XSTANDARD_COMPONENT
+ || type == ICAL_XDAYLIGHT_COMPONENT) {
+ current_max_date = icaltime_null_time ();
+ current_tzname = NULL;
+
+ /* Step through the properties. We want to find the TZNAME, and
+ the largest DTSTART or RDATE. */
+ prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
+ while (prop) {
+ switch (icalproperty_isa (prop)) {
+ case ICAL_TZNAME_PROPERTY:
+ current_tzname = icalproperty_get_tzname (prop);
+ break;
+
+ case ICAL_DTSTART_PROPERTY:
+ dtstart = icalproperty_get_dtstart (prop);
+ if (icaltime_compare (dtstart, current_max_date) > 0)
+ current_max_date = dtstart;
+
+ break;
+
+ case ICAL_RDATE_PROPERTY:
+ rdate = icalproperty_get_rdate (prop);
+ if (icaltime_compare (rdate.time, current_max_date) > 0)
+ current_max_date = rdate.time;
+
+ break;
+
+ default:
+ break;
+ }
+
+ prop = icalcomponent_get_next_property (comp,
+ ICAL_ANY_PROPERTY);
+ }
+
+ if (current_tzname) {
+ if (type == ICAL_XSTANDARD_COMPONENT) {
+ if (!standard_tzname
+ || icaltime_compare (current_max_date,
+ standard_max_date) > 0) {
+ standard_max_date = current_max_date;
+ standard_tzname = current_tzname;
+ }
+ } else {
+ if (!daylight_tzname
+ || icaltime_compare (current_max_date,
+ daylight_max_date) > 0) {
+ daylight_max_date = current_max_date;
+ daylight_tzname = current_tzname;
+ }
+ }
+ }
+ }
+
+ comp = icalcomponent_get_next_component (component,
+ ICAL_ANY_COMPONENT);
+ }
+
+ /* If both standard and daylight TZNAMEs were found, if they are the same
+ we return just one, else we format them like "EST/EDT". */
+ if (standard_tzname && daylight_tzname) {
+ int standard_len, daylight_len;
+ char *tznames;
+
+ if (!strcmp (standard_tzname, daylight_tzname))
+ return strdup (standard_tzname);
+
+ standard_len = strlen (standard_tzname);
+ daylight_len = strlen (daylight_tzname);
+ tznames = malloc (standard_len + daylight_len + 2);
+ strcpy (tznames, standard_tzname);
+ tznames[standard_len] = '/';
+ strcpy (tznames + standard_len + 1, daylight_tzname);
+ return tznames;
+ } else {
+ const char *tznames;
+
+ /* If either of the TZNAMEs was found just return that, else NULL. */
+ tznames = standard_tzname ? standard_tzname : daylight_tzname;
+ return strdup (tznames);
+ }
+}
+
static void
icaltimezone_ensure_coverage (icaltimezone *zone,
@@ -228,7 +420,7 @@ icaltimezone_ensure_coverage (icaltimezone *zone,
int changes_end_year;
if (!zone->component)
- icaltimezone_load (zone);
+ icaltimezone_load_builtin_timezone (zone);
if (icaltimezone_minimum_expansion_year == -1) {
struct tm *tmp_tm;
@@ -922,80 +1114,37 @@ icaltimezone_adjust_change (icaltimezonechange *tt,
}
-
-/* Compares 2 VTIMEZONE components to see if they match, ignoring their TZIDs.
- It returns 1 if they match, 0 if they don't, or -1 on error. */
-int
-icaltimezone_compare_vtimezone (icalcomponent *vtimezone1,
- icalcomponent *vtimezone2)
+char*
+icaltimezone_get_tzid (icaltimezone *zone)
{
- icalproperty *prop1, *prop2;
- const char *tzid1, *tzid2;
- char *tzid2_copy, *string1, *string2;
- int cmp;
-
- /* Get the TZID property of the first VTIMEZONE. */
- prop1 = icalcomponent_get_first_property (vtimezone1, ICAL_TZID_PROPERTY);
- if (!prop1)
- return -1;
-
- tzid1 = icalproperty_get_tzid (prop1);
- if (!tzid1)
- return -1;
-
- /* Get the TZID property of the second VTIMEZONE. */
- prop2 = icalcomponent_get_first_property (vtimezone1, ICAL_TZID_PROPERTY);
- if (!prop2)
- return -1;
-
- tzid2 = icalproperty_get_tzid (prop2);
- if (!tzid2)
- return -1;
-
- /* Copy the second TZID, and set the property to the same as the first
- TZID, since we don't care if these match of not. */
- tzid2_copy = strdup (tzid2);
- icalproperty_set_tzid (prop2, tzid1);
-
- /* Now convert both VTIMEZONEs to strings and compare them. */
- string1 = icalcomponent_as_ical_string (vtimezone1);
- if (!string1) {
- free (tzid2_copy);
- return -1;
- }
-
- string2 = icalcomponent_as_ical_string (vtimezone2);
- if (!string2) {
- free (string1);
- free (tzid2_copy);
- return -1;
- }
+ /* If this is a local time, without a timezone, return NULL. */
+ if (!zone)
+ return NULL;
- cmp = strcmp (string1, string2);
-
- free (string1);
- free (string2);
-
- /* Now reset the second TZID. */
- icalproperty_set_tzid (prop2, tzid2_copy);
- free (tzid2_copy);
+ if (!zone->component)
+ icaltimezone_load_builtin_timezone (zone);
- return (cmp == 0) ? 1 : 0;
+ return zone->tzid;
}
-
char*
-icaltimezone_get_tzid (icaltimezone *zone)
+icaltimezone_get_location (icaltimezone *zone)
{
- return zone->tzid;
+ if (!zone->component)
+ icaltimezone_load_builtin_timezone (zone);
+
+ return zone->location;
}
char*
-icaltimezone_get_location (icaltimezone *zone)
+icaltimezone_get_tznames (icaltimezone *zone)
{
- return zone->location;
+ if (!zone->component)
+ icaltimezone_load_builtin_timezone (zone);
+
+ return zone->tznames;
}
@@ -1003,6 +1152,9 @@ icaltimezone_get_location (icaltimezone *zone)
double
icaltimezone_get_latitude (icaltimezone *zone)
{
+ if (!zone->component)
+ icaltimezone_load_builtin_timezone (zone);
+
return zone->latitude;
}
@@ -1011,6 +1163,9 @@ icaltimezone_get_latitude (icaltimezone *zone)
double
icaltimezone_get_longitude (icaltimezone *zone)
{
+ if (!zone->component)
+ icaltimezone_load_builtin_timezone (zone);
+
return zone->longitude;
}
@@ -1019,8 +1174,22 @@ icaltimezone_get_longitude (icaltimezone *zone)
icalcomponent*
icaltimezone_get_component (icaltimezone *zone)
{
+ if (!zone->component)
+ icaltimezone_load_builtin_timezone (zone);
+
return zone->component;
+}
+
+/* Sets the VTIMEZONE component of an icaltimezone, initializing the tzid,
+ location & tzname fields. It returns 1 on success or 0 on failure, i.e.
+ no TZID was found. */
+int
+icaltimezone_set_component (icaltimezone *zone,
+ icalcomponent *comp)
+{
+ icaltimezone_reset (zone);
+ return icaltimezone_get_vtimezone_properties (zone, comp);
}
@@ -1037,7 +1206,8 @@ icaltimezone_array_append_from_vtimezone (icalarray *timezones,
{
icaltimezone zone;
- if (icaltimezone_init_from_vtimezone (&zone, child))
+ icaltimezone_init (&zone);
+ if (icaltimezone_get_vtimezone_properties (&zone, child))
icalarray_append (timezones, &zone);
}
@@ -1061,7 +1231,95 @@ icaltimezone_get_builtin_timezones (void)
}
-/* Returns an icaltimezone corresponding to the given location. */
+/* Returns a single builtin timezone, given its Olson city name. */
+icaltimezone*
+icaltimezone_get_builtin_timezone (const char *location)
+{
+ icaltimezone *zone;
+ int lower, upper, middle, cmp;
+ char *zone_location;
+
+ fprintf (stderr, "Getting builtin timezone: %s\n", location);
+
+ if (!location || !location[0])
+ return NULL;
+
+ if (!strcmp (location, "UTC"))
+ return &utc_timezone;
+
+ if (!builtin_timezones)
+ icaltimezone_init_builtin_timezones ();
+
+ /* Do a simple binary search. */
+ lower = middle = 0;
+ upper = builtin_timezones->num_elements;
+
+ while (lower < upper) {
+ middle = (lower + upper) >> 1;
+ zone = icalarray_element_at (builtin_timezones, middle);
+ zone_location = icaltimezone_get_location (zone);
+ fprintf (stderr, " comparing with: %s\n", zone_location);
+ cmp = strcmp (location, zone_location);
+ if (cmp == 0)
+ return zone;
+ else if (cmp < 0)
+ upper = middle;
+ else
+ lower = middle + 1;
+ }
+
+ fprintf (stderr, " not found\n");
+
+ return NULL;
+}
+
+
+/* Returns a single builtin timezone, given its TZID. */
+icaltimezone*
+icaltimezone_get_builtin_timezone_from_tzid (const char *tzid)
+{
+ int num_slashes = 0;
+ const char *p, *zone_tzid;
+ icaltimezone *zone;
+
+ fprintf (stderr, "Getting builtin timezone from TZID: %s\n", tzid);
+
+ /* Check that the TZID starts with our unique prefix. */
+ if (strncmp (tzid, TZID_PREFIX, TZID_PREFIX_LEN))
+ return NULL;
+
+ /* Get the location, which is after the 3rd '/' character. */
+ p = tzid;
+ for (p = tzid; *p; p++) {
+ if (*p == '/') {
+ num_slashes++;
+ if (num_slashes == 3)
+ break;
+ }
+ }
+
+ if (num_slashes != 3)
+ return NULL;
+
+ p++;
+
+ /* Now we can use the function to get the builtin timezone from the
+ location string. */
+ zone = icaltimezone_get_builtin_timezone (p);
+ if (!zone)
+ return NULL;
+
+ /* Check that the builtin TZID matches exactly. We don't want to return
+ a different version of the VTIMEZONE. */
+ zone_tzid = icaltimezone_get_tzid (zone);
+ if (!strcmp (zone_tzid, tzid))
+ return zone;
+ else
+ return NULL;
+}
+
+
+/* Returns the special UTC timezone. */
icaltimezone*
icaltimezone_get_utc_timezone (void)
{
@@ -1104,6 +1362,8 @@ icaltimezone_parse_zone_tab (void)
icalerror_assert (builtin_timezones == NULL,
"Parsing zones.tab file multiple times");
+ builtin_timezones = icalarray_new (sizeof (icaltimezone), 32);
+
filename_len = strlen (ZONEINFO_DIRECTORY) + strlen (ZONES_TAB_FILENAME)
+ 2;
@@ -1123,8 +1383,6 @@ icaltimezone_parse_zone_tab (void)
return;
}
- builtin_timezones = icalarray_new (sizeof (icaltimezone), 32);
-
while (fgets (buf, sizeof(buf), fp)) {
if (*buf == '#') continue;
@@ -1174,13 +1432,17 @@ icaltimezone_parse_zone_tab (void)
/* Loads the builtin VTIMEZONE data for the given timezone. */
static void
-icaltimezone_load (icaltimezone *zone)
+icaltimezone_load_builtin_timezone (icaltimezone *zone)
{
char *filename;
int filename_len;
FILE *fp;
icalparser *parser;
- icalcomponent *comp;
+ icalcomponent *comp, *subcomp;
+
+ /* If the location isn't set, it isn't a builtin timezone. */
+ if (!zone->location || !zone->location[0])
+ return;
filename_len = strlen (ZONEINFO_DIRECTORY) + strlen (zone->location) + 6;
@@ -1207,7 +1469,14 @@ icaltimezone_load (icaltimezone *zone)
fclose (fp);
/* Find the VTIMEZONE component inside the VCALENDAR. There should be 1. */
- zone->component = icalcomponent_get_first_component (comp, ICAL_VTIMEZONE_COMPONENT);
+ subcomp = icalcomponent_get_first_component (comp,
+ ICAL_VTIMEZONE_COMPONENT);
+ if (!subcomp) {
+ icalerror_set_errno(ICAL_PARSE_ERROR);
+ return;
+ }
+
+ icaltimezone_get_vtimezone_properties (zone, subcomp);
}
diff --git a/libical/src/libical/icaltimezone.h b/libical/src/libical/icaltimezone.h
index 2f6487e1e9..693d6bbb80 100644
--- a/libical/src/libical/icaltimezone.h
+++ b/libical/src/libical/icaltimezone.h
@@ -29,11 +29,19 @@
#include <stdio.h> /* For FILE* */
#include "icaltime.h"
+#include "icalarray.h"
+#include "icalcomponent.h"
-/* An opaque struct representing a timezone. */
-typedef struct _icaltimezone icaltimezone;
+/*
+ * Creating/Destroying individual icaltimezones.
+ */
+/* Creates a new icaltimezone. */
+icaltimezone *icaltimezone_new (void);
+
+/* Frees all memory used for the icaltimezone. */
+void icaltimezone_free (icaltimezone *zone);
/*
@@ -46,6 +54,9 @@ icalarray* icaltimezone_get_builtin_timezones (void);
/* Returns a single builtin timezone, given its Olson city name. */
icaltimezone* icaltimezone_get_builtin_timezone (const char *location);
+/* Returns a single builtin timezone, given its TZID. */
+icaltimezone* icaltimezone_get_builtin_timezone_from_tzid (const char *tzid);
+
/* Returns the UTC timezone. */
icaltimezone* icaltimezone_get_utc_timezone (void);
@@ -55,6 +66,12 @@ char* icaltimezone_get_tzid (icaltimezone *zone);
/* Returns the city name of a timezone. */
char* icaltimezone_get_location (icaltimezone *zone);
+/* Returns the TZNAME properties used in the latest STANDARD and DAYLIGHT
+ components. If they are the same it will return just one, e.g. "LMT".
+ If they are different it will format them like "EST/EDT". Note that this
+ may also return NULL. */
+char* icaltimezone_get_tznames (icaltimezone *zone);
+
/* Returns the latitude of a builtin timezone. */
double icaltimezone_get_latitude (icaltimezone *zone);
@@ -64,6 +81,11 @@ double icaltimezone_get_longitude (icaltimezone *zone);
/* Returns the VTIMEZONE component of a timezone. */
icalcomponent* icaltimezone_get_component (icaltimezone *zone);
+/* Sets the VTIMEZONE component of an icaltimezone, initializing the tzid,
+ location & tzname fields. It returns 1 on success or 0 on failure, i.e.
+ no TZID was found. */
+int icaltimezone_set_component (icaltimezone *zone,
+ icalcomponent *comp);
/*
* Converting times between timezones.
@@ -94,21 +116,8 @@ int icaltimezone_get_utc_offset_of_utc_time (icaltimezone *zone,
-
-/*
- * Comparing VTIMEZONE components.
- */
-
-/* Compares 2 VTIMEZONE components to see if they match, ignoring their TZIDs.
- It returns 1 if they match, 0 if they don't, or -1 on error. */
-int icaltimezone_compare_vtimezone (icalcomponent *vtimezone1,
- icalcomponent *vtimezone2);
-
-
-
-
/*
- * Handling arrays of timezones.
+ * Handling arrays of timezones. Mainly for internal use.
*/
icalarray* icaltimezone_array_new (void);