From 88f6334af616b32fb8851d79faa8d31df552fea4 Mon Sep 17 00:00:00 2001 From: Vivia Nikolaidou Date: Mon, 10 Aug 2015 15:31:37 +0300 Subject: [PATCH] datetime: accept just a time as ISO 8601 string and use today's date then If no date and only a time is given in gst_date_time_new_from_iso8601_string(), assume that it is "today" and try to parse the time-only string. "Today" is assumed to be in the timezone provided by the user (if any), otherwise Z - just like the behavior of the existing code. https://bugzilla.gnome.org/show_bug.cgi?id=753455 --- gst/gstdatetime.c | 103 +++++++++++++++++++++++----------- tests/check/gst/gstdatetime.c | 70 +++++++++++++++++++++++ 2 files changed, 140 insertions(+), 33 deletions(-) diff --git a/gst/gstdatetime.c b/gst/gstdatetime.c index 8c0eda577e..6e7ea27b35 100644 --- a/gst/gstdatetime.c +++ b/gst/gstdatetime.c @@ -748,7 +748,13 @@ gst_date_time_to_iso8601_string (GstDateTime * datetime) * @string: ISO 8601-formatted datetime string. * * Tries to parse common variants of ISO-8601 datetime strings into a - * #GstDateTime. + * #GstDateTime. Possible input formats are (for example): + * 2012-06-30T22:46:43Z, 2012, 2012-06, 2012-06-30, 2012-06-30T22:46:43-0430, + * 2012-06-30T22:46Z, 2012-06-30T22:46-0430, 2012-06-30 22:46, + * 2012-06-30 22:46:43, 2012-06-00, 2012-00-00, 2012-00-30, 22:46:43Z, 22:46Z, + * 22:46:43-0430, 22:46-0430, 22:46:30, 22:46 + * If no date is provided, it is assumed to be "today" in the timezone + * provided (if any), otherwise UTC. * * Free-function: gst_date_time_unref * @@ -759,6 +765,7 @@ GstDateTime * gst_date_time_new_from_iso8601_string (const gchar * string) { gint year = -1, month = -1, day = -1, hour = -1, minute = -1; + gint gmt_offset_hour = -99, gmt_offset_min = -99; gdouble second = -1.0; gfloat tzoffset = 0.0; guint64 usecs; @@ -770,40 +777,45 @@ gst_date_time_new_from_iso8601_string (const gchar * string) len = strlen (string); - if (len < 4 || !g_ascii_isdigit (string[0]) || !g_ascii_isdigit (string[1]) - || !g_ascii_isdigit (string[2]) || !g_ascii_isdigit (string[3])) + /* The input string is expected to start either with a year (4 digits) or + * with an hour (2 digits). Hour must be followed by minute. In any case, + * the string must be at least 4 characters long and start with 2 digits */ + if (len < 4 || !g_ascii_isdigit (string[0]) || !g_ascii_isdigit (string[1])) return NULL; - ret = sscanf (string, "%04d-%02d-%02d", &year, &month, &day); + if (g_ascii_isdigit (string[2]) && g_ascii_isdigit (string[3])) { + ret = sscanf (string, "%04d-%02d-%02d", &year, &month, &day); - if (ret == 0) - return NULL; + if (ret == 0) + return NULL; - if (ret == 3 && day <= 0) { - ret = 2; - day = -1; + if (ret == 3 && day <= 0) { + ret = 2; + day = -1; + } + + if (ret >= 2 && month <= 0) { + ret = 1; + month = day = -1; + } + + if (ret >= 1 && year <= 0) + return NULL; + + else if (ret >= 1 && len < 16) + /* YMD is 10 chars. XMD + HM will be 16 chars. if it is less, + * it make no sense to continue. We will stay with YMD. */ + goto ymd; + + string += 10; + /* Exit if there is no expeceted value on this stage */ + if (!(*string == 'T' || *string == '-' || *string == ' ')) + goto ymd; + + string += 1; } - - if (ret >= 2 && month <= 0) { - ret = 1; - month = day = -1; - } - - if (ret >= 1 && year <= 0) - return NULL; - - else if (ret >= 1 && len < 16) - /* YMD is 10 chars. XMD + HM will be 16 chars. if it is less, - * it make no sense to continue. We will stay with YMD. */ - goto ymd; - - string += 10; - /* Exit if there is no expeceted value on this stage */ - if (!(*string == 'T' || *string == '-' || *string == ' ')) - goto ymd; - - /* if hour or minute fails, then we will use onlly ymd. */ - hour = g_ascii_strtoull (string + 1, (gchar **) & string, 10); + /* if hour or minute fails, then we will use only ymd. */ + hour = g_ascii_strtoull (string, (gchar **) & string, 10); if (hour > 24 || *string != ':') goto ymd; @@ -838,7 +850,7 @@ gst_date_time_new_from_iso8601_string (const gchar * string) goto ymd_hms; else { /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */ - gint gmt_offset_hour = -1, gmt_offset_min = -1, gmt_offset = -1; + gint gmt_offset = -1; gchar *plus_pos = NULL; gchar *neg_pos = NULL; gchar *pos = NULL; @@ -863,9 +875,11 @@ gst_date_time_new_from_iso8601_string (const gchar * string) GST_DEBUG ("Parsing timezone: %s", pos); if (ret_tz == 2) { + if (neg_pos != NULL && neg_pos + 1 == pos) { + gmt_offset_hour *= -1; + gmt_offset_min *= -1; + } gmt_offset = gmt_offset_hour * 60 + gmt_offset_min; - if (neg_pos != NULL && neg_pos + 1 == pos) - gmt_offset *= -1; tzoffset = gmt_offset / 60.0; @@ -876,8 +890,31 @@ gst_date_time_new_from_iso8601_string (const gchar * string) } ymd_hms: + if (year == -1 || month == -1 || day == -1) { + GDateTime *now_utc, *now_in_given_tz; + + /* No date was supplied: make it today */ + now_utc = g_date_time_new_now_utc (); + if (tzoffset != 0.0) { + /* If a timezone offset was supplied, get the date of that timezone */ + g_assert (gmt_offset_min != -99); + g_assert (gmt_offset_hour != -99); + now_in_given_tz = + g_date_time_add_minutes (now_utc, + (60 * gmt_offset_hour) + gmt_offset_min); + g_date_time_unref (now_utc); + } else { + now_in_given_tz = now_utc; + } + g_date_time_get_ymd (now_in_given_tz, &year, &month, &day); + g_date_time_unref (now_in_given_tz); + } return gst_date_time_new (tzoffset, year, month, day, hour, minute, second); ymd: + if (year == -1) { + /* No date was supplied and time failed to parse */ + return NULL; + } return gst_date_time_new_ymd (year, month, day); } diff --git a/tests/check/gst/gstdatetime.c b/tests/check/gst/gstdatetime.c index eb637c4b08..ae15dc55bc 100644 --- a/tests/check/gst/gstdatetime.c +++ b/tests/check/gst/gstdatetime.c @@ -385,6 +385,7 @@ GST_START_TEST (test_GstDateTime_iso8601) { GstDateTime *dt, *dt2; gchar *str, *str2; + GDateTime *gdt, *gdt2; dt = gst_date_time_new_now_utc (); fail_unless (gst_date_time_has_year (dt)); @@ -632,6 +633,75 @@ GST_START_TEST (test_GstDateTime_iso8601) fail_unless (!gst_date_time_has_day (dt)); fail_unless (!gst_date_time_has_time (dt)); gst_date_time_unref (dt); + + + /* only time provided - we assume today's date */ + gdt = g_date_time_new_now_utc (); + + dt = gst_date_time_new_from_iso8601_string ("15:50:33"); + fail_unless (gst_date_time_get_year (dt) == g_date_time_get_year (gdt)); + fail_unless (gst_date_time_get_month (dt) == g_date_time_get_month (gdt)); + fail_unless (gst_date_time_get_day (dt) == + g_date_time_get_day_of_month (gdt)); + fail_unless (gst_date_time_get_hour (dt) == 15); + fail_unless (gst_date_time_get_minute (dt) == 50); + fail_unless (gst_date_time_get_second (dt) == 33); + gst_date_time_unref (dt); + + dt = gst_date_time_new_from_iso8601_string ("15:50:33Z"); + fail_unless (gst_date_time_get_year (dt) == g_date_time_get_year (gdt)); + fail_unless (gst_date_time_get_month (dt) == g_date_time_get_month (gdt)); + fail_unless (gst_date_time_get_day (dt) == + g_date_time_get_day_of_month (gdt)); + fail_unless (gst_date_time_get_hour (dt) == 15); + fail_unless (gst_date_time_get_minute (dt) == 50); + fail_unless (gst_date_time_get_second (dt) == 33); + gst_date_time_unref (dt); + + dt = gst_date_time_new_from_iso8601_string ("15:50"); + fail_unless (gst_date_time_get_year (dt) == g_date_time_get_year (gdt)); + fail_unless (gst_date_time_get_month (dt) == g_date_time_get_month (gdt)); + fail_unless (gst_date_time_get_day (dt) == + g_date_time_get_day_of_month (gdt)); + fail_unless (gst_date_time_get_hour (dt) == 15); + fail_unless (gst_date_time_get_minute (dt) == 50); + fail_unless (!gst_date_time_has_second (dt)); + gst_date_time_unref (dt); + + dt = gst_date_time_new_from_iso8601_string ("15:50Z"); + fail_unless (gst_date_time_get_year (dt) == g_date_time_get_year (gdt)); + fail_unless (gst_date_time_get_month (dt) == g_date_time_get_month (gdt)); + fail_unless (gst_date_time_get_day (dt) == + g_date_time_get_day_of_month (gdt)); + fail_unless (gst_date_time_get_hour (dt) == 15); + fail_unless (gst_date_time_get_minute (dt) == 50); + fail_unless (!gst_date_time_has_second (dt)); + gst_date_time_unref (dt); + + gdt2 = g_date_time_add_minutes (gdt, -270); + g_date_time_unref (gdt); + + dt = gst_date_time_new_from_iso8601_string ("15:50:33-0430"); + fail_unless (gst_date_time_get_year (dt) == g_date_time_get_year (gdt2)); + fail_unless (gst_date_time_get_month (dt) == g_date_time_get_month (gdt2)); + fail_unless (gst_date_time_get_day (dt) == + g_date_time_get_day_of_month (gdt2)); + fail_unless (gst_date_time_get_hour (dt) == 15); + fail_unless (gst_date_time_get_minute (dt) == 50); + fail_unless (gst_date_time_get_second (dt) == 33); + gst_date_time_unref (dt); + + dt = gst_date_time_new_from_iso8601_string ("15:50-0430"); + fail_unless (gst_date_time_get_year (dt) == g_date_time_get_year (gdt2)); + fail_unless (gst_date_time_get_month (dt) == g_date_time_get_month (gdt2)); + fail_unless (gst_date_time_get_day (dt) == + g_date_time_get_day_of_month (gdt2)); + fail_unless (gst_date_time_get_hour (dt) == 15); + fail_unless (gst_date_time_get_minute (dt) == 50); + fail_unless (!gst_date_time_has_second (dt)); + gst_date_time_unref (dt); + + g_date_time_unref (gdt2); } GST_END_TEST;