44 *
55 * Portions Copyright (c) 2002-2010, PostgreSQL Global Development Group
66 *
7- * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.54 2010/04/22 01:55:52 itagaki Exp $
7+ * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.55 2010/04/24 22:54:56 momjian Exp $
88 *
99 *-----------------------------------------------------------------------
1010 */
4141 * DOES NOT WORK RELIABLY: on some platforms the second setlocale() call
4242 * will change the memory save is pointing at.To do this sort of thing
4343 * safely, you *must* pstrdup what setlocale returns the first time.
44+ *
45+ * FYI, The Open Group locale standard is defined here:
46+ *
47+ * http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html
4448 *----------
4549 */
4650
@@ -424,7 +428,6 @@ PGLC_localeconv(void)
424428char * grouping ;
425429char * thousands_sep ;
426430int encoding ;
427-
428431#ifdef WIN32
429432char * save_lc_ctype ;
430433#endif
@@ -435,25 +438,48 @@ PGLC_localeconv(void)
435438
436439free_struct_lconv (& CurrentLocaleConv );
437440
438- /*Set user's values of monetary and numeric locales */
441+ /*Save user's values of monetary and numeric locales */
439442save_lc_monetary = setlocale (LC_MONETARY ,NULL );
440443if (save_lc_monetary )
441444save_lc_monetary = pstrdup (save_lc_monetary );
445+
442446save_lc_numeric = setlocale (LC_NUMERIC ,NULL );
443447if (save_lc_numeric )
444448save_lc_numeric = pstrdup (save_lc_numeric );
445449
446450#ifdef WIN32
447- /* set user's value of ctype locale */
451+ /*
452+ * Ideally, monetary and numeric local symbols could be returned in
453+ * any server encoding. Unfortunately, the WIN32 API does not allow
454+ * setlocale() to return values in a codepage/CTYPE that uses more
455+ * than two bytes per character, like UTF-8:
456+ *
457+ * http://msdn.microsoft.com/en-us/library/x99tb11d.aspx
458+ *
459+ * Evidently, LC_CTYPE allows us to control the encoding used
460+ * for strings returned by localeconv(). The Open Group
461+ * standard, mentioned at the top of this C file, doesn't
462+ * explicitly state this.
463+ *
464+ * Therefore, we set LC_CTYPE to match LC_NUMERIC or LC_MONETARY
465+ * (which cannot be UTF8), call localeconv(), and then convert from
466+ * the numeric/monitary LC_CTYPE to the server encoding. One
467+ * example use of this is for the Euro symbol.
468+ *
469+ * Perhaps someday we will use GetLocaleInfoW() which returns values
470+ * in UTF16 and convert from that.
471+ */
472+
473+ /* save user's value of ctype locale */
448474save_lc_ctype = setlocale (LC_CTYPE ,NULL );
449475if (save_lc_ctype )
450476save_lc_ctype = pstrdup (save_lc_ctype );
451- #endif
452477
453- /* Get formatting information for numeric */
454- #ifdef WIN32
478+ /* use numeric to set the ctype */
455479setlocale (LC_CTYPE ,locale_numeric );
456480#endif
481+
482+ /* Get formatting information for numeric */
457483setlocale (LC_NUMERIC ,locale_numeric );
458484extlconv = localeconv ();
459485encoding = pg_get_encoding_from_locale (locale_numeric );
@@ -462,10 +488,12 @@ PGLC_localeconv(void)
462488thousands_sep = db_encoding_strdup (encoding ,extlconv -> thousands_sep );
463489grouping = strdup (extlconv -> grouping );
464490
465- /* Get formatting information for monetary */
466491#ifdef WIN32
492+ /* use monetary to set the ctype */
467493setlocale (LC_CTYPE ,locale_monetary );
468494#endif
495+
496+ /* Get formatting information for monetary */
469497setlocale (LC_MONETARY ,locale_monetary );
470498extlconv = localeconv ();
471499encoding = pg_get_encoding_from_locale (locale_monetary );
@@ -500,7 +528,7 @@ PGLC_localeconv(void)
500528}
501529
502530#ifdef WIN32
503- /*try to restore internal ctype settings */
531+ /*Try to restore internal ctype settings */
504532if (save_lc_ctype )
505533{
506534setlocale (LC_CTYPE ,save_lc_ctype );
@@ -514,13 +542,15 @@ PGLC_localeconv(void)
514542
515543#ifdef WIN32
516544/*
517- * On win32, strftime() returns the encoding in CP_ACP, which is likely
518- * different from SERVER_ENCODING. This is especially important in Japanese
519- * versions of Windows which will use SJIS encoding, which we don't support
520- * as a server encoding.
545+ * On WIN32, strftime() returns the encoding in CP_ACP (the default
546+ * operating system codpage for that computer), which is likely different
547+ * from SERVER_ENCODING. This is especially important in Japanese versions
548+ * of Windows which will use SJIS encoding, which we don't support as a
549+ * server encoding.
521550 *
522- * Replace strftime() with a version that gets the string in UTF16 and then
523- * converts it to the appropriate encoding as necessary.
551+ * So, instead of using strftime(), use wcsftime() to return the value in
552+ * wide characters (internally UTF16) and then convert it to the appropriate
553+ * database encoding.
524554 *
525555 * Note that this only affects the calls to strftime() in this file, which are
526556 * used to get the locale-aware strings. Other parts of the backend use
@@ -537,7 +567,6 @@ strftime_win32(char *dst, size_t dstlen, const wchar_t *format, const struct tm
537567
538568len = wcsftime (wbuf ,MAX_L10N_DATA ,format ,tm );
539569if (len == 0 )
540-
541570/*
542571 * strftime call failed - return 0 with the contents of dst
543572 * unspecified
@@ -564,7 +593,9 @@ strftime_win32(char *dst, size_t dstlen, const wchar_t *format, const struct tm
564593return len ;
565594}
566595
596+ /* redefine strftime() */
567597#define strftime (a ,b ,c ,d ) strftime_win32(a,b,L##c,d)
598+
568599#endif /* WIN32 */
569600
570601
@@ -580,7 +611,6 @@ cache_locale_time(void)
580611char buf [MAX_L10N_DATA ];
581612char * ptr ;
582613int i ;
583-
584614#ifdef WIN32
585615char * save_lc_ctype ;
586616#endif
@@ -591,20 +621,22 @@ cache_locale_time(void)
591621
592622elog (DEBUG3 ,"cache_locale_time() executed; locale: \"%s\"" ,locale_time );
593623
624+ /* save user's value of time locale */
625+ save_lc_time = setlocale (LC_TIME ,NULL );
626+ if (save_lc_time )
627+ save_lc_time = pstrdup (save_lc_time );
628+
594629#ifdef WIN32
595- /* set user's value of ctype locale */
630+ /* See the WIN32 comment near the top of PGLC_localeconv() */
631+ /* save user's value of ctype locale */
596632save_lc_ctype = setlocale (LC_CTYPE ,NULL );
597633if (save_lc_ctype )
598634save_lc_ctype = pstrdup (save_lc_ctype );
599635
636+ /* use lc_time to set the ctype */
600637setlocale (LC_CTYPE ,locale_time );
601638#endif
602639
603- /* set user's value of time locale */
604- save_lc_time = setlocale (LC_TIME ,NULL );
605- if (save_lc_time )
606- save_lc_time = pstrdup (save_lc_time );
607-
608640setlocale (LC_TIME ,locale_time );
609641
610642timenow = time (NULL );