*** ./doc/src/sgml/func.sgml.orig	2005-12-25 18:38:45.000000000 -0200
--- ./doc/src/sgml/func.sgml	2005-12-25 18:55:10.000000000 -0200
***************
*** 4648,4653 ****
--- 4648,4658 ----
          <entry><literal>FX&nbsp;Month&nbsp;DD&nbsp;Day</literal></entry>
         </row>   
         <row>
+         <entry><literal>TM</literal> prefix</entry>
+         <entry>translation mode (print localized day and month names)</entry>
+         <entry><literal>TMMonth</literal></entry>
+        </row>       
+        <row>
          <entry><literal>SP</literal> suffix</entry>
          <entry>spell mode (not yet implemented)</entry>
          <entry><literal>DDSP</literal></entry>
***************
*** 4670,4675 ****
--- 4675,4686 ----
  
       <listitem>
        <para>
+        <literal>TM</literal> does not include trailing blanks.
+       </para>
+      </listitem>
+ 
+      <listitem>
+       <para>
         <function>to_timestamp</function> and <function>to_date</function>
         skip multiple blank spaces in the input string if the <literal>FX</literal> option 
         is not used. <literal>FX</literal> must be specified as the first item
*** ./src/backend/utils/adt/formatting.c.orig	2005-12-23 23:16:14.000000000 -0200
--- ./src/backend/utils/adt/formatting.c	2005-12-25 18:03:32.000000000 -0200
***************
*** 73,78 ****
--- 73,79 ----
  #include <unistd.h>
  #include <math.h>
  #include <float.h>
+ #include <locale.h>
  
  #include "utils/builtins.h"
  #include "utils/date.h"
***************
*** 82,87 ****
--- 83,90 ----
  #include "utils/numeric.h"
  #include "utils/pg_locale.h"
  
+ #define	_(x)	gettext((x))
+ 
  /* ----------
   * Routines type
   * ----------
***************
*** 167,172 ****
--- 170,179 ----
  	"August", "September", "October", "November", "December", NULL
  };
  
+ static char *days_short[] = {
+ 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
+ };
+ 
  /* ----------
   * AC / DC
   * ----------
***************
*** 466,471 ****
--- 473,479 ----
  #define DCH_S_TH	0x02
  #define DCH_S_th	0x04
  #define DCH_S_SP	0x08
+ #define DCH_S_TM	0x10
  
  /* ----------
   * Suffix tests
***************
*** 478,483 ****
--- 486,492 ----
  
  #define S_FM(_s)	(((_s) & DCH_S_FM) ? 1 : 0)
  #define S_SP(_s)	(((_s) & DCH_S_SP) ? 1 : 0)
+ #define S_TM(_s)	(((_s) & DCH_S_TM) ? 1 : 0)
  
  /* ----------
   * Suffixes definition for DATE-TIME TO/FROM CHAR
***************
*** 486,491 ****
--- 495,502 ----
  static KeySuffix DCH_suff[] = {
  	{"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
  	{"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
+ 	{"TM", 2, DCH_S_TM, SUFFTYPE_PREFIX},
+ 	{"tm", 2, DCH_S_TM, SUFFTYPE_PREFIX},
  	{"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
  	{"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
  	{"SP", 2, DCH_S_SP, SUFFTYPE_POSTFIX},
***************
*** 929,934 ****
--- 940,949 ----
  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
***************
*** 1330,1336 ****
  			 * The input string is shorter than format picture, so it's good
  			 * time to break this loop...
  			 *
! 			 * Note: this isn't relevant for TO_CHAR mode, beacuse it use
  			 * 'inout' allocated by format picture length.
  			 */
  			break;
--- 1345,1351 ----
  			 * The input string is shorter than format picture, so it's good
  			 * time to break this loop...
  			 *
! 			 * Note: this isn't relevant for TO_CHAR mode, because it uses
  			 * 'inout' allocated by format picture length.
  			 */
  			break;
***************
*** 2062,2068 ****
  		tmfc = (TmFromChar *) data;
  
  	/*
! 	 * In the FROM-char is not difference between "January" or "JANUARY" or
  	 * "january", all is before search convert to "first-upper". This
  	 * convention is used for MONTH, MON, DAY, DY
  	 */
--- 2077,2083 ----
  		tmfc = (TmFromChar *) data;
  
  	/*
! 	 * In the FROM-char there is no difference between "January" or "JANUARY" or
  	 * "january", all is before search convert to "first-upper". This
  	 * convention is used for MONTH, MON, DAY, DY
  	 */
***************
*** 2166,2187 ****
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			strcpy(workbuff, months_full[tm->tm_mon - 1]);
! 			sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(workbuff));
  			return strlen(p_inout);
  
  		case DCH_Month:
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
  			return strlen(p_inout);
  
  		case DCH_month:
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
  			*inout = pg_tolower((unsigned char) *inout);
  			return strlen(p_inout);
  
--- 2181,2211 ----
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			if (S_TM(suf))
! 				strcpy(workbuff, localize_month_full(tm->tm_mon - 1));
! 			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);
  
  		case DCH_Month:
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			if (S_TM(suf))
! 				sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1));
! 			else
! 				sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, months_full[tm->tm_mon - 1]);
  			return strlen(p_inout);
  
  		case DCH_month:
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			if (S_TM(suf))
! 				sprintf(inout, "%*s", 0, localize_month_full(tm->tm_mon - 1));
! 			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);
  
***************
*** 2189,2195 ****
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			strcpy(inout, months[tm->tm_mon - 1]);
  			str_toupper(inout);
  			return strlen(p_inout);
  
--- 2213,2222 ----
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			if (S_TM(suf))
! 				strcpy(inout, localize_month(tm->tm_mon - 1));
! 			else
! 				strcpy(inout, months[tm->tm_mon - 1]);
  			str_toupper(inout);
  			return strlen(p_inout);
  
***************
*** 2197,2210 ****
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			strcpy(inout, months[tm->tm_mon - 1]);
  			return strlen(p_inout);
  
  		case DCH_mon:
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			strcpy(inout, months[tm->tm_mon - 1]);
  			*inout = pg_tolower((unsigned char) *inout);
  			return strlen(p_inout);
  
--- 2224,2243 ----
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			if (S_TM(suf))
! 				strcpy(inout, localize_month(tm->tm_mon - 1));
! 			else
! 				strcpy(inout, months[tm->tm_mon - 1]);
  			return strlen(p_inout);
  
  		case DCH_mon:
  			INVALID_FOR_INTERVAL;
  			if (!tm->tm_mon)
  				return -1;
! 			if (S_TM(suf))
! 				strcpy(inout, localize_month(tm->tm_mon - 1));
! 			else
! 				strcpy(inout, months[tm->tm_mon - 1]);
  			*inout = pg_tolower((unsigned char) *inout);
  			return strlen(p_inout);
  
***************
*** 2232,2268 ****
  			break;
  		case DCH_DAY:
  			INVALID_FOR_INTERVAL;
! 			strcpy(workbuff, days[tm->tm_wday]);
! 			sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, str_toupper(workbuff));
  			return strlen(p_inout);
  
  		case DCH_Day:
  			INVALID_FOR_INTERVAL;
! 			sprintf(inout, "%*s", S_FM(suf) ? 0 : -9, days[tm->tm_wday]);
  			return strlen(p_inout);
  
  		case DCH_day:
  			INVALID_FOR_INTERVAL;
! 			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;
! 			strcpy(inout, days[tm->tm_wday]);
  			str_toupper(inout);
! 			return 3;			/* truncate */
  
  		case DCH_Dy:
  			INVALID_FOR_INTERVAL;
! 			strcpy(inout, days[tm->tm_wday]);
! 			return 3;			/* truncate */
  
  		case DCH_dy:
  			INVALID_FOR_INTERVAL;
! 			strcpy(inout, days[tm->tm_wday]);
  			*inout = pg_tolower((unsigned char) *inout);
! 			return 3;			/* truncate */
  
  		case DCH_DDD:
  			if (is_to_char)
--- 2265,2319 ----
  			break;
  		case DCH_DAY:
  			INVALID_FOR_INTERVAL;
! 			if (S_TM(suf))
! 				strcpy(workbuff, localize_day_full(tm->tm_wday));
! 			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));
! 			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));
! 			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));
! 			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));
! 			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));
! 			else
! 				strcpy(inout, days_short[tm->tm_wday]);
  			*inout = pg_tolower((unsigned char) *inout);
! 			return strlen(p_inout);
  
  		case DCH_DDD:
  			if (is_to_char)
***************
*** 2802,2807 ****
--- 2853,3020 ----
  	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
   ***************************************************************************/
