[glib] g_date_time_format: improve support for alt digits
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] g_date_time_format: improve support for alt digits
- Date: Sun, 4 Sep 2011 03:06:43 +0000 (UTC)
commit 2d7051e3a33a70a01d5ee06c1f10c347f6c08baf
Author: Ryan Lortie <desrt desrt ca>
Date: Sat Sep 3 20:12:53 2011 -0400
g_date_time_format: improve support for alt digits
Improve a few situations where g_date_time_format() was getting the
padding wrong when displaying alt digits (eg: Arabic numerals) for
formatting time.
We now depend on nl_langinfo (_NL_CTYPE_OUTDIGITn_WC) to do the
conversion, which is very likely glibc-specific, but our previous method
relied on a glibc-specific printf() feature, so no harm done there.
Add a configure check for nl_langinfo (_NL_CTYPE_OUTDIGITn_WC).
Uncomment a few testcases that were failing previously.
https://bugzilla.gnome.org/show_bug.cgi?id=658107
configure.ac | 20 ++++++
glib/gdatetime.c | 167 +++++++++++++++++++++++++-----------------------
glib/tests/gdatetime.c | 19 ++---
3 files changed, 114 insertions(+), 92 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 59e52c0..307e606 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1330,6 +1330,26 @@ if test x$glib_cv_langinfo_time = xyes; then
AC_DEFINE(HAVE_LANGINFO_TIME,1,[Have nl_langinfo (PM_STR)])
fi
+dnl Check for nl_langinfo and _NL_CTYPE_OUTDIGITn_WC
+AC_CACHE_CHECK([for nl_langinfo (_NL_CTYPE_OUTDIGITn_WC)], glib_cv_langinfo_outdigit,[
+ AC_TRY_COMPILE([#include <langinfo.h>],
+ [char *str;
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT0_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT1_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT2_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT3_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT4_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT5_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT6_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT7_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT8_WC);
+ str = nl_langinfo (_NL_CTYPE_OUTDIGIT9_WC);],
+ [glib_cv_langinfo_outdigit=yes],
+ [glib_cv_langinfo_outdigit=no])])
+if test x$glib_cv_langinfo_outdigit = xyes; then
+ AC_DEFINE(HAVE_LANGINFO_OUTDIGIT,1,[Have nl_langinfo (_NL_CTYPE_OUTDIGITn_WC)])
+fi
+
dnl ****************************************
dnl *** posix_memalign ***
dnl ****************************************
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
index 8eea23b..76f4f36 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -2076,42 +2076,61 @@ 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)
-{
- const gchar *width_str;
-
- if (pad == 0)
- width_str = "";
- else
+format_number (GString *str,
+ gboolean use_alt_digits,
+ gchar pad,
+ gint width,
+ guint32 number)
+{
+ const gunichar ascii_digits[10] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
+ };
+ const gunichar *digits = ascii_digits;
+ gunichar tmp[10];
+ gint i = 0;
+
+ g_return_if_fail (width <= 10);
+
+#ifdef HAVE_LANGINFO_OUTDIGIT
+ if (use_alt_digits)
{
- switch (width)
+ static gunichar alt_digits[10];
+ static gsize initialised;
+ /* 2^32 has 10 digits */
+
+ if G_UNLIKELY (g_once_init_enter (&initialised))
{
- 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;
+#define DO_DIGIT(n) \
+ { \
+ union { guint integer; char *pointer; } val; \
+ val.pointer = nl_langinfo (_NL_CTYPE_OUTDIGIT## n ##_WC); \
+ alt_digits[n] = val.integer; \
+ }
+ 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);
- g_snprintf (fmt, len, "%%%s%sd", alt_digits ? "I": "", width_str);
+ while (i)
+ g_string_append_unichar (str, tmp[--i]);
}
/**
@@ -2393,7 +2412,6 @@ g_date_time_format (GDateTime *datetime,
gboolean alt_digits = FALSE;
gboolean pad_set = FALSE;
gchar pad = '\0';
- gchar fmt[20];
gchar *ampm;
g_return_val_if_fail (datetime != NULL, NULL);
@@ -2443,16 +2461,16 @@ g_date_time_format (GDateTime *datetime,
}
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);
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ 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));
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ g_date_time_get_day_of_month (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));
+ 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",
@@ -2461,54 +2479,46 @@ g_date_time_format (GDateTime *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);
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ 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));
+ 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':
- 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));
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ 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);
- }
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ (g_date_time_get_hour (datetime) + 11) % 12 + 1);
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));
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 3,
+ 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));
+ format_number (outstr, alt_digits, pad_set ? pad : ' ', 2,
+ 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);
- }
+ 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':
- 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));
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ 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));
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ g_date_time_get_minute (datetime));
break;
case 'O':
alt_digits = TRUE;
@@ -2546,8 +2556,8 @@ g_date_time_format (GDateTime *datetime,
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));
+ 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');
@@ -2559,21 +2569,16 @@ g_date_time_format (GDateTime *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));
+ format_number (outstr, alt_digits, 0, 0,
+ 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));
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ 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);
- }
+ format_number (outstr, alt_digits, 0, 0,
+ g_date_time_get_day_of_week (datetime) % 7);
break;
case 'x':
{
@@ -2590,12 +2595,12 @@ g_date_time_format (GDateTime *datetime,
}
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);
+ format_number (outstr, alt_digits, pad_set ? pad : '0', 2,
+ 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));
+ format_number (outstr, alt_digits, 0, 0,
+ g_date_time_get_year (datetime));
break;
case 'z':
if (datetime->tz != NULL)
diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c
index 6747a96..070ccff 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -896,17 +896,14 @@ test_modifiers (void)
setlocale (LC_ALL, "fa_IR.utf-8");
if (strstr (setlocale (LC_ALL, NULL), "fa_IR") != NULL)
{
- TEST_PRINTF_TIME (23, 0, 0, "%OH", "\333\262\333\263");
- TEST_PRINTF_TIME (23, 0, 0, "%OI", "\333\261\333\261");
- TEST_PRINTF_TIME (23, 0, 0, "%OM", "\333\260");
-
- TEST_PRINTF_DATE (2011, 7, 1, "%Om", "\333\267");
- TEST_PRINTF_DATE (2011, 7, 1, "%-Om", "\333\267");
-/* These do not currently work as expected, since glib's printf
- counts arabic digits as two characters for some reason
- TEST_PRINTF_DATE (2011, 7, 1, "%0Om", "0\333\267");
- TEST_PRINTF_DATE (2011, 7, 1, "%_Om", " \333\267");
-*/
+ TEST_PRINTF_TIME (23, 0, 0, "%OH", "\333\262\333\263"); /* '23' */
+ TEST_PRINTF_TIME (23, 0, 0, "%OI", "\333\261\333\261"); /* '11' */
+ TEST_PRINTF_TIME (23, 0, 0, "%OM", "\333\260\333\260"); /* '00' */
+
+ TEST_PRINTF_DATE (2011, 7, 1, "%Om", "\333\260\333\267"); /* '07' */
+ TEST_PRINTF_DATE (2011, 7, 1, "%0Om", "\333\260\333\267"); /* '07' */
+ TEST_PRINTF_DATE (2011, 7, 1, "%-Om", "\333\267"); /* '7' */
+ TEST_PRINTF_DATE (2011, 7, 1, "%_Om", " \333\267"); /* ' 7' */
}
else
g_test_message ("locale fa_IR not available, skipping O modifier tests");
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]