[libgdata] Fixed handling of ISO 8601 dates (not date times) in calendar events



commit ff36399a92ced3c6a478a5961963d6ac2547d46f
Author: Philip Withnall <philip tecnocode co uk>
Date:   Thu Apr 23 20:04:37 2009 +0100

    Fixed handling of ISO 8601 dates (not date times) in calendar events
    
    Pure dates (not including a time) were not being handled at all. Parsing
    and producing XML for them now works, and a date parsing test case has been
    added.
---
 gdata/gdata-gdata.c                            |    4 +-
 gdata/gdata-gdata.h                            |    6 +-
 gdata/gdata-parser.c                           |   30 ++++++++
 gdata/gdata-private.h                          |    2 +
 gdata/services/calendar/gdata-calendar-event.c |   31 +++++++--
 gdata/tests/calendar.c                         |   89 +++++++++++++++++++++++-
 6 files changed, 153 insertions(+), 9 deletions(-)

diff --git a/gdata/gdata-gdata.c b/gdata/gdata-gdata.c
index e9c4833..e85fdac 100644
--- a/gdata/gdata-gdata.c
+++ b/gdata/gdata-gdata.c
@@ -119,6 +119,7 @@ gdata_gd_feed_link_free (GDataGDFeedLink *self)
  * gdata_gd_when_new:
  * @start_time: when the event starts or (for zero-duration events) when it occurs
  * @end_time: when the event ends, or %NULL
+ * @is_date: %TRUE if @start_time and @end_time specify dates rather than times, %FALSE otherwise
  * @value_string: a string to represent the time period, or %NULL
  * @reminders: a #GList of #GDataGDReminder<!-- -->s for the time period, or %NULL
  *
@@ -131,7 +132,7 @@ gdata_gd_feed_link_free (GDataGDFeedLink *self)
  * Return value: a new #GDataGDWhen, or %NULL on error
  **/
 GDataGDWhen *
-gdata_gd_when_new (GTimeVal *start_time, GTimeVal *end_time, const gchar *value_string, GList *reminders)
+gdata_gd_when_new (GTimeVal *start_time, GTimeVal *end_time, gboolean is_date, const gchar *value_string, GList *reminders)
 {
 	GDataGDWhen *self;
 
@@ -147,6 +148,7 @@ gdata_gd_when_new (GTimeVal *start_time, GTimeVal *end_time, const gchar *value_
 		self->end_time.tv_usec = 0;
 	}
 
+	self->is_date = is_date;
 	self->value_string = g_strdup (value_string);
 	self->reminders = reminders;
 
diff --git a/gdata/gdata-gdata.h b/gdata/gdata-gdata.h
index a7372fe..ed162b0 100644
--- a/gdata/gdata-gdata.h
+++ b/gdata/gdata-gdata.h
@@ -75,13 +75,14 @@ void gdata_gd_feed_link_free (GDataGDFeedLink *self);
  * GDataGDWhen:
  * @start_time: when the event starts or (for zero-duration events) when it occurs
  * @end_time: when the event ends
+ * @is_date: %TRUE if @start_time and @end_time specify dates rather than times, %FALSE otherwise
  * @value_string: a string to represent the time period, or %NULL
  * @reminders: a #GList of #GDataGDReminder<!-- -->s for the time period, or %NULL
  *
  * A structure fully representing a GData "when" element. The @start_time field is required, but the others are optional.
  *
  * If @end_time is empty (all fields are zero), the structure is considered to represent: an instance in time if
- * @start_time is a time, or an entire day if @start_time is a date.
+ * @start_time is a time (if @is_date is %FALSE), or an entire day if @start_time is a date (if @is_date is %TRUE).
  *
  * See the <ulink type="http" url="http://code.google.com/apis/gdata/elements.html#gdWhen";>GData specification</ulink>
  * for more information.
@@ -89,11 +90,12 @@ void gdata_gd_feed_link_free (GDataGDFeedLink *self);
 typedef struct {
 	GTimeVal start_time;
 	GTimeVal end_time;
+	gboolean is_date;
 	gchar *value_string;
 	GList *reminders;
 } GDataGDWhen;
 
-GDataGDWhen *gdata_gd_when_new (GTimeVal *start_time, GTimeVal *end_time, const gchar *value_string, GList *reminders);
+GDataGDWhen *gdata_gd_when_new (GTimeVal *start_time, GTimeVal *end_time, gboolean is_date, const gchar *value_string, GList *reminders);
 void gdata_gd_when_free (GDataGDWhen *self);
 
 /**
diff --git a/gdata/gdata-parser.c b/gdata/gdata-parser.c
index f0d713a..7c5f960 100644
--- a/gdata/gdata-parser.c
+++ b/gdata/gdata-parser.c
@@ -20,6 +20,8 @@
 #include <config.h>
 #include <glib.h>
 #include <glib/gi18n-lib.h>
+#include <sys/time.h>
+#include <time.h>
 
 #include "gdata-service.h"
 #include "gdata-parser.h"
@@ -93,3 +95,31 @@ gdata_parser_error_duplicate_element (const gchar *element_name, const gchar *pa
 		     element_name, parent_element_name);
 	return FALSE;
 }
+
+gboolean
+gdata_parser_time_val_from_date (const gchar *date, GTimeVal *_time)
+{
+	gchar *iso8601_date;
+	gboolean success;
+
+	if (strlen (date) != 10 && strlen (date) != 8)
+		return FALSE;
+
+	iso8601_date = g_strdup_printf ("%sT00:00:00Z", date);
+	success = g_time_val_from_iso8601 (iso8601_date, _time);
+	g_free (iso8601_date);
+
+	return success;
+}
+
+gchar *
+gdata_parser_date_from_time_val (GTimeVal *_time)
+{
+	time_t secs;
+	struct tm *tm;
+
+	secs = _time->tv_sec;
+	tm = gmtime (&secs);
+
+	return g_strdup_printf ("%4d-%02d-%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
+}
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index 41d7fcb..6bb994d 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -53,6 +53,8 @@ gboolean gdata_parser_error_unknown_property_value (const gchar *element_name, c
 gboolean gdata_parser_error_required_property_missing (const gchar *element_name, const gchar *property_name, GError **error);
 gboolean gdata_parser_error_required_element_missing (const gchar *element_name, const gchar *parent_element_name, GError **error);
 gboolean gdata_parser_error_duplicate_element (const gchar *element_name, const gchar *parent_element_name, GError **error);
+gboolean gdata_parser_time_val_from_date (const gchar *date, GTimeVal *_time);
+gchar *gdata_parser_date_from_time_val (GTimeVal *_time) G_GNUC_WARN_UNUSED_RESULT;
 
 G_END_DECLS
 
diff --git a/gdata/services/calendar/gdata-calendar-event.c b/gdata/services/calendar/gdata-calendar-event.c
index ae9ba47..450a39a 100644
--- a/gdata/services/calendar/gdata-calendar-event.c
+++ b/gdata/services/calendar/gdata-calendar-event.c
@@ -466,10 +466,13 @@ parse_xml (GDataEntry *entry, xmlDoc *doc, xmlNode *node, GError **error)
 		xmlChar *start_time, *end_time, *value_string;
 		GTimeVal start_time_timeval, end_time_timeval;
 		GDataGDWhen *when;
+		gboolean is_date = FALSE;
 
 		/* Start time */
 		start_time = xmlGetProp (node, (xmlChar*) "startTime");
-		if (g_time_val_from_iso8601 ((gchar*) start_time, &start_time_timeval) == FALSE) {
+		if (gdata_parser_time_val_from_date ((gchar*) start_time, &start_time_timeval) == TRUE) {
+			is_date = TRUE;
+		} else if (g_time_val_from_iso8601 ((gchar*) start_time, &start_time_timeval) == FALSE) {
 			/* Error */
 			gdata_parser_error_not_iso8601_format ("gd:when", "entry", (gchar*) start_time, error);
 			xmlFree (start_time);
@@ -480,7 +483,14 @@ parse_xml (GDataEntry *entry, xmlDoc *doc, xmlNode *node, GError **error)
 		/* End time (optional) */
 		end_time = xmlGetProp (node, (xmlChar*) "endTime");
 		if (end_time != NULL) {
-			if (g_time_val_from_iso8601 ((gchar*) end_time, &end_time_timeval) == FALSE) {
+			gboolean success;
+
+			if (is_date == TRUE)
+				success = gdata_parser_time_val_from_date ((gchar*) end_time, &end_time_timeval);
+			else
+				success = g_time_val_from_iso8601 ((gchar*) end_time, &end_time_timeval);
+
+			if (success == FALSE) {
 				/* Error */
 				gdata_parser_error_not_iso8601_format ("gd:when", "entry", (gchar*) end_time, error);
 				xmlFree (end_time);
@@ -490,7 +500,7 @@ parse_xml (GDataEntry *entry, xmlDoc *doc, xmlNode *node, GError **error)
 		}
 
 		value_string = xmlGetProp (node, (xmlChar*) "value");
-		when = gdata_gd_when_new (&start_time_timeval, (end_time == NULL) ? NULL : &end_time_timeval, (gchar*) value_string, NULL);
+		when = gdata_gd_when_new (&start_time_timeval, (end_time == NULL) ? NULL : &end_time_timeval, is_date, (gchar*) value_string, NULL);
 		xmlFree (value_string);
 
 		gdata_calendar_event_add_time (self, when);
@@ -613,14 +623,25 @@ get_xml (GDataEntry *entry, GString *xml_string)
 		g_string_append (xml_string, "<gCal:anyoneCanAddSelf value='false'/>");
 
 	for (i = priv->times; i != NULL; i = i->next) {
+		gchar *start_time;
 		GDataGDWhen *when = (GDataGDWhen*) i->data;
 
-		gchar *start_time = g_time_val_to_iso8601 (&(when->start_time));
+		if (when->is_date == TRUE)
+			start_time = gdata_parser_date_from_time_val (&(when->start_time));
+		else
+			start_time = g_time_val_to_iso8601 (&(when->start_time));
+
 		g_string_append_printf (xml_string, "<gd:when startTime='%s'", start_time);
 		g_free (start_time);
 
 		if (when->end_time.tv_sec != 0 || when->end_time.tv_usec != 0) {
-			gchar *end_time = g_time_val_to_iso8601 (&(when->end_time));
+			gchar *end_time;
+
+			if (when->is_date == TRUE)
+				end_time = gdata_parser_date_from_time_val (&(when->end_time));
+			else
+				end_time = g_time_val_to_iso8601 (&(when->end_time));
+
 			g_string_append_printf (xml_string, " endTime='%s'", end_time);
 			g_free (end_time);
 		}
diff --git a/gdata/tests/calendar.c b/gdata/tests/calendar.c
index 912cbc7..f59b848 100644
--- a/gdata/tests/calendar.c
+++ b/gdata/tests/calendar.c
@@ -247,7 +247,7 @@ test_insert_simple (void)
 	gdata_calendar_event_add_person (event, who);
 	g_time_val_from_iso8601 ("2009-04-17T15:00:00.000Z", &start_time);
 	g_time_val_from_iso8601 ("2009-04-17T17:00:00.000Z", &end_time);
-	when = gdata_gd_when_new (&start_time, &end_time, NULL, NULL);
+	when = gdata_gd_when_new (&start_time, &end_time, FALSE, NULL, NULL);
 	gdata_calendar_event_add_time (event, when);
 
 	/* Check the XML */
@@ -286,6 +286,92 @@ test_insert_simple (void)
 	g_object_unref (new_event);
 }
 
+static void
+test_xml_dates (void)
+{
+	GDataCalendarEvent *event;
+	GList *times, *i;
+	GDataGDWhen *when;
+	gchar *xml;
+	GError *error = NULL;
+
+	event = gdata_calendar_event_new_from_xml (
+		"<entry xmlns='http://www.w3.org/2005/Atom' "
+		 	"xmlns:gd='http://schemas.google.com/g/2005' "
+		 	"xmlns:gCal='http://schemas.google.com/gCal/2005' "
+		 	"xmlns:app='http://www.w3.org/2007/app'>"
+		 	"<title type='text'>Tennis with Beth</title>"
+		 	"<content type='text'>Meet for a quick lesson.</content>"
+		 	"<category term='http://schemas.google.com/g/2005#event' scheme='http://schemas.google.com/g/2005#kind'/>"
+		 	"<gd:when startTime='2009-04-17'/>"
+		 	"<gd:when startTime='2009-04-17T15:00:00Z'/>"
+		 	"<gd:when startTime='2009-04-27' endTime='20090506'/>"
+		 "</entry>", -1, &error);
+	g_assert_no_error (error);
+	g_assert (GDATA_IS_ENTRY (event));
+	g_clear_error (&error);
+
+	/* Check the times */
+	times = i = gdata_calendar_event_get_times (event);
+
+	/* First time */
+	when = (GDataGDWhen*) i->data;
+	g_assert (i->next != NULL);
+	g_assert (when->is_date == TRUE);
+	g_assert_cmpint (when->start_time.tv_sec, ==, 1239926400);
+	g_assert_cmpint (when->start_time.tv_usec, ==, 0);
+	g_assert_cmpint (when->end_time.tv_sec, ==, 0);
+	g_assert_cmpint (when->end_time.tv_usec, ==, 0);
+	g_assert (when->value_string == NULL);
+	g_assert (when->reminders == NULL);
+
+	/* Second time */
+	i = i->next;
+	when = (GDataGDWhen*) i->data;
+	g_assert (i->next != NULL);
+	g_assert (when->is_date == FALSE);
+	g_assert_cmpint (when->start_time.tv_sec, ==, 1239926400 + 54000);
+	g_assert_cmpint (when->start_time.tv_usec, ==, 0);
+	g_assert_cmpint (when->end_time.tv_sec, ==, 0);
+	g_assert_cmpint (when->end_time.tv_usec, ==, 0);
+	g_assert (when->value_string == NULL);
+	g_assert (when->reminders == NULL);
+
+	/* Third time */
+	i = i->next;
+	when = (GDataGDWhen*) i->data;
+	g_assert (i->next == NULL);
+	g_assert (when->is_date == TRUE);
+	g_assert_cmpint (when->start_time.tv_sec, ==, 1239926400 + 864000);
+	g_assert_cmpint (when->start_time.tv_usec, ==, 0);
+	g_assert_cmpint (when->end_time.tv_sec, ==, 1241568000);
+	g_assert_cmpint (when->end_time.tv_usec, ==, 0);
+	g_assert (when->value_string == NULL);
+	g_assert (when->reminders == NULL);
+
+	/* Check the XML */
+	xml = gdata_entry_get_xml (GDATA_ENTRY (event));
+	g_assert_cmpstr (xml, ==,
+			 "<entry xmlns='http://www.w3.org/2005/Atom' "
+			 	"xmlns:gd='http://schemas.google.com/g/2005' "
+			 	"xmlns:gCal='http://schemas.google.com/gCal/2005' "
+			 	"xmlns:app='http://www.w3.org/2007/app'>"
+			 	"<title type='text'>Tennis with Beth</title>"
+			 	"<content type='text'>Meet for a quick lesson.</content>"
+			 	"<category term='http://schemas.google.com/g/2005#event' scheme='http://schemas.google.com/g/2005#kind'/>"
+			 	"<gCal:guestsCanModify value='false'/>"
+				"<gCal:guestsCanInviteOthers value='false'/>"
+				"<gCal:guestsCanSeeGuests value='false'/>"
+				"<gCal:anyoneCanAddSelf value='false'/>"
+			 	"<gd:when startTime='2009-04-17'/>"
+			 	"<gd:when startTime='2009-04-17T15:00:00Z'/>"
+			 	"<gd:when startTime='2009-04-27' endTime='2009-05-06'/>"
+			 "</entry>");
+	g_free (xml);
+
+	g_object_unref (event);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -308,6 +394,7 @@ main (int argc, char *argv[])
 	g_test_add_func ("/calendar/query/events", test_query_events);
 	if (g_test_slow () == TRUE)
 		g_test_add_func ("/calendar/insert/simple", test_insert_simple);
+	g_test_add_func ("/calendar/xml/dates", test_xml_dates);
 
 	retval = g_test_run ();
 	if (service != NULL)



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