#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
#ifdef HAVE_LANGINFO_TIME
#include <langinfo.h>
#endif
#include "gslice.h"
#include "gatomic.h"
+#include "gcharset.h"
+#include "gconvert.h"
#include "gfileutils.h"
#include "ghash.h"
#include "gmain.h"
struct _GDateTime
{
- /* 1 is 0001-01-01 in Proleptic Gregorian */
- gint32 days;
-
/* Microsecond timekeeping within Day */
guint64 usec;
GTimeZone *tz;
gint interval;
+ /* 1 is 0001-01-01 in Proleptic Gregorian */
+ gint32 days;
+
volatile gint ref_count;
};
*
* Atomically increments the reference count of @datetime by one.
*
- * Return value: the #GDateTime with the reference count increased
+ * Returns: the #GDateTime with the reference count increased
*
* Since: 2.26
*/
GDateTime *datetime;
gint64 full_time;
+ g_return_val_if_fail (tz != NULL, NULL);
+
+ 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)
*
* Returns: a #GDateTime, or %NULL
*
- * Since: 2.26.
+ * Since: 2.26
**/
GDateTime *
g_date_time_new_local (gint year,
*
* Returns: a #GDateTime, or %NULL
*
- * Since: 2.26.
+ * Since: 2.26
**/
GDateTime *
g_date_time_new_utc (gint year,
*
* 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
* @years: the number of years
*
* Creates a copy of @datetime and adds the specified number of years to the
- * copy.
+ * 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
* @months: the number of months
*
* Creates a copy of @datetime and adds the specified number of months to the
- * copy.
+ * 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
* @weeks: the number of weeks
*
* Creates a copy of @datetime and adds the specified number of weeks to the
- * copy.
+ * 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
* @days: the number of days
*
* Creates a copy of @datetime and adds the specified number of days to the
- * copy.
+ * 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
* @datetime: a #GDateTime
* @hours: the number of hours to add
*
- * Creates a copy of @datetime and adds the specified number of hours
+ * Creates a copy of @datetime and adds the specified number of hours.
+ * Add negative values to subtract hours.
*
- * 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
* @minutes: the number of minutes to add
*
* Creates a copy of @datetime adding the specified number of minutes.
+ * Add negative values to subtract minutes.
*
- * 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
* @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
* @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
* A comparison function for #GDateTimes that is suitable
* as a #GCompareFunc. Both #GDateTimes must be non-%NULL.
*
- * Return value: -1, 0 or 1 if @dt1 is less than, equal to or greater
+ * Returns: -1, 0 or 1 if @dt1 is less than, equal to or greater
* than @dt2.
*
* Since: 2.26
*
* Calculates the difference in time between @end and @begin. The
* #GTimeSpan that is returned is effectively @end - @begin (ie:
- * positive if the first simparameter is larger).
+ * positive if the first parameter is larger).
*
- * Return value: the difference between the two #GDateTime, as a time
+ * Returns: the difference between the two #GDateTime, as a time
* span expressed in microseconds.
*
* Since: 2.26
*
* Hashes @datetime into a #guint, suitable for use within #GHashTable.
*
- * Return value: a #guint containing the hash
+ * Returns: a #guint containing the hash
*
* Since: 2.26
*/
* Equal here means that they represent the same moment after converting
* 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_get_ymd:
* @datetime: a #GDateTime.
- * @year: (out): the return location for the gregorian year, or %NULL.
- * @month: (out): the return location for the month of the year, or %NULL.
- * @day: (out): the return location for the day of the month, or %NULL.
+ * @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.
*
* Retrieves the Gregorian day, month, and year of a given #GDateTime.
*
*
* Retrieves the year represented by @datetime in the Gregorian calendar.
*
- * Return value: the year represented by @datetime
+ * Returns: the year represented by @datetime
*
* Since: 2.26
*/
* Retrieves the month of the year represented by @datetime in the Gregorian
* calendar.
*
- * Return value: the month represented by @datetime
+ * Returns: the month represented by @datetime
*
* Since: 2.26
*/
* Retrieves the day of the month represented by @datetime in the gregorian
* calendar.
*
- * Return value: the day of the month
+ * Returns: the day of the month
*
* Since: 2.26
*/
* Retrieves the ISO 8601 day of the week on which @datetime falls (1 is
* Monday, 2 is Tuesday... 7 is Sunday).
*
- * Return value: the day of the week
+ * Returns: the day of the week
*
* Since: 2.26
*/
* Retrieves the day of the year represented by @datetime in the Gregorian
* calendar.
*
- * Return value: the day of the year
+ * Returns: the day of the year
*
* Since: 2.26
*/
*
* Retrieves the hour of the day represented by @datetime
*
- * Return value: the hour of the day
+ * Returns: the hour of the day
*
* Since: 2.26
*/
*
* Retrieves the minute of the hour represented by @datetime
*
- * Return value: the minute of the hour
+ * Returns: the minute of the hour
*
* Since: 2.26
*/
*
* Retrieves the second of the minute represented by @datetime
*
- * Return value: the second represented by @datetime
+ * Returns: the second represented by @datetime
*
* Since: 2.26
*/
*
* Retrieves the microsecond of the date represented by @datetime
*
- * Return value: the microsecond of the second
+ * Returns: the microsecond of the second
*
* Since: 2.26
*/
/* 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;
+
+ 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;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
format_number (GString *str,
gboolean use_alt_digits,
- gchar pad,
+ gchar *pad,
gint width,
guint32 number)
{
- const gunichar ascii_digits[10] = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
+ const gchar *ascii_digits[10] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
};
- const gunichar *digits = ascii_digits;
- gunichar tmp[10];
+ 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 gunichar alt_digits[10];
+ 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) \
- { \
- union { guint integer; char *pointer; } val; \
- val.pointer = nl_langinfo (_NL_CTYPE_OUTDIGIT## n ##_WC); \
- alt_digits[n] = val.integer; \
- }
+ 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
while (number);
while (pad && i < width)
- tmp[i++] = pad == '0' ? digits[0] : pad;
+ tmp[i++] = *pad == '0' ? digits[0] : pad;
/* should really be impossible */
g_assert (i <= 10);
while (i)
- g_string_append_unichar (str, tmp[--i]);
+ g_string_append (str, tmp[--i]);
+}
+
+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.
+ */
+static gboolean
+g_date_time_locale_format_locale (GDateTime *datetime,
+ const gchar *format,
+ GString *outstr,
+ gboolean locale_is_utf8)
+{
+ gchar *utf8_format;
+ gboolean success;
+
+ if (locale_is_utf8)
+ return g_date_time_format_locale (datetime, format, outstr,
+ locale_is_utf8);
+
+ utf8_format = g_locale_to_utf8 (format, -1, NULL, NULL, NULL);
+ if (!utf8_format)
+ return FALSE;
+
+ success = g_date_time_format_locale (datetime, utf8_format, outstr,
+ locale_is_utf8);
+ g_free (utf8_format);
+ return success;
+}
+
+/* g_date_time_format() subroutine that takes a UTF-8 format
+ * string and produces a locale-encoded date/time string.
+ */
+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 TRUE;
}
/**
*
* The following format specifiers are supported:
*
- * <variablelist>
- * <varlistentry><term>
- * <literal>\%a</literal>:
- * </term><listitem><simpara>
- * the abbreviated weekday name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%A</literal>:
- * </term><listitem><simpara>
- * the full weekday name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%b</literal>:
- * </term><listitem><simpara>
- * the abbreviated month name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%B</literal>:
- * </term><listitem><simpara>
- * the full month name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%c</literal>:
- * </term><listitem><simpara>
- * the preferred date and time representation for the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%C</literal>:
- * </term><listitem><simpara>
- * The century number (year/100) as a 2-digit integer (00-99)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%d</literal>:
- * </term><listitem><simpara>
- * the day of the month as a decimal number (range 01 to 31)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%e</literal>:
- * </term><listitem><simpara>
- * the day of the month as a decimal number (range 1 to 31)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%F</literal>:
- * </term><listitem><simpara>
- * equivalent to <literal>\%Y-\%m-\%d</literal> (the ISO 8601 date
- * format)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%g</literal>:
- * </term><listitem><simpara>
- * the last two digits of the ISO 8601 week-based year as a decimal
- * number (00-99). This works well with \%V and \%u.
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%G</literal>:
- * </term><listitem><simpara>
- * the ISO 8601 week-based year as a decimal number. This works well
- * with \%V and \%u.
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%h</literal>:
- * </term><listitem><simpara>
- * equivalent to <literal>\%b</literal>
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%H</literal>:
- * </term><listitem><simpara>
- * the hour as a decimal number using a 24-hour clock (range 00 to
- * 23)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%I</literal>:
- * </term><listitem><simpara>
- * the hour as a decimal number using a 12-hour clock (range 01 to
- * 12)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%j</literal>:
- * </term><listitem><simpara>
- * the day of the year as a decimal number (range 001 to 366)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%k</literal>:
- * </term><listitem><simpara>
- * the hour (24-hour clock) as a decimal number (range 0 to 23);
- * single digits are preceded by a blank
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%l</literal>:
- * </term><listitem><simpara>
- * the hour (12-hour clock) as a decimal number (range 1 to 12);
- * single digits are preceded by a blank
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%m</literal>:
- * </term><listitem><simpara>
- * the month as a decimal number (range 01 to 12)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%M</literal>:
- * </term><listitem><simpara>
- * the minute as a decimal number (range 00 to 59)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%p</literal>:
- * </term><listitem><simpara>
- * 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".
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%P</literal>:
- * </term><listitem><simpara>
- * like \%p but lowercase: "am" or "pm" or a corresponding string for
- * the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%r</literal>:
- * </term><listitem><simpara>
- * the time in a.m. or p.m. notation
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%R</literal>:
- * </term><listitem><simpara>
- * the time in 24-hour notation (<literal>\%H:\%M</literal>)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%s</literal>:
- * </term><listitem><simpara>
- * the number of seconds since the Epoch, that is, since 1970-01-01
- * 00:00:00 UTC
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%S</literal>:
- * </term><listitem><simpara>
- * the second as a decimal number (range 00 to 60)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%t</literal>:
- * </term><listitem><simpara>
- * a tab character
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%T</literal>:
- * </term><listitem><simpara>
- * the time in 24-hour notation with seconds (<literal>\%H:\%M:\%S</literal>)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%u</literal>:
- * </term><listitem><simpara>
- * 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.
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%V</literal>:
- * </term><listitem><simpara>
- * 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.
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%w</literal>:
- * </term><listitem><simpara>
- * 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.
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%x</literal>:
- * </term><listitem><simpara>
- * the preferred date representation for the current locale without
- * the time
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%X</literal>:
- * </term><listitem><simpara>
- * the preferred time representation for the current locale without
- * the date
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%y</literal>:
- * </term><listitem><simpara>
- * the year as a decimal number without the century
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%Y</literal>:
- * </term><listitem><simpara>
- * the year as a decimal number including the century
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%z</literal>:
- * </term><listitem><simpara>
- * the time-zone as hour offset from UTC
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%Z</literal>:
- * </term><listitem><simpara>
- * the time zone or name or abbreviation
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>\%\%</literal>:
- * </term><listitem><simpara>
- * a literal <literal>\%</literal> character
- * </simpara></listitem></varlistentry>
- * </variablelist>
+ * - \%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:
- * <variablelist>
- * <varlistentry>
- * <term>O</term>
- * <listitem>
- * Use alternative numeric symbols, if the current locale
- * supports those.
- * </listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>_</term>
- * <listitem>
- * Pad a numeric result with spaces.
- * This overrides the default padding for the specifier.
- * </listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>-</term>
- * <listitem>
- * Do not pad a numeric result.
- * This overrides the default padding for the specifier.
- * </listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>0</term>
- * <listitem>
- * Pad a numeric result with zeros.
- * This overrides the default padding for the specifier.
- * </listitem>
- * </varlistentry>
- * </variablelist>
+ *
+ * - 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().
+ * or %NULL in the case that there was an error. The string
+ * should be freed with g_free().
*
* Since: 2.26
*/
const gchar *format)
{
GString *outstr;
- gchar *tmp;
- gunichar c;
- gboolean in_mod = FALSE;
- gboolean alt_digits = FALSE;
- gboolean pad_set = FALSE;
- gchar pad = '\0';
- gchar *ampm;
+ 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);
outstr = g_string_sized_new (strlen (format) * 2);
- in_mod = FALSE;
- for (; *format; format = g_utf8_next_char (format))
+ if (!g_date_time_format_locale (datetime, format, outstr, locale_is_utf8))
{
- c = g_utf8_get_char (format);
-
- switch (c)
- {
- case '%':
- if (!in_mod)
- {
- in_mod = TRUE;
- alt_digits = FALSE;
- pad_set = FALSE;
- 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 'c':
- {
- tmp = g_date_time_format (datetime, PREFERRED_DATE_TIME_FMT);
- g_string_append (outstr, tmp);
- g_free (tmp);
- }
- 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 = g_utf8_strup (GET_AMPM (datetime), -1);
- g_string_append (outstr, ampm);
- g_free (ampm);
- break;
- case 'P':
- ampm = g_utf8_strdown (GET_AMPM (datetime), -1);
- g_string_append (outstr, ampm);
- g_free (ampm);
- break;
- case 'r':
- {
- tmp = g_date_time_format (datetime, PREFERRED_12HR_TIME_FMT);
- g_string_append (outstr, tmp);
- g_free (tmp);
- }
- 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':
- {
- tmp = g_date_time_format (datetime, PREFERRED_DATE_FMT);
- g_string_append (outstr, tmp);
- g_free (tmp);
- }
- break;
- case 'X':
- {
- tmp = g_date_time_format (datetime, PREFERRED_TIME_FMT);
- g_string_append (outstr, tmp);
- g_free (tmp);
- }
- 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':
- if (datetime->tz != NULL)
- {
- gint64 offset = g_date_time_get_utc_offset (datetime)
- / USEC_PER_SECOND;
-
- g_string_append_printf (outstr, "%+03d%02d",
- (int) offset / 3600,
- (int) abs(offset) / 60 % 60);
- }
- else
- g_string_append (outstr, "+0000");
- break;
- case 'Z':
- g_string_append (outstr, g_date_time_get_timezone_abbreviation (datetime));
- break;
- case '%':
- g_string_append_c (outstr, '%');
- break;
- case '-':
- pad_set = TRUE;
- pad = 0;
- goto next_mod;
- case '_':
- pad_set = TRUE;
- pad = ' ';
- goto next_mod;
- case '0':
- pad_set = TRUE;
- pad = '0';
- goto next_mod;
- default:
- goto bad_format;
- }
- in_mod = FALSE;
- }
- else
- g_string_append_unichar (outstr, c);
- }
-next_mod: ;
+ g_string_free (outstr, TRUE);
+ return NULL;
}
- return g_string_free (outstr, FALSE);
+ if (locale_is_utf8)
+ return g_string_free (outstr, FALSE);
-bad_format:
+ utf8 = g_locale_to_utf8 (outstr->str, outstr->len, NULL, NULL, NULL);
g_string_free (outstr, TRUE);
- return NULL;
+ return utf8;
}