[glib/wip/rancell/iso8601-2: 4/4] GDateTime: Support parsing ISO 8601 strings.
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/rancell/iso8601-2: 4/4] GDateTime: Support parsing ISO 8601 strings.
- Date: Wed, 5 Jul 2017 02:42:34 +0000 (UTC)
commit 3978920fa85a99378cf6703e23809675610f4dd8
Author: Robert Ancell <robert ancell canonical com>
Date: Thu Aug 25 11:53:54 2016 +1200
GDateTime: Support parsing ISO 8601 strings.
https://bugzilla.gnome.org/show_bug.cgi?id=753459
docs/reference/glib/glib-sections.txt | 1 +
glib/gdatetime.c | 310 +++++++++++++++++++++++++++++++++
glib/gdatetime.h | 4 +
glib/tests/gdatetime.c | 241 +++++++++++++++++++++++++
4 files changed, 556 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index e09d4d3..e8a7fc8 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -1667,6 +1667,7 @@ g_date_time_new_from_unix_utc
<SUBSECTION>
g_date_time_new_from_timeval_local
g_date_time_new_from_timeval_utc
+g_date_time_new_from_iso8601
<SUBSECTION>
g_date_time_new
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
index 745a32a..07c6a85 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -22,6 +22,7 @@
* Thiago Santos <thiago sousa santos collabora co uk>
* Emmanuele Bassi <ebassi linux intel com>
* Ryan Lortie <desrt desrt ca>
+ * Robert Ancell <robert ancell canonical com>
*/
/* Algorithms within this file are based on the Calendar FAQ by
@@ -902,6 +903,315 @@ g_date_time_new_from_timeval_utc (const GTimeVal *tv)
return datetime;
}
+static gboolean
+get_iso8601_int (const gchar *text, gsize length, gint *value)
+{
+ gint i, v = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ const gchar c = text[i];
+ if (c < '0' || c > '9')
+ return FALSE;
+ v = v * 10 + (c - '0');
+ }
+
+ *value = v;
+ return TRUE;
+}
+
+static gboolean
+get_iso8601_seconds (const gchar *text, gsize length, gdouble *value)
+{
+ gint i;
+ gdouble multiplier = 0.1, v = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ gchar c = text[i];
+ if (c == '.' || c == ',')
+ {
+ i++;
+ break;
+ }
+ if (c < '0' || c > '9')
+ return FALSE;
+ v = v * 10 + (c - '0');
+ }
+
+ for (; i < length; i++)
+ {
+ gchar c = text[i];
+ if (c < '0' || c > '9')
+ return FALSE;
+ v += (c - '0') * multiplier;
+ multiplier *= 0.1;
+ }
+
+ *value = v;
+ return TRUE;
+}
+
+static GDateTime *
+g_date_time_new_ordinal (GTimeZone *tz, gint year, gint ordinal_day, gint hour, gint minute, gdouble seconds)
+{
+ GDateTime *dt;
+
+ if (ordinal_day < 1 || ordinal_day > (GREGORIAN_LEAP (year) ? 366 : 365))
+ return NULL;
+
+ dt = g_date_time_new (tz, year, 1, 1, hour, minute, seconds);
+ dt->days += ordinal_day - 1;
+
+ return dt;
+}
+
+static GDateTime *
+g_date_time_new_week (GTimeZone *tz, gint year, gint week, gint week_day, gint hour, gint minute, gdouble
seconds)
+{
+ gint64 p;
+ gint max_week, jan4_week_day, ordinal_day;
+ GDateTime *dt;
+
+ p = (year * 365 + (year / 4) - (year / 100) + (year / 400)) % 7;
+ max_week = p == 4 ? 53 : 52;
+
+ if (week < 1 || week > max_week || week_day < 1 || week_day > 7)
+ return NULL;
+
+ dt = g_date_time_new (tz, year, 1, 4, 0, 0, 0);
+ g_date_time_get_week_number (dt, NULL, &jan4_week_day, NULL);
+ ordinal_day = (week * 7) + week_day - (jan4_week_day + 3);
+ if (ordinal_day < 0)
+ {
+ year--;
+ ordinal_day += GREGORIAN_LEAP (year) ? 366 : 365;
+ }
+ else if (ordinal_day > (GREGORIAN_LEAP (year) ? 366 : 365))
+ {
+ ordinal_day -= (GREGORIAN_LEAP (year) ? 366 : 365);
+ year++;
+ }
+
+ return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
+}
+
+static GDateTime *
+parse_iso8601_date (const gchar *text, gsize length,
+ gint hour, gint minute, gdouble seconds, GTimeZone *tz)
+{
+ /* YYYY-MM-DD */
+ if (length == 10 && text[4] == '-' && text[7] == '-')
+ {
+ int year, month, day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 5, 2, &month) ||
+ !get_iso8601_int (text + 8, 2, &day))
+ return NULL;
+ return g_date_time_new (tz, year, month, day, hour, minute, seconds);
+ }
+ /* YYYY-DDD */
+ else if (length == 8 && text[4] == '-')
+ {
+ gint year, ordinal_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 5, 3, &ordinal_day))
+ return NULL;
+ return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
+ }
+ /* YYYY-Www-D */
+ else if (length == 10 && text[4] == '-' && text[5] == 'W' && text[8] == '-')
+ {
+ gint year, week, week_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 6, 2, &week) ||
+ !get_iso8601_int (text + 9, 1, &week_day))
+ return NULL;
+ return g_date_time_new_week (tz, year, week, week_day, hour, minute, seconds);
+ }
+ /* YYYYWwwD */
+ else if (length == 8 && text[4] == 'W')
+ {
+ gint year, week, week_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 5, 2, &week) ||
+ !get_iso8601_int (text + 7, 1, &week_day))
+ return NULL;
+ return g_date_time_new_week (tz, year, week, week_day, hour, minute, seconds);
+ }
+ /* YYYYMMDD */
+ else if (length == 8)
+ {
+ int year, month, day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 4, 2, &month) ||
+ !get_iso8601_int (text + 6, 2, &day))
+ return NULL;
+ return g_date_time_new (tz, year, month, day, hour, minute, seconds);
+ }
+ /* YYYYDDD */
+ else if (length == 7)
+ {
+ gint year, ordinal_day;
+ if (!get_iso8601_int (text, 4, &year) ||
+ !get_iso8601_int (text + 4, 3, &ordinal_day))
+ return NULL;
+ return g_date_time_new_ordinal (tz, year, ordinal_day, hour, minute, seconds);
+ }
+ else
+ return FALSE;
+}
+
+static GTimeZone *
+parse_iso8601_timezone (const gchar *text, gsize length, gssize *tz_offset)
+{
+ gint i, tz_length, offset_hours, offset_minutes;
+
+ /* UTC uses Z suffix */
+ if (length > 0 && text[length - 1] == 'Z')
+ {
+ *tz_offset = length - 1;
+ return g_time_zone_new_utc ();
+ }
+
+ /* Look for '+' or '-' of offset */
+ for (i = length - 1; i >= 0; i--)
+ if (text[i] == '+' || text[i] == '-')
+ break;
+ if (i < 0)
+ return NULL;
+ tz_length = length - i;
+
+ /* +hh:mm or -hh:mm */
+ if (tz_length == 6 && text[i+3] == ':')
+ {
+ if (!get_iso8601_int (text + i + 1, 2, &offset_hours) ||
+ !get_iso8601_int (text + i + 4, 2, &offset_minutes))
+ return NULL;
+ }
+ /* +hhmm or -hhmm */
+ else if (tz_length == 5)
+ {
+ if (!get_iso8601_int (text + i + 1, 2, &offset_hours) ||
+ !get_iso8601_int (text + i + 3, 2, &offset_minutes))
+ return NULL;
+ }
+ /* +hh or -hh */
+ else if (tz_length == 3)
+ {
+ if (!get_iso8601_int (text + i + 1, 2, &offset_hours))
+ return NULL;
+ offset_minutes = 0;
+ }
+ else
+ return NULL;
+
+ *tz_offset = i;
+ return g_time_zone_new (text + i);
+}
+
+static gboolean
+parse_iso8601_time (const gchar *text, gsize length,
+ gint *hour, gint *minute, gdouble *seconds, GTimeZone **tz)
+{
+ gssize tz_offset = -1;
+
+ /* Check for timezone suffix */
+ *tz = parse_iso8601_timezone (text, length, &tz_offset);
+ if (tz_offset >= 0)
+ length = tz_offset;
+
+ /* hh:mm:ss(.sss) */
+ if (length >= 8 && text[2] == ':' && text[5] == ':')
+ {
+ return get_iso8601_int (text, 2, hour) &&
+ get_iso8601_int (text + 3, 2, minute) &&
+ get_iso8601_seconds (text + 6, length - 6, seconds);
+ }
+ /* hhmmss(.sss) */
+ else if (length >= 6)
+ {
+ return get_iso8601_int (text, 2, hour) &&
+ get_iso8601_int (text + 2, 2, minute) &&
+ get_iso8601_seconds (text + 4, length - 4, seconds);
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * g_date_time_new_from_iso8601:
+ * @text: an ISO 8601 formatted time string.
+ * @default_tz: (nullable): a #GTimeZone to use if the text doesn't contain a timezone, or %NULL.
+ *
+ * Creates a #GDateTime corresponding to the given [ISO 8601 formatted
string](https://en.wikipedia.org/wiki/ISO_8601)
+ * @text. Only the following subset of ISO 8601 is supported:
+ *
+ * <date>T<time> or <date>t<time> or <date> <time>
+ *
+ * <date> is in the form:
+ * YYYY-MM-DD - Year/month/day, e.g. 2016-08-24.
+ * YYYYMMDD - Same as above without dividers.
+ * YYYY-DDD - Ordinal day where DDD is from 001 to 366, e.g. 2016-237.
+ * YYYYDDD - Same as above without dividers.
+ * YYYY-Www-D - Week day where ww is from 01 to 52 and D from 1-7, e.g.
+ 2016-W34-3.
+ * YYYYWwwD - Same as above without dividers.
+ *
+ * <time> is in the form:
+ * hh:mm:ss(.sss) - Hours, minutes, seconds (subseconds), e.g. 22:10:42.123.
+ * hhmmss(.sss) - Same as above without dividers.
+ *
+ * Time can a timezone suffix in the form:
+ * Z - UTC.
+ * +hh:mm or -hh:mm - Offset from UTC in hours and minutes, e.g. +12:00.
+ * +hh or -hh - Offset from UTC in hours, e.g. +12.
+ *
+ * This call can fail (returning %NULL) if @text is not a valid ISO 8601
+ * formatted string.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: (transfer full): (nullable): a new #GDateTime, or %NULL
+ *
+ * Since: 2.54
+ */
+GDateTime *
+g_date_time_new_from_iso8601 (const gchar *text, GTimeZone *default_tz)
+{
+ gint length, date_length = -1;
+ gint hour = 0, minute = 0;
+ gdouble seconds = 0.0;
+ GTimeZone *tz = NULL;
+ GDateTime *datetime = NULL;
+
+ g_return_val_if_fail (text != NULL, NULL);
+
+ /* Date and time is separated by 'T', 't', or ' ' */
+ for (length = 0; text[length] != '\0'; length++)
+ {
+ if (date_length < 0 && (text[length] == 'T' || text[length] == 't' || text[length] == ' '))
+ date_length = length;
+ }
+
+ if (date_length < 0)
+ return NULL;
+
+ if (!parse_iso8601_time (text + date_length + 1, length - (date_length + 1),
+ &hour, &minute, &seconds, &tz))
+ goto out;
+ if (tz == NULL && default_tz == NULL)
+ return NULL;
+
+ datetime = parse_iso8601_date (text, date_length, hour, minute, seconds, tz ? tz : default_tz);
+
+out:
+ if (tz != NULL)
+ g_time_zone_unref (tz);
+ return datetime;
+}
+
/* full new functions {{{1 */
/**
diff --git a/glib/gdatetime.h b/glib/gdatetime.h
index 927e29c..02f1fc7 100644
--- a/glib/gdatetime.h
+++ b/glib/gdatetime.h
@@ -118,6 +118,10 @@ GDateTime * g_date_time_new_from_timeval_local (const G
GLIB_AVAILABLE_IN_ALL
GDateTime * g_date_time_new_from_timeval_utc (const GTimeVal *tv);
+GLIB_AVAILABLE_IN_2_54
+GDateTime * g_date_time_new_from_iso8601 (const gchar *text,
+ GTimeZone *default_tz);
+
GLIB_AVAILABLE_IN_ALL
GDateTime * g_date_time_new (GTimeZone *tz,
gint year,
diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c
index 4cb5a0a..7b2a419 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -25,11 +25,13 @@
#include <locale.h>
#define ASSERT_DATE(dt,y,m,d) G_STMT_START { \
+ g_assert ((dt)); \
g_assert_cmpint ((y), ==, g_date_time_get_year ((dt))); \
g_assert_cmpint ((m), ==, g_date_time_get_month ((dt))); \
g_assert_cmpint ((d), ==, g_date_time_get_day_of_month ((dt))); \
} G_STMT_END
#define ASSERT_TIME(dt,H,M,S) G_STMT_START { \
+ g_assert ((dt)); \
g_assert_cmpint ((H), ==, g_date_time_get_hour ((dt))); \
g_assert_cmpint ((M), ==, g_date_time_get_minute ((dt))); \
g_assert_cmpint ((S), ==, g_date_time_get_second ((dt))); \
@@ -476,6 +478,244 @@ test_GDateTime_new_from_timeval_utc (void)
}
static void
+test_GDateTime_new_from_iso8601 (void)
+{
+ GDateTime *dt;
+ GTimeZone *tz;
+
+ /* Need non-empty string */
+ dt = g_date_time_new_from_iso8601 ("", NULL);
+ g_assert_null (dt);
+
+ /* Needs to be correctly formatted */
+ dt = g_date_time_new_from_iso8601 ("not a date", NULL);
+ g_assert_null (dt);
+
+ /* Check common case */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+
+ /* Timezone is required in text or passed as arg */
+ tz = g_time_zone_new_utc ();
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42", tz);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ g_date_time_unref (dt);
+ g_time_zone_unref (tz);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42", NULL);
+ g_assert_null (dt);
+
+ /* Can't have whitespace */
+ dt = g_date_time_new_from_iso8601 ("2016 08 24T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42Z ", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 (" 2016-08-24T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Check lowercase time separator or space allowed */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24t22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24 22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+
+ /* Check dates without separators allowed */
+ dt = g_date_time_new_from_iso8601 ("20160824T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+
+ /* Months are two digits */
+ dt = g_date_time_new_from_iso8601 ("2016-1-01T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Days are two digits */
+ dt = g_date_time_new_from_iso8601 ("2016-01-1T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Need consistent usage of separators */
+ dt = g_date_time_new_from_iso8601 ("2016-0824T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("201608-24T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Check month within valid range */
+ dt = g_date_time_new_from_iso8601 ("2016-00-13T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-13-13T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Check day within valid range */
+ dt = g_date_time_new_from_iso8601 ("2016-01-00T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-01-32T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Check ordinal days work */
+ dt = g_date_time_new_from_iso8601 ("2016-237T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+ dt = g_date_time_new_from_iso8601 ("2016237T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+
+ /* Check ordinal leap days */
+ dt = g_date_time_new_from_iso8601 ("2016-366T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 12, 31);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+ dt = g_date_time_new_from_iso8601 ("2017-365T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2017, 12, 31);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+ dt = g_date_time_new_from_iso8601 ("2017-366T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Days start at 1 */
+ dt = g_date_time_new_from_iso8601 ("2016-000T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Limited to number of days in the year (2016 is a leap year) */
+ dt = g_date_time_new_from_iso8601 ("2016-367T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Days are two digits */
+ dt = g_date_time_new_from_iso8601 ("2016-1T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-12T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Check week days work */
+ dt = g_date_time_new_from_iso8601 ("2016-W34-3T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+ dt = g_date_time_new_from_iso8601 ("2016W343T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_date_time_unref (dt);
+
+ /* We don't support weeks without weekdays (valid ISO 8601) */
+ dt = g_date_time_new_from_iso8601 ("2016-W34T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016W34T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Weeks are two digits */
+ dt = g_date_time_new_from_iso8601 ("2016-W3-1T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Weeks start at 1 */
+ dt = g_date_time_new_from_iso8601 ("2016-W00-1T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Week one might be in the previous year */
+ dt = g_date_time_new_from_iso8601 ("2015-W01-1T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2014, 12, 29);
+ g_date_time_unref (dt);
+
+ /* Last week might be in next year */
+ dt = g_date_time_new_from_iso8601 ("2015-W53-7T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 1, 3);
+ g_date_time_unref (dt);
+
+ /* Week 53 doesn't always exist */
+ dt = g_date_time_new_from_iso8601 ("2016-W53-1T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Limited to number of days in the week */
+ dt = g_date_time_new_from_iso8601 ("2016-W34-0T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-W34-8T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Days are one digit */
+ dt = g_date_time_new_from_iso8601 ("2016-W34-99T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Check week day changes depending on year */
+ dt = g_date_time_new_from_iso8601 ("2017-W34-1T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2017, 8, 21);
+ g_date_time_unref (dt);
+
+ /* Check week day changes depending on leap years */
+ dt = g_date_time_new_from_iso8601 ("1900-W01-1T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 1900, 1, 1);
+ g_date_time_unref (dt);
+
+ /* YYYY-MM not allowed (NOT valid ISO 8601) */
+ dt = g_date_time_new_from_iso8601 ("2016-08T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* We don't support omitted year (valid ISO 8601) */
+ dt = g_date_time_new_from_iso8601 ("--08-24T22:10:42Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("--0824T22:10:42Z", NULL);
+ g_assert_null (dt);
+
+ /* Check subseconds work */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42.123456Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42.123456);
+ g_date_time_unref (dt);
+
+ /* Check time separators optional */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T221042.123456Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42.123456);
+ g_date_time_unref (dt);
+
+ /* We don't support times without minutes / seconds (valid ISO 8601) */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10Z", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T2210Z", NULL);
+ g_assert_null (dt);
+
+ /* UTC time uses 'Z' */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42Z", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, 0);
+ g_date_time_unref (dt);
+
+ /* Check timezone works */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42+12:00", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, 12 * G_TIME_SPAN_HOUR);
+ g_date_time_unref (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42+12", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, 12 * G_TIME_SPAN_HOUR);
+ g_date_time_unref (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22:10:42-02", NULL);
+ ASSERT_DATE (dt, 2016, 8, 24);
+ ASSERT_TIME (dt, 22, 10, 42);
+ g_assert_cmpint (g_date_time_get_utc_offset (dt), ==, -2 * G_TIME_SPAN_HOUR);
+ g_date_time_unref (dt);
+
+ /* Timezone seconds not allowed */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22-12:00:00", NULL);
+ g_assert_null (dt);
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22-12:00:00.000", NULL);
+ g_assert_null (dt);
+
+ /* Timezone hours two digits */
+ dt = g_date_time_new_from_iso8601 ("2016-08-24T22-2Z", NULL);
+ g_assert_null (dt);
+}
+
+static void
test_GDateTime_to_unix (void)
{
GDateTime *dt;
@@ -1753,6 +1993,7 @@ main (gint argc,
g_test_add_func ("/GDateTime/new_from_timeval", test_GDateTime_new_from_timeval);
g_test_add_func ("/GDateTime/new_from_timeval_utc", test_GDateTime_new_from_timeval_utc);
g_test_add_func ("/GDateTime/new_from_timeval/overflow", test_GDateTime_new_from_timeval_overflow);
+ g_test_add_func ("/GDateTime/new_from_iso8601", test_GDateTime_new_from_iso8601);
g_test_add_func ("/GDateTime/new_full", test_GDateTime_new_full);
g_test_add_func ("/GDateTime/now", test_GDateTime_now);
g_test_add_func ("/GDateTime/printf", test_GDateTime_printf);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]