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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include "glib-compat-private.h"
25 #include "gst_private.h"
26 #include "gstdatetime.h"
33 * @short_description: A date, time and timezone structure
35 * Struct to store date, time and timezone information altogether.
36 * #GstDateTime is refcounted and immutable.
38 * Date information is handled using the proleptic Gregorian calendar.
40 * Provides basic creation functions and accessor functions to its fields.
46 * gst_date_time_get_year:
47 * @datetime: a #GstDateTime
49 * Returns the year of this #GstDateTime
51 * Return value: The year of this #GstDateTime
56 * gst_date_time_get_month:
57 * @datetime: a #GstDateTime
59 * Returns the month of this #GstDateTime. January is 1, February is 2, etc..
61 * Return value: The month of this #GstDateTime
66 * gst_date_time_get_day:
67 * @datetime: a #GstDateTime
69 * Returns the day of this #GstDateTime.
71 * Return value: The day of this #GstDateTime
76 * gst_date_time_get_hour:
77 * @datetime: a #GstDateTime
79 * Retrieves the hour of the day represented by @datetime in the gregorian
80 * calendar. The return is in the range of 0 to 23.
82 * Return value: the hour of the day
88 * gst_date_time_get_microsecond:
89 * @datetime: a #GstDateTime
91 * Retrieves the fractional part of the seconds in microseconds represented by
92 * @datetime in the gregorian calendar.
94 * Return value: the microsecond of the second
100 * gst_date_time_get_minute:
101 * @datetime: a #GstDateTime
103 * Retrieves the minute of the hour represented by @datetime in the gregorian
106 * Return value: the minute of the hour
112 * gst_date_time_get_second:
113 * @datetime: a #GstDateTime
115 * Retrieves the second of the minute represented by @datetime in the gregorian
118 * Return value: the second represented by @datetime
124 * gst_date_time_get_second:
125 * @datetime: a #GstDateTime
127 * Retrieves the second of the minute represented by @datetime in the gregorian
130 * Return value: the second represented by @datetime
136 * gst_date_time_get_time_zone_offset:
137 * @datetime: a #GstDateTime
139 * Retrieves the offset from UTC in hours that the timezone specified
140 * by @datetime represents. Timezones ahead (to the east) of UTC have positive
141 * values, timezones before (to the west) of UTC have negative values.
142 * If @datetime represents UTC time, then the offset is zero.
144 * Return value: the offset from UTC in hours
149 * gst_date_time_new_from_unix_epoch_local_time:
150 * @secs: seconds from the Unix epoch
152 * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
153 * @secs. The #GstDateTime is in the local timezone.
155 * Free-function: gst_date_time_unref
157 * Return value: (transfer full): the newly created #GstDateTime
163 * gst_date_time_new_from_unix_epoch_utc:
164 * @secs: seconds from the Unix epoch
166 * Creates a new #GstDateTime using the time since Jan 1, 1970 specified by
167 * @secs. The #GstDateTime is in the UTC timezone.
169 * Free-function: gst_date_time_unref
171 * Return value: (transfer full): the newly created #GstDateTime
177 * gst_date_time_new_local_time:
178 * @year: the gregorian year
179 * @month: the gregorian month
180 * @day: the day of the gregorian month
181 * @hour: the hour of the day
182 * @minute: the minute of the hour
183 * @seconds: the second of the minute
185 * Creates a new #GstDateTime using the date and times in the gregorian calendar
186 * in the local timezone.
188 * @year should be from 1 to 9999, @month should be from 1 to 12, @day from
189 * 1 to 31, @hour from 0 to 23, @minutes and @seconds from 0 to 59.
191 * Free-function: gst_date_time_unref
193 * Return value: (transfer full): the newly created #GstDateTime
200 * @tzoffset: Offset from UTC in hours.
201 * @year: the gregorian year
202 * @month: the gregorian month
203 * @day: the day of the gregorian month
204 * @hour: the hour of the day
205 * @minute: the minute of the hour
206 * @seconds: the second of the minute
208 * Creates a new #GstDateTime using the date and times in the gregorian calendar
209 * in the supplied timezone.
211 * @year should be from 1 to 9999, @month should be from 1 to 12, @day from
212 * 1 to 31, @hour from 0 to 23, @minutes and @seconds from 0 to 59.
214 * Note that @tzoffset is a float and was chosen so for being able to handle
215 * some fractional timezones, while it still keeps the readability of
216 * represeting it in hours for most timezones.
218 * Free-function: gst_date_time_unref
220 * Return value: (transfer full): the newly created #GstDateTime
226 * gst_date_time_new_now_local_time:
228 * Creates a new #GstDateTime representing the current date and time.
230 * Free-function: gst_date_time_unref
232 * Return value: (transfer full): the newly created #GstDateTime which should
233 * be freed with gst_date_time_unref().
239 * gst_date_time_new_now_utc:
241 * Creates a new #GstDateTime that represents the current instant at Universal
244 * Free-function: gst_date_time_unref
246 * Return value: (transfer full): the newly created #GstDateTime which should
247 * be freed with gst_date_time_unref().
253 #define GST_DATE_TIME_SEC_PER_DAY (G_GINT64_CONSTANT (86400))
254 #define GST_DATE_TIME_USEC_PER_DAY (G_GINT64_CONSTANT (86400000000))
255 #define GST_DATE_TIME_USEC_PER_HOUR (G_GINT64_CONSTANT (3600000000))
256 #define GST_DATE_TIME_USEC_PER_MINUTE (G_GINT64_CONSTANT (60000000))
257 #define GST_DATE_TIME_USEC_PER_SECOND (G_GINT64_CONSTANT (1000000))
258 #define GST_DATE_TIME_USEC_PER_MILLISECOND (G_GINT64_CONSTANT (1000))
260 /* FIXME: this bug was resolved NOTGNOME */
261 /* Jan 5th 2011 (Edward) : GLib's GDateTime is broken in regards to gmt offset
262 * on macosx. Re-enable it once the following bug is fixed:
263 * https://bugzilla.gnome.org/show_bug.cgi?id=638666 */
265 #undef GLIB_HAS_GDATETIME
267 #define GLIB_HAS_GDATETIME
271 #ifndef GLIB_HAS_GDATETIME
273 #define MAX_SUPPORTED_YEAR 9999
274 #define GREGORIAN_LEAP(y) (((y%4)==0)&&(!(((y%100)==0)&&((y%400)!=0))))
276 static const guint16 days_in_months[2][13] = {
277 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
278 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
284 * As we don't have a datetime math API, we can have fields split here.
285 * (There is still some math done internally, but nothing really relevant).
287 * If we ever add one, we should go for a days since some epoch counter.
288 * (Proleptic Gregorian with 0001-01-01 as day 1)
293 guint64 usec; /* Microsecond timekeeping within Day */
297 volatile gint ref_count;
301 * Returns the utc offset in seconds for this time structure
304 gmt_offset (struct tm *tm, time_t t)
306 #if defined (HAVE_TM_GMTOFF)
307 return tm->tm_gmtoff;
317 return (int) difftime (t, t2);
322 gst_date_time_set_local_timezone (GstDateTime * dt)
327 g_return_if_fail (dt != NULL);
329 memset (&tt, 0, sizeof (tt));
331 tt.tm_mday = gst_date_time_get_day (dt);
332 tt.tm_mon = gst_date_time_get_month (dt) - 1;
333 tt.tm_year = gst_date_time_get_year (dt) - 1900;
334 tt.tm_hour = gst_date_time_get_hour (dt);
335 tt.tm_min = gst_date_time_get_minute (dt);
336 tt.tm_sec = gst_date_time_get_second (dt);
340 dt->tzoffset = gmt_offset (&tt, t) / 60;
344 gst_date_time_alloc (void)
346 GstDateTime *datetime;
348 datetime = g_slice_new0 (GstDateTime);
349 datetime->ref_count = 1;
355 gst_date_time_free (GstDateTime * datetime)
357 g_slice_free (GstDateTime, datetime);
361 gst_date_time_new_from_date (gint year, gint month, gint day)
365 g_return_val_if_fail (year > 0 && year <= 9999, NULL);
366 g_return_val_if_fail ((month > 0 && month <= 12), NULL);
367 g_return_val_if_fail ((day > 0 && day <= 31), NULL);
369 dt = gst_date_time_alloc ();
374 gst_date_time_set_local_timezone (dt);
380 gst_date_time_get_year (const GstDateTime * datetime)
382 g_return_val_if_fail (datetime != NULL, 0);
384 return datetime->year;
388 gst_date_time_get_month (const GstDateTime * datetime)
390 g_return_val_if_fail (datetime != NULL, 0);
392 return datetime->month;
396 gst_date_time_get_day (const GstDateTime * datetime)
398 g_return_val_if_fail (datetime != NULL, 0);
400 return datetime->day;
404 gst_date_time_get_hour (const GstDateTime * datetime)
406 g_return_val_if_fail (datetime != NULL, 0);
407 return (datetime->usec / GST_DATE_TIME_USEC_PER_HOUR);
411 gst_date_time_get_microsecond (const GstDateTime * datetime)
413 g_return_val_if_fail (datetime != NULL, 0);
414 return (datetime->usec % GST_DATE_TIME_USEC_PER_SECOND);
418 gst_date_time_get_minute (const GstDateTime * datetime)
420 g_return_val_if_fail (datetime != NULL, 0);
421 return (datetime->usec % GST_DATE_TIME_USEC_PER_HOUR) /
422 GST_DATE_TIME_USEC_PER_MINUTE;
426 gst_date_time_get_second (const GstDateTime * datetime)
428 g_return_val_if_fail (datetime != NULL, 0);
429 return (datetime->usec % GST_DATE_TIME_USEC_PER_MINUTE) /
430 GST_DATE_TIME_USEC_PER_SECOND;
434 gst_date_time_get_time_zone_offset (const GstDateTime * datetime)
436 g_return_val_if_fail (datetime != NULL, 0);
438 return datetime->tzoffset / 60.0f;
442 gst_date_time_new_from_unix_epoch_local_time (gint64 secs)
448 memset (&tm, 0, sizeof (tm));
451 #ifdef HAVE_LOCALTIME_R
452 localtime_r (&tt, &tm);
454 memcpy (&tm, localtime (&tt), sizeof (struct tm));
457 dt = gst_date_time_new (0, tm.tm_year + 1900,
458 tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
459 gst_date_time_set_local_timezone (dt);
464 gst_date_time_new_from_unix_epoch_utc (gint64 secs)
470 memset (&tm, 0, sizeof (tm));
476 memcpy (&tm, gmtime (&tt), sizeof (struct tm));
479 dt = gst_date_time_new (0, tm.tm_year + 1900,
480 tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
485 gst_date_time_new_local_time (gint year, gint month, gint day, gint hour,
486 gint minute, gdouble seconds)
490 dt = gst_date_time_new (0, year, month, day, hour, minute, seconds);
492 gst_date_time_set_local_timezone (dt);
498 gst_date_time_new (gfloat tzoffset, gint year, gint month, gint day, gint hour,
499 gint minute, gdouble seconds)
503 g_return_val_if_fail (hour >= 0 && hour < 24, NULL);
504 g_return_val_if_fail (minute >= 0 && minute < 60, NULL);
505 g_return_val_if_fail (seconds >= 0 && seconds < 60, NULL);
506 g_return_val_if_fail (tzoffset >= -12.0 && tzoffset <= 12.0, NULL);
508 if (!(dt = gst_date_time_new_from_date (year, month, day)))
511 dt->usec = (hour * GST_DATE_TIME_USEC_PER_HOUR)
512 + (minute * GST_DATE_TIME_USEC_PER_MINUTE)
513 + (guint64) (floor (seconds * GST_DATE_TIME_USEC_PER_SECOND + 0.5));
515 /* we store in minutes */
516 dt->tzoffset = (gint) (tzoffset * 60.0);
522 gst_date_time_new_now_local_time (void)
524 GstDateTime *datetime;
526 g_get_current_time (&tv);
528 datetime = gst_date_time_new_from_unix_epoch_local_time (tv.tv_sec);
529 datetime->usec += tv.tv_usec;
530 gst_date_time_set_local_timezone (datetime);
535 gst_date_time_copy (const GstDateTime * dt)
537 GstDateTime *copy = gst_date_time_alloc ();
539 memcpy (copy, dt, sizeof (GstDateTime));
546 gst_date_time_to_utc (const GstDateTime * dt)
553 g_return_val_if_fail (dt != NULL, NULL);
555 utc = gst_date_time_copy (dt);
557 usec = dt->usec - dt->tzoffset * GST_DATE_TIME_USEC_PER_MINUTE;
558 days = usec / GST_DATE_TIME_USEC_PER_DAY;
563 leap = GREGORIAN_LEAP (utc->year) ? 1 : 0;
565 /* check if we should update month/year */
567 if (utc->month == 1) {
573 if (GREGORIAN_LEAP (utc->year))
574 utc->day = days_in_months[1][utc->month];
576 utc->day = days_in_months[0][utc->month];
577 } else if (utc->day > days_in_months[leap][utc->month]) {
578 if (utc->month == 12) {
589 GST_DATE_TIME_USEC_PER_DAY + (usec % GST_DATE_TIME_USEC_PER_DAY);
591 utc->usec = usec % GST_DATE_TIME_USEC_PER_DAY;
597 gst_date_time_new_now_utc (void)
599 GstDateTime *now, *utc;
601 now = gst_date_time_new_now_local_time ();
602 utc = gst_date_time_to_utc (now);
603 gst_date_time_unref (now);
608 priv_gst_date_time_compare (gconstpointer dt1, gconstpointer dt2)
613 a = gst_date_time_to_utc (dt1);
614 b = gst_date_time_to_utc (dt2);
616 #define GST_DATE_TIME_COMPARE_VALUE(a,b,v) \
617 if ((a)->v > (b)->v) { \
620 } else if ((a)->v < (b)->v) { \
625 GST_DATE_TIME_COMPARE_VALUE (a, b, year);
626 GST_DATE_TIME_COMPARE_VALUE (a, b, month);
627 GST_DATE_TIME_COMPARE_VALUE (a, b, day);
628 GST_DATE_TIME_COMPARE_VALUE (a, b, usec);
630 #undef GST_DATE_TIME_COMPARE_VALUE
633 gst_date_time_unref (a);
634 gst_date_time_unref (b);
644 volatile gint ref_count;
648 gst_date_time_new_from_gdatetime (GDateTime * dt)
655 gst_dt = g_slice_new (GstDateTime);
656 gst_dt->datetime = dt;
657 gst_dt->ref_count = 1;
662 gst_date_time_get_year (const GstDateTime * datetime)
664 return g_date_time_get_year (datetime->datetime);
668 gst_date_time_get_month (const GstDateTime * datetime)
670 return g_date_time_get_month (datetime->datetime);
674 gst_date_time_get_day (const GstDateTime * datetime)
676 return g_date_time_get_day_of_month (datetime->datetime);
680 gst_date_time_get_hour (const GstDateTime * datetime)
682 return g_date_time_get_hour (datetime->datetime);
686 gst_date_time_get_minute (const GstDateTime * datetime)
688 return g_date_time_get_minute (datetime->datetime);
692 gst_date_time_get_second (const GstDateTime * datetime)
694 return g_date_time_get_second (datetime->datetime);
698 gst_date_time_get_microsecond (const GstDateTime * datetime)
700 return g_date_time_get_microsecond (datetime->datetime);
704 gst_date_time_get_time_zone_offset (const GstDateTime * datetime)
706 return (g_date_time_get_utc_offset (datetime->datetime) /
707 G_USEC_PER_SEC) / 3600.0;
711 gst_date_time_new_from_unix_epoch_local_time (gint64 secs)
714 gst_date_time_new_from_gdatetime (g_date_time_new_from_unix_local (secs));
718 gst_date_time_new_from_unix_epoch_utc (gint64 secs)
721 gst_date_time_new_from_gdatetime (g_date_time_new_from_unix_utc (secs));
725 gst_date_time_new_local_time (gint year, gint month, gint day, gint hour,
726 gint minute, gdouble seconds)
728 return gst_date_time_new_from_gdatetime (g_date_time_new_local (year, month,
729 day, hour, minute, seconds));
733 gst_date_time_new_now_local_time (void)
735 return gst_date_time_new_from_gdatetime (g_date_time_new_now_local ());
739 gst_date_time_new_now_utc (void)
741 return gst_date_time_new_from_gdatetime (g_date_time_new_now_utc ());
745 priv_gst_date_time_compare (gconstpointer dt1, gconstpointer dt2)
747 const GstDateTime *datetime1 = dt1;
748 const GstDateTime *datetime2 = dt2;
749 return g_date_time_compare (datetime1->datetime, datetime2->datetime);
753 gst_date_time_new (gfloat tzoffset, gint year, gint month, gint day, gint hour,
754 gint minute, gdouble seconds)
759 gint tzhour, tzminute;
761 tzhour = (gint) ABS (tzoffset);
762 tzminute = (gint) ((ABS (tzoffset) - tzhour) * 60);
764 g_snprintf (buf, 6, "%c%02d%02d", tzoffset >= 0 ? '+' : '-', tzhour,
767 tz = g_time_zone_new (buf);
769 dt = g_date_time_new (tz, year, month, day, hour, minute, seconds);
770 g_time_zone_unref (tz);
771 return gst_date_time_new_from_gdatetime (dt);
775 gst_date_time_free (GstDateTime * datetime)
777 g_date_time_unref (datetime->datetime);
778 g_slice_free (GstDateTime, datetime);
785 * @datetime: a #GstDateTime
787 * Atomically increments the reference count of @datetime by one.
789 * Return value: (transfer full): the reference @datetime
794 gst_date_time_ref (GstDateTime * datetime)
796 g_return_val_if_fail (datetime != NULL, NULL);
797 g_return_val_if_fail (datetime->ref_count > 0, NULL);
798 g_atomic_int_inc (&datetime->ref_count);
803 * gst_date_time_unref:
804 * @datetime: (transfer full): a #GstDateTime
806 * Atomically decrements the reference count of @datetime by one. When the
807 * reference count reaches zero, the structure is freed.
812 gst_date_time_unref (GstDateTime * datetime)
814 g_return_if_fail (datetime != NULL);
815 g_return_if_fail (datetime->ref_count > 0);
817 if (g_atomic_int_dec_and_test (&datetime->ref_count))
818 gst_date_time_free (datetime);