X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgdatetime.c;h=cbbf620088f9a70a1696a057e1496d43e33f75f0;hb=49a5d0f6f2aed99cd78f25655f137f4448e47d92;hp=76bbd621b8c96f13ba1b0e98d9c3266972ce9935;hpb=b8c2fd7e05e18cb45eb83c751df394e0e3174084;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gdatetime.c b/glib/gdatetime.c index 76bbd62..cbbf620 100644 --- a/glib/gdatetime.c +++ b/glib/gdatetime.c @@ -50,17 +50,16 @@ #include #include -#ifdef HAVE_UNISTD_H -#include -#endif - #ifdef HAVE_LANGINFO_TIME #include #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" @@ -80,7 +79,7 @@ /** * SECTION:date-time * @title: GDateTime - * @short_description: A structure representing Date and Time + * @short_description: a structure representing Date and Time * @see_also: #GTimeZone * * #GDateTime is a structure that combines a Gregorian date and time @@ -112,9 +111,6 @@ struct _GDateTime { - /* 1 is 0001-01-01 in Proleptic Gregorian */ - gint32 days; - /* Microsecond timekeeping within Day */ guint64 usec; @@ -122,6 +118,9 @@ struct _GDateTime GTimeZone *tz; gint interval; + /* 1 is 0001-01-01 in Proleptic Gregorian */ + gint32 days; + volatile gint ref_count; }; @@ -175,6 +174,8 @@ static const guint16 days_in_year[2][13] = #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) static const gint weekday_item[2][7] = { @@ -197,9 +198,9 @@ static const gint month_item[2][12] = #define GET_AMPM(d) ((g_date_time_get_hour (d) < 12) \ /* Translators: 'before midday' indicator */ \ - ? C_("GDateTime", "am") \ + ? C_("GDateTime", "AM") \ /* Translators: 'after midday' indicator */ \ - : C_("GDateTime", "pm")) + : C_("GDateTime", "PM")) /* 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") @@ -210,6 +211,8 @@ static const gint month_item[2][12] = /* Translators: this is the preferred format for expressing the time */ #define PREFERRED_TIME_FMT C_("GDateTime", "%H:%M:%S") +/* Translators: this is the preferred format for expressing 12 hour time */ +#define PREFERRED_12HR_TIME_FMT C_("GDateTime", "%I:%M:%S %p") #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))) @@ -435,7 +438,7 @@ g_date_time_alloc (GTimeZone *tz) * * 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 */ @@ -941,6 +944,16 @@ g_date_time_new (GTimeZone *tz, 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) @@ -982,7 +995,7 @@ g_date_time_new (GTimeZone *tz, * * Returns: a #GDateTime, or %NULL * - * Since: 2.26. + * Since: 2.26 **/ GDateTime * g_date_time_new_local (gint year, @@ -1019,7 +1032,7 @@ 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, @@ -1048,7 +1061,7 @@ 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 @@ -1067,9 +1080,9 @@ g_date_time_add (GDateTime *datetime, * @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 @@ -1102,9 +1115,9 @@ g_date_time_add_years (GDateTime *datetime, * @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 @@ -1145,9 +1158,9 @@ g_date_time_add_months (GDateTime *datetime, * @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 @@ -1167,9 +1180,9 @@ g_date_time_add_weeks (GDateTime *datetime, * @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 @@ -1191,9 +1204,10 @@ g_date_time_add_days (GDateTime *datetime, * @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 @@ -1211,8 +1225,9 @@ g_date_time_add_hours (GDateTime *datetime, * @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 @@ -1231,8 +1246,9 @@ g_date_time_add_minutes (GDateTime *datetime, * @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 @@ -1255,9 +1271,9 @@ g_date_time_add_seconds (GDateTime *datetime, * @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 @@ -1353,7 +1369,7 @@ g_date_time_add_full (GDateTime *datetime, * 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 @@ -1383,9 +1399,9 @@ g_date_time_compare (gconstpointer dt1, * * 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 @@ -1407,7 +1423,7 @@ g_date_time_difference (GDateTime *end, * * 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 */ @@ -1427,7 +1443,7 @@ g_date_time_hash (gconstpointer datetime) * 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 */ @@ -1442,9 +1458,9 @@ g_date_time_equal (gconstpointer dt1, /** * 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. * @@ -1541,7 +1557,7 @@ end: * * 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 */ @@ -1564,7 +1580,7 @@ g_date_time_get_year (GDateTime *datetime) * 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 */ @@ -1587,7 +1603,7 @@ g_date_time_get_month (GDateTime *datetime) * 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 */ @@ -1735,7 +1751,7 @@ g_date_time_get_week_of_year (GDateTime *datetime) * 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 */ @@ -1755,7 +1771,7 @@ g_date_time_get_day_of_week (GDateTime *datetime) * 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 */ @@ -1778,7 +1794,7 @@ g_date_time_get_day_of_year (GDateTime *datetime) * * 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 */ @@ -1796,7 +1812,7 @@ g_date_time_get_hour (GDateTime *datetime) * * 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 */ @@ -1814,7 +1830,7 @@ g_date_time_get_minute (GDateTime *datetime) * * 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 */ @@ -1832,7 +1848,7 @@ g_date_time_get_second (GDateTime *datetime) * * 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 */ @@ -2075,43 +2091,434 @@ g_date_time_to_utc (GDateTime *datetime) /* Format {{{1 */ -static void -get_numeric_format (gchar *fmt, - gsize len, - gboolean alt_digits, - gchar pad, - gint width) +static gboolean +format_z (GString *outstr, + gint offset, + guint colons) { - const gchar *width_str; + gint hours; + gint minutes; + gint seconds; - if (pad == 0) - width_str = ""; - else + hours = offset / 3600; + minutes = ABS (offset) / 60 % 60; + seconds = ABS (offset) % 60; + + switch (colons) { - switch (width) + 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) { - case 0: - width_str = ""; - break; - default: - g_warning ("get_numeric_format: width %d not handled", width); - /* fall thru */ - case 2: - if (pad == '0') - width_str = "02"; - else - width_str = "2"; - break; - case 3: - if (pad == '0') - width_str = "03"; - else - width_str = "3"; - break; + g_string_append_printf (outstr, ":%02d", minutes); + + if (seconds != 0) + g_string_append_printf (outstr, ":%02d", seconds); } + break; + + default: + return FALSE; } - g_snprintf (fmt, len, "%%%s%sd", alt_digits ? "I": "", width_str); + return TRUE; +} + +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); + } + + digits = alt_digits; + } +#endif /* HAVE_LANGINFO_OUTDIGIT */ + + do + { + 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]); +} + +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; } /** @@ -2123,266 +2530,93 @@ get_numeric_format (gchar *fmt, * Creates a newly allocated string representing the requested @format. * * The format strings understood by this function are a subset of the - * strftime() format language. 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. + * 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 representation 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) - * - * %%D: - * - * equivalent to %%m/%%d/%%y - * - * - * %%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. - * - * - * %%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 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 hour offset from UTC - * - * - * %%Z: - * - * the time zone or name or abbreviation - * - * - * %%%: - * - * a literal %% character - * - * + * - \%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. - * - * - * + * + * - 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 */ @@ -2391,273 +2625,27 @@ g_date_time_format (GDateTime *datetime, 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 fmt[20]; - 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': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_year (datetime) / 100); - break; - case 'd': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_day_of_month (datetime)); - break; - case 'D': - g_string_append_printf (outstr, "%02d/%02d/%02d", - g_date_time_get_month (datetime), - g_date_time_get_day_of_month (datetime), - g_date_time_get_year (datetime)); - break; - case 'e': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : ' ', 2); - g_string_append_printf (outstr, fmt, 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': - g_string_append_printf (outstr, "%02d", g_date_time_get_week_numbering_year (datetime) % 100); - break; - case 'G': - g_string_append_printf (outstr, "%d", g_date_time_get_week_numbering_year (datetime)); - break; - case 'h': - g_string_append (outstr, MONTH_ABBR (datetime)); - break; - case 'H': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_hour (datetime)); - break; - case 'I': - { - gint hour = g_date_time_get_hour (datetime) % 12; - if (hour == 0) - hour = 12; - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, hour); - } - break; - case 'j': - get_numeric_format (fmt, sizeof(fmt), FALSE, pad_set ? pad : '0', 3); - g_string_append_printf (outstr, fmt, g_date_time_get_day_of_year (datetime)); - break; - case 'k': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : ' ', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_hour (datetime)); - break; - case 'l': - { - gint hour = g_date_time_get_hour (datetime) % 12; - if (hour == 0) - hour = 12; - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : ' ', 2); - g_string_append_printf (outstr, fmt, hour); - } - break; - case 'n': - g_string_append_c (outstr, '\n'); - break; - case 'm': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_month (datetime)); - break; - case 'M': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, 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': - { - gint hour = g_date_time_get_hour (datetime) % 12; - if (hour == 0) - hour = 12; - ampm = g_utf8_strup (GET_AMPM (datetime), -1); - g_string_append_printf (outstr, "%02d:%02d:%02d %s", - hour, - g_date_time_get_minute (datetime), - g_date_time_get_second (datetime), - ampm); - g_free (ampm); - } - 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': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, 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': - get_numeric_format (fmt, sizeof(fmt), alt_digits, 0, 0); - g_string_append_printf (outstr, fmt, g_date_time_get_day_of_week (datetime)); - break; - case 'V': - get_numeric_format (fmt, sizeof(fmt), alt_digits, '0', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_week_of_year (datetime)); - break; - case 'w': - { - gint day_of_week = g_date_time_get_day_of_week (datetime); - if (day_of_week == 7) - day_of_week = 0; - get_numeric_format (fmt, sizeof(fmt), alt_digits, 0, 0); - g_string_append_printf (outstr, fmt, day_of_week); - } - break; - case 'W': - get_numeric_format (fmt, sizeof(fmt), alt_digits, '0', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_day_of_year (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': - get_numeric_format (fmt, sizeof(fmt), alt_digits, pad_set ? pad : '0', 2); - g_string_append_printf (outstr, fmt, g_date_time_get_year (datetime) % 100); - break; - case 'Y': - get_numeric_format (fmt, sizeof(fmt), alt_digits, 0, 0); - g_string_append_printf (outstr, fmt, 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; }