datetime: accept just a time as ISO 8601 string and use today's date then
authorVivia Nikolaidou <vivia@ahiru.eu>
Mon, 10 Aug 2015 12:31:37 +0000 (15:31 +0300)
committerTim-Philipp Müller <tim@centricular.com>
Tue, 11 Aug 2015 08:50:50 +0000 (09:50 +0100)
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
tests/check/gst/gstdatetime.c

index 8c0eda577ecf2b1b922b43fb50393dd0fb981abb..6e7ea27b356638af35c03c8ff84fe4793ddfed5b 100644 (file)
@@ -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 >= 2 && month <= 0) {
+      ret = 1;
+      month = day = -1;
+    }
 
-  if (ret >= 1 && year <= 0)
-    return NULL;
+    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;
+    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 += 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);
+    string += 1;
+  }
+  /* 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);
 }
 
index eb637c4b08545e86f10762970c1b235353d0eaa6..ae15dc55bcc81aa1aa35be3f701af0c1a4830073 100644 (file)
@@ -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;