[libsoup] Fix soup_date_to_string() to deal with non-UTC times correctly
- From: Dan Winship <danw src gnome org>
- To: svn-commits-list gnome org
- Subject: [libsoup] Fix soup_date_to_string() to deal with non-UTC times correctly
- Date: Sat, 18 Apr 2009 11:46:24 -0400 (EDT)
commit 9a830c22cbf64d71e64b45b20d32f306ecc23a3d
Author: Dan Winship <danw gnome org>
Date: Sat Apr 18 11:37:26 2009 -0400
Fix soup_date_to_string() to deal with non-UTC times correctly
soup_date_new_from_string: fix setting of UTC vs floating for ISO8601
and RFC2822 timestamps. Allow a time of "24:00:00" (per ISO).
soup_date_to_string: coerce @date to UTC for HTTP and cookie dates,
output correct offset for RFC2822, ISO compact, and ISO full.
---
libsoup/soup-date.c | 234 ++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 184 insertions(+), 50 deletions(-)
diff --git a/libsoup/soup-date.c b/libsoup/soup-date.c
index daf2ffe..13364ba 100644
--- a/libsoup/soup-date.c
+++ b/libsoup/soup-date.c
@@ -103,6 +103,64 @@ soup_date_get_type (void)
return type_volatile;
}
+static void
+soup_date_fixup (SoupDate *date)
+{
+ /* We only correct date->second if it's negative or too high
+ * to be a leap second.
+ */
+ if (date->second < 0 || date->second > 61) {
+ date->minute += date->second / 60;
+ date->second %= 60;
+ if (date->second < 0)
+ date->second += 60;
+ }
+
+ if (date->minute < 0 || date->minute > 59) {
+ date->hour += date->minute / 60;
+ date->minute %= 60;
+ if (date->minute < 0)
+ date->minute += 60;
+ }
+
+ if (date->hour < 0 || date->hour > 23) {
+ date->day += date->hour / 24;
+ date->hour %= 24;
+ if (date->hour < 0)
+ date->hour += 24;
+ }
+
+ /* Have to make sure month is valid before we can look at the
+ * day.
+ */
+ if (date->month < 1 || date->month > 12) {
+ date->year += ((date->month - 1) / 12) + 1;
+ date->month = ((date->month - 1) % 12) + 1;
+ if (date->month < 1)
+ date->month += 12;
+ }
+
+ if (date->day < 0) {
+ while (date->day < 0) {
+ if (date->month == 1) {
+ date->month = 12;
+ date->year--;
+ } else
+ date->month--;
+ date->day += days_in_month (date->month, date->year);
+ }
+ } else {
+ while (date->day > days_in_month (date->month, date->year)) {
+ date->day -= days_in_month (date->month, date->year);
+ if (date->month == 12) {
+ date->month = 1;
+ date->year++;
+ } else
+ date->month++;
+ }
+ }
+}
+
/**
* soup_date_new:
* @year: the year (1-9999)
@@ -110,7 +168,7 @@ soup_date_get_type (void)
* @day: the day of the month (1-31, as appropriate for @month)
* @hour: the hour (0-23)
* @minute: the minute (0-59)
- * @second: the second (0-59)
+ * @second: the second (0-59, or up to 61 for leap seconds)
*
* Creates a #SoupDate representing the indicated time, UTC.
*
@@ -206,7 +264,10 @@ parse_iso8601_date (SoupDate *date, const char *date_string)
else
val = 60 * (val / 100) + (val % 100);
date->offset = sign * val;
- date->utc = sign && !val;
+ date->utc = !val;
+ } else {
+ date->offset = 0;
+ date->utc = FALSE;
}
return !*date_string;
@@ -303,7 +364,7 @@ parse_timezone (SoupDate *date, const char **date_string)
else
val = 60 * (val / 100) + (val % 100);
date->offset = sign * val;
- date->utc = sign && !val;
+ date->utc = (sign == -1) && !val;
} else if (**date_string == 'Z') {
date->offset = 0;
date->utc = TRUE;
@@ -406,7 +467,8 @@ parse_textual_date (SoupDate *date, const char *date_string)
* and reasonable approximations thereof. (Eg, it is lenient about
* whitespace, leading "0"s, etc.)
*
- * Return value: a new #SoupDate
+ * Return value: a new #SoupDate, or %NULL if @date_string could not
+ * be parsed.
**/
SoupDate *
soup_date_new_from_string (const char *date_string)
@@ -441,13 +503,25 @@ soup_date_new_from_string (const char *date_string)
date->month < 1 || date->month > 12 ||
date->day < 1 ||
date->day > days_in_month (date->month, date->year) ||
- date->hour < 0 || date->hour > 23 ||
+ date->hour < 0 || date->hour > 24 ||
date->minute < 0 || date->minute > 59 ||
- date->second < 0 || date->second > 59) {
- g_slice_free (SoupDate, date);
+ date->second < 0 || date->second > 61) {
+ soup_date_free (date);
return NULL;
- } else
- return date;
+ }
+ if (date->hour == 24) {
+ /* ISO8601 allows this explicitly. We allow it for
+ * other types as well just for simplicity.
+ */
+ if (date->minute == 0 && date->second == 0)
+ soup_date_fixup (date);
+ else {
+ soup_date_free (date);
+ return NULL;
+ }
+ }
+
+ return date;
}
/**
@@ -496,50 +570,110 @@ soup_date_to_string (SoupDate *date, SoupDateFormat format)
{
g_return_val_if_fail (date != NULL, NULL);
- /* FIXME: offset, 8601 zones, etc */
-
- switch (format) {
- case SOUP_DATE_HTTP:
- /* "Sun, 06 Nov 1994 08:49:37 GMT" */
- return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d GMT",
- soup_date_weekday (date), date->day,
- months[date->month - 1],
- date->year, date->hour, date->minute,
- date->second);
-
- case SOUP_DATE_COOKIE:
- /* "Sun, 06-Nov-1994 08:49:37 GMT" */
- return g_strdup_printf ("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
- soup_date_weekday (date), date->day,
- months[date->month - 1],
- date->year, date->hour, date->minute,
- date->second);
-
- case SOUP_DATE_ISO8601_COMPACT:
- return g_strdup_printf ("%04d%02d%02dT%02d%02d%02d",
- date->year, date->month, date->day,
- date->hour, date->minute, date->second);
- case SOUP_DATE_ISO8601_FULL:
- return g_strdup_printf ("%04d-%02d-%02dT%02d:%02d:%02d",
- date->year, date->month, date->day,
- date->hour, date->minute, date->second);
- case SOUP_DATE_ISO8601_XMLRPC:
+ if (format == SOUP_DATE_HTTP || format == SOUP_DATE_COOKIE) {
+ /* HTTP and COOKIE formats require UTC timestamp, so coerce
+ * @date if it's non-UTC.
+ */
+ SoupDate utcdate;
+
+ if (date->offset != 0) {
+ memcpy (&utcdate, date, sizeof (SoupDate));
+ utcdate.minute += utcdate.offset;
+ utcdate.offset = 0;
+ utcdate.utc = TRUE;
+ soup_date_fixup (&utcdate);
+ date = &utcdate;
+ }
+
+ switch (format) {
+ case SOUP_DATE_HTTP:
+ /* "Sun, 06 Nov 1994 08:49:37 GMT" */
+ return g_strdup_printf (
+ "%s, %02d %s %04d %02d:%02d:%02d GMT",
+ soup_date_weekday (date), date->day,
+ months[date->month - 1], date->year,
+ date->hour, date->minute, date->second);
+
+ case SOUP_DATE_COOKIE:
+ /* "Sun, 06-Nov-1994 08:49:37 GMT" */
+ return g_strdup_printf (
+ "%s, %02d-%s-%04d %02d:%02d:%02d GMT",
+ soup_date_weekday (date), date->day,
+ months[date->month - 1], date->year,
+ date->hour, date->minute, date->second);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+ } else if (format == SOUP_DATE_ISO8601_XMLRPC) {
+ /* Always "floating", ignore offset */
return g_strdup_printf ("%04d%02d%02dT%02d:%02d:%02d",
date->year, date->month, date->day,
date->hour, date->minute, date->second);
- case SOUP_DATE_RFC2822:
- {
- int hour_offset = abs (date->offset) / 60;
- /* "Sun, 6 Nov 1994 09:49:37 -0100" */
- return g_strdup_printf ("%s, %d %s %04d %02d:%02d:%02d %c%02d%02d",
- soup_date_weekday (date), date->day,
- months[date->month - 1],
- date->year, date->hour, date->minute,
- date->second, (date->offset > 0) ? '-' : '+',
- hour_offset, abs (date->offset) - (hour_offset * 60));
- }
- default:
- return NULL;
+ } else {
+ int hour_offset, minute_offset;
+ char zone[8], sign;
+
+ /* For other ISO8601 formats or RFC2822, use the
+ * offset given in @date. For ISO8601 formats, use "Z"
+ * for UTC, +-offset for non-UTC, and nothing for
+ * floating. For RFC2822, use +-offset for UTC or
+ * non-UTC, and -0000 for floating.
+ */
+ hour_offset = abs (date->offset) / 60;
+ minute_offset = abs (date->offset) - hour_offset * 60;
+
+ switch (format) {
+ case SOUP_DATE_ISO8601_COMPACT:
+ /* "19941106T084937[zone]" */
+ if (date->utc)
+ strcpy (zone, "Z");
+ else if (date->offset) {
+ g_snprintf (zone, sizeof (zone), "%c%02d%02d",
+ date->offset > 0 ? '-' : '+',
+ hour_offset, minute_offset);
+ } else
+ *zone = '\0';
+
+ return g_strdup_printf (
+ "%04d%02d%02dT%02d%02d%02d%s",
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second,
+ zone);
+
+ case SOUP_DATE_ISO8601_FULL:
+ /* "1994-11-06T08:49:37[zone]" */
+ if (date->utc)
+ strcpy (zone, "Z");
+ else if (date->offset) {
+ g_snprintf (zone, sizeof (zone), "%c%02d:%02d",
+ date->offset > 0 ? '-' : '+',
+ hour_offset, minute_offset);
+ } else
+ *zone = '\0';
+
+ return g_strdup_printf (
+ "%04d-%02d-%02dT%02d:%02d:%02d%s",
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second,
+ zone);
+
+ case SOUP_DATE_RFC2822:
+ /* "Sun, 6 Nov 1994 09:49:37 -0100" */
+ if (date->offset)
+ sign = (date->offset > 0) ? '-' : '+';
+ else
+ sign = date->utc ? '+' : '-';
+ return g_strdup_printf (
+ "%s, %d %s %04d %02d:%02d:%02d %c%02d%02d",
+ soup_date_weekday (date), date->day,
+ months[date->month - 1], date->year,
+ date->hour, date->minute, date->second,
+ sign, hour_offset, minute_offset);
+
+ default:
+ return NULL;
+ }
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]