2 * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gst_private.h"
25 #include "glib-compat-private.h"
26 #include "gstdatetime.h"
35 * @short_description: A date, time and timezone structure
37 * Struct to store date, time and timezone information altogether.
38 * #GstDateTime is refcounted and immutable.
40 * Date information is handled using the [proleptic Gregorian calendar].
42 * Provides basic creation functions and accessor functions to its fields.
44 * [proleptic Gregorian calendar]: https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
49 GST_DATE_TIME_FIELDS_INVALID = 0,
50 GST_DATE_TIME_FIELDS_Y, /* have year */
51 GST_DATE_TIME_FIELDS_YM, /* have year and month */
52 GST_DATE_TIME_FIELDS_YMD, /* have year, month and day */
53 GST_DATE_TIME_FIELDS_YMD_HM,
54 GST_DATE_TIME_FIELDS_YMD_HMS
55 /* Note: if we ever add more granularity here, e.g. for microsecs,
56 * the compare function will need updating */
61 GstMiniObject mini_object;
65 GstDateTimeFields fields;
68 GType _gst_date_time_type = 0;
69 GST_DEFINE_MINI_OBJECT_TYPE (GstDateTime, gst_date_time);
71 static void gst_date_time_free (GstDateTime * datetime);
74 * gst_date_time_new_from_g_date_time:
75 * @dt: (transfer full) (nullable): the #GDateTime.
77 * Creates a new #GstDateTime from a #GDateTime object.
79 * Returns: (transfer full) (nullable): a newly created #GstDateTime,
80 * or %NULL if @dt is %NULL.
83 gst_date_time_new_from_g_date_time (GDateTime * dt)
90 gst_dt = g_slice_new (GstDateTime);
92 gst_mini_object_init (GST_MINI_OBJECT_CAST (gst_dt), 0, GST_TYPE_DATE_TIME,
93 NULL, NULL, (GstMiniObjectFreeFunction) gst_date_time_free);
95 gst_dt->datetime = dt;
96 gst_dt->fields = GST_DATE_TIME_FIELDS_YMD_HMS;
101 * gst_date_time_to_g_date_time:
102 * @datetime: GstDateTime.
104 * Creates a new #GDateTime from a fully defined #GstDateTime object.
106 * Returns: (transfer full) (nullable): a newly created #GDateTime, or
107 * %NULL on error or if @datetime does not have a year, month, day, hour,
111 gst_date_time_to_g_date_time (GstDateTime * datetime)
113 g_return_val_if_fail (datetime != NULL, NULL);
115 if (datetime->fields != GST_DATE_TIME_FIELDS_YMD_HMS)
118 return g_date_time_add (datetime->datetime, 0);
122 * gst_date_time_has_year:
123 * @datetime: a #GstDateTime
125 * Returns: %TRUE if @datetime<!-- -->'s year field is set (which should always
126 * be the case), otherwise %FALSE
129 gst_date_time_has_year (const GstDateTime * datetime)
131 g_return_val_if_fail (datetime != NULL, FALSE);
133 return (datetime->fields >= GST_DATE_TIME_FIELDS_Y);
137 * gst_date_time_has_month:
138 * @datetime: a #GstDateTime
140 * Returns: %TRUE if @datetime<!-- -->'s month field is set, otherwise %FALSE
143 gst_date_time_has_month (const GstDateTime * datetime)
145 g_return_val_if_fail (datetime != NULL, FALSE);
147 return (datetime->fields >= GST_DATE_TIME_FIELDS_YM);
151 * gst_date_time_has_day:
152 * @datetime: a #GstDateTime
154 * Returns: %TRUE if @datetime<!-- -->'s day field is set, otherwise %FALSE
157 gst_date_time_has_day (const GstDateTime * datetime)
159 g_return_val_if_fail (datetime != NULL, FALSE);
161 return (datetime->fields >= GST_DATE_TIME_FIELDS_YMD);
165 * gst_date_time_has_time:
166 * @datetime: a #GstDateTime
168 * Returns: %TRUE if @datetime<!-- -->'s hour and minute fields are set,
172 gst_date_time_has_time (const GstDateTime * datetime)
174 g_return_val_if_fail (datetime != NULL, FALSE);
176 return (datetime->fields >= GST_DATE_TIME_FIELDS_YMD_HM);
180 * gst_date_time_has_second:
181 * @datetime: a #GstDateTime
183 * Returns: %TRUE if @datetime<!-- -->'s second field is set, otherwise %FALSE
186 gst_date_time_has_second (const GstDateTime * datetime)
188 g_return_val_if_fail (datetime != NULL, FALSE);
190 return (datetime->fields >= GST_DATE_TIME_FIELDS_YMD_HMS);
194 * gst_date_time_get_year:
195 * @datetime: a #GstDateTime
197 * Returns the year of this #GstDateTime.
198 * Call gst_date_time_has_year() before, to avoid warnings.
200 * Return value: The year of this #GstDateTime
203 gst_date_time_get_year (const GstDateTime * datetime)
205 g_return_val_if_fail (datetime != NULL, 0);
207 return g_date_time_get_year (datetime->datetime);
211 * gst_date_time_get_month:
212 * @datetime: a #GstDateTime
214 * Returns the month of this #GstDateTime. January is 1, February is 2, etc..
216 * Return value: The month of this #GstDateTime, or -1 if none is set.
219 gst_date_time_get_month (const GstDateTime * datetime)
221 g_return_val_if_fail (datetime != NULL, 0);
223 if (!gst_date_time_has_month (datetime))
226 return g_date_time_get_month (datetime->datetime);
230 * gst_date_time_get_day:
231 * @datetime: a #GstDateTime
233 * Returns the day of the month of this #GstDateTime.
235 * Return value: The day of this #GstDateTime, or -1 if none is set.
238 gst_date_time_get_day (const GstDateTime * datetime)
240 g_return_val_if_fail (datetime != NULL, 0);
242 if (!gst_date_time_has_day (datetime))
245 return g_date_time_get_day_of_month (datetime->datetime);
249 * gst_date_time_get_hour:
250 * @datetime: a #GstDateTime
252 * Retrieves the hour of the day represented by @datetime in the gregorian
253 * calendar. The return is in the range of 0 to 23.
255 * Return value: the hour of the day, or -1 if none is set.
258 gst_date_time_get_hour (const GstDateTime * datetime)
260 g_return_val_if_fail (datetime != NULL, 0);
262 if (!gst_date_time_has_time (datetime))
265 return g_date_time_get_hour (datetime->datetime);
269 * gst_date_time_get_minute:
270 * @datetime: a #GstDateTime
272 * Retrieves the minute of the hour represented by @datetime in the gregorian
275 * Return value: the minute of the hour, or -1 if none is set.
278 gst_date_time_get_minute (const GstDateTime * datetime)
280 g_return_val_if_fail (datetime != NULL, 0);
282 if (!gst_date_time_has_time (datetime))
285 return g_date_time_get_minute (datetime->datetime);
289 * gst_date_time_get_second:
290 * @datetime: a #GstDateTime
292 * Retrieves the second of the minute represented by @datetime in the gregorian
295 * Return value: the second represented by @datetime, or -1 if none is set.
298 gst_date_time_get_second (const GstDateTime * datetime)
300 g_return_val_if_fail (datetime != NULL, 0);
302 if (!gst_date_time_has_second (datetime))
305 return g_date_time_get_second (datetime->datetime);
309 * gst_date_time_get_microsecond:
310 * @datetime: a #GstDateTime
312 * Retrieves the fractional part of the seconds in microseconds represented by
313 * @datetime in the gregorian calendar.
315 * Return value: the microsecond of the second, or -1 if none is set.
318 gst_date_time_get_microsecond (const GstDateTime * datetime)
320 g_return_val_if_fail (datetime != NULL, 0);
322 if (!gst_date_time_has_second (datetime))
325 return g_date_time_get_microsecond (datetime->datetime);
329 * gst_date_time_get_time_zone_offset:
330 * @datetime: a #GstDateTime
332 * Retrieves the offset from UTC in hours that the timezone specified
333 * by @datetime represents. Timezones ahead (to the east) of UTC have positive
334 * values, timezones before (to the west) of UTC have negative values.
335 * If @datetime represents UTC time, then the offset is zero.
337 * Return value: the offset from UTC in hours, or %G_MAXDOUBLE if none is set.
340 gst_date_time_get_time_zone_offset (const GstDateTime * datetime)
342 g_return_val_if_fail (datetime != NULL, 0.0);
344 if (!gst_date_time_has_time (datetime))
347 return (g_date_time_get_utc_offset (datetime->datetime) /
348 G_USEC_PER_SEC) / 3600.0;
352 * gst_date_time_new_y:
353 * @year: the gregorian year
355 * Creates a new #GstDateTime using the date and times in the gregorian calendar
356 * in the local timezone.
358 * @year should be from 1 to 9999.
360 * Return value: (transfer full) (nullable): the newly created #GstDateTime,
364 gst_date_time_new_y (gint year)
366 return gst_date_time_new (0.0, year, -1, -1, -1, -1, -1);
370 * gst_date_time_new_ym:
371 * @year: the gregorian year
372 * @month: the gregorian month
374 * Creates a new #GstDateTime using the date and times in the gregorian calendar
375 * in the local timezone.
377 * @year should be from 1 to 9999, @month should be from 1 to 12.
379 * If value is -1 then all over value will be ignored. For example
380 * if @month == -1, then #GstDateTime will created only for @year.
382 * Return value: (transfer full) (nullable): the newly created #GstDateTime,
386 gst_date_time_new_ym (gint year, gint month)
388 return gst_date_time_new (0.0, year, month, -1, -1, -1, -1);
392 * gst_date_time_new_ymd:
393 * @year: the gregorian year
394 * @month: the gregorian month
395 * @day: the day of the gregorian month
397 * Creates a new #GstDateTime using the date and times in the gregorian calendar
398 * in the local timezone.
400 * @year should be from 1 to 9999, @month should be from 1 to 12, @day from
403 * If value is -1 then all over value will be ignored. For example
404 * if @month == -1, then #GstDateTime will created only for @year. If
405 * @day == -1, then #GstDateTime will created for @year and @month and
408 * Return value: (transfer full) (nullable): the newly created #GstDateTime,
412 gst_date_time_new_ymd (gint year, gint month, gint day)
414 return gst_date_time_new (0.0, year, month, day, -1, -1, -1);
418 * gst_date_time_new_from_unix_epoch_local_time:
419 * @secs: seconds from the Unix epoch
421 * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
422 * @secs. The #GstDateTime is in the local timezone.
424 * Return value: (transfer full) (nullable): the newly created #GstDateTime,
428 gst_date_time_new_from_unix_epoch_local_time (gint64 secs)
432 datetime = g_date_time_new_from_unix_local (secs);
436 return gst_date_time_new_from_g_date_time (datetime);
440 * gst_date_time_new_from_unix_epoch_utc:
441 * @secs: seconds from the Unix epoch
443 * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
444 * @secs. The #GstDateTime is in the UTC timezone.
446 * Return value: (transfer full) (nullable): the newly created #GstDateTime,
450 gst_date_time_new_from_unix_epoch_utc (gint64 secs)
454 datetime = g_date_time_new_from_unix_utc (secs);
458 return gst_date_time_new_from_g_date_time (datetime);
462 * gst_date_time_new_from_unix_epoch_local_time_usecs:
463 * @usecs: microseconds from the Unix epoch
465 * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
466 * @usecs. The #GstDateTime is in the local timezone.
468 * Returns: (transfer full) (nullable): a newly created #GstDateTime, or %NULL
474 gst_date_time_new_from_unix_epoch_local_time_usecs (gint64 usecs)
476 GDateTime *dt, *datetime;
477 gint64 secs = usecs / G_USEC_PER_SEC;
478 gint64 usec_part = usecs % G_USEC_PER_SEC;
480 dt = g_date_time_new_from_unix_local (secs);
483 datetime = g_date_time_add_seconds (dt, (gdouble) usec_part / G_USEC_PER_SEC);
484 g_date_time_unref (dt);
488 return gst_date_time_new_from_g_date_time (datetime);
492 * gst_date_time_new_from_unix_epoch_utc_usecs:
493 * @usecs: microseconds from the Unix epoch
495 * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
496 * @usecs. The #GstDateTime is in UTC.
498 * Returns: (transfer full) (nullable): a newly created #GstDateTime, or %NULL
504 gst_date_time_new_from_unix_epoch_utc_usecs (gint64 usecs)
506 GDateTime *dt, *datetime;
507 gint64 secs = usecs / G_USEC_PER_SEC;
508 gint64 usec_part = usecs % G_USEC_PER_SEC;
510 dt = g_date_time_new_from_unix_utc (secs);
513 datetime = g_date_time_add_seconds (dt, (gdouble) usec_part / G_USEC_PER_SEC);
514 g_date_time_unref (dt);
518 return gst_date_time_new_from_g_date_time (datetime);
521 static GstDateTimeFields
522 gst_date_time_check_fields (gint * year, gint * month, gint * day,
523 gint * hour, gint * minute, gdouble * seconds)
527 *hour = *minute = *seconds = 0;
528 return GST_DATE_TIME_FIELDS_Y;
529 } else if (*day == -1) {
531 *hour = *minute = *seconds = 0;
532 return GST_DATE_TIME_FIELDS_YM;
533 } else if (*hour == -1) {
534 *hour = *minute = *seconds = 0;
535 return GST_DATE_TIME_FIELDS_YMD;
536 } else if (*seconds == -1) {
538 return GST_DATE_TIME_FIELDS_YMD_HM;
540 return GST_DATE_TIME_FIELDS_YMD_HMS;
544 * gst_date_time_new_local_time:
545 * @year: the gregorian year
546 * @month: the gregorian month, or -1
547 * @day: the day of the gregorian month, or -1
548 * @hour: the hour of the day, or -1
549 * @minute: the minute of the hour, or -1
550 * @seconds: the second of the minute, or -1
552 * Creates a new #GstDateTime using the date and times in the gregorian calendar
553 * in the local timezone.
555 * @year should be from 1 to 9999, @month should be from 1 to 12, @day from
556 * 1 to 31, @hour from 0 to 23, @minutes and @seconds from 0 to 59.
558 * If @month is -1, then the #GstDateTime created will only contain @year,
559 * and all other fields will be considered not set.
561 * If @day is -1, then the #GstDateTime created will only contain @year and
562 * @month and all other fields will be considered not set.
564 * If @hour is -1, then the #GstDateTime created will only contain @year and
565 * @month and @day, and the time fields will be considered not set. In this
566 * case @minute and @seconds should also be -1.
568 * Return value: (transfer full) (nullable): the newly created #GstDateTime,
572 gst_date_time_new_local_time (gint year, gint month, gint day, gint hour,
573 gint minute, gdouble seconds)
575 GstDateTimeFields fields;
577 GstDateTime *datetime;
579 if (year <= 0 || year > 9999)
582 if ((month <= 0 || month > 12) && month != -1)
585 if ((day <= 0 || day > 31) && day != -1)
588 if ((hour < 0 || hour >= 24) && hour != -1)
591 if ((minute < 0 || minute >= 60) && minute != -1)
594 if ((seconds < 0 || seconds >= 60) && seconds != -1)
597 fields = gst_date_time_check_fields (&year, &month, &day,
598 &hour, &minute, &seconds);
600 dt = g_date_time_new_local (year, month, day, hour, minute, seconds);
604 datetime = gst_date_time_new_from_g_date_time (dt);
605 if (datetime == NULL)
608 datetime->fields = fields;
613 * gst_date_time_new_now_local_time:
615 * Creates a new #GstDateTime representing the current date and time.
617 * Return value: (transfer full) (nullable): the newly created #GstDateTime which should
618 * be freed with gst_date_time_unref(), or %NULL on error.
621 gst_date_time_new_now_local_time (void)
625 dt = g_date_time_new_now_local ();
629 return gst_date_time_new_from_g_date_time (dt);
633 * gst_date_time_new_now_utc:
635 * Creates a new #GstDateTime that represents the current instant at Universal
638 * Return value: (transfer full) (nullable): the newly created #GstDateTime which should
639 * be freed with gst_date_time_unref(), or %NULL on error.
642 gst_date_time_new_now_utc (void)
646 dt = g_date_time_new_now_utc ();
650 return gst_date_time_new_from_g_date_time (dt);
654 __gst_date_time_compare (const GstDateTime * dt1, const GstDateTime * dt2)
658 /* we assume here that GST_DATE_TIME_FIELDS_YMD_HMS is the highest
659 * resolution, and ignore microsecond differences on purpose for now */
660 if (dt1->fields != dt2->fields)
661 return GST_VALUE_UNORDERED;
663 /* This will round down to nearest second, which is what we want. We're
664 * not comparing microseconds on purpose here, since we're not
665 * serialising them when doing new_utc_now() + to_string() */
667 g_date_time_to_unix (dt1->datetime) - g_date_time_to_unix (dt2->datetime);
669 return GST_VALUE_LESS_THAN;
671 return GST_VALUE_GREATER_THAN;
673 return GST_VALUE_EQUAL;
678 * @tzoffset: Offset from UTC in hours.
679 * @year: the gregorian year
680 * @month: the gregorian month
681 * @day: the day of the gregorian month
682 * @hour: the hour of the day
683 * @minute: the minute of the hour
684 * @seconds: the second of the minute
686 * Creates a new #GstDateTime using the date and times in the gregorian calendar
687 * in the supplied timezone.
689 * @year should be from 1 to 9999, @month should be from 1 to 12, @day from
690 * 1 to 31, @hour from 0 to 23, @minutes and @seconds from 0 to 59.
692 * Note that @tzoffset is a float and was chosen so for being able to handle
693 * some fractional timezones, while it still keeps the readability of
694 * representing it in hours for most timezones.
696 * If value is -1 then all over value will be ignored. For example
697 * if @month == -1, then #GstDateTime will be created only for @year. If
698 * @day == -1, then #GstDateTime will be created for @year and @month and
701 * Return value: (transfer full) (nullable): the newly created #GstDateTime,
705 gst_date_time_new (gfloat tzoffset, gint year, gint month, gint day, gint hour,
706 gint minute, gdouble seconds)
708 GstDateTimeFields fields;
712 GstDateTime *datetime;
713 gint tzhour, tzminute;
715 if (year <= 0 || year > 9999)
718 if ((month <= 0 || month > 12) && month != -1)
721 if ((day <= 0 || day > 31) && day != -1)
724 if ((hour < 0 || hour >= 24) && hour != -1)
727 if ((minute < 0 || minute >= 60) && minute != -1)
730 if ((seconds < 0 || seconds >= 60) && seconds != -1)
733 if (tzoffset < -12.0 || tzoffset > 12.0)
736 if ((hour < 0 || minute < 0) &&
737 (hour != -1 || minute != -1 || seconds != -1 || tzoffset != 0.0))
740 tzhour = (gint) ABS (tzoffset);
741 tzminute = (gint) ((ABS (tzoffset) - tzhour) * 60);
743 g_snprintf (buf, 6, "%c%02d%02d", tzoffset >= 0 ? '+' : '-', tzhour,
746 #if GLIB_CHECK_VERSION (2, 67, 1)
747 /* g_time_zone_new() would always return UTC if the identifier can't be
748 * parsed, which is rather suboptimal. */
749 tz = g_time_zone_new_identifier (buf);
753 tz = g_time_zone_new (buf);
756 fields = gst_date_time_check_fields (&year, &month, &day,
757 &hour, &minute, &seconds);
759 dt = g_date_time_new (tz, year, month, day, hour, minute, seconds);
760 g_time_zone_unref (tz);
763 return NULL; /* date failed validation */
765 datetime = gst_date_time_new_from_g_date_time (dt);
766 datetime->fields = fields;
772 __gst_date_time_serialize (GstDateTime * datetime, gboolean serialize_usecs)
778 /* we always have at least the year */
779 s = g_string_new (NULL);
780 g_string_append_printf (s, "%04u", gst_date_time_get_year (datetime));
782 if (datetime->fields == GST_DATE_TIME_FIELDS_Y)
786 g_string_append_printf (s, "-%02u", gst_date_time_get_month (datetime));
788 if (datetime->fields == GST_DATE_TIME_FIELDS_YM)
791 /* add day of month */
792 g_string_append_printf (s, "-%02u", gst_date_time_get_day (datetime));
794 if (datetime->fields == GST_DATE_TIME_FIELDS_YMD)
798 g_string_append_printf (s, "T%02u:%02u", gst_date_time_get_hour (datetime),
799 gst_date_time_get_minute (datetime));
801 if (datetime->fields == GST_DATE_TIME_FIELDS_YMD_HM)
805 g_string_append_printf (s, ":%02u", gst_date_time_get_second (datetime));
807 /* add microseconds */
808 if (serialize_usecs) {
809 msecs = gst_date_time_get_microsecond (datetime);
811 g_string_append_printf (s, ".%06u", msecs);
812 /* trim trailing 0s */
813 while (s->str[s->len - 1] == '0')
814 g_string_truncate (s, s->len - 1);
822 gmt_offset = gst_date_time_get_time_zone_offset (datetime);
823 if (gmt_offset == 0) {
824 g_string_append_c (s, 'Z');
826 guint tzhour, tzminute;
828 tzhour = (guint) ABS (gmt_offset);
829 tzminute = (guint) ((ABS (gmt_offset) - tzhour) * 60);
831 g_string_append_c (s, (gmt_offset >= 0) ? '+' : '-');
832 g_string_append_printf (s, "%02u%02u", tzhour, tzminute);
837 return g_string_free (s, FALSE);
841 * gst_date_time_to_iso8601_string:
842 * @datetime: a #GstDateTime.
844 * Create a minimal string compatible with ISO-8601. Possible output formats
845 * are (for example): `2012`, `2012-06`, `2012-06-23`, `2012-06-23T23:30Z`,
846 * `2012-06-23T23:30+0100`, `2012-06-23T23:30:59Z`, `2012-06-23T23:30:59+0100`
848 * Returns: (nullable): a newly allocated string formatted according
849 * to ISO 8601 and only including the datetime fields that are
850 * valid, or %NULL in case there was an error.
853 gst_date_time_to_iso8601_string (GstDateTime * datetime)
855 g_return_val_if_fail (datetime != NULL, NULL);
857 if (datetime->fields == GST_DATE_TIME_FIELDS_INVALID)
860 return __gst_date_time_serialize (datetime, FALSE);
864 * gst_date_time_new_from_iso8601_string:
865 * @string: ISO 8601-formatted datetime string.
867 * Tries to parse common variants of ISO-8601 datetime strings into a
868 * #GstDateTime. Possible input formats are (for example):
869 * `2012-06-30T22:46:43Z`, `2012`, `2012-06`, `2012-06-30`, `2012-06-30T22:46:43-0430`,
870 * `2012-06-30T22:46Z`, `2012-06-30T22:46-0430`, `2012-06-30 22:46`,
871 * `2012-06-30 22:46:43`, `2012-06-00`, `2012-00-00`, `2012-00-30`, `22:46:43Z`, `22:46Z`,
872 * `22:46:43-0430`, `22:46-0430`, `22:46:30`, `22:46`
873 * If no date is provided, it is assumed to be "today" in the timezone
874 * provided (if any), otherwise UTC.
876 * Returns: (transfer full) (nullable): a newly created #GstDateTime,
880 gst_date_time_new_from_iso8601_string (const gchar * string)
882 gint year = -1, month = -1, day = -1, hour = -1, minute = -1;
883 gint gmt_offset_hour = -99, gmt_offset_min = -99;
884 gdouble second = -1.0;
885 gfloat tzoffset = 0.0;
889 g_return_val_if_fail (string != NULL, NULL);
891 GST_DEBUG ("Parsing '%s' into a datetime", string);
893 len = strlen (string);
895 /* The input string is expected to start either with a year (4 digits) or
896 * with an hour (2 digits). Hour must be followed by minute. In any case,
897 * the string must be at least 4 characters long and start with 2 digits */
898 if (len < 4 || !g_ascii_isdigit (string[0]) || !g_ascii_isdigit (string[1]))
901 if (g_ascii_isdigit (string[2]) && g_ascii_isdigit (string[3])) {
902 ret = sscanf (string, "%04d-%02d-%02d", &year, &month, &day);
907 if (ret == 3 && day <= 0) {
912 if (ret >= 2 && month <= 0) {
917 if (ret >= 1 && (year <= 0 || year > 9999 || month > 12 || day > 31))
920 else if (ret >= 1 && len < 16)
921 /* YMD is 10 chars. XMD + HM will be 16 chars. if it is less,
922 * it make no sense to continue. We will stay with YMD. */
926 /* Exit if there is no expected value on this stage */
927 if (!(*string == 'T' || *string == '-' || *string == ' '))
932 /* if hour or minute fails, then we will use only ymd. */
933 hour = g_ascii_strtoull (string, (gchar **) & string, 10);
934 if (hour > 24 || *string != ':')
938 minute = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
943 if (*string == ':') {
944 second = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
945 /* if we fail here, we still can reuse hour and minute. We
946 * will still attempt to parse any timezone information */
951 if (*string == '.' || *string == ',') {
952 const gchar *usec_start = string + 1;
955 usecs = g_ascii_strtoull (string + 1, (gchar **) & string, 10);
956 if (usecs != G_MAXUINT64 && string > usec_start) {
957 digits = (guint) (string - usec_start);
958 second += (gdouble) usecs / pow (10.0, digits);
967 /* reuse some code from gst-plugins-base/gst-libs/gst/tag/gstxmptag.c */
968 gint gmt_offset = -1;
969 gchar *plus_pos = NULL;
970 gchar *neg_pos = NULL;
973 GST_LOG ("Checking for timezone information");
975 /* check if there is timezone info */
976 plus_pos = strrchr (string, '+');
977 neg_pos = strrchr (string, '-');
983 if (pos && strlen (pos) >= 3) {
986 ret_tz = sscanf (pos, "%d:%d", &gmt_offset_hour, &gmt_offset_min);
988 ret_tz = sscanf (pos, "%02d%02d", &gmt_offset_hour, &gmt_offset_min);
990 GST_DEBUG ("Parsing timezone: %s", pos);
993 if (neg_pos != NULL && neg_pos + 1 == pos) {
994 gmt_offset_hour *= -1;
995 gmt_offset_min *= -1;
997 gmt_offset = gmt_offset_hour * 60 + gmt_offset_min;
999 tzoffset = gmt_offset / 60.0;
1001 GST_LOG ("Timezone offset: %f (%d minutes)", tzoffset, gmt_offset);
1003 GST_WARNING ("Failed to parse timezone information");
1008 if (year == -1 || month == -1 || day == -1) {
1009 GDateTime *now_utc, *now_in_given_tz;
1011 /* No date was supplied: make it today */
1012 now_utc = g_date_time_new_now_utc ();
1016 if (tzoffset != 0.0) {
1017 /* If a timezone offset was supplied, get the date of that timezone */
1018 g_assert (gmt_offset_min != -99);
1019 g_assert (gmt_offset_hour != -99);
1021 g_date_time_add_minutes (now_utc,
1022 (60 * gmt_offset_hour) + gmt_offset_min);
1023 g_date_time_unref (now_utc);
1024 if (!now_in_given_tz)
1027 now_in_given_tz = now_utc;
1029 g_date_time_get_ymd (now_in_given_tz, &year, &month, &day);
1030 g_date_time_unref (now_in_given_tz);
1032 return gst_date_time_new (tzoffset, year, month, day, hour, minute, second);
1035 /* No date was supplied and time failed to parse */
1038 return gst_date_time_new_ymd (year, month, day);
1042 gst_date_time_free (GstDateTime * datetime)
1044 g_date_time_unref (datetime->datetime);
1046 #ifdef USE_POISONING
1047 memset (datetime, 0xff, sizeof (GstDateTime));
1050 g_slice_free (GstDateTime, datetime);
1054 * gst_date_time_ref:
1055 * @datetime: a #GstDateTime
1057 * Atomically increments the reference count of @datetime by one.
1059 * Return value: (transfer full): the reference @datetime
1062 gst_date_time_ref (GstDateTime * datetime)
1064 return (GstDateTime *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (datetime));
1068 * gst_date_time_unref:
1069 * @datetime: (transfer full): a #GstDateTime
1071 * Atomically decrements the reference count of @datetime by one. When the
1072 * reference count reaches zero, the structure is freed.
1075 gst_date_time_unref (GstDateTime * datetime)
1077 gst_mini_object_unref (GST_MINI_OBJECT_CAST (datetime));
1081 _priv_gst_date_time_initialize (void)
1083 _gst_date_time_type = gst_date_time_get_type ();