From d2b694d825ce13bd87d6f76be040466de34f3787 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 24 Nov 2006 15:26:18 +0000 Subject: [PATCH] Fix to_char() locale handling to honor LC_TIME, not LC_MESSAGES. Euler Taveira de Oliveira --- src/backend/utils/adt/formatting.c | 317 ++++++++++++----------------- src/backend/utils/adt/pg_locale.c | 5 +- 2 files changed, 137 insertions(+), 185 deletions(-) diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index d97c1bf405..93aad1a901 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- * formatting.c * - * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.113 2006/10/04 00:29:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.114 2006/11/24 15:26:18 momjian Exp $ * * * Portions Copyright (c) 1999-2006, PostgreSQL Global Development Group @@ -73,7 +73,7 @@ #include #include #include -#include +#include #include "utils/builtins.h" #include "utils/date.h" @@ -83,8 +83,6 @@ #include "utils/numeric.h" #include "utils/pg_locale.h" -#define _(x) gettext((x)) - /* ---------- * Routines type * ---------- @@ -163,7 +161,6 @@ struct FormatNode /* ---------- * Full months - * This needs to be NLS-localized someday. * ---------- */ static char *months_full[] = { @@ -942,10 +939,6 @@ static NUMCacheEntry *NUM_cache_search(char *str); static NUMCacheEntry *NUM_cache_getnew(char *str); static void NUM_cache_remove(NUMCacheEntry *ent); -static char *localize_month_full(int index); -static char *localize_month(int index); -static char *localize_day_full(int index); -static char *localize_day(int index); /* ---------- * Fast sequential search, use index for data selection which @@ -2074,6 +2067,17 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, struct pg_tm *tm = NULL; TmFromChar *tmfc = NULL; TmToChar *tmtc = NULL; + char *save_loc = NULL; + + /* + * Set the LC_TIME only to do some operation (strftime) and then + * set it back. See pg_locale.c for explanations. + */ + if (S_TM(suf)) + { + save_loc = setlocale(LC_TIME, NULL); + setlocale(LC_TIME, locale_time); + } if (is_to_char) { @@ -2189,9 +2193,20 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - strcpy(workbuff, localize_month_full(tm->tm_mon - 1)); + { + /* + * tm_mon in 'pg_tm struct' based on one, but rather POSIX 'tm struct' based on zero. + * See notes at the top of this file. + */ + tm->tm_mon = tm->tm_mon - 1; + strftime(workbuff, sizeof(workbuff), "%B", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(workbuff, months_full[tm->tm_mon - 1]); + } sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff)); return strlen(p_inout); @@ -2200,9 +2215,22 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1)); + { + /* + * tm_mon in 'pg_tm struct' based on one, but rather POSIX 'tm struct' based on zero. + * See notes at the top of this file. + */ + tm->tm_mon = tm->tm_mon - 1; + strftime(inout, 32, "%B", (struct tm *) tm); + /* capitalize output */ + inout[0] = pg_toupper((unsigned char) inout[0]); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]); + } return strlen(p_inout); case DCH_month: @@ -2210,9 +2238,20 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1)); + { + /* + * tm_mon in 'pg_tm struct' based on one, but rather POSIX 'tm struct' based on zero. + * See notes at the top of this file. + */ + tm->tm_mon = tm->tm_mon - 1; + strftime(inout, 32, "%B", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]); + } *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); @@ -2221,9 +2260,20 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - strcpy(inout, localize_month(tm->tm_mon - 1)); + { + /* + * tm_mon in 'pg_tm struct' based on one, but rather POSIX 'tm struct' based on zero. + * See notes at the top of this file. + */ + tm->tm_mon = tm->tm_mon - 1; + strftime(inout, 32, "%b", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(inout, months[tm->tm_mon - 1]); + } str_toupper(inout); return strlen(p_inout); @@ -2232,9 +2282,22 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - strcpy(inout, localize_month(tm->tm_mon - 1)); + { + /* + * tm_mon in 'pg_tm struct' based on one, but rather POSIX 'tm struct' based on zero. + * See notes at the top of this file. + */ + tm->tm_mon = tm->tm_mon - 1; + strftime(inout, 32, "%b", (struct tm *) tm); + /* capitalize output */ + inout[0] = pg_toupper((unsigned char) inout[0]); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(inout, months[tm->tm_mon - 1]); + } return strlen(p_inout); case DCH_mon: @@ -2242,9 +2305,20 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, if (!tm->tm_mon) return -1; if (S_TM(suf)) - strcpy(inout, localize_month(tm->tm_mon - 1)); + { + /* + * tm_mon in 'pg_tm struct' based on one, but rather POSIX 'tm struct' based on zero. + * See notes at the top of this file. + */ + tm->tm_mon = tm->tm_mon - 1; + strftime(inout, 32, "%b", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(inout, months[tm->tm_mon - 1]); + } *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); @@ -2273,52 +2347,92 @@ dch_date(int arg, char *inout, int suf, bool is_to_char, bool is_interval, case DCH_DAY: INVALID_FOR_INTERVAL; if (S_TM(suf)) - strcpy(workbuff, localize_day_full(tm->tm_wday)); + { + strftime(workbuff, sizeof(workbuff), "%A", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(workbuff, days[tm->tm_wday]); + } sprintf(inout, "%*s", (S_FM(suf) || S_TM(suf)) ? 0 : -9, str_toupper(workbuff)); return strlen(p_inout); case DCH_Day: INVALID_FOR_INTERVAL; if (S_TM(suf)) - sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday)); + { + strftime(inout, 32, "%A", (struct tm *) tm); + /* capitalize output */ + inout[0] = pg_toupper((unsigned char) inout[0]); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]); + } return strlen(p_inout); case DCH_day: INVALID_FOR_INTERVAL; if (S_TM(suf)) - sprintf(inout, "%*s", 0, localize_day_full(tm->tm_wday)); + { + strftime(inout, 32, "%A", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]); + } *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); case DCH_DY: INVALID_FOR_INTERVAL; if (S_TM(suf)) - strcpy(inout, localize_day(tm->tm_wday)); + { + strftime(inout, 32, "%a", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(inout, days_short[tm->tm_wday]); + } str_toupper(inout); return strlen(p_inout); case DCH_Dy: INVALID_FOR_INTERVAL; if (S_TM(suf)) - strcpy(inout, localize_day(tm->tm_wday)); + { + strftime(inout, 32, "%a", (struct tm *) tm); + /* capitalize output */ + inout[0] = pg_toupper((unsigned char) inout[0]); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(inout, days_short[tm->tm_wday]); + } return strlen(p_inout); case DCH_dy: INVALID_FOR_INTERVAL; if (S_TM(suf)) - strcpy(inout, localize_day(tm->tm_wday)); + { + strftime(inout, 32, "%a", (struct tm *) tm); + /* set it back; see comments in pg_locale.c */ + setlocale(LC_TIME, save_loc); + } else + { strcpy(inout, days_short[tm->tm_wday]); + } *inout = pg_tolower((unsigned char) *inout); return strlen(p_inout); @@ -2860,167 +2974,6 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval) return res; } -static char * -localize_month_full(int index) -{ - char *m = NULL; - - switch (index) - { - case 0: - m = _("January"); - break; - case 1: - m = _("February"); - break; - case 2: - m = _("March"); - break; - case 3: - m = _("April"); - break; - case 4: - m = _("May"); - break; - case 5: - m = _("June"); - break; - case 6: - m = _("July"); - break; - case 7: - m = _("August"); - break; - case 8: - m = _("September"); - break; - case 9: - m = _("October"); - break; - case 10: - m = _("November"); - break; - case 11: - m = _("December"); - break; - } - - return m; -} - -static char * -localize_month(int index) -{ - char *m = NULL; - - switch (index) - { - case 0: - m = _("Jan"); - break; - case 1: - m = _("Feb"); - break; - case 2: - m = _("Mar"); - break; - case 3: - m = _("Apr"); - break; - case 4: - m = _("May"); - break; - case 5: - m = _("Jun"); - break; - case 6: - m = _("Jul"); - break; - case 7: - m = _("Aug"); - break; - case 8: - m = _("Sep"); - break; - case 9: - m = _("Oct"); - break; - case 10: - m = _("Nov"); - break; - case 11: - m = _("Dec"); - break; - } - - return m; -} - -static char * -localize_day_full(int index) -{ - char *d = NULL; - - switch (index) - { - case 0: - d = _("Sunday"); - break; - case 1: - d = _("Monday"); - break; - case 2: - d = _("Tuesday"); - break; - case 3: - d = _("Wednesday"); - break; - case 4: - d = _("Thursday"); - break; - case 5: - d = _("Friday"); - break; - case 6: - d = _("Saturday"); - break; - } - - return d; -} - -static char * -localize_day(int index) -{ - char *d = NULL; - - switch (index) - { - case 0: - d = _("Sun"); - break; - case 1: - d = _("Mon"); - break; - case 2: - d = _("Tue"); - break; - case 3: - d = _("Wed"); - break; - case 4: - d = _("Thu"); - break; - case 5: - d = _("Fri"); - break; - case 6: - d = _("Sat"); - break; - } - - return d; -} /**************************************************************************** * Public routines diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index da81d79f39..9ffe9acc03 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -4,7 +4,7 @@ * * Portions Copyright (c) 2002-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.37 2006/10/04 00:29:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/pg_locale.c,v 1.38 2006/11/24 15:26:18 momjian Exp $ * *----------------------------------------------------------------------- */ @@ -26,8 +26,7 @@ * required information obtained from localeconv(), and set them back. * The cached information is only used by the formatting functions * (to_char, etc.) and the money type. For the user, this should all be - * transparent. (Actually, LC_TIME doesn't do anything at all right - * now.) + * transparent. * * !!! NOW HEAR THIS !!! * -- 2.40.0