From 13e2070a9a341a4e3b8d0d17c637e5b837cf06f0 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Sat, 17 Feb 2007 08:51:12 +0000 Subject: [PATCH] New Win32-only function. Use the wide character Win32 API to do the work 2007-02-17 Tor Lillqvist * glib/gdate.c (win32_strftime_helper): New Win32-only function. Use the wide character Win32 API to do the work of strftime(): GetThreadLocale(), GetLocaleInfoW(), GetDateFormatW() and GetTimeFormatW(). (g_date_strftime): On Windows use win32_strftime_helper() instead of strftime() to avoid codepage issues with strftime(). Unfortunately using wcsftime() would not help either. (#404832) svn path=/trunk/; revision=5339 --- ChangeLog | 10 ++ glib/gdate.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 404 insertions(+) diff --git a/ChangeLog b/ChangeLog index ab18c7b..20f4654 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-02-17 Tor Lillqvist + + * glib/gdate.c (win32_strftime_helper): New Win32-only + function. Use the wide character Win32 API to do the work of + strftime(): GetThreadLocale(), GetLocaleInfoW(), GetDateFormatW() + and GetTimeFormatW(). + (g_date_strftime): On Windows use win32_strftime_helper() + instead of strftime() to avoid codepage issues with strftime(). + Unfortunately using wcsftime() would not help either. (#404832) + 2007-02-16 Soren Sandmann * tests/sequence-test.c: For move, test moving between two diff --git a/glib/gdate.c b/glib/gdate.c index c2616d1..f7ccc1f 100644 --- a/glib/gdate.c +++ b/glib/gdate.c @@ -42,6 +42,10 @@ #include #include +#ifdef G_OS_WIN32 +#include +#endif + #include "galias.h" GDate* @@ -1420,6 +1424,384 @@ g_date_order (GDate *date1, } } +#ifdef G_OS_WIN32 +static gsize +win32_strftime_helper (const GDate *d, + const gchar *format, + const struct tm *tm, + gchar *s, + gsize slen) +{ + SYSTEMTIME systemtime; + TIME_ZONE_INFORMATION tzinfo; + LCID lcid; + int n, k; + GArray *result; + const gchar *p; + gunichar c; + const wchar_t digits[] = L"0123456789"; + gchar *convbuf; + glong convlen = 0; + gsize retval; + + systemtime.wYear = tm->tm_year + 1900; + systemtime.wMonth = tm->tm_mon + 1; + systemtime.wDayOfWeek = tm->tm_wday; + systemtime.wDay = tm->tm_mday; + systemtime.wHour = tm->tm_hour; + systemtime.wMinute = tm->tm_min; + systemtime.wSecond = tm->tm_sec; + systemtime.wMilliseconds = 0; + + lcid = GetThreadLocale (); + result = g_array_sized_new (FALSE, FALSE, sizeof (wchar_t), MAX (128, strlen (format) * 2)); + + p = format; + while (*p) + { + c = g_utf8_get_char (p); + if (c == '%') + { + p = g_utf8_next_char (p); + if (!*p) + { + s[0] = '\0'; + g_array_free (result, TRUE); + + return 0; + } + + c = g_utf8_get_char (p); + if (c == 'E' || c == 'O') + { + /* Ignore modified conversion specifiers for now. */ + p = g_utf8_next_char (p); + if (!*p) + { + s[0] = '\0'; + g_array_free (result, TRUE); + + return 0; + } + + c = g_utf8_get_char (p); + } + + switch (c) + { + case 'a': + if (systemtime.wDayOfWeek == 0) + k = 6; + else + k = systemtime.wDayOfWeek - 1; + n = GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, NULL, 0); + g_array_set_size (result, result->len + n); + GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + break; + case 'A': + if (systemtime.wDayOfWeek == 0) + k = 6; + else + k = systemtime.wDayOfWeek - 1; + n = GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, NULL, 0); + g_array_set_size (result, result->len + n); + GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + break; + case 'b': + case 'h': + n = GetLocaleInfoW (lcid, LOCALE_SABBREVMONTHNAME1+systemtime.wMonth-1, NULL, 0); + g_array_set_size (result, result->len + n); + GetLocaleInfoW (lcid, LOCALE_SABBREVMONTHNAME1+systemtime.wMonth-1, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + break; + case 'B': + n = GetLocaleInfoW (lcid, LOCALE_SMONTHNAME1+systemtime.wMonth-1, NULL, 0); + g_array_set_size (result, result->len + n); + GetLocaleInfoW (lcid, LOCALE_SMONTHNAME1+systemtime.wMonth-1, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + break; + case 'c': + n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0); + if (n > 0) + { + g_array_set_size (result, result->len + n); + GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + } + g_array_append_vals (result, L" ", 1); + n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0); + if (n > 0) + { + g_array_set_size (result, result->len + n); + GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + } + break; + case 'C': + g_array_append_vals (result, digits + systemtime.wYear/1000, 1); + g_array_append_vals (result, digits + (systemtime.wYear/1000)%10, 1); + break; + case 'd': + g_array_append_vals (result, digits + systemtime.wDay/10, 1); + g_array_append_vals (result, digits + systemtime.wDay%10, 1); + break; + case 'D': + g_array_append_vals (result, digits + systemtime.wMonth/10, 1); + g_array_append_vals (result, digits + systemtime.wMonth%10, 1); + g_array_append_vals (result, L"/", 1); + g_array_append_vals (result, digits + systemtime.wDay/10, 1); + g_array_append_vals (result, digits + systemtime.wDay%10, 1); + g_array_append_vals (result, L"/", 1); + g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1); + g_array_append_vals (result, digits + systemtime.wYear%10, 1); + break; + case 'e': + if (systemtime.wDay >= 10) + g_array_append_vals (result, digits + systemtime.wDay/10, 1); + else + g_array_append_vals (result, L" ", 1); + g_array_append_vals (result, digits + systemtime.wDay%10, 1); + break; + + /* A GDate has no time fields, so for now we can + * hardcode all time conversions into zeros (or 12 for + * %I). The alternative code snippets in the #else + * branches are here ready to be taken into use when + * needed by a g_strftime() or g_date_and_time_format() + * or whatever. + */ + case 'H': +#if 1 + g_array_append_vals (result, L"00", 2); +#else + g_array_append_vals (result, digits + systemtime.wHour/10, 1); + g_array_append_vals (result, digits + systemtime.wHour%10, 1); +#endif + break; + case 'I': +#if 1 + g_array_append_vals (result, L"12", 2); +#else + if (systemtime.wHour == 0) + g_array_append_vals (result, L"12", 2); + else + { + g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1); + g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1); + } +#endif + break; + case 'j': + g_array_append_vals (result, digits + (tm->tm_yday+1)/100, 1); + g_array_append_vals (result, digits + ((tm->tm_yday+1)/10)%10, 1); + g_array_append_vals (result, digits + (tm->tm_yday+1)%10, 1); + break; + case 'm': + g_array_append_vals (result, digits + systemtime.wMonth/10, 1); + g_array_append_vals (result, digits + systemtime.wMonth%10, 1); + break; + case 'M': +#if 1 + g_array_append_vals (result, L"00", 2); +#else + g_array_append_vals (result, digits + systemtime.wMinute/10, 1); + g_array_append_vals (result, digits + systemtime.wMinute%10, 1); +#endif + break; + case 'n': + g_array_append_vals (result, L"\n", 1); + break; + case 'p': + n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0); + if (n > 0) + { + g_array_set_size (result, result->len + n); + GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + } + break; + case 'r': + /* This is a rather odd format. Hard to say what to do. + * Let's always use the POSIX %I:%M:%S %p + */ +#if 1 + g_array_append_vals (result, L"12:00:00", 8); +#else + if (systemtime.wHour == 0) + g_array_append_vals (result, L"12", 2); + else + { + g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1); + g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1); + } + g_array_append_vals (result, L":", 1); + g_array_append_vals (result, digits + systemtime.wMinute/10, 1); + g_array_append_vals (result, digits + systemtime.wMinute%10, 1); + g_array_append_vals (result, L":", 1); + g_array_append_vals (result, digits + systemtime.wSecond/10, 1); + g_array_append_vals (result, digits + systemtime.wSecond%10, 1); + g_array_append_vals (result, L" ", 1); +#endif + n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0); + if (n > 0) + { + g_array_set_size (result, result->len + n); + GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + } + break; + case 'R': +#if 1 + g_array_append_vals (result, L"00:00", 5); +#else + g_array_append_vals (result, digits + systemtime.wHour/10, 1); + g_array_append_vals (result, digits + systemtime.wHour%10, 1); + g_array_append_vals (result, L":", 1); + g_array_append_vals (result, digits + systemtime.wMinute/10, 1); + g_array_append_vals (result, digits + systemtime.wMinute%10, 1); +#endif + break; + case 'S': +#if 1 + g_array_append_vals (result, L"00", 2); +#else + g_array_append_vals (result, digits + systemtime.wSecond/10, 1); + g_array_append_vals (result, digits + systemtime.wSecond%10, 1); +#endif + break; + case 't': + g_array_append_vals (result, L"\t", 1); + break; + case 'T': +#if 1 + g_array_append_vals (result, L"00:00:00", 8); +#else + g_array_append_vals (result, digits + systemtime.wHour/10, 1); + g_array_append_vals (result, digits + systemtime.wHour%10, 1); + g_array_append_vals (result, L":", 1); + g_array_append_vals (result, digits + systemtime.wMinute/10, 1); + g_array_append_vals (result, digits + systemtime.wMinute%10, 1); + g_array_append_vals (result, L":", 1); + g_array_append_vals (result, digits + systemtime.wSecond/10, 1); + g_array_append_vals (result, digits + systemtime.wSecond%10, 1); +#endif + break; + case 'u': + if (systemtime.wDayOfWeek == 0) + g_array_append_vals (result, L"7", 1); + else + g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1); + break; + case 'U': + n = g_date_get_sunday_week_of_year (d); + g_array_append_vals (result, digits + n/10, 1); + g_array_append_vals (result, digits + n%10, 1); + break; + case 'V': + n = g_date_get_iso8601_week_of_year (d); + g_array_append_vals (result, digits + n/10, 1); + g_array_append_vals (result, digits + n%10, 1); + break; + case 'w': + g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1); + break; + case 'W': + n = g_date_get_monday_week_of_year (d); + g_array_append_vals (result, digits + n/10, 1); + g_array_append_vals (result, digits + n%10, 1); + break; + case 'x': + n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0); + if (n > 0) + { + g_array_set_size (result, result->len + n); + GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + } + break; + case 'X': + n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0); + if (n > 0) + { + g_array_set_size (result, result->len + n); + GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); + g_array_set_size (result, result->len - 1); + } + break; + case 'y': + g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1); + g_array_append_vals (result, digits + systemtime.wYear%10, 1); + break; + case 'Y': + g_array_append_vals (result, digits + systemtime.wYear/1000, 1); + g_array_append_vals (result, digits + (systemtime.wYear/100)%10, 1); + g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1); + g_array_append_vals (result, digits + systemtime.wYear%10, 1); + break; + case 'Z': + n = GetTimeZoneInformation (&tzinfo); + if (n == TIME_ZONE_ID_UNKNOWN) + ; + else if (n == TIME_ZONE_ID_STANDARD) + g_array_append_vals (result, tzinfo.StandardName, wcslen (tzinfo.StandardName)); + else if (n == TIME_ZONE_ID_DAYLIGHT) + g_array_append_vals (result, tzinfo.DaylightName, wcslen (tzinfo.DaylightName)); + break; + case '%': + g_array_append_vals (result, L"%", 1); + break; + } + } + else if (c <= 0xFFFF) + { + wchar_t wc = c; + g_array_append_vals (result, &wc, 1); + } + else + { + glong nwc; + wchar_t *ws; + + ws = g_ucs4_to_utf16 (&c, 1, NULL, &nwc, NULL); + g_array_append_vals (result, ws, nwc); + g_free (ws); + } + p = g_utf8_next_char (p); + } + + convbuf = g_utf16_to_utf8 ((wchar_t *) result->data, result->len, NULL, &convlen, NULL); + g_array_free (result, TRUE); + + if (!convbuf) + { + s[0] = '\0'; + return 0; + } + + if (slen <= convlen) + { + /* Ensure only whole characters are copied into the buffer. */ + gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen); + g_assert (end != NULL); + convlen = end - convbuf; + + /* Return 0 because the buffer isn't large enough. */ + retval = 0; + } + else + retval = convlen; + + memcpy (s, convbuf, convlen); + s[convlen] = '\0'; + g_free (convbuf); + + return retval; +} + +#endif + gsize g_date_strftime (gchar *s, gsize slen, @@ -1427,6 +1809,7 @@ g_date_strftime (gchar *s, const GDate *d) { struct tm tm; +#ifndef G_OS_WIN32 gsize locale_format_len = 0; gchar *locale_format; gsize tmplen; @@ -1436,6 +1819,7 @@ g_date_strftime (gchar *s, gchar *convbuf; GError *error = NULL; gsize retval; +#endif g_return_val_if_fail (g_date_valid (d), 0); g_return_val_if_fail (slen > 0, 0); @@ -1444,6 +1828,15 @@ g_date_strftime (gchar *s, g_date_to_struct_tm (d, &tm); +#ifdef G_OS_WIN32 + if (!g_utf8_validate (format, -1, NULL)) + { + s[0] = '\0'; + return 0; + } + return win32_strftime_helper (d, format, &tm, s, slen); +#else + locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error); if (error) @@ -1517,6 +1910,7 @@ g_date_strftime (gchar *s, g_free (convbuf); return retval; +#endif } #define __G_DATE_C__ -- 2.7.4