* Copyright (C) 2009-2010 Christian Hergert <chris@dronelabs.com>
* Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
* Copyright (C) 2010 Emmanuele Bassi <ebassi@linux.intel.com>
+ * Copyright © 2010 Codethink Limited
*
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * licence, or (at your option) any later version.
*
- * This is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
+ * This is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
+ * USA.
+ *
+ * Authors: Christian Hergert <chris@dronelabs.com>
+ * Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+ * Emmanuele Bassi <ebassi@linux.intel.com>
+ * Ryan Lortie <desrt@desrt.ca>
*/
/* Algorithms within this file are based on the Calendar FAQ by
* to its correctness.
*/
-#include "config.h"
+/* Prologue {{{1 */
-#include "glib.h"
+#include "config.h"
#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#ifdef HAVE_LANGINFO_TIME
+#include <langinfo.h>
#endif
+#include "gdatetime.h"
+
+#include "gslice.h"
+#include "gatomic.h"
+#include "gcharset.h"
+#include "gconvert.h"
+#include "gfileutils.h"
+#include "ghash.h"
+#include "gmain.h"
+#include "gmappedfile.h"
+#include "gstrfuncs.h"
+#include "gtestutils.h"
+#include "gthread.h"
+#include "gtimezone.h"
+
+#include "glibintl.h"
+
#ifndef G_OS_WIN32
#include <sys/time.h>
#include <time.h>
#endif /* !G_OS_WIN32 */
-#include "glibintl.h"
-
-#include "gdatetime.h"
-
/**
* SECTION:date-time
* @title: GDateTime
- * @short_description: A Date and Time structure
+ * @short_description: a structure representing Date and Time
+ * @see_also: #GTimeZone
*
- * #GDateTime is a structure that combines a date and time into a single
- * structure. It provides many conversion and methods to manipulate dates
- * and times. Time precision is provided down to microseconds.
+ * #GDateTime is a structure that combines a Gregorian date and time
+ * into a single structure. It provides many conversion and methods to
+ * manipulate dates and times. Time precision is provided down to
+ * microseconds and the time can range (proleptically) from 0001-01-01
+ * 00:00:00 to 9999-12-31 23:59:59.999999. #GDateTime follows POSIX
+ * time in the sense that it is oblivious to leap seconds.
*
- * #GDateTime is an immutable object: once it has been created it cannot be
- * modified further. All modifiers will create a new #GDateTime.
+ * #GDateTime is an immutable object; once it has been created it cannot
+ * be modified further. All modifiers will create a new #GDateTime.
+ * Nearly all such functions can fail due to the date or time going out
+ * of range, in which case %NULL will be returned.
*
* #GDateTime is reference counted: the reference count is increased by calling
* g_date_time_ref() and decreased by calling g_date_time_unref(). When the
* reference count drops to 0, the resources allocated by the #GDateTime
* structure are released.
*
- * Internally, #GDateTime uses the Julian Day Number since the
- * initial Julian Period (-4712 BC). However, the public API uses the
- * internationally accepted Gregorian Calendar.
+ * Many parts of the API may produce non-obvious results. As an
+ * example, adding two months to January 31st will yield March 31st
+ * whereas adding one month and then one month again will yield either
+ * March 28th or March 29th. Also note that adding 24 hours is not
+ * always the same as adding one day (since days containing daylight
+ * savings time transitions are either 23 or 25 hours in length).
*
* #GDateTime is available since GLib 2.26.
*/
+struct _GDateTime
+{
+ /* Microsecond timekeeping within Day */
+ guint64 usec;
+
+ /* TimeZone information */
+ GTimeZone *tz;
+ gint interval;
+
+ /* 1 is 0001-01-01 in Proleptic Gregorian */
+ gint32 days;
+
+ volatile gint ref_count;
+};
+
+/* Time conversion {{{1 */
+
+#define UNIX_EPOCH_START 719163
+#define INSTANT_TO_UNIX(instant) \
+ ((instant)/USEC_PER_SECOND - UNIX_EPOCH_START * SEC_PER_DAY)
+#define UNIX_TO_INSTANT(unix) \
+ (((unix) + UNIX_EPOCH_START * SEC_PER_DAY) * USEC_PER_SECOND)
+
+#define DAYS_IN_4YEARS 1461 /* days in 4 years */
+#define DAYS_IN_100YEARS 36524 /* days in 100 years */
+#define DAYS_IN_400YEARS 146097 /* days in 400 years */
+
#define USEC_PER_SECOND (G_GINT64_CONSTANT (1000000))
#define USEC_PER_MINUTE (G_GINT64_CONSTANT (60000000))
#define USEC_PER_HOUR (G_GINT64_CONSTANT (3600000000))
#define USEC_PER_MILLISECOND (G_GINT64_CONSTANT (1000))
#define USEC_PER_DAY (G_GINT64_CONSTANT (86400000000))
+#define SEC_PER_DAY (G_GINT64_CONSTANT (86400))
+
+#define SECS_PER_MINUTE (60)
+#define SECS_PER_HOUR (60 * SECS_PER_MINUTE)
+#define SECS_PER_DAY (24 * SECS_PER_HOUR)
+#define SECS_PER_YEAR (365 * SECS_PER_DAY)
+#define SECS_PER_JULIAN (DAYS_PER_PERIOD * SECS_PER_DAY)
-#define GREGORIAN_LEAP(y) (((y % 4) == 0) && (!(((y % 100) == 0) && ((y % 400) != 0))))
+#define GREGORIAN_LEAP(y) ((((y) % 4) == 0) && (!((((y) % 100) == 0) && (((y) % 400) != 0))))
#define JULIAN_YEAR(d) ((d)->julian / 365.25)
#define DAYS_PER_PERIOD (G_GINT64_CONSTANT (2914695))
-#define GET_AMPM(d,l) ((g_date_time_get_hour (d) < 12) \
- ? (l ? C_("GDateTime", "am") : C_("GDateTime", "AM")) \
- : (l ? C_("GDateTime", "pm") : C_("GDateTime", "PM")))
-
-#define WEEKDAY_ABBR(d) (get_weekday_name_abbr (g_date_time_get_day_of_week (datetime)))
-#define WEEKDAY_FULL(d) (get_weekday_name (g_date_time_get_day_of_week (datetime)))
+static const guint16 days_in_months[2][13] =
+{
+ { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
-#define MONTH_ABBR(d) (get_month_name_abbr (g_date_time_get_month (datetime)))
-#define MONTH_FULL(d) (get_month_name (g_date_time_get_month (datetime)))
+static const guint16 days_in_year[2][13] =
+{
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
-/* Translators: this is the preferred format for expressing the date */
-#define GET_PREFERRED_DATE(d) (g_date_time_printf ((d), C_("GDateTime", "%m/%d/%y")))
+#ifdef HAVE_LANGINFO_TIME
-/* Translators: this is the preferred format for expressing the time */
-#define GET_PREFERRED_TIME(d) (g_date_time_printf ((d), C_("GDateTime", "%H:%M:%S")))
+#define GET_AMPM(d) ((g_date_time_get_hour (d) < 12) ? \
+ nl_langinfo (AM_STR) : \
+ nl_langinfo (PM_STR))
-typedef struct _GTimeZone GTimeZone;
+#define PREFERRED_DATE_TIME_FMT nl_langinfo (D_T_FMT)
+#define PREFERRED_DATE_FMT nl_langinfo (D_FMT)
+#define PREFERRED_TIME_FMT nl_langinfo (T_FMT)
+#define PREFERRED_TIME_FMT nl_langinfo (T_FMT)
+#define PREFERRED_12HR_TIME_FMT nl_langinfo (T_FMT_AMPM)
-struct _GDateTime
+static const gint weekday_item[2][7] =
{
- /* Julian Period, 0 is Initial Epoch */
- gint period : 3;
-
- /* Day within Julian Period */
- guint julian : 22;
+ { ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABDAY_1 },
+ { DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7, DAY_1 }
+};
- /* Microsecond timekeeping within Day */
- guint64 usec : 37;
+static const gint month_item[2][12] =
+{
+ { ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12 },
+ { MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, MON_10, MON_11, MON_12 },
+};
- gint reserved : 2;
+#define WEEKDAY_ABBR(d) nl_langinfo (weekday_item[0][g_date_time_get_day_of_week (d) - 1])
+#define WEEKDAY_FULL(d) nl_langinfo (weekday_item[1][g_date_time_get_day_of_week (d) - 1])
+#define MONTH_ABBR(d) nl_langinfo (month_item[0][g_date_time_get_month (d) - 1])
+#define MONTH_FULL(d) nl_langinfo (month_item[1][g_date_time_get_month (d) - 1])
- /* TimeZone information, NULL is UTC */
- GTimeZone *tz;
+#else
- volatile gint ref_count;
-};
+#define GET_AMPM(d) ((g_date_time_get_hour (d) < 12) \
+ /* Translators: 'before midday' indicator */ \
+ ? C_("GDateTime", "AM") \
+ /* Translators: 'after midday' indicator */ \
+ : C_("GDateTime", "PM"))
-struct _GTimeZone
-{
- /* TZ abbreviation (e.g. PST) */
- gchar *name;
+/* Translators: this is the preferred format for expressing the date and the time */
+#define PREFERRED_DATE_TIME_FMT C_("GDateTime", "%a %b %e %H:%M:%S %Y")
- gint64 offset;
+/* Translators: this is the preferred format for expressing the date */
+#define PREFERRED_DATE_FMT C_("GDateTime", "%m/%d/%y")
- guint is_dst : 1;
-};
+/* Translators: this is the preferred format for expressing the time */
+#define PREFERRED_TIME_FMT C_("GDateTime", "%H:%M:%S")
-static const guint16 days_in_months[2][13] =
-{
- { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
- { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
-};
+/* Translators: this is the preferred format for expressing 12 hour time */
+#define PREFERRED_12HR_TIME_FMT C_("GDateTime", "%I:%M:%S %p")
-static const guint16 days_in_year[2][13] =
-{
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
- { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
-};
+#define WEEKDAY_ABBR(d) (get_weekday_name_abbr (g_date_time_get_day_of_week (d)))
+#define WEEKDAY_FULL(d) (get_weekday_name (g_date_time_get_day_of_week (d)))
+#define MONTH_ABBR(d) (get_month_name_abbr (g_date_time_get_month (d)))
+#define MONTH_FULL(d) (get_month_name (g_date_time_get_month (d)))
static const gchar *
get_month_name (gint month)
switch (month)
{
case 1:
- return C_("GDateTime", "January");
+ return C_("full month name", "January");
case 2:
- return C_("GDateTime", "February");
+ return C_("full month name", "February");
case 3:
- return C_("GDateTime", "March");
+ return C_("full month name", "March");
case 4:
- return C_("GDateTime", "April");
+ return C_("full month name", "April");
case 5:
- return C_("GDateTime", "May");
+ return C_("full month name", "May");
case 6:
- return C_("GDateTime", "June");
+ return C_("full month name", "June");
case 7:
- return C_("GDateTime", "July");
+ return C_("full month name", "July");
case 8:
- return C_("GDateTime", "August");
+ return C_("full month name", "August");
case 9:
- return C_("GDateTime", "September");
+ return C_("full month name", "September");
case 10:
- return C_("GDateTime", "October");
+ return C_("full month name", "October");
case 11:
- return C_("GDateTime", "November");
+ return C_("full month name", "November");
case 12:
- return C_("GDateTime", "December");
+ return C_("full month name", "December");
default:
g_warning ("Invalid month number %d", month);
switch (month)
{
case 1:
- return C_("GDateTime", "Jan");
+ return C_("abbreviated month name", "Jan");
case 2:
- return C_("GDateTime", "Feb");
+ return C_("abbreviated month name", "Feb");
case 3:
- return C_("GDateTime", "Mar");
+ return C_("abbreviated month name", "Mar");
case 4:
- return C_("GDateTime", "Apr");
+ return C_("abbreviated month name", "Apr");
case 5:
- return C_("GDateTime", "May");
+ return C_("abbreviated month name", "May");
case 6:
- return C_("GDateTime", "Jun");
+ return C_("abbreviated month name", "Jun");
case 7:
- return C_("GDateTime", "Jul");
+ return C_("abbreviated month name", "Jul");
case 8:
- return C_("GDateTime", "Aug");
+ return C_("abbreviated month name", "Aug");
case 9:
- return C_("GDateTime", "Sep");
+ return C_("abbreviated month name", "Sep");
case 10:
- return C_("GDateTime", "Oct");
+ return C_("abbreviated month name", "Oct");
case 11:
- return C_("GDateTime", "Nov");
+ return C_("abbreviated month name", "Nov");
case 12:
- return C_("GDateTime", "Dec");
+ return C_("abbreviated month name", "Dec");
default:
g_warning ("Invalid month number %d", month);
switch (day)
{
case 1:
- return C_("GDateTime", "Monday");
+ return C_("full weekday name", "Monday");
case 2:
- return C_("GDateTime", "Tuesday");
+ return C_("full weekday name", "Tuesday");
case 3:
- return C_("GDateTime", "Wednesday");
+ return C_("full weekday name", "Wednesday");
case 4:
- return C_("GDateTime", "Thursday");
+ return C_("full weekday name", "Thursday");
case 5:
- return C_("GDateTime", "Friday");
+ return C_("full weekday name", "Friday");
case 6:
- return C_("GDateTime", "Saturday");
+ return C_("full weekday name", "Saturday");
case 7:
- return C_("GDateTime", "Sunday");
+ return C_("full weekday name", "Sunday");
default:
g_warning ("Invalid week day number %d", day);
switch (day)
{
case 1:
- return C_("GDateTime", "Mon");
+ return C_("abbreviated weekday name", "Mon");
case 2:
- return C_("GDateTime", "Tue");
+ return C_("abbreviated weekday name", "Tue");
case 3:
- return C_("GDateTime", "Wed");
+ return C_("abbreviated weekday name", "Wed");
case 4:
- return C_("GDateTime", "Thu");
+ return C_("abbreviated weekday name", "Thu");
case 5:
- return C_("GDateTime", "Fri");
+ return C_("abbreviated weekday name", "Fri");
case 6:
- return C_("GDateTime", "Sat");
+ return C_("abbreviated weekday name", "Sat");
case 7:
- return C_("GDateTime", "Sun");
+ return C_("abbreviated weekday name", "Sun");
default:
g_warning ("Invalid week day number %d", day);
return NULL;
}
-static inline void
-g_date_time_add_days_internal (GDateTime *datetime,
- gint64 days)
-{
- gint __day = datetime->julian + days;
- if (__day < 1)
- {
- datetime->period += -1 + (__day / DAYS_PER_PERIOD);
- datetime->period += DAYS_PER_PERIOD + (__day % DAYS_PER_PERIOD);
- }
- else if (__day > DAYS_PER_PERIOD)
- {
- datetime->period += (datetime->julian + days) / DAYS_PER_PERIOD;
- datetime->julian = (datetime->julian + days) % DAYS_PER_PERIOD;
- }
- else
- datetime->julian += days;
-}
-
-static inline void
-g_date_time_add_usec (GDateTime *datetime,
- gint64 usecs)
-{
- gint64 __usec = datetime->usec + usecs;
- gint __days = __usec / USEC_PER_DAY;
-
- if (__usec < 0)
- __days -= 1;
-
- if (__days != 0)
- g_date_time_add_days_internal (datetime, __days);
-
- if (__usec < 0)
- datetime->usec = USEC_PER_DAY + (__usec % USEC_PER_DAY);
- else
- datetime->usec = __usec % USEC_PER_DAY;
-}
-
-#define ZONEINFO_DIR "zoneinfo"
-#define TZ_MAGIC "TZif"
-#define TZ_MAGIC_LEN (strlen (TZ_MAGIC))
-#define TZ_HEADER_SIZE 44
-#define TZ_TIMECNT_OFFSET 32
-#define TZ_TYPECNT_OFFSET 36
-#define TZ_TRANSITIONS_OFFSET 44
-
-#define TZ_TTINFO_SIZE 6
-#define TZ_TTINFO_GMTOFF_OFFSET 0
-#define TZ_TTINFO_ISDST_OFFSET 4
-#define TZ_TTINFO_NAME_OFFSET 5
-
-static gchar *
-get_tzdata_path (const gchar *tz_name)
-{
- gchar *retval;
-
- if (tz_name != NULL)
- {
- const gchar *tz_dir = g_getenv ("TZDIR");
-
- if (tz_dir != NULL)
- retval = g_build_filename (tz_dir, tz_name, NULL);
- else
- retval = g_build_filename ("/", "usr", "share", ZONEINFO_DIR, tz_name, NULL);
- }
- else
- {
- /* an empty tz_name means "the current timezone file". tzset(3) defines
- * it to be /usr/share/zoneinfo/localtime, and it also allows an
- * /etc/localtime as a symlink to the localtime file under
- * /usr/share/zoneinfo or to the correct timezone file. Fedora does not
- * have /usr/share/zoneinfo/localtime, but it does have a real
- * /etc/localtime.
- *
- * in any case, this path should resolve correctly.
- */
- retval = g_build_filename ("/", "etc", "localtime", NULL);
- }
-
- return retval;
-}
-
-/*
- * Parses tzdata database times to get timezone info.
- *
- * @tzname: Olson database name for the timezone
- * @start: Time offset from epoch we want to know the timezone
- * @_is_dst: Returns if this time in the timezone is in DST
- * @_offset: Returns the offset from UTC for this timezone
- * @_name: Returns the abreviated name for thie timezone
- */
-static gboolean
-parse_tzdata (const gchar *tzname,
- gint64 start,
- gboolean is_utc,
- gboolean *_is_dst,
- gint64 *_offset,
- gchar **_name)
-{
- gchar *filename, *contents;
- gsize length;
- guint32 timecnt, typecnt;
- gint transitions_size, ttinfo_map_size;
- guint8 *ttinfo_map, *ttinfos;
- gint start_transition = -1;
- guint32 *transitions;
- gint32 offset;
- guint8 isdst;
- guint8 name_offset;
- gint i;
- GError *error;
-
- filename = get_tzdata_path (tzname);
-
- /* XXX: should we be caching this in memory for faster access?
- * and if so, how do we expire the cache?
- */
- error = NULL;
- if (!g_file_get_contents (filename, &contents, &length, &error))
- {
- g_free (filename);
- return FALSE;
- }
-
- g_free (filename);
-
- if (length < TZ_HEADER_SIZE ||
- (strncmp (contents, TZ_MAGIC, TZ_MAGIC_LEN) != 0))
- {
- g_free (contents);
- return FALSE;
- }
-
- timecnt = GUINT32_FROM_BE (*(guint32 *)(contents + TZ_TIMECNT_OFFSET));
- typecnt = GUINT32_FROM_BE (*(guint32 *)(contents + TZ_TYPECNT_OFFSET));
-
- transitions = (guint32 *)(contents + TZ_TRANSITIONS_OFFSET);
- transitions_size = timecnt * sizeof (*transitions);
- ttinfo_map = (void *)(contents + TZ_TRANSITIONS_OFFSET + transitions_size);
- ttinfo_map_size = timecnt;
- ttinfos = (void *)(ttinfo_map + ttinfo_map_size);
-
- /*
- * Find the first transition that contains the 'start' time
- */
- for (i = 1; i < timecnt && start_transition == -1; i++)
- {
- gint32 transition_time = GINT32_FROM_BE (transitions[i]);
-
- /* if is_utc is not set, we need to add this time offset to compare with
- * start, because it is already on the timezone time */
- if (!is_utc)
- {
- gint32 off;
-
- off = *(gint32 *)(ttinfos + ttinfo_map[i] * TZ_TTINFO_SIZE +
- TZ_TTINFO_GMTOFF_OFFSET);
- off = GINT32_FROM_BE (off);
-
- transition_time += off;
- }
-
- if (transition_time > start)
- {
- start_transition = ttinfo_map[i - 1];
- break;
- }
- }
-
- if (start_transition == -1)
- {
- if (timecnt)
- start_transition = ttinfo_map[timecnt - 1];
- else
- start_transition = 0;
- }
-
- /* Copy the data out of the corresponding ttinfo structs */
- offset = *(gint32 *)(ttinfos + start_transition * TZ_TTINFO_SIZE +
- TZ_TTINFO_GMTOFF_OFFSET);
- offset = GINT32_FROM_BE (offset);
- isdst = *(ttinfos + start_transition * TZ_TTINFO_SIZE +
- TZ_TTINFO_ISDST_OFFSET);
- name_offset = *(ttinfos + start_transition * TZ_TTINFO_SIZE +
- TZ_TTINFO_NAME_OFFSET);
-
- if (_name != NULL)
- *_name = g_strdup ((gchar*) (ttinfos + TZ_TTINFO_SIZE * typecnt + name_offset));
-
- g_free (contents);
-
- if (_offset)
- *_offset = offset;
-
- if (_is_dst)
- *_is_dst = isdst;
-
- return TRUE;
-}
-
-/*< internal >
- * g_time_zone_new_from_epoc:
- * @tzname: The Olson's database timezone name
- * @epoch: The epoch offset
- * @is_utc: If the @epoch is in UTC or already in the @tzname timezone
- *
- * Creates a new timezone
- */
-static GTimeZone *
-g_time_zone_new_from_epoch (const gchar *tzname,
- gint64 epoch,
- gboolean is_utc)
-{
- GTimeZone *tz = NULL;
- gint64 offset;
- gboolean is_dst;
- gchar *name = NULL;
-
- if (parse_tzdata (tzname, epoch, is_utc, &is_dst, &offset, &name))
- {
- tz = g_slice_new (GTimeZone);
- tz->is_dst = is_dst;
- tz->offset = offset;
- tz->name = name;
- }
-
- return tz;
-}
-
-#define SECS_PER_MINUTE (60)
-#define SECS_PER_HOUR (60 * SECS_PER_MINUTE)
-#define SECS_PER_DAY (24 * SECS_PER_HOUR)
-#define SECS_PER_YEAR (365 * SECS_PER_DAY)
-#define SECS_PER_JULIAN (DAYS_PER_PERIOD * SECS_PER_DAY)
-
-static gint64
-g_date_time_secs_offset (GDateTime * dt)
-{
- gint64 secs;
- gint d, y, h, m, s;
- gint leaps;
-
- y = g_date_time_get_year (dt) - 1970;
- d = g_date_time_get_day_of_year (dt);
- h = g_date_time_get_hour (dt);
- m = g_date_time_get_minute (dt);
- s = g_date_time_get_second (dt);
-
- /* FIXME this is an approximation */
- leaps = y / 4;
-
- secs = 0;
- secs += y * SECS_PER_YEAR;
- secs += d * SECS_PER_DAY;
- secs += leaps * SECS_PER_DAY;
- secs += h * SECS_PER_HOUR;
- secs += m * SECS_PER_MINUTE;
- secs += s;
-
- return secs;
-}
-
-/*< internal >
- * g_date_time_create_time_zone:
- * @dt: a #GDateTime
- * @tzname: the name of the timezone
- *
- * Creates a timezone from a #GDateTime (disregarding its own timezone).
- * This function transforms the #GDateTime into seconds since the epoch
- * and creates a timezone for it in the @tzname zone.
- *
- * Return value: a newly created #GTimeZone
- */
-static GTimeZone *
-g_date_time_create_time_zone (GDateTime *dt,
- const gchar *tzname)
-{
- gint64 secs;
-
- secs = g_date_time_secs_offset (dt);
-
- return g_time_zone_new_from_epoch (tzname, secs, FALSE);
-}
-
-static GDateTime *
-g_date_time_new (void)
-{
- GDateTime *datetime;
-
- datetime = g_slice_new0 (GDateTime);
- datetime->ref_count = 1;
-
- return datetime;
-}
+#endif /* HAVE_LANGINFO_TIME */
-static GTimeZone *
-g_time_zone_copy (const GTimeZone *timezone)
+static inline gint
+ymd_to_days (gint year,
+ gint month,
+ gint day)
{
- GTimeZone *tz;
-
- if (G_UNLIKELY (timezone == NULL))
- return NULL;
-
- tz = g_slice_new (GTimeZone);
- memcpy (tz, timezone, sizeof (GTimeZone));
-
- tz->name = g_strdup (timezone->name);
-
- return tz;
-}
+ gint64 days;
-static void
-g_time_zone_free (GTimeZone *timezone)
-{
- if (G_LIKELY (timezone != NULL))
- {
- g_free (timezone->name);
- g_slice_free (GTimeZone, timezone);
- }
-}
+ days = (year - 1) * 365 + ((year - 1) / 4) - ((year - 1) / 100)
+ + ((year - 1) / 400);
-static void
-g_date_time_free (GDateTime *datetime)
-{
- if (G_UNLIKELY (datetime == NULL))
- return;
+ days += days_in_year[0][month - 1];
+ if (GREGORIAN_LEAP (year) && month > 2)
+ day++;
- if (datetime->tz)
- g_time_zone_free (datetime->tz);
+ days += day;
- g_slice_free (GDateTime, datetime);
+ return days;
}
static void
-g_date_time_get_week_number (const GDateTime *datetime,
- gint *week_number,
- gint *day_of_week,
- gint *day_of_year)
+g_date_time_get_week_number (GDateTime *datetime,
+ gint *week_number,
+ gint *day_of_week,
+ gint *day_of_year)
{
gint a, b, c, d, e, f, g, n, s, month, day, year;
- g_date_time_get_dmy (datetime, &day, &month, &year);
+ g_date_time_get_ymd (datetime, &year, &month, &day);
if (month <= 2)
{
*day_of_year = f + 1;
}
+/* Lifecycle {{{1 */
+
+static GDateTime *
+g_date_time_alloc (GTimeZone *tz)
+{
+ GDateTime *datetime;
+
+ datetime = g_slice_new0 (GDateTime);
+ datetime->tz = g_time_zone_ref (tz);
+ datetime->ref_count = 1;
+
+ return datetime;
+}
+
/**
- * g_date_time_add:
+ * g_date_time_ref:
* @datetime: a #GDateTime
- * @timespan: a #GTimeSpan
*
- * Creates a copy of @datetime and adds the specified timespan to the copy.
+ * Atomically increments the reference count of @datetime by one.
*
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
+ * Returns: the #GDateTime with the reference count increased
*
* Since: 2.26
*/
-GDateTime*
-g_date_time_add (const GDateTime *datetime,
- GTimeSpan timespan)
+GDateTime *
+g_date_time_ref (GDateTime *datetime)
{
- GDateTime *dt;
-
g_return_val_if_fail (datetime != NULL, NULL);
+ g_return_val_if_fail (datetime->ref_count > 0, NULL);
- dt = g_date_time_copy (datetime);
- g_date_time_add_usec (dt, timespan);
+ g_atomic_int_inc (&datetime->ref_count);
- return dt;
+ return datetime;
}
/**
- * g_date_time_add_years:
+ * g_date_time_unref:
* @datetime: a #GDateTime
- * @years: the number of years
*
- * Creates a copy of @datetime and adds the specified number of years to the
- * copy.
+ * Atomically decrements the reference count of @datetime by one.
*
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
+ * When the reference count reaches zero, the resources allocated by
+ * @datetime are freed
*
* Since: 2.26
*/
-GDateTime*
-g_date_time_add_years (const GDateTime *datetime,
- gint years)
+void
+g_date_time_unref (GDateTime *datetime)
{
- GDateTime *dt;
- gint day;
-
- g_return_val_if_fail (datetime != NULL, NULL);
+ g_return_if_fail (datetime != NULL);
+ g_return_if_fail (datetime->ref_count > 0);
- day = g_date_time_get_day_of_month (datetime);
- if (g_date_time_is_leap_year (datetime) &&
- g_date_time_get_month (datetime) == 2)
+ if (g_atomic_int_dec_and_test (&datetime->ref_count))
{
- if (day == 29)
- day--;
+ g_time_zone_unref (datetime->tz);
+ g_slice_free (GDateTime, datetime);
}
-
- dt = g_date_time_new_from_date (g_date_time_get_year (datetime) + years,
- g_date_time_get_month (datetime),
- day);
- dt->usec = datetime->usec;
-
- return dt;
}
-/**
- * g_date_time_add_months:
+/* Internal state transformers {{{1 */
+/*< internal >
+ * g_date_time_to_instant:
* @datetime: a #GDateTime
- * @months: the number of months
*
- * Creates a copy of @datetime and adds the specified number of months to the
- * copy.
+ * Convert a @datetime into an instant.
*
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
+ * An instant is a number that uniquely describes a particular
+ * microsecond in time, taking time zone considerations into account.
+ * (ie: "03:00 -0400" is the same instant as "02:00 -0500").
*
- * Since: 2.26
+ * An instant is always positive but we use a signed return value to
+ * avoid troubles with C.
*/
-GDateTime*
-g_date_time_add_months (const GDateTime *datetime,
- gint months)
-{
- GDateTime *dt;
- gint year,
- month,
- day,
- i,
- a;
- const guint16 *days;
+static gint64
+g_date_time_to_instant (GDateTime *datetime)
+{
+ gint64 offset;
- g_return_val_if_fail (datetime != NULL, NULL);
- g_return_val_if_fail (months != 0, NULL);
+ offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
+ offset *= USEC_PER_SECOND;
- month = g_date_time_get_month (datetime);
- year = g_date_time_get_year (datetime);
- a = months > 0 ? 1 : -1;
+ return datetime->days * USEC_PER_DAY + datetime->usec - offset;
+}
- for (i = 0; i < ABS (months); i++)
- {
- month += a;
- if (month < 1)
- {
- year--;
- month = 12;
- }
- else if (month > 12)
- {
- year++;
- month = 1;
- }
+/*< internal >
+ * g_date_time_from_instant:
+ * @tz: a #GTimeZone
+ * @instant: a instant in time
+ *
+ * Creates a #GDateTime from a time zone and an instant.
+ *
+ * This might fail if the time ends up being out of range.
+ */
+static GDateTime *
+g_date_time_from_instant (GTimeZone *tz,
+ gint64 instant)
+{
+ GDateTime *datetime;
+ gint64 offset;
+
+ if (instant < 0 || instant > G_GINT64_CONSTANT (1000000000000000000))
+ return NULL;
+
+ datetime = g_date_time_alloc (tz);
+ datetime->interval = g_time_zone_find_interval (tz,
+ G_TIME_TYPE_UNIVERSAL,
+ INSTANT_TO_UNIX (instant));
+ offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
+ offset *= USEC_PER_SECOND;
+
+ instant += offset;
+
+ datetime->days = instant / USEC_PER_DAY;
+ datetime->usec = instant % USEC_PER_DAY;
+
+ if (datetime->days < 1 || 3652059 < datetime->days)
+ {
+ g_date_time_unref (datetime);
+ datetime = NULL;
+ }
+
+ return datetime;
+}
+
+
+/*< internal >
+ * g_date_time_deal_with_date_change:
+ * @datetime: a #GDateTime
+ *
+ * This function should be called whenever the date changes by adding
+ * days, months or years. It does three things.
+ *
+ * First, we ensure that the date falls between 0001-01-01 and
+ * 9999-12-31 and return %FALSE if it does not.
+ *
+ * Next we update the ->interval field.
+ *
+ * Finally, we ensure that the resulting date and time pair exists (by
+ * ensuring that our time zone has an interval containing it) and
+ * adjusting as required. For example, if we have the time 02:30:00 on
+ * March 13 2010 in Toronto and we add 1 day to it, we would end up with
+ * 2:30am on March 14th, which doesn't exist. In that case, we bump the
+ * time up to 3:00am.
+ */
+static gboolean
+g_date_time_deal_with_date_change (GDateTime *datetime)
+{
+ GTimeType was_dst;
+ gint64 full_time;
+ gint64 usec;
+
+ if (datetime->days < 1 || datetime->days > 3652059)
+ return FALSE;
+
+ was_dst = g_time_zone_is_dst (datetime->tz, datetime->interval);
+
+ full_time = datetime->days * USEC_PER_DAY + datetime->usec;
+
+
+ usec = full_time % USEC_PER_SECOND;
+ full_time /= USEC_PER_SECOND;
+ full_time -= UNIX_EPOCH_START * SEC_PER_DAY;
+
+ datetime->interval = g_time_zone_adjust_time (datetime->tz,
+ was_dst,
+ &full_time);
+ full_time += UNIX_EPOCH_START * SEC_PER_DAY;
+ full_time *= USEC_PER_SECOND;
+ full_time += usec;
+
+ datetime->days = full_time / USEC_PER_DAY;
+ datetime->usec = full_time % USEC_PER_DAY;
+
+ /* maybe daylight time caused us to shift to a different day,
+ * but it definitely didn't push us into a different year */
+ return TRUE;
+}
+
+static GDateTime *
+g_date_time_replace_days (GDateTime *datetime,
+ gint days)
+{
+ GDateTime *new;
+
+ new = g_date_time_alloc (datetime->tz);
+ new->interval = datetime->interval;
+ new->usec = datetime->usec;
+ new->days = days;
+
+ if (!g_date_time_deal_with_date_change (new))
+ {
+ g_date_time_unref (new);
+ new = NULL;
}
- day = g_date_time_get_day_of_month (datetime);
- days = days_in_months [GREGORIAN_LEAP (year) ? 1 : 0];
+ return new;
+}
- if (days[month] < day)
- day = days[month];
+/* now/unix/timeval Constructors {{{1 */
- dt = g_date_time_new_from_date (year, month, day);
- dt->usec = datetime->usec;
+/*< internal >
+ * g_date_time_new_from_timeval:
+ * @tz: a #GTimeZone
+ * @tv: a #GTimeVal
+ *
+ * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
+ * given time zone @tz.
+ *
+ * The time contained in a #GTimeVal is always stored in the form of
+ * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
+ * given time zone.
+ *
+ * This call can fail (returning %NULL) if @tv represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+static GDateTime *
+g_date_time_new_from_timeval (GTimeZone *tz,
+ const GTimeVal *tv)
+{
+ return g_date_time_from_instant (tz, tv->tv_usec +
+ UNIX_TO_INSTANT (tv->tv_sec));
+}
- return dt;
+/*< internal >
+ * g_date_time_new_from_unix:
+ * @tz: a #GTimeZone
+ * @t: the Unix time
+ *
+ * Creates a #GDateTime corresponding to the given Unix time @t in the
+ * given time zone @tz.
+ *
+ * Unix time is the number of seconds that have elapsed since 1970-01-01
+ * 00:00:00 UTC, regardless of the time zone given.
+ *
+ * This call can fail (returning %NULL) if @t represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+static GDateTime *
+g_date_time_new_from_unix (GTimeZone *tz,
+ gint64 secs)
+{
+ return g_date_time_from_instant (tz, UNIX_TO_INSTANT (secs));
}
/**
- * g_date_time_add_weeks:
+ * g_date_time_new_now:
+ * @tz: a #GTimeZone
+ *
+ * Creates a #GDateTime corresponding to this exact instant in the given
+ * time zone @tz. The time is as accurate as the system allows, to a
+ * maximum accuracy of 1 microsecond.
+ *
+ * This function will always succeed unless the system clock is set to
+ * truly insane values (or unless GLib is still being used after the
+ * year 9999).
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_now (GTimeZone *tz)
+{
+ GTimeVal tv;
+
+ g_get_current_time (&tv);
+
+ return g_date_time_new_from_timeval (tz, &tv);
+}
+
+/**
+ * g_date_time_new_now_local:
+ *
+ * Creates a #GDateTime corresponding to this exact instant in the local
+ * time zone.
+ *
+ * This is equivalent to calling g_date_time_new_now() with the time
+ * zone returned by g_time_zone_new_local().
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_now_local (void)
+{
+ GDateTime *datetime;
+ GTimeZone *local;
+
+ local = g_time_zone_new_local ();
+ datetime = g_date_time_new_now (local);
+ g_time_zone_unref (local);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_now_utc:
+ *
+ * Creates a #GDateTime corresponding to this exact instant in UTC.
+ *
+ * This is equivalent to calling g_date_time_new_now() with the time
+ * zone returned by g_time_zone_new_utc().
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_now_utc (void)
+{
+ GDateTime *datetime;
+ GTimeZone *utc;
+
+ utc = g_time_zone_new_utc ();
+ datetime = g_date_time_new_now (utc);
+ g_time_zone_unref (utc);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_from_unix_local:
+ * @t: the Unix time
+ *
+ * Creates a #GDateTime corresponding to the given Unix time @t in the
+ * local time zone.
+ *
+ * Unix time is the number of seconds that have elapsed since 1970-01-01
+ * 00:00:00 UTC, regardless of the local time offset.
+ *
+ * This call can fail (returning %NULL) if @t represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_from_unix_local (gint64 t)
+{
+ GDateTime *datetime;
+ GTimeZone *local;
+
+ local = g_time_zone_new_local ();
+ datetime = g_date_time_new_from_unix (local, t);
+ g_time_zone_unref (local);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_from_unix_utc:
+ * @t: the Unix time
+ *
+ * Creates a #GDateTime corresponding to the given Unix time @t in UTC.
+ *
+ * Unix time is the number of seconds that have elapsed since 1970-01-01
+ * 00:00:00 UTC.
+ *
+ * This call can fail (returning %NULL) if @t represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_from_unix_utc (gint64 t)
+{
+ GDateTime *datetime;
+ GTimeZone *utc;
+
+ utc = g_time_zone_new_utc ();
+ datetime = g_date_time_new_from_unix (utc, t);
+ g_time_zone_unref (utc);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_from_timeval_local:
+ * @tv: a #GTimeVal
+ *
+ * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
+ * local time zone.
+ *
+ * The time contained in a #GTimeVal is always stored in the form of
+ * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
+ * local time offset.
+ *
+ * This call can fail (returning %NULL) if @tv represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_from_timeval_local (const GTimeVal *tv)
+{
+ GDateTime *datetime;
+ GTimeZone *local;
+
+ local = g_time_zone_new_local ();
+ datetime = g_date_time_new_from_timeval (local, tv);
+ g_time_zone_unref (local);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_from_timeval_utc:
+ * @tv: a #GTimeVal
+ *
+ * Creates a #GDateTime corresponding to the given #GTimeVal @tv in UTC.
+ *
+ * The time contained in a #GTimeVal is always stored in the form of
+ * seconds elapsed since 1970-01-01 00:00:00 UTC.
+ *
+ * This call can fail (returning %NULL) if @tv represents a time outside
+ * of the supported range of #GDateTime.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_from_timeval_utc (const GTimeVal *tv)
+{
+ GDateTime *datetime;
+ GTimeZone *utc;
+
+ utc = g_time_zone_new_utc ();
+ datetime = g_date_time_new_from_timeval (utc, tv);
+ g_time_zone_unref (utc);
+
+ return datetime;
+}
+
+/* full new functions {{{1 */
+
+/**
+ * g_date_time_new:
+ * @tz: a #GTimeZone
+ * @year: the year component of the date
+ * @month: the month component of the date
+ * @day: the day component of the date
+ * @hour: the hour component of the date
+ * @minute: the minute component of the date
+ * @seconds: the number of seconds past the minute
+ *
+ * Creates a new #GDateTime corresponding to the given date and time in
+ * the time zone @tz.
+ *
+ * The @year must be between 1 and 9999, @month between 1 and 12 and @day
+ * between 1 and 28, 29, 30 or 31 depending on the month and the year.
+ *
+ * @hour must be between 0 and 23 and @minute must be between 0 and 59.
+ *
+ * @seconds must be at least 0.0 and must be strictly less than 60.0.
+ * It will be rounded down to the nearest microsecond.
+ *
+ * If the given time is not representable in the given time zone (for
+ * example, 02:30 on March 14th 2010 in Toronto, due to daylight savings
+ * time) then the time will be rounded up to the nearest existing time
+ * (in this case, 03:00). If this matters to you then you should verify
+ * the return value for containing the same as the numbers you gave.
+ *
+ * In the case that the given time is ambiguous in the given time zone
+ * (for example, 01:30 on November 7th 2010 in Toronto, due to daylight
+ * savings time) then the time falling within standard (ie:
+ * non-daylight) time is taken.
+ *
+ * It not considered a programmer error for the values to this function
+ * to be out of range, but in the case that they are, the function will
+ * return %NULL.
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new (GTimeZone *tz,
+ gint year,
+ gint month,
+ gint day,
+ gint hour,
+ gint minute,
+ gdouble seconds)
+{
+ GDateTime *datetime;
+ gint64 full_time;
+
+ if (year < 1 || year > 9999 ||
+ month < 1 || month > 12 ||
+ day < 1 || day > 31 ||
+ hour < 0 || hour > 23 ||
+ minute < 0 || minute > 59 ||
+ seconds < 0.0 || seconds >= 60.0)
+ return NULL;
+
+ datetime = g_date_time_alloc (tz);
+ datetime->days = ymd_to_days (year, month, day);
+ datetime->usec = (hour * USEC_PER_HOUR)
+ + (minute * USEC_PER_MINUTE)
+ + (gint64) (seconds * USEC_PER_SECOND);
+
+ full_time = SEC_PER_DAY *
+ (ymd_to_days (year, month, day) - UNIX_EPOCH_START) +
+ SECS_PER_HOUR * hour +
+ SECS_PER_MINUTE * minute +
+ (int) seconds;
+
+ datetime->interval = g_time_zone_adjust_time (datetime->tz,
+ G_TIME_TYPE_STANDARD,
+ &full_time);
+
+ full_time += UNIX_EPOCH_START * SEC_PER_DAY;
+ datetime->days = full_time / SEC_PER_DAY;
+ datetime->usec = (full_time % SEC_PER_DAY) * USEC_PER_SECOND;
+ datetime->usec += ((int) (seconds * USEC_PER_SECOND)) % USEC_PER_SECOND;
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_local:
+ * @year: the year component of the date
+ * @month: the month component of the date
+ * @day: the day component of the date
+ * @hour: the hour component of the date
+ * @minute: the minute component of the date
+ * @seconds: the number of seconds past the minute
+ *
+ * Creates a new #GDateTime corresponding to the given date and time in
+ * the local time zone.
+ *
+ * This call is equivalent to calling g_date_time_new() with the time
+ * zone returned by g_time_zone_new_local().
+ *
+ * Returns: a #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_local (gint year,
+ gint month,
+ gint day,
+ gint hour,
+ gint minute,
+ gdouble seconds)
+{
+ GDateTime *datetime;
+ GTimeZone *local;
+
+ local = g_time_zone_new_local ();
+ datetime = g_date_time_new (local, year, month, day, hour, minute, seconds);
+ g_time_zone_unref (local);
+
+ return datetime;
+}
+
+/**
+ * g_date_time_new_utc:
+ * @year: the year component of the date
+ * @month: the month component of the date
+ * @day: the day component of the date
+ * @hour: the hour component of the date
+ * @minute: the minute component of the date
+ * @seconds: the number of seconds past the minute
+ *
+ * Creates a new #GDateTime corresponding to the given date and time in
+ * UTC.
+ *
+ * This call is equivalent to calling g_date_time_new() with the time
+ * zone returned by g_time_zone_new_utc().
+ *
+ * Returns: a #GDateTime, or %NULL
+ *
+ * Since: 2.26
+ **/
+GDateTime *
+g_date_time_new_utc (gint year,
+ gint month,
+ gint day,
+ gint hour,
+ gint minute,
+ gdouble seconds)
+{
+ GDateTime *datetime;
+ GTimeZone *utc;
+
+ utc = g_time_zone_new_utc ();
+ datetime = g_date_time_new (utc, year, month, day, hour, minute, seconds);
+ g_time_zone_unref (utc);
+
+ return datetime;
+}
+
+/* Adders {{{1 */
+
+/**
+ * g_date_time_add:
* @datetime: a #GDateTime
- * @weeks: the number of weeks
+ * @timespan: a #GTimeSpan
*
- * Creates a copy of @datetime and adds the specified number of weeks to the
- * copy.
+ * Creates a copy of @datetime and adds the specified timespan to the copy.
*
- * Return value: the newly created #GDateTime which should be freed with
+ * Returns: the newly created #GDateTime which should be freed with
* g_date_time_unref().
*
* Since: 2.26
*/
GDateTime*
-g_date_time_add_weeks (const GDateTime *datetime,
- gint weeks)
+g_date_time_add (GDateTime *datetime,
+ GTimeSpan timespan)
{
- g_return_val_if_fail (datetime != NULL, NULL);
-
- return g_date_time_add_days (datetime, weeks * 7);
+ return g_date_time_from_instant (datetime->tz, timespan +
+ g_date_time_to_instant (datetime));
}
/**
- * g_date_time_add_days:
+ * g_date_time_add_years:
* @datetime: a #GDateTime
- * @days: the number of days
+ * @years: the number of years
*
- * Creates a copy of @datetime and adds the specified number of days to the
- * copy.
+ * Creates a copy of @datetime and adds the specified number of years to the
+ * copy. Add negative values to subtract years.
*
- * Return value: the newly created #GDateTime which should be freed with
+ * Returns: the newly created #GDateTime which should be freed with
* g_date_time_unref().
*
* Since: 2.26
*/
-GDateTime*
-g_date_time_add_days (const GDateTime *datetime,
- gint days)
+GDateTime *
+g_date_time_add_years (GDateTime *datetime,
+ gint years)
{
- GDateTime *dt;
+ gint year, month, day;
g_return_val_if_fail (datetime != NULL, NULL);
- dt = g_date_time_copy (datetime);
- g_date_time_add_days_internal (dt, days);
+ if (years < -10000 || years > 10000)
+ return NULL;
- return dt;
+ g_date_time_get_ymd (datetime, &year, &month, &day);
+ year += years;
+
+ /* only possible issue is if we've entered a year with no February 29
+ */
+ if (month == 2 && day == 29 && !GREGORIAN_LEAP (year))
+ day = 28;
+
+ return g_date_time_replace_days (datetime, ymd_to_days (year, month, day));
}
/**
- * g_date_time_add_hours:
+ * g_date_time_add_months:
* @datetime: a #GDateTime
- * @hours: the number of hours to add
+ * @months: the number of months
*
- * Creates a copy of @datetime and adds the specified number of hours
+ * Creates a copy of @datetime and adds the specified number of months to the
+ * copy. Add negative values to subtract months.
*
- * Return value: the newly created #GDateTime which should be freed with
+ * Returns: the newly created #GDateTime which should be freed with
* g_date_time_unref().
*
* Since: 2.26
*/
GDateTime*
-g_date_time_add_hours (const GDateTime *datetime,
- gint hours)
+g_date_time_add_months (GDateTime *datetime,
+ gint months)
{
- GDateTime *dt;
+ gint year, month, day;
g_return_val_if_fail (datetime != NULL, NULL);
+ g_date_time_get_ymd (datetime, &year, &month, &day);
- dt = g_date_time_copy (datetime);
- g_date_time_add_usec (dt, (gint64) hours * USEC_PER_HOUR);
+ if (months < -120000 || months > 120000)
+ return NULL;
- return dt;
+ year += months / 12;
+ month += months % 12;
+ if (month < 1)
+ {
+ month += 12;
+ year--;
+ }
+ else if (month > 12)
+ {
+ month -= 12;
+ year++;
+ }
+
+ day = MIN (day, days_in_months[GREGORIAN_LEAP (year)][month]);
+
+ return g_date_time_replace_days (datetime, ymd_to_days (year, month, day));
}
/**
- * g_date_time_add_seconds:
+ * g_date_time_add_weeks:
* @datetime: a #GDateTime
- * @seconds: the number of seconds to add
+ * @weeks: the number of weeks
*
- * Creates a copy of @datetime and adds the specified number of seconds.
+ * Creates a copy of @datetime and adds the specified number of weeks to the
+ * copy. Add negative values to subtract weeks.
*
- * Return value: the newly created #GDateTime which should be freed with
+ * Returns: the newly created #GDateTime which should be freed with
* g_date_time_unref().
*
* Since: 2.26
*/
GDateTime*
-g_date_time_add_seconds (const GDateTime *datetime,
- gint seconds)
+g_date_time_add_weeks (GDateTime *datetime,
+ gint weeks)
{
- GDateTime *dt;
-
g_return_val_if_fail (datetime != NULL, NULL);
- dt = g_date_time_copy (datetime);
- g_date_time_add_usec (dt, (gint64) seconds * USEC_PER_SECOND);
-
- return dt;
+ return g_date_time_add_days (datetime, weeks * 7);
}
/**
- * g_date_time_add_milliseconds:
+ * g_date_time_add_days:
* @datetime: a #GDateTime
- * @milliseconds: the number of milliseconds to add
+ * @days: the number of days
*
- * Creates a copy of @datetime adding the specified number of milliseconds.
+ * Creates a copy of @datetime and adds the specified number of days to the
+ * copy. Add negative values to subtract days.
*
- * Return value: the newly created #GDateTime which should be freed with
+ * Returns: the newly created #GDateTime which should be freed with
* g_date_time_unref().
*
* Since: 2.26
*/
GDateTime*
-g_date_time_add_milliseconds (const GDateTime *datetime,
- gint milliseconds)
+g_date_time_add_days (GDateTime *datetime,
+ gint days)
{
- GDateTime *dt;
-
g_return_val_if_fail (datetime != NULL, NULL);
- dt = g_date_time_copy (datetime);
- g_date_time_add_usec (dt, (gint64) milliseconds * USEC_PER_MILLISECOND);
+ if (days < -3660000 || days > 3660000)
+ return NULL;
- return dt;
+ return g_date_time_replace_days (datetime, datetime->days + days);
+}
+
+/**
+ * g_date_time_add_hours:
+ * @datetime: a #GDateTime
+ * @hours: the number of hours to add
+ *
+ * Creates a copy of @datetime and adds the specified number of hours.
+ * Add negative values to subtract hours.
+ *
+ * Returns: the newly created #GDateTime which should be freed with
+ * g_date_time_unref().
+ *
+ * Since: 2.26
+ */
+GDateTime*
+g_date_time_add_hours (GDateTime *datetime,
+ gint hours)
+{
+ return g_date_time_add (datetime, hours * USEC_PER_HOUR);
}
/**
* @datetime: a #GDateTime
* @minutes: the number of minutes to add
*
- * Creates a copy of @datetime adding the specified number of minutes.
+ * Creates a copy of @datetime adding the specified number of minutes.
+ * Add negative values to subtract minutes.
+ *
+ * Returns: the newly created #GDateTime which should be freed with
+ * g_date_time_unref().
+ *
+ * Since: 2.26
+ */
+GDateTime*
+g_date_time_add_minutes (GDateTime *datetime,
+ gint minutes)
+{
+ return g_date_time_add (datetime, minutes * USEC_PER_MINUTE);
+}
+
+
+/**
+ * g_date_time_add_seconds:
+ * @datetime: a #GDateTime
+ * @seconds: the number of seconds to add
+ *
+ * Creates a copy of @datetime and adds the specified number of seconds.
+ * Add negative values to subtract seconds.
*
- * Return value: the newly created #GDateTime which should be freed with
+ * Returns: the newly created #GDateTime which should be freed with
* g_date_time_unref().
*
* Since: 2.26
*/
GDateTime*
-g_date_time_add_minutes (const GDateTime *datetime,
- gint minutes)
+g_date_time_add_seconds (GDateTime *datetime,
+ gdouble seconds)
{
- GDateTime *dt;
-
- g_return_val_if_fail (datetime != NULL, NULL);
-
- dt = g_date_time_copy (datetime);
- g_date_time_add_usec (dt, (gint64) minutes * USEC_PER_MINUTE);
-
- return dt;
+ return g_date_time_add (datetime, seconds * USEC_PER_SECOND);
}
/**
* @seconds: the number of seconds to add
*
* Creates a new #GDateTime adding the specified values to the current date and
- * time in @datetime.
+ * time in @datetime. Add negative values to subtract.
*
- * Return value: the newly created #GDateTime that should be freed with
+ * Returns: the newly created #GDateTime that should be freed with
* g_date_time_unref().
*
* Since: 2.26
*/
GDateTime *
-g_date_time_add_full (const GDateTime *datetime,
- gint years,
- gint months,
- gint days,
- gint hours,
- gint minutes,
- gint seconds)
-{
- GDateTime *tmp, *dt;
+g_date_time_add_full (GDateTime *datetime,
+ gint years,
+ gint months,
+ gint days,
+ gint hours,
+ gint minutes,
+ gdouble seconds)
+{
+ gint year, month, day;
+ gint64 full_time;
+ GDateTime *new;
+ gint interval;
g_return_val_if_fail (datetime != NULL, NULL);
+ g_date_time_get_ymd (datetime, &year, &month, &day);
+
+ months += years * 12;
+
+ if (months < -120000 || months > 120000)
+ return NULL;
+
+ if (days < -3660000 || days > 3660000)
+ return NULL;
+
+ year += months / 12;
+ month += months % 12;
+ if (month < 1)
+ {
+ month += 12;
+ year--;
+ }
+ else if (month > 12)
+ {
+ month -= 12;
+ year++;
+ }
+
+ day = MIN (day, days_in_months[GREGORIAN_LEAP (year)][month]);
- dt = g_date_time_add_years (datetime, years);
- tmp = dt;
+ /* full_time is now in unix (local) time */
+ full_time = datetime->usec / USEC_PER_SECOND + SEC_PER_DAY *
+ (ymd_to_days (year, month, day) + days - UNIX_EPOCH_START);
- dt = g_date_time_add_months (tmp, months);
- g_date_time_unref (tmp);
- tmp = dt;
+ interval = g_time_zone_adjust_time (datetime->tz,
+ g_time_zone_is_dst (datetime->tz,
+ datetime->interval),
+ &full_time);
- dt = g_date_time_add_days (tmp, days);
- g_date_time_unref (tmp);
- tmp = dt;
+ /* move to UTC unix time */
+ full_time -= g_time_zone_get_offset (datetime->tz, interval);
- dt = g_date_time_add_hours (tmp, hours);
- g_date_time_unref (tmp);
- tmp = dt;
+ /* convert back to an instant, add back fractional seconds */
+ full_time += UNIX_EPOCH_START * SEC_PER_DAY;
+ full_time = full_time * USEC_PER_SECOND +
+ datetime->usec % USEC_PER_SECOND;
- dt = g_date_time_add_minutes (tmp, minutes);
- g_date_time_unref (tmp);
- tmp = dt;
+ /* do the actual addition now */
+ full_time += (hours * USEC_PER_HOUR) +
+ (minutes * USEC_PER_MINUTE) +
+ (gint64) (seconds * USEC_PER_SECOND);
- dt = g_date_time_add_seconds (tmp, seconds);
- g_date_time_unref (tmp);
+ /* find the new interval */
+ interval = g_time_zone_find_interval (datetime->tz,
+ G_TIME_TYPE_UNIVERSAL,
+ INSTANT_TO_UNIX (full_time));
- return dt;
+ /* convert back into local time */
+ full_time += USEC_PER_SECOND *
+ g_time_zone_get_offset (datetime->tz, interval);
+
+ /* split into days and usec of a new datetime */
+ new = g_date_time_alloc (datetime->tz);
+ new->interval = interval;
+ new->days = full_time / USEC_PER_DAY;
+ new->usec = full_time % USEC_PER_DAY;
+
+ /* XXX validate */
+
+ return new;
}
+/* Compare, difference, hash, equal {{{1 */
/**
* g_date_time_compare:
* @dt1: first #GDateTime to compare
* @dt2: second #GDateTime to compare
*
- * qsort()-style comparison for #GDateTime<!-- -->'s. Both #GDateTime<-- -->'s
- * must be non-%NULL.
+ * A comparison function for #GDateTimes that is suitable
+ * as a #GCompareFunc. Both #GDateTimes must be non-%NULL.
*
- * Return value: 0 for equal, less than zero if dt1 is less than dt2, greater
- * than zero if dt2 is greator than dt1.
+ * Returns: -1, 0 or 1 if @dt1 is less than, equal to or greater
+ * than @dt2.
*
* Since: 2.26
*/
g_date_time_compare (gconstpointer dt1,
gconstpointer dt2)
{
- const GDateTime *a, *b;
+ gint64 difference;
- a = dt1;
- b = dt2;
+ difference = g_date_time_difference ((GDateTime *) dt1, (GDateTime *) dt2);
- if ((a->period == b->period) &&
- (a->julian == b->julian) &&
- (a->usec == b->usec))
- {
- return 0;
- }
- else if ((a->period > b->period) ||
- ((a->period == b->period) && (a->julian > b->julian)) ||
- ((a->period == b->period) && (a->julian == b->julian) && a->usec > b->usec))
- {
- return 1;
- }
- else
+ if (difference < 0)
return -1;
-}
-
-/**
- * g_date_time_copy:
- * @datetime: a #GDateTime
- *
- * Creates a copy of @datetime.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_copy (const GDateTime *datetime)
-{
- GDateTime *copied;
- g_return_val_if_fail (datetime != NULL, NULL);
-
- copied = g_date_time_new ();
- copied->period = datetime->period;
- copied->julian = datetime->julian;
- copied->usec = datetime->usec;
- copied->tz = g_time_zone_copy (datetime->tz);
+ else if (difference > 0)
+ return 1;
- return copied;
+ else
+ return 0;
}
/**
- * g_date_time_day:
- * @datetime: a #GDateTime
+ * g_date_time_difference:
+ * @end: a #GDateTime
+ * @begin: a #GDateTime
*
- * Creates a new #GDateTime at Midnight on the date represented by @datetime.
+ * Calculates the difference in time between @end and @begin. The
+ * #GTimeSpan that is returned is effectively @end - @begin (ie:
+ * positive if the first parameter is larger).
*
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
+ * Returns: the difference between the two #GDateTime, as a time
+ * span expressed in microseconds.
*
* Since: 2.26
*/
-GDateTime*
-g_date_time_day (const GDateTime *datetime)
+GTimeSpan
+g_date_time_difference (GDateTime *end,
+ GDateTime *begin)
{
- GDateTime *date;
-
- g_return_val_if_fail (datetime != NULL, NULL);
-
- date = g_date_time_copy (datetime);
- date->usec = 0;
+ g_return_val_if_fail (begin != NULL, 0);
+ g_return_val_if_fail (end != NULL, 0);
- return date;
+ return g_date_time_to_instant (end) -
+ g_date_time_to_instant (begin);
}
/**
- * g_date_time_difference:
- * @begin: a #GDateTime
- * @end: a #GDateTime
- *
- * Calculates the known difference in time between @begin and @end.
+ * g_date_time_hash:
+ * @datetime: a #GDateTime
*
- * Since the exact precision cannot always be known due to incomplete
- * historic information, an attempt is made to calculate the difference.
+ * Hashes @datetime into a #guint, suitable for use within #GHashTable.
*
- * Return value: the difference between the two #GDateTime, as a time
- * span expressed in microseconds.
+ * Returns: a #guint containing the hash
*
* Since: 2.26
*/
-GTimeSpan
-g_date_time_difference (const GDateTime *begin,
- const GDateTime *end)
+guint
+g_date_time_hash (gconstpointer datetime)
{
- g_return_val_if_fail (begin != NULL, 0);
- g_return_val_if_fail (end != NULL, 0);
-
- if (begin->period != 0 || end->period != 0)
- {
- g_warning ("GDateTime only supports the current Julian period");
- return 0;
- }
-
- return ((end->julian - begin->julian) * USEC_PER_DAY) + (end->usec - begin->usec);
+ return g_date_time_to_instant ((GDateTime *) datetime);
}
/**
* Checks to see if @dt1 and @dt2 are equal.
*
* Equal here means that they represent the same moment after converting
- * them to the same timezone.
+ * them to the same time zone.
*
- * Return value: %TRUE if @dt1 and @dt2 are equal
+ * Returns: %TRUE if @dt1 and @dt2 are equal
*
* Since: 2.26
*/
g_date_time_equal (gconstpointer dt1,
gconstpointer dt2)
{
- const GDateTime *a, *b;
- GDateTime *a_utc, *b_utc;
- gint64 a_epoch, b_epoch;
-
- a = dt1;
- b = dt2;
-
- a_utc = g_date_time_to_utc ((GDateTime *) a);
- b_utc = g_date_time_to_utc ((GDateTime *) b);
-
- a_epoch = g_date_time_to_epoch (a_utc);
- b_epoch = g_date_time_to_epoch (b_utc);
-
- g_date_time_unref (a_utc);
- g_date_time_unref (b_utc);
-
- return a_epoch == b_epoch;
+ return g_date_time_difference ((GDateTime *) dt1, (GDateTime *) dt2) == 0;
}
+/* Year, Month, Day Getters {{{1 */
/**
- * g_date_time_get_day_of_week:
- * @datetime: a #GDateTime
- *
- * Retrieves the day of the week represented by @datetime within the gregorian
- * calendar. 1 is Sunday, 2 is Monday, etc.
+ * g_date_time_get_ymd:
+ * @datetime: a #GDateTime.
+ * @year: (out) (allow-none): the return location for the gregorian year, or %NULL.
+ * @month: (out) (allow-none): the return location for the month of the year, or %NULL.
+ * @day: (out) (allow-none): the return location for the day of the month, or %NULL.
*
- * Return value: the day of the week
+ * Retrieves the Gregorian day, month, and year of a given #GDateTime.
*
* Since: 2.26
- */
-gint
-g_date_time_get_day_of_week (const GDateTime *datetime)
-{
- gint a, y, m,
- year = 0,
- month = 0,
- day = 0,
- dow;
+ **/
+void
+g_date_time_get_ymd (GDateTime *datetime,
+ gint *year,
+ gint *month,
+ gint *day)
+{
+ gint the_year;
+ gint the_month;
+ gint the_day;
+ gint remaining_days;
+ gint y100_cycles;
+ gint y4_cycles;
+ gint y1_cycles;
+ gint preceding;
+ gboolean leap;
- g_return_val_if_fail (datetime != NULL, 0);
+ g_return_if_fail (datetime != NULL);
+
+ remaining_days = datetime->days;
/*
- * See Calendar FAQ Section 2.6 for algorithm information
- * http://www.tondering.dk/claus/cal/calendar29.txt
+ * We need to convert an offset in days to its year/month/day representation.
+ * Leap years makes this a little trickier than it should be, so we use
+ * 400, 100 and 4 years cycles here to get to the correct year.
*/
- g_date_time_get_dmy (datetime, &day, &month, &year);
- a = (14 - month) / 12;
- y = year - a;
- m = month + (12 * a) - 2;
- dow = ((day + y + (y / 4) - (y / 100) + (y / 400) + (31 * m) / 12) % 7);
-
- /* 1 is Monday and 7 is Sunday */
- return (dow == 0) ? 7 : dow;
-}
-
-/**
- * g_date_time_get_day_of_month:
- * @datetime: a #GDateTime
- *
- * Retrieves the day of the month represented by @datetime in the gregorian
- * calendar.
- *
- * Return value: the day of the month
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_day_of_month (const GDateTime *datetime)
-{
- gint day_of_year,
- i;
- const guint16 *days;
- guint16 last = 0;
+ /* Our days offset starts sets 0001-01-01 as day 1, if it was day 0 our
+ * math would be simpler, so let's do it */
+ remaining_days--;
- g_return_val_if_fail (datetime != NULL, 0);
+ the_year = (remaining_days / DAYS_IN_400YEARS) * 400 + 1;
+ remaining_days = remaining_days % DAYS_IN_400YEARS;
- days = days_in_year[g_date_time_is_leap_year (datetime) ? 1 : 0];
- g_date_time_get_week_number (datetime, NULL, NULL, &day_of_year);
+ y100_cycles = remaining_days / DAYS_IN_100YEARS;
+ remaining_days = remaining_days % DAYS_IN_100YEARS;
+ the_year += y100_cycles * 100;
- for (i = 1; i <= 12; i++)
- {
- if (days [i] >= day_of_year)
- return day_of_year - last;
- last = days [i];
- }
+ y4_cycles = remaining_days / DAYS_IN_4YEARS;
+ remaining_days = remaining_days % DAYS_IN_4YEARS;
+ the_year += y4_cycles * 4;
- g_warn_if_reached ();
- return 0;
-}
+ y1_cycles = remaining_days / 365;
+ the_year += y1_cycles;
+ remaining_days = remaining_days % 365;
-/**
- * g_date_time_get_day_of_year:
- * @datetime: a #GDateTime
- *
- * Retrieves the day of the year represented by @datetime in the Gregorian
- * calendar.
- *
- * Return value: the day of the year
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_day_of_year (const GDateTime *datetime)
-{
- gint doy = 0;
+ if (y1_cycles == 4 || y100_cycles == 4) {
+ g_assert (remaining_days == 0);
- g_return_val_if_fail (datetime != NULL, 0);
+ /* special case that indicates that the date is actually one year before,
+ * in the 31th of December */
+ the_year--;
+ the_month = 12;
+ the_day = 31;
+ goto end;
+ }
- g_date_time_get_week_number (datetime, NULL, NULL, &doy);
- return doy;
-}
+ /* now get the month and the day */
+ leap = y1_cycles == 3 && (y4_cycles != 24 || y100_cycles == 3);
-/**
- * g_date_time_get_dmy:
- * @datetime: a #GDateTime.
- * @day: (out): the return location for the day of the month, or %NULL.
- * @month: (out): the return location for the monty of the year, or %NULL.
- * @year: (out): the return location for the gregorian year, or %NULL.
- *
- * Retrieves the Gregorian day, month, and year of a given #GDateTime.
- *
- * Since: 2.26
- */
-void
-g_date_time_get_dmy (const GDateTime *datetime,
- gint *day,
- gint *month,
- gint *year)
-{
- gint a, b, c, d, e, m;
+ g_assert (leap == GREGORIAN_LEAP(the_year));
- a = datetime->julian + 32044;
- b = ((4 * a) + 3) / 146097;
- c = a - ((b * 146097) / 4);
- d = ((4 * c) + 3) / 1461;
- e = c - (1461 * d) / 4;
- m = (5 * e + 2) / 153;
+ the_month = (remaining_days + 50) >> 5;
+ preceding = (days_in_year[0][the_month - 1] + (the_month > 2 && leap));
+ if (preceding > remaining_days)
+ {
+ /* estimate is too large */
+ the_month -= 1;
+ preceding -= leap ? days_in_months[1][the_month]
+ : days_in_months[0][the_month];
+ }
- if (day != NULL)
- *day = e - (((153 * m) + 2) / 5) + 1;
+ remaining_days -= preceding;
+ g_assert(0 <= remaining_days);
- if (month != NULL)
- *month = m + 3 - (12 * (m / 10));
+ the_day = remaining_days + 1;
- if (year != NULL)
- *year = (b * 100) + d - 4800 + (m / 10);
+end:
+ if (year)
+ *year = the_year;
+ if (month)
+ *month = the_month;
+ if (day)
+ *day = the_day;
}
/**
- * g_date_time_get_hour:
- * @datetime: a #GDateTime
+ * g_date_time_get_year:
+ * @datetime: A #GDateTime
*
- * Retrieves the hour of the day represented by @datetime
+ * Retrieves the year represented by @datetime in the Gregorian calendar.
*
- * Return value: the hour of the day
+ * Returns: the year represented by @datetime
*
* Since: 2.26
*/
gint
-g_date_time_get_hour (const GDateTime *datetime)
+g_date_time_get_year (GDateTime *datetime)
{
+ gint year;
+
g_return_val_if_fail (datetime != NULL, 0);
- return (datetime->usec / USEC_PER_HOUR);
+ g_date_time_get_ymd (datetime, &year, NULL, NULL);
+
+ return year;
}
/**
- * g_date_time_get_julian:
+ * g_date_time_get_month:
* @datetime: a #GDateTime
- * @period: (out): a location for the Julian period
- * @julian: (out): a location for the day in the Julian period
- * @hour: (out): a location for the hour of the day
- * @minute: (out): a location for the minute of the hour
- * @second: (out): a location for hte second of the minute
*
- * Retrieves the Julian period, day, hour, minute, and second which @datetime
- * represents in the Julian calendar.
+ * Retrieves the month of the year represented by @datetime in the Gregorian
+ * calendar.
+ *
+ * Returns: the month represented by @datetime
*
* Since: 2.26
*/
-void
-g_date_time_get_julian (const GDateTime *datetime,
- gint *period,
- gint *julian,
- gint *hour,
- gint *minute,
- gint *second)
+gint
+g_date_time_get_month (GDateTime *datetime)
{
- g_return_if_fail (datetime != NULL);
-
- if (period)
- *period = datetime->period;
-
- if (julian)
- *julian = datetime->julian;
+ gint month;
- if (hour)
- *hour = (datetime->usec / USEC_PER_HOUR);
+ g_return_val_if_fail (datetime != NULL, 0);
- if (minute)
- *minute = (datetime->usec % USEC_PER_HOUR) / USEC_PER_MINUTE;
+ g_date_time_get_ymd (datetime, NULL, &month, NULL);
- if (second)
- *second = (datetime->usec % USEC_PER_MINUTE) / USEC_PER_SECOND;
+ return month;
}
/**
- * g_date_time_get_microsecond:
+ * g_date_time_get_day_of_month:
* @datetime: a #GDateTime
*
- * Retrieves the microsecond of the date represented by @datetime
+ * Retrieves the day of the month represented by @datetime in the gregorian
+ * calendar.
*
- * Return value: the microsecond of the second
+ * Returns: the day of the month
*
* Since: 2.26
*/
gint
-g_date_time_get_microsecond (const GDateTime *datetime)
+g_date_time_get_day_of_month (GDateTime *datetime)
{
+ gint day_of_year,
+ i;
+ const guint16 *days;
+ guint16 last = 0;
+
g_return_val_if_fail (datetime != NULL, 0);
- return (datetime->usec % USEC_PER_SECOND);
+ days = days_in_year[GREGORIAN_LEAP (g_date_time_get_year (datetime)) ? 1 : 0];
+ g_date_time_get_week_number (datetime, NULL, NULL, &day_of_year);
+
+ for (i = 1; i <= 12; i++)
+ {
+ if (days [i] >= day_of_year)
+ return day_of_year - last;
+ last = days [i];
+ }
+
+ g_warn_if_reached ();
+ return 0;
}
+/* Week of year / day of week getters {{{1 */
/**
- * g_date_time_get_millisecond:
+ * g_date_time_get_week_numbering_year:
* @datetime: a #GDateTime
*
- * Retrieves the millisecond of the date represented by @datetime
+ * Returns the ISO 8601 week-numbering year in which the week containing
+ * @datetime falls.
*
- * Return value: the millisecond of the second
+ * This function, taken together with g_date_time_get_week_of_year() and
+ * g_date_time_get_day_of_week() can be used to determine the full ISO
+ * week date on which @datetime falls.
*
- * Since: 2.26
- */
-gint
-g_date_time_get_millisecond (const GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec % USEC_PER_SECOND) / USEC_PER_MILLISECOND;
-}
-
-/**
- * g_date_time_get_minute:
- * @datetime: a #GDateTime
+ * This is usually equal to the normal Gregorian year (as returned by
+ * g_date_time_get_year()), except as detailed below:
*
- * Retrieves the minute of the hour represented by @datetime
+ * For Thursday, the week-numbering year is always equal to the usual
+ * calendar year. For other days, the number is such that every day
+ * within a complete week (Monday to Sunday) is contained within the
+ * same week-numbering year.
*
- * Return value: the minute of the hour
+ * For Monday, Tuesday and Wednesday occurring near the end of the year,
+ * this may mean that the week-numbering year is one greater than the
+ * calendar year (so that these days have the same week-numbering year
+ * as the Thursday occurring early in the next year).
*
- * Since: 2.26
- */
-gint
-g_date_time_get_minute (const GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec % USEC_PER_HOUR) / USEC_PER_MINUTE;
-}
-
-/**
- * g_date_time_get_month:
- * @datetime: a #GDateTime
+ * For Friday, Saturaday and Sunday occurring near the start of the year,
+ * this may mean that the week-numbering year is one less than the
+ * calendar year (so that these days have the same week-numbering year
+ * as the Thursday occurring late in the previous year).
*
- * Retrieves the month of the year represented by @datetime in the Gregorian
- * calendar.
+ * An equivalent description is that the week-numbering year is equal to
+ * the calendar year containing the majority of the days in the current
+ * week (Monday to Sunday).
+ *
+ * Note that January 1 0001 in the proleptic Gregorian calendar is a
+ * Monday, so this function never returns 0.
*
- * Return value: the month represented by @datetime
+ * Returns: the ISO 8601 week-numbering year for @datetime
*
* Since: 2.26
- */
+ **/
gint
-g_date_time_get_month (const GDateTime *datetime)
+g_date_time_get_week_numbering_year (GDateTime *datetime)
{
- gint month;
+ gint year, month, day, weekday;
- g_return_val_if_fail (datetime != NULL, 0);
+ g_date_time_get_ymd (datetime, &year, &month, &day);
+ weekday = g_date_time_get_day_of_week (datetime);
- g_date_time_get_dmy (datetime, NULL, &month, NULL);
+ /* January 1, 2, 3 might be in the previous year if they occur after
+ * Thursday.
+ *
+ * Jan 1: Friday, Saturday, Sunday => day 1: weekday 5, 6, 7
+ * Jan 2: Saturday, Sunday => day 2: weekday 6, 7
+ * Jan 3: Sunday => day 3: weekday 7
+ *
+ * So we have a special case if (day - weekday) <= -4
+ */
+ if (month == 1 && (day - weekday) <= -4)
+ return year - 1;
+
+ /* December 29, 30, 31 might be in the next year if they occur before
+ * Thursday.
+ *
+ * Dec 31: Monday, Tuesday, Wednesday => day 31: weekday 1, 2, 3
+ * Dec 30: Monday, Tuesday => day 30: weekday 1, 2
+ * Dec 29: Monday => day 29: weekday 1
+ *
+ * So we have a special case if (day - weekday) >= 28
+ */
+ else if (month == 12 && (day - weekday) >= 28)
+ return year + 1;
- return month;
+ else
+ return year;
}
/**
- * g_date_time_get_second:
+ * g_date_time_get_week_of_year:
* @datetime: a #GDateTime
*
- * Retrieves the second of the minute represented by @datetime
- *
- * Return value: the second represented by @datetime
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_second (const GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec % USEC_PER_MINUTE) / USEC_PER_SECOND;
-}
-
-/**
- * g_date_time_get_utc_offset:
- * @datetime: a #GDateTime
+ * Returns the ISO 8601 week number for the week containing @datetime.
+ * The ISO 8601 week number is the same for every day of the week (from
+ * Moday through Sunday). That can produce some unusual results
+ * (described below).
*
- * Retrieves the offset from UTC that the local timezone specified by
- * @datetime represents.
+ * The first week of the year is week 1. This is the week that contains
+ * the first Thursday of the year. Equivalently, this is the first week
+ * that has more than 4 of its days falling within the calendar year.
*
- * If @datetime represents UTC time, then the offset is zero.
+ * The value 0 is never returned by this function. Days contained
+ * within a year but occurring before the first ISO 8601 week of that
+ * year are considered as being contained in the last week of the
+ * previous year. Similarly, the final days of a calendar year may be
+ * considered as being part of the first ISO 8601 week of the next year
+ * if 4 or more days of that week are contained within the new year.
*
- * Return value: the offset, expressed as a time span expressed in
- * microseconds.
+ * Returns: the ISO 8601 week number for @datetime.
*
* Since: 2.26
*/
-GTimeSpan
-g_date_time_get_utc_offset (const GDateTime *datetime)
+gint
+g_date_time_get_week_of_year (GDateTime *datetime)
{
- gint offset = 0;
+ gint weeknum;
g_return_val_if_fail (datetime != NULL, 0);
- if (datetime->tz != NULL)
- offset = datetime->tz->offset;
+ g_date_time_get_week_number (datetime, &weeknum, NULL, NULL);
- return (gint64) offset * USEC_PER_SECOND;
+ return weeknum;
}
/**
- * g_date_time_get_timezone_name:
+ * g_date_time_get_day_of_week:
* @datetime: a #GDateTime
*
- * Retrieves the Olson's database timezone name of the timezone specified
- * by @datetime.
+ * Retrieves the ISO 8601 day of the week on which @datetime falls (1 is
+ * Monday, 2 is Tuesday... 7 is Sunday).
*
- * Return value: (transfer none): the name of the timezone. The returned
- * string is owned by the #GDateTime and it should not be modified or
- * freed
+ * Returns: the day of the week
*
* Since: 2.26
*/
-G_CONST_RETURN gchar *
-g_date_time_get_timezone_name (const GDateTime *datetime)
+gint
+g_date_time_get_day_of_week (GDateTime *datetime)
{
- g_return_val_if_fail (datetime != NULL, NULL);
-
- if (datetime->tz != NULL)
- return datetime->tz->name;
+ g_return_val_if_fail (datetime != NULL, 0);
- return "UTC";
+ return (datetime->days - 1) % 7 + 1;
}
+/* Day of year getter {{{1 */
/**
- * g_date_time_get_year:
- * @datetime: A #GDateTime
+ * g_date_time_get_day_of_year:
+ * @datetime: a #GDateTime
*
- * Retrieves the year represented by @datetime in the Gregorian calendar.
+ * Retrieves the day of the year represented by @datetime in the Gregorian
+ * calendar.
*
- * Return value: the year represented by @datetime
+ * Returns: the day of the year
*
* Since: 2.26
*/
gint
-g_date_time_get_year (const GDateTime *datetime)
+g_date_time_get_day_of_year (GDateTime *datetime)
{
- gint year;
+ gint doy = 0;
g_return_val_if_fail (datetime != NULL, 0);
- g_date_time_get_dmy (datetime, NULL, NULL, &year);
-
- return year;
+ g_date_time_get_week_number (datetime, NULL, NULL, &doy);
+ return doy;
}
+/* Time component getters {{{1 */
+
/**
- * g_date_time_hash:
+ * g_date_time_get_hour:
* @datetime: a #GDateTime
*
- * Hashes @datetime into a #guint, suitable for use within #GHashTable.
+ * Retrieves the hour of the day represented by @datetime
*
- * Return value: a #guint containing the hash
+ * Returns: the hour of the day
*
* Since: 2.26
*/
-guint
-g_date_time_hash (gconstpointer datetime)
+gint
+g_date_time_get_hour (GDateTime *datetime)
{
- return (guint) (*((guint64 *) datetime));
+ g_return_val_if_fail (datetime != NULL, 0);
+
+ return (datetime->usec / USEC_PER_HOUR);
}
/**
- * g_date_time_is_leap_year:
+ * g_date_time_get_minute:
* @datetime: a #GDateTime
*
- * Determines if @datetime represents a date known to fall within
- * a leap year in the Gregorian calendar.
+ * Retrieves the minute of the hour represented by @datetime
*
- * Return value: %TRUE if @datetime is a leap year.
+ * Returns: the minute of the hour
*
* Since: 2.26
*/
-gboolean
-g_date_time_is_leap_year (const GDateTime *datetime)
+gint
+g_date_time_get_minute (GDateTime *datetime)
{
- gint year;
-
- g_return_val_if_fail (datetime != NULL, FALSE);
-
- year = g_date_time_get_year (datetime);
+ g_return_val_if_fail (datetime != NULL, 0);
- return GREGORIAN_LEAP (year);
+ return (datetime->usec % USEC_PER_HOUR) / USEC_PER_MINUTE;
}
/**
- * g_date_time_is_daylight_savings:
+ * g_date_time_get_second:
* @datetime: a #GDateTime
*
- * Determines if @datetime represents a date known to fall within daylight
- * savings time in the gregorian calendar.
+ * Retrieves the second of the minute represented by @datetime
*
- * Return value: %TRUE if @datetime falls within daylight savings time.
+ * Returns: the second represented by @datetime
*
* Since: 2.26
*/
-gboolean
-g_date_time_is_daylight_savings (const GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, FALSE);
-
- if (!datetime->tz)
- return FALSE;
-
- return datetime->tz->is_dst;
-}
-
-static inline gint
-date_to_julian (gint year,
- gint month,
- gint day)
+gint
+g_date_time_get_second (GDateTime *datetime)
{
- gint a = (14 - month) / 12;
- gint y = year + 4800 - a;
- gint m = month + (12 * a) - 3;
+ g_return_val_if_fail (datetime != NULL, 0);
- return day
- + (((153 * m) + 2) / 5)
- + (y * 365)
- + (y / 4)
- - (y / 100)
- + (y / 400)
- - 32045;
+ return (datetime->usec % USEC_PER_MINUTE) / USEC_PER_SECOND;
}
/**
- * g_date_time_new_from_date:
- * @year: the Gregorian year
- * @month: the Gregorian month
- * @day: the day in the Gregorian month
+ * g_date_time_get_microsecond:
+ * @datetime: a #GDateTime
*
- * Creates a new #GDateTime using the specified date within the Gregorian
- * calendar.
+ * Retrieves the microsecond of the date represented by @datetime
*
- * Return value: the newly created #GDateTime or %NULL if it is outside of
- * the representable range.
+ * Returns: the microsecond of the second
*
* Since: 2.26
*/
-GDateTime *
-g_date_time_new_from_date (gint year,
- gint month,
- gint day)
+gint
+g_date_time_get_microsecond (GDateTime *datetime)
{
- GDateTime *dt;
-
- g_return_val_if_fail (year > -4712 && year <= 3268, NULL);
- g_return_val_if_fail (month > 0 && month <= 12, NULL);
- g_return_val_if_fail (day > 0 && day <= 31, NULL);
-
- dt = g_date_time_new ();
- dt->julian = date_to_julian (year, month, day);
- dt->tz = g_date_time_create_time_zone (dt, NULL);
+ g_return_val_if_fail (datetime != NULL, 0);
- return dt;
+ return (datetime->usec % USEC_PER_SECOND);
}
/**
- * g_date_time_new_from_epoch:
- * @t: seconds from the Unix epoch
+ * g_date_time_get_seconds:
+ * @datetime: a #GDateTime
*
- * Creates a new #GDateTime using the time since Jan 1, 1970 specified by @t.
+ * Retrieves the number of seconds since the start of the last minute,
+ * including the fractional part.
*
- * Return value: the newly created #GDateTime
+ * Returns: the number of seconds
*
* Since: 2.26
- */
-GDateTime*
-g_date_time_new_from_epoch (gint64 t) /* IN */
+ **/
+gdouble
+g_date_time_get_seconds (GDateTime *datetime)
{
- struct tm tm;
- time_t tt;
-
- tt = (time_t) t;
- memset (&tm, 0, sizeof (tm));
-
- /* XXX: GLib should probably have a wrapper for this */
-#ifdef HAVE_LOCALTIME_R
- localtime_r (&tt, &tm);
-#else
- {
- struct tm *ptm = localtime (&timet);
-
- if (ptm == NULL)
- {
- /* Happens at least in Microsoft's C library if you pass a
- * negative time_t. Use 2000-01-01 as default date.
- */
-#ifndef G_DISABLE_CHECKS
- g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, "ptm != NULL");
-#endif
-
- tm.tm_mon = 0;
- tm.tm_mday = 1;
- tm.tm_year = 100;
- }
- else
- memcpy ((void *) &tm, (void *) ptm, sizeof (struct tm));
- }
-#endif /* HAVE_LOCALTIME_R */
+ g_return_val_if_fail (datetime != NULL, 0);
- return g_date_time_new_full (tm.tm_year + 1900,
- tm.tm_mon + 1,
- tm.tm_mday,
- tm.tm_hour,
- tm.tm_min,
- tm.tm_sec,
- NULL);
+ return (datetime->usec % USEC_PER_MINUTE) / 1000000.0;
}
+/* Exporters {{{1 */
/**
- * g_date_time_new_from_timeval:
- * @tv: #GTimeVal
+ * g_date_time_to_unix:
+ * @datetime: a #GDateTime
*
- * Creates a new #GDateTime using the date and time specified by #GTimeVal.
+ * Gives the Unix time corresponding to @datetime, rounding down to the
+ * nearest second.
*
- * Return value: the newly created #GDateTime
+ * Unix time is the number of seconds that have elapsed since 1970-01-01
+ * 00:00:00 UTC, regardless of the time zone associated with @datetime.
+ *
+ * Returns: the Unix time corresponding to @datetime
*
* Since: 2.26
- */
-GDateTime *
-g_date_time_new_from_timeval (GTimeVal *tv)
+ **/
+gint64
+g_date_time_to_unix (GDateTime *datetime)
{
- GDateTime *datetime;
-
- g_return_val_if_fail (tv != NULL, NULL);
-
- datetime = g_date_time_new_from_epoch (tv->tv_sec);
- datetime->usec += tv->tv_usec;
- datetime->tz = g_date_time_create_time_zone (datetime, NULL);
-
- return datetime;
+ return INSTANT_TO_UNIX (g_date_time_to_instant (datetime));
}
/**
- * g_date_time_new_full:
- * @year: the Gregorian year
- * @month: the Gregorian month
- * @day: the day of the Gregorian month
- * @hour: the hour of the day
- * @minute: the minute of the hour
- * @second: the second of the minute
- * @timezone: (allow-none): the Olson's database timezone name, or %NULL
- * for local (e.g. America/New_York)
+ * g_date_time_to_timeval:
+ * @datetime: a #GDateTime
+ * @tv: a #GTimeVal to modify
+ *
+ * Stores the instant in time that @datetime represents into @tv.
*
- * Creates a new #GDateTime using the date and times in the Gregorian calendar.
+ * The time contained in a #GTimeVal is always stored in the form of
+ * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the time
+ * zone associated with @datetime.
*
- * Return value: the newly created #GDateTime
+ * On systems where 'long' is 32bit (ie: all 32bit systems and all
+ * Windows systems), a #GTimeVal is incapable of storing the entire
+ * range of values that #GDateTime is capable of expressing. On those
+ * systems, this function returns %FALSE to indicate that the time is
+ * out of range.
+ *
+ * On systems where 'long' is 64bit, this function never fails.
+ *
+ * Returns: %TRUE if successful, else %FALSE
*
* Since: 2.26
- */
-GDateTime *
-g_date_time_new_full (gint year,
- gint month,
- gint day,
- gint hour,
- gint minute,
- gint second,
- const gchar *timezone)
+ **/
+gboolean
+g_date_time_to_timeval (GDateTime *datetime,
+ GTimeVal *tv)
{
- GDateTime *dt;
-
- g_return_val_if_fail (hour >= 0 && hour < 24, NULL);
- g_return_val_if_fail (minute >= 0 && minute < 60, NULL);
- g_return_val_if_fail (second >= 0 && second <= 60, NULL);
-
- if ((dt = g_date_time_new_from_date (year, month, day)) == NULL)
- return NULL;
+ tv->tv_sec = INSTANT_TO_UNIX (g_date_time_to_instant (datetime));
+ tv->tv_usec = datetime->usec % USEC_PER_SECOND;
- dt->usec = (hour * USEC_PER_HOUR)
- + (minute * USEC_PER_MINUTE)
- + (second * USEC_PER_SECOND);
-
- dt->tz = g_date_time_create_time_zone (dt, timezone);
- if (timezone != NULL && dt->tz == NULL)
- {
- /* timezone creation failed */
- g_date_time_unref (dt);
- dt = NULL;
- }
-
- return dt;
+ return TRUE;
}
+/* Timezone queries {{{1 */
/**
- * g_date_time_new_now:
+ * g_date_time_get_utc_offset:
+ * @datetime: a #GDateTime
*
- * Creates a new #GDateTime representing the current date and time.
+ * Determines the offset to UTC in effect at the time and in the time
+ * zone of @datetime.
*
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
+ * The offset is the number of microseconds that you add to UTC time to
+ * arrive at local time for the time zone (ie: negative numbers for time
+ * zones west of GMT, positive numbers for east).
+ *
+ * If @datetime represents UTC time, then the offset is always zero.
+ *
+ * Returns: the number of microseconds that should be added to UTC to
+ * get the local time
*
* Since: 2.26
- */
-GDateTime*
-g_date_time_new_now (void)
+ **/
+GTimeSpan
+g_date_time_get_utc_offset (GDateTime *datetime)
{
- GTimeVal tv;
+ gint offset;
- g_get_current_time (&tv);
+ g_return_val_if_fail (datetime != NULL, 0);
+
+ offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
- return g_date_time_new_from_timeval (&tv);
+ return (gint64) offset * USEC_PER_SECOND;
}
/**
- * g_date_time_printf:
- * @datetime: A #GDateTime
- * @format: a valid UTF-8 string, containing the format for the #GDateTime
+ * g_date_time_get_timezone_abbreviation:
+ * @datetime: a #GDateTime
*
- * Creates a newly allocated string representing the requested @format.
+ * Determines the time zone abbreviation to be used at the time and in
+ * the time zone of @datetime.
*
- * The following format specifiers are supported:
+ * For example, in Toronto this is currently "EST" during the winter
+ * months and "EDT" during the summer months when daylight savings
+ * time is in effect.
*
- * %%a The abbreviated weekday name according to the current locale.
- * %%A The full weekday name according to the current locale.
- * %%b The abbreviated month name according to the current locale.
- * %%B The full month name according to the current locale.
- * %%d The day of the month as a decimal number (range 01 to 31).
- * %%e The day of the month as a decimal number (range 1 to 31).
- * %%F Equivalent to %Y-%m-%d (the ISO 8601 date format).
- * %%h Equivalent to %b.
- * %%H The hour as a decimal number using a 24-hour clock (range 00 to 23).
- * %%I The hour as a decimal number using a 12-hour clock (range 01 to 12).
- * %%j The day of the year as a decimal number (range 001 to 366).
- * %%k The hour (24-hour clock) as a decimal number (range 0 to 23);
- * single digits are preceded by a blank.
- * %%l The hour (12-hour clock) as a decimal number (range 1 to 12);
- * single digits are preceded by a blank.
- * %%m The month as a decimal number (range 01 to 12).
- * %%M The minute as a decimal number (range 00 to 59).
- * %%N The micro-seconds as a decimal number.
- * %%p Either "AM" or "PM" according to the given time value, or the
- * corresponding strings for the current locale. Noon is treated
- * as "PM" and midnight as "AM".
- * %%P Like %%p but lowercase: "am" or "pm" or a corresponding string for
- * the current locale.
- * %%r The time in a.m. or p.m. notation.
- * %%R The time in 24-hour notation (%H:%M).
- * %%s The number of seconds since the Epoch, that is, since 1970-01-01
- * 00:00:00 UTC.
- * %%S The second as a decimal number (range 00 to 60).
- * %%t A tab character.
- * %%u The day of the week as a decimal, range 1 to 7, Monday being 1.
- * %%W The week number of the current year as a decimal number.
- * %%x The preferred date representation for the current locale without
- * the time.
- * %%X The preferred date representation for the current locale without
- * the date.
- * %%y The year as a decimal number without the century.
- * %%Y The year as a decimal number including the century.
- * %%Z Alphabetic time zone abbreviation (e.g. EDT).
- * %%% A literal %% character.
- *
- * Return value: a newly allocated string formatted to the requested format or
- * %NULL in the case that there was an error. The string should be freed
- * with g_free().
+ * Returns: (transfer none): the time zone abbreviation. The returned
+ * string is owned by the #GDateTime and it should not be
+ * modified or freed
*
* Since: 2.26
- */
-gchar *
-g_date_time_printf (const GDateTime *datetime,
- const gchar *format)
+ **/
+const gchar *
+g_date_time_get_timezone_abbreviation (GDateTime *datetime)
{
- GString *outstr;
- const gchar *tmp;
- gchar *tmp2,
- c;
- glong utf8len;
- gint i;
- gboolean in_mod;
-
g_return_val_if_fail (datetime != NULL, NULL);
- g_return_val_if_fail (format != NULL, NULL);
- g_return_val_if_fail (g_utf8_validate (format, -1, NULL), NULL);
-
- outstr = g_string_sized_new (strlen (format) * 2);
- utf8len = g_utf8_strlen (format, -1);
- in_mod = FALSE;
-
- for (i = 0; i < utf8len; i++)
- {
- tmp = g_utf8_offset_to_pointer (format, i);
- c = g_utf8_get_char (tmp);
- switch (c)
- {
- case '%':
- if (!in_mod)
- {
- in_mod = TRUE;
- break;
- }
- /* Fall through */
- default:
- if (in_mod)
- {
- switch (c)
- {
- case 'a':
- g_string_append (outstr, WEEKDAY_ABBR (datetime));
- break;
- case 'A':
- g_string_append (outstr, WEEKDAY_FULL (datetime));
- break;
- case 'b':
- g_string_append (outstr, MONTH_ABBR (datetime));
- break;
- case 'B':
- g_string_append (outstr, MONTH_FULL (datetime));
- break;
- case 'd':
- g_string_append_printf (outstr, "%02d", g_date_time_get_day_of_month (datetime));
- break;
- case 'e':
- g_string_append_printf (outstr, "%2d", g_date_time_get_day_of_month (datetime));
- break;
- case 'F':
- g_string_append_printf (outstr, "%d-%02d-%02d",
- g_date_time_get_year (datetime),
- g_date_time_get_month (datetime),
- g_date_time_get_day_of_month (datetime));
- break;
- case 'h':
- g_string_append (outstr, MONTH_ABBR (datetime));
- break;
- case 'H':
- g_string_append_printf (outstr, "%02d", g_date_time_get_hour (datetime));
- break;
- case 'I':
- if (g_date_time_get_hour (datetime) == 0)
- g_string_append (outstr, "12");
- else
- g_string_append_printf (outstr, "%02d", g_date_time_get_hour (datetime) % 12);
- break;
- case 'j':
- g_string_append_printf (outstr, "%03d", g_date_time_get_day_of_year (datetime));
- break;
- case 'k':
- g_string_append_printf (outstr, "%2d", g_date_time_get_hour (datetime));
- break;
- case 'l':
- if (g_date_time_get_hour (datetime) == 0)
- g_string_append (outstr, "12");
- else
- g_string_append_printf (outstr, "%2d", g_date_time_get_hour (datetime) % 12);
- break;
- case 'm':
- g_string_append_printf (outstr, "%02d", g_date_time_get_month (datetime));
- break;
- case 'M':
- g_string_append_printf (outstr, "%02d", g_date_time_get_minute (datetime));
- break;
- case 'N':
- g_string_append_printf (outstr, "%"G_GUINT64_FORMAT, datetime->usec % USEC_PER_SECOND);
- break;
- case 'p':
- g_string_append (outstr, GET_AMPM (datetime, FALSE));
- break;
- case 'P':
- g_string_append (outstr, GET_AMPM (datetime, TRUE));
- break;
- case 'r':
- {
- gint hour = g_date_time_get_hour (datetime) % 12;
- if (hour == 0)
- hour = 12;
- g_string_append_printf (outstr, "%02d:%02d:%02d %s",
- hour,
- g_date_time_get_minute (datetime),
- g_date_time_get_second (datetime),
- GET_AMPM (datetime, FALSE));
- }
- break;
- case 'R':
- g_string_append_printf (outstr, "%02d:%02d",
- g_date_time_get_hour (datetime),
- g_date_time_get_minute (datetime));
- break;
- case 's':
- g_string_append_printf (outstr, "%" G_GINT64_FORMAT, g_date_time_to_epoch (datetime));
- break;
- case 'S':
- g_string_append_printf (outstr, "%02d", g_date_time_get_second (datetime));
- break;
- case 't':
- g_string_append_c (outstr, '\t');
- break;
- case 'u':
- g_string_append_printf (outstr, "%d", g_date_time_get_day_of_week (datetime));
- break;
- case 'W':
- g_string_append_printf (outstr, "%d", g_date_time_get_day_of_year (datetime) / 7);
- break;
- case 'x':
- {
- tmp2 = GET_PREFERRED_DATE (datetime);
- g_string_append (outstr, tmp2);
- g_free (tmp2);
- }
- break;
- case 'X':
- {
- tmp2 = GET_PREFERRED_TIME (datetime);
- g_string_append (outstr, tmp2);
- g_free (tmp2);
- }
- break;
- case 'y':
- g_string_append_printf (outstr, "%02d", g_date_time_get_year (datetime) % 100);
- break;
- case 'Y':
- g_string_append_printf (outstr, "%d", g_date_time_get_year (datetime));
- break;
- case 'Z':
- if (datetime->tz != NULL)
- g_string_append_printf (outstr, "%s", datetime->tz->name);
- else
- g_string_append_printf (outstr, "UTC");
- break;
- case '%':
- g_string_append_c (outstr, '%');
- break;
- case 'n':
- g_string_append_c (outstr, '\n');
- break;
- default:
- goto bad_format;
- }
- in_mod = FALSE;
- }
- else
- g_string_append_unichar (outstr, c);
- }
- }
-
- return g_string_free (outstr, FALSE);
-
-bad_format:
- g_string_free (outstr, TRUE);
- return NULL;
+ return g_time_zone_get_abbreviation (datetime->tz, datetime->interval);
}
/**
- * g_date_time_ref:
+ * g_date_time_is_daylight_savings:
* @datetime: a #GDateTime
*
- * Atomically increments the reference count of @datetime by one.
+ * Determines if daylight savings time is in effect at the time and in
+ * the time zone of @datetime.
*
- * Return value: the #GDateTime with the reference count increased
+ * Returns: %TRUE if daylight savings time is in effect
*
* Since: 2.26
- */
-GDateTime *
-g_date_time_ref (GDateTime *datetime)
+ **/
+gboolean
+g_date_time_is_daylight_savings (GDateTime *datetime)
{
- g_return_val_if_fail (datetime != NULL, NULL);
- g_return_val_if_fail (datetime->ref_count > 0, NULL);
-
- g_atomic_int_inc (&datetime->ref_count);
+ g_return_val_if_fail (datetime != NULL, FALSE);
- return datetime;
+ return g_time_zone_is_dst (datetime->tz, datetime->interval);
}
+/* Timezone convert {{{1 */
/**
- * g_date_time_unref:
+ * g_date_time_to_timezone:
* @datetime: a #GDateTime
+ * @tz: the new #GTimeZone
*
- * Atomically decrements the reference count of @datetime by one.
+ * Create a new #GDateTime corresponding to the same instant in time as
+ * @datetime, but in the time zone @tz.
*
- * When the reference count reaches zero, the resources allocated by
- * @datetime are freed
+ * This call can fail in the case that the time goes out of bounds. For
+ * example, converting 0001-01-01 00:00:00 UTC to a time zone west of
+ * Greenwich will fail (due to the year 0 being out of range).
+ *
+ * You should release the return value by calling g_date_time_unref()
+ * when you are done with it.
+ *
+ * Returns: a new #GDateTime, or %NULL
*
* Since: 2.26
- */
-void
-g_date_time_unref (GDateTime *datetime)
+ **/
+GDateTime *
+g_date_time_to_timezone (GDateTime *datetime,
+ GTimeZone *tz)
{
- g_return_if_fail (datetime != NULL);
- g_return_if_fail (datetime->ref_count > 0);
-
- if (g_atomic_int_dec_and_test (&datetime->ref_count))
- g_date_time_free (datetime);
+ return g_date_time_from_instant (tz, g_date_time_to_instant (datetime));
}
/**
* g_date_time_to_local:
* @datetime: a #GDateTime
*
- * Creates a new #GDateTime with @datetime converted to local time.
+ * Creates a new #GDateTime corresponding to the same instant in time as
+ * @datetime, but in the local time zone.
+ *
+ * This call is equivalent to calling g_date_time_to_timezone() with the
+ * time zone returned by g_time_zone_new_local().
*
- * Return value: the newly created #GDateTime
+ * Returns: the newly created #GDateTime
*
* Since: 2.26
- */
+ **/
GDateTime *
-g_date_time_to_local (const GDateTime *datetime)
+g_date_time_to_local (GDateTime *datetime)
{
- GDateTime *dt;
-
- g_return_val_if_fail (datetime != NULL, NULL);
-
- dt = g_date_time_copy (datetime);
- if (dt->tz == NULL)
- {
- dt->tz = g_date_time_create_time_zone (dt, NULL);
- if (dt->tz == NULL)
- return dt;
+ GDateTime *new;
+ GTimeZone *local;
- g_date_time_add_usec (dt, dt->tz->offset * USEC_PER_SECOND);
- }
+ local = g_time_zone_new_local ();
+ new = g_date_time_to_timezone (datetime, local);
+ g_time_zone_unref (local);
- return dt;
+ return new;
}
/**
- * g_date_time_to_epoch:
+ * g_date_time_to_utc:
* @datetime: a #GDateTime
*
- * Converts @datetime into an integer representing seconds since the
- * Unix epoch
+ * Creates a new #GDateTime corresponding to the same instant in time as
+ * @datetime, but in UTC.
+ *
+ * This call is equivalent to calling g_date_time_to_timezone() with the
+ * time zone returned by g_time_zone_new_utc().
*
- * Return value: @datetime as seconds since the Unix epoch
+ * Returns: the newly created #GDateTime
*
* Since: 2.26
- */
-gint64
-g_date_time_to_epoch (const GDateTime *datetime)
+ **/
+GDateTime *
+g_date_time_to_utc (GDateTime *datetime)
{
- struct tm tm;
- gint year,
- month,
- day;
+ GDateTime *new;
+ GTimeZone *utc;
- g_return_val_if_fail (datetime != NULL, 0);
- g_return_val_if_fail (datetime->period == 0, 0);
+ utc = g_time_zone_new_utc ();
+ new = g_date_time_to_timezone (datetime, utc);
+ g_time_zone_unref (utc);
- g_date_time_get_dmy (datetime, &day, &month, &year);
+ return new;
+}
- /* FIXME we use gint64, we shold expand these limits */
- if (year < 1970)
- return 0;
- else if (year > 2037)
- return G_MAXINT64;
+/* Format {{{1 */
+
+static gboolean
+format_z (GString *outstr,
+ gint offset,
+ guint colons)
+{
+ gint hours;
+ gint minutes;
+ gint seconds;
+
+ hours = offset / 3600;
+ minutes = ABS (offset) / 60 % 60;
+ seconds = ABS (offset) % 60;
+
+ switch (colons)
+ {
+ case 0:
+ g_string_append_printf (outstr, "%+03d%02d",
+ hours,
+ minutes);
+ break;
- memset (&tm, 0, sizeof (tm));
+ case 1:
+ g_string_append_printf (outstr, "%+03d:%02d",
+ hours,
+ minutes);
+ break;
+
+ case 2:
+ g_string_append_printf (outstr, "%+03d:%02d:%02d",
+ hours,
+ minutes,
+ seconds);
+ break;
+
+ case 3:
+ g_string_append_printf (outstr, "%+03d", hours);
+
+ if (minutes != 0 || seconds != 0)
+ {
+ g_string_append_printf (outstr, ":%02d", minutes);
+
+ if (seconds != 0)
+ g_string_append_printf (outstr, ":%02d", seconds);
+ }
+ break;
- tm.tm_year = year - 1900;
- tm.tm_mon = month - 1;
- tm.tm_mday = day;
- tm.tm_hour = g_date_time_get_hour (datetime);
- tm.tm_min = g_date_time_get_minute (datetime);
- tm.tm_sec = g_date_time_get_second (datetime);
- tm.tm_isdst = -1;
+ default:
+ return FALSE;
+ }
- return (gint64) mktime (&tm);
+ return TRUE;
}
-/**
- * g_date_time_to_timeval:
- * @datetime: a #GDateTime
- * @tv: A #GTimeVal
- *
- * Converts @datetime into a #GTimeVal and stores the result into @timeval.
- *
- * Since: 2.26
- */
-void
-g_date_time_to_timeval (const GDateTime *datetime,
- GTimeVal *tv)
-{
- g_return_if_fail (datetime != NULL);
+static void
+format_number (GString *str,
+ gboolean use_alt_digits,
+ gchar *pad,
+ gint width,
+ guint32 number)
+{
+ const gchar *ascii_digits[10] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+ };
+ const gchar **digits = ascii_digits;
+ const gchar *tmp[10];
+ gint i = 0;
+
+ g_return_if_fail (width <= 10);
+
+#ifdef HAVE_LANGINFO_OUTDIGIT
+ if (use_alt_digits)
+ {
+ static const gchar *alt_digits[10];
+ static gsize initialised;
+ /* 2^32 has 10 digits */
+
+ if G_UNLIKELY (g_once_init_enter (&initialised))
+ {
+#define DO_DIGIT(n) \
+ alt_digits[n] = nl_langinfo (_NL_CTYPE_OUTDIGIT## n ##_MB)
+ DO_DIGIT(0); DO_DIGIT(1); DO_DIGIT(2); DO_DIGIT(3); DO_DIGIT(4);
+ DO_DIGIT(5); DO_DIGIT(6); DO_DIGIT(7); DO_DIGIT(8); DO_DIGIT(9);
+#undef DO_DIGIT
+ g_once_init_leave (&initialised, TRUE);
+ }
- tv->tv_sec = 0;
- tv->tv_usec = 0;
+ digits = alt_digits;
+ }
+#endif /* HAVE_LANGINFO_OUTDIGIT */
- if (G_LIKELY (datetime->period == 0))
+ do
{
- tv->tv_sec = g_date_time_to_epoch (datetime);
- tv->tv_usec = datetime->usec % USEC_PER_SECOND;
+ tmp[i++] = digits[number % 10];
+ number /= 10;
}
+ while (number);
+
+ while (pad && i < width)
+ tmp[i++] = *pad == '0' ? digits[0] : pad;
+
+ /* should really be impossible */
+ g_assert (i <= 10);
+
+ while (i)
+ g_string_append (str, tmp[--i]);
}
-/**
- * g_date_time_to_utc:
- * @datetime: a #GDateTime
- *
- * Creates a new #GDateTime that reprents @datetime in Universal coordinated
- * time.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
+static gboolean g_date_time_format_locale (GDateTime *datetime,
+ const gchar *format,
+ GString *outstr,
+ gboolean locale_is_utf8);
+
+/* g_date_time_format() subroutine that takes a locale-encoded format
+ * string and produces a locale-encoded date/time string.
*/
-GDateTime *
-g_date_time_to_utc (const GDateTime *datetime)
+static gboolean
+g_date_time_locale_format_locale (GDateTime *datetime,
+ const gchar *format,
+ GString *outstr,
+ gboolean locale_is_utf8)
{
- GDateTime *dt;
- GTimeSpan ts;
+ gchar *utf8_format;
+ gboolean success;
- g_return_val_if_fail (datetime != NULL, NULL);
+ if (locale_is_utf8)
+ return g_date_time_format_locale (datetime, format, outstr,
+ locale_is_utf8);
- ts = g_date_time_get_utc_offset (datetime) * -1;
- dt = g_date_time_add (datetime, ts);
- dt->tz = NULL;
+ utf8_format = g_locale_to_utf8 (format, -1, NULL, NULL, NULL);
+ if (!utf8_format)
+ return FALSE;
- return dt;
+ success = g_date_time_format_locale (datetime, utf8_format, outstr,
+ locale_is_utf8);
+ g_free (utf8_format);
+ return success;
}
-/**
- * g_date_time_new_today:
- *
- * Createsa new #GDateTime that represents Midnight on the current day.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
+/* g_date_time_format() subroutine that takes a UTF-8 format
+ * string and produces a locale-encoded date/time string.
*/
-GDateTime*
-g_date_time_new_today (void)
-{
- GDateTime *dt;
-
- dt = g_date_time_new_now ();
- dt->usec = 0;
+static gboolean
+g_date_time_format_locale (GDateTime *datetime,
+ const gchar *format,
+ GString *outstr,
+ gboolean locale_is_utf8)
+{
+ guint len;
+ guint colons;
+ gchar *tmp;
+ gunichar c;
+ gboolean alt_digits = FALSE;
+ gboolean pad_set = FALSE;
+ gchar *pad = "";
+ gchar *ampm;
+ const gchar *tz;
+
+ while (*format)
+ {
+ len = strcspn (format, "%");
+ if (len)
+ {
+ if (locale_is_utf8)
+ g_string_append_len (outstr, format, len);
+ else
+ {
+ tmp = g_locale_from_utf8 (format, len, NULL, NULL, NULL);
+ if (!tmp)
+ return FALSE;
+ g_string_append (outstr, tmp);
+ g_free (tmp);
+ }
+ }
+
+ format += len;
+ if (!*format)
+ break;
+
+ g_assert (*format == '%');
+ format++;
+ if (!*format)
+ break;
+
+ colons = 0;
+ alt_digits = FALSE;
+ pad_set = FALSE;
+
+ next_mod:
+ c = g_utf8_get_char (format);
+ format = g_utf8_next_char (format);
+ switch (c)
+ {
+ case 'a':
+ g_string_append (outstr, WEEKDAY_ABBR (datetime));
+ break;
+ case 'A':
+ g_string_append (outstr, WEEKDAY_FULL (datetime));
+ break;
+ case 'b':
+ g_string_append (outstr, MONTH_ABBR (datetime));
+ break;
+ case 'B':
+ g_string_append (outstr, MONTH_FULL (datetime));
+ break;
+ case 'c':
+ {
+ if (!g_date_time_locale_format_locale (datetime, PREFERRED_DATE_TIME_FMT,
+ outstr, locale_is_utf8))
+ return FALSE;
+ }
+ break;
+ case 'C':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_year (datetime) / 100);
+ break;
+ case 'd':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_day_of_month (datetime));
+ break;
+ case 'e':
+ format_number (outstr, alt_digits, pad_set ? pad : " ", 2,
+ g_date_time_get_day_of_month (datetime));
+ break;
+ case 'F':
+ g_string_append_printf (outstr, "%d-%02d-%02d",
+ g_date_time_get_year (datetime),
+ g_date_time_get_month (datetime),
+ g_date_time_get_day_of_month (datetime));
+ break;
+ case 'g':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_week_numbering_year (datetime) % 100);
+ break;
+ case 'G':
+ format_number (outstr, alt_digits, pad_set ? pad : 0, 0,
+ g_date_time_get_week_numbering_year (datetime));
+ break;
+ case 'h':
+ g_string_append (outstr, MONTH_ABBR (datetime));
+ break;
+ case 'H':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_hour (datetime));
+ break;
+ case 'I':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ (g_date_time_get_hour (datetime) + 11) % 12 + 1);
+ break;
+ case 'j':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 3,
+ g_date_time_get_day_of_year (datetime));
+ break;
+ case 'k':
+ format_number (outstr, alt_digits, pad_set ? pad : " ", 2,
+ g_date_time_get_hour (datetime));
+ break;
+ case 'l':
+ format_number (outstr, alt_digits, pad_set ? pad : " ", 2,
+ (g_date_time_get_hour (datetime) + 11) % 12 + 1);
+ break;
+ case 'n':
+ g_string_append_c (outstr, '\n');
+ break;
+ case 'm':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_month (datetime));
+ break;
+ case 'M':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_minute (datetime));
+ break;
+ case 'O':
+ alt_digits = TRUE;
+ goto next_mod;
+ case 'p':
+ ampm = (gchar *) GET_AMPM (datetime);
+ if (!locale_is_utf8)
+ {
+ ampm = tmp = g_locale_to_utf8 (ampm, -1, NULL, NULL, NULL);
+ if (!tmp)
+ return FALSE;
+ }
+ ampm = g_utf8_strup (ampm, -1);
+ if (!locale_is_utf8)
+ {
+ g_free (tmp);
+ tmp = g_locale_from_utf8 (ampm, -1, NULL, NULL, NULL);
+ g_free (ampm);
+ if (!tmp)
+ return FALSE;
+ ampm = tmp;
+ }
+ g_string_append (outstr, ampm);
+ g_free (ampm);
+ break;
+ case 'P':
+ ampm = (gchar *) GET_AMPM (datetime);
+ if (!locale_is_utf8)
+ {
+ ampm = tmp = g_locale_to_utf8 (ampm, -1, NULL, NULL, NULL);
+ if (!tmp)
+ return FALSE;
+ }
+ ampm = g_utf8_strdown (ampm, -1);
+ if (!locale_is_utf8)
+ {
+ g_free (tmp);
+ tmp = g_locale_from_utf8 (ampm, -1, NULL, NULL, NULL);
+ g_free (ampm);
+ if (!tmp)
+ return FALSE;
+ ampm = tmp;
+ }
+ g_string_append (outstr, ampm);
+ g_free (ampm);
+ break;
+ case 'r':
+ {
+ if (!g_date_time_locale_format_locale (datetime, PREFERRED_12HR_TIME_FMT,
+ outstr, locale_is_utf8))
+ return FALSE;
+ }
+ break;
+ case 'R':
+ g_string_append_printf (outstr, "%02d:%02d",
+ g_date_time_get_hour (datetime),
+ g_date_time_get_minute (datetime));
+ break;
+ case 's':
+ g_string_append_printf (outstr, "%" G_GINT64_FORMAT, g_date_time_to_unix (datetime));
+ break;
+ case 'S':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_second (datetime));
+ break;
+ case 't':
+ g_string_append_c (outstr, '\t');
+ break;
+ case 'T':
+ g_string_append_printf (outstr, "%02d:%02d:%02d",
+ g_date_time_get_hour (datetime),
+ g_date_time_get_minute (datetime),
+ g_date_time_get_second (datetime));
+ break;
+ case 'u':
+ format_number (outstr, alt_digits, 0, 0,
+ g_date_time_get_day_of_week (datetime));
+ break;
+ case 'V':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_week_of_year (datetime));
+ break;
+ case 'w':
+ format_number (outstr, alt_digits, 0, 0,
+ g_date_time_get_day_of_week (datetime) % 7);
+ break;
+ case 'x':
+ {
+ if (!g_date_time_locale_format_locale (datetime, PREFERRED_DATE_FMT,
+ outstr, locale_is_utf8))
+ return FALSE;
+ }
+ break;
+ case 'X':
+ {
+ if (!g_date_time_locale_format_locale (datetime, PREFERRED_TIME_FMT,
+ outstr, locale_is_utf8))
+ return FALSE;
+ }
+ break;
+ case 'y':
+ format_number (outstr, alt_digits, pad_set ? pad : "0", 2,
+ g_date_time_get_year (datetime) % 100);
+ break;
+ case 'Y':
+ format_number (outstr, alt_digits, 0, 0,
+ g_date_time_get_year (datetime));
+ break;
+ case 'z':
+ {
+ gint64 offset;
+ if (datetime->tz != NULL)
+ offset = g_date_time_get_utc_offset (datetime) / USEC_PER_SECOND;
+ else
+ offset = 0;
+ if (!format_z (outstr, (int) offset, colons))
+ return FALSE;
+ }
+ break;
+ case 'Z':
+ tz = g_date_time_get_timezone_abbreviation (datetime);
+ if (!locale_is_utf8)
+ {
+ tz = tmp = g_locale_from_utf8 (tz, -1, NULL, NULL, NULL);
+ if (!tmp)
+ return FALSE;
+ }
+ g_string_append (outstr, tz);
+ if (!locale_is_utf8)
+ g_free (tmp);
+ break;
+ case '%':
+ g_string_append_c (outstr, '%');
+ break;
+ case '-':
+ pad_set = TRUE;
+ pad = "";
+ goto next_mod;
+ case '_':
+ pad_set = TRUE;
+ pad = " ";
+ goto next_mod;
+ case '0':
+ pad_set = TRUE;
+ pad = "0";
+ goto next_mod;
+ case ':':
+ /* Colons are only allowed before 'z' */
+ if (*format && *format != 'z' && *format != ':')
+ return FALSE;
+ colons++;
+ goto next_mod;
+ default:
+ return FALSE;
+ }
+ }
- return dt;
+ return TRUE;
}
/**
- * g_date_time_new_utc_now:
+ * g_date_time_format:
+ * @datetime: A #GDateTime
+ * @format: a valid UTF-8 string, containing the format for the
+ * #GDateTime
*
- * Creates a new #GDateTime that represents the current instant in Universal
- * Coordinated Time (UTC).
+ * Creates a newly allocated string representing the requested @format.
*
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
+ * The format strings understood by this function are a subset of the
+ * strftime() format language as specified by C99. The \%D, \%U and \%W
+ * conversions are not supported, nor is the 'E' modifier. The GNU
+ * extensions \%k, \%l, \%s and \%P are supported, however, as are the
+ * '0', '_' and '-' modifiers.
+ *
+ * In contrast to strftime(), this function always produces a UTF-8
+ * string, regardless of the current locale. Note that the rendering of
+ * many formats is locale-dependent and may not match the strftime()
+ * output exactly.
+ *
+ * The following format specifiers are supported:
+ *
+ * - \%a: the abbreviated weekday name according to the current locale
+ * - \%A: the full weekday name according to the current locale
+ * - \%b: the abbreviated month name according to the current locale
+ * - \%B: the full month name according to the current locale
+ * - \%c: the preferred date and time rpresentation for the current locale
+ * - \%C: the century number (year/100) as a 2-digit integer (00-99)
+ * - \%d: the day of the month as a decimal number (range 01 to 31)
+ * - \%e: the day of the month as a decimal number (range 1 to 31)
+ * - \%F: equivalent to `%Y-%m-%d` (the ISO 8601 date format)
+ * - \%g: the last two digits of the ISO 8601 week-based year as a
+ * decimal number (00-99). This works well with \%V and \%u.
+ * - \%G: the ISO 8601 week-based year as a decimal number. This works
+ * well with \%V and \%u.
+ * - \%h: equivalent to \%b
+ * - \%H: the hour as a decimal number using a 24-hour clock (range 00 to 23)
+ * - \%I: the hour as a decimal number using a 12-hour clock (range 01 to 12)
+ * - \%j: the day of the year as a decimal number (range 001 to 366)
+ * - \%k: the hour (24-hour clock) as a decimal number (range 0 to 23);
+ * single digits are preceded by a blank
+ * - \%l: the hour (12-hour clock) as a decimal number (range 1 to 12);
+ * single digits are preceded by a blank
+ * - \%m: the month as a decimal number (range 01 to 12)
+ * - \%M: the minute as a decimal number (range 00 to 59)
+ * - \%p: either "AM" or "PM" according to the given time value, or the
+ * corresponding strings for the current locale. Noon is treated as
+ * "PM" and midnight as "AM".
+ * - \%P: like \%p but lowercase: "am" or "pm" or a corresponding string for
+ * the current locale
+ * - \%r: the time in a.m. or p.m. notation
+ * - \%R: the time in 24-hour notation (\%H:\%M)
+ * - \%s: the number of seconds since the Epoch, that is, since 1970-01-01
+ * 00:00:00 UTC
+ * - \%S: the second as a decimal number (range 00 to 60)
+ * - \%t: a tab character
+ * - \%T: the time in 24-hour notation with seconds (\%H:\%M:\%S)
+ * - \%u: the ISO 8601 standard day of the week as a decimal, range 1 to 7,
+ * Monday being 1. This works well with \%G and \%V.
+ * - \%V: the ISO 8601 standard week number of the current year as a decimal
+ * number, range 01 to 53, where week 1 is the first week that has at
+ * least 4 days in the new year. See g_date_time_get_week_of_year().
+ * This works well with \%G and \%u.
+ * - \%w: the day of the week as a decimal, range 0 to 6, Sunday being 0.
+ * This is not the ISO 8601 standard format -- use \%u instead.
+ * - \%x: the preferred date representation for the current locale without
+ * the time
+ * - \%X: the preferred time representation for the current locale without
+ * the date
+ * - \%y: the year as a decimal number without the century
+ * - \%Y: the year as a decimal number including the century
+ * - \%z: the time zone as an offset from UTC (+hhmm)
+ * - \%:z: the time zone as an offset from UTC (+hh:mm).
+ * This is a gnulib strftime() extension. Since: 2.38
+ * - \%::z: the time zone as an offset from UTC (+hh:mm:ss). This is a
+ * gnulib strftime() extension. Since: 2.38
+ * - \%:::z: the time zone as an offset from UTC, with : to necessary
+ * precision (e.g., -04, +05:30). This is a gnulib strftime() extension. Since: 2.38
+ * - \%Z: the time zone or name or abbreviation
+ * - \%\%: a literal \% character
+ *
+ * Some conversion specifications can be modified by preceding the
+ * conversion specifier by one or more modifier characters. The
+ * following modifiers are supported for many of the numeric
+ * conversions:
+ *
+ * - O: Use alternative numeric symbols, if the current locale supports those.
+ * - _: Pad a numeric result with spaces. This overrides the default padding
+ * for the specifier.
+ * - -: Do not pad a numeric result. This overrides the default padding
+ * for the specifier.
+ * - 0: Pad a numeric result with zeros. This overrides the default padding
+ * for the specifier.
+ *
+ * Returns: a newly allocated string formatted to the requested format
+ * or %NULL in the case that there was an error. The string
+ * should be freed with g_free().
*
* Since: 2.26
*/
-GDateTime *
-g_date_time_new_utc_now (void)
+gchar *
+g_date_time_format (GDateTime *datetime,
+ const gchar *format)
{
- GDateTime *utc, *now;
+ GString *outstr;
+ gchar *utf8;
+ gboolean locale_is_utf8 = g_get_charset (NULL);
+
+ g_return_val_if_fail (datetime != NULL, NULL);
+ g_return_val_if_fail (format != NULL, NULL);
+ g_return_val_if_fail (g_utf8_validate (format, -1, NULL), NULL);
- now = g_date_time_new_now ();
- utc = g_date_time_to_utc (now);
- g_date_time_unref (now);
+ outstr = g_string_sized_new (strlen (format) * 2);
+
+ if (!g_date_time_format_locale (datetime, format, outstr, locale_is_utf8))
+ {
+ g_string_free (outstr, TRUE);
+ return NULL;
+ }
- return utc;
+ if (locale_is_utf8)
+ return g_string_free (outstr, FALSE);
+
+ utf8 = g_locale_to_utf8 (outstr->str, outstr->len, NULL, NULL, NULL);
+ g_string_free (outstr, TRUE);
+ return utf8;
}
+
+
+/* Epilogue {{{1 */
+/* vim:set foldmethod=marker: */