Piyush Newe wrote:
> Hi,
> 
> I was randomly testing some date related stuff on PG & observed that the
> outputs were wrong.
> 
> e.g.
> postgres=# SELECT TO_DATE('01-jan-2010',  'DD-MON-YY');
>   to_date
> ------------
>  3910-01-01  <--------- Look at this
> (1 row)
> 
> postgres=# SELECT TO_DATE('01-jan-2010',  'DD-MON-YYYY');
>   to_date
> ------------
>  2010-01-01
> (1 row)

I have done some work on this problem, and have developed the attached
patch.  It genarates the output in the final column of this table:

                                            Oracle          PostgreSQL      
With PG Patch
 1  TO_DATE('01-jan-1',  'DD-MON-Y')        01-JAN-2011     01-JAN-2001     
01-JAN-2001+
 2  TO_DATE('01-jan-1',  'DD-MON-YY')       01-JAN-2001     01-JAN-2001     
01-JAN-2001
 3  TO_DATE('01-jan-1',  'DD-MON-YYY')      01-JAN-2001     01-JAN-2001     
01-JAN-2001
 4  TO_DATE('01-jan-1',  'DD-MON-YYYY')     01-JAN-0001     01-JAN-0001     
01-JAN-0001
 5  TO_DATE('01-jan-10',  'DD-MON-Y')       Error           01-JAN-2010     
01-JAN-2010
 6  TO_DATE('01-jan-10',  'DD-MON-YY')      01-JAN-2010     01-JAN-2010     
01-JAN-2010
 7  TO_DATE('01-jan-10',  'DD-MON-YYY')     01-JAN-2010     01-JAN-2010     
01-JAN-2010
 8  TO_DATE('01-jan-10',  'DD-MON-YYYY')    01-JAN-0010     01-JAN-0010     
01-JAN-0010
 9  TO_DATE('01-jan-067',  'DD-MON-Y')      Error           01-JAN-2067     
01-JAN-2067
10  TO_DATE('01-jan-111',  'DD-MON-YY')     01-JAN-0111     01-JAN-2011     
01-JAN-2111*+
11  TO_DATE('01-jan-678',  'DD-MON-YYY')    01-JAN-2678     01-JAN-1678     
01-JAN-1678+
12  TO_DATE('01-jan-001',  'DD-MON-YYYY')   01-JAN-0001     01-JAN-0001     
01-JAN-0001
13  TO_DATE('01-jan-2010',  'DD-MON-Y')     Error           01-JAN-4010     
01-JAN-2010*
14  TO_DATE('01-jan-2010',  'DD-MON-YY')    01-JAN-2010     01-JAN-3910     
01-JAN-2010*
15  TO_DATE('01-jan-2010',  'DD-MON-YYY')   Error           01-JAN-3010     
01-JAN-2010*
16  TO_DATE('01-jan-2010',  'DD-MON-YYYY')  01-JAN-2010     01-JAN-2010     
01-JAN-2010

I marked with '*' every case where the patch doesn't match current PG,
and used a '+' to mark every case where it doesn't match Oracle.

I know Tom was worried that because the year field took more digits than
specified, it would prevent numeric columns from being pulled apart, but
our code has this check:

    if (S_FM(node->suffix) || is_next_separator(node))
    {
        /*
         * This node is in Fill Mode, or the next node is known to be a
         * non-digit value, so we just slurp as many characters as we can get.
         */
        errno = 0;
        result = strtol(init, src, 10);
    }

The reason these tests are accepting an unlimited number of digits is
because it is at the end of the string.  If you place a digit field
right after it, it will not use more characters than specified:

        test=> select to_date('9876', 'YYY');
          to_date
        ------------
         9876-01-01
        (1 row)
        
        test=> select to_date('9876', 'YYYMM');
          to_date
        ------------
         1987-06-01
        (1 row)

Yes, not documented, but I assume the coder was trying to be helpful.

-- 
  Bruce Momjian  <br...@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + It's impossible for everything to be true. +
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
new file mode 100644
index 726a1f4..f4677af
*** a/src/backend/utils/adt/formatting.c
--- b/src/backend/utils/adt/formatting.c
*************** static void dump_node(FormatNode *node, 
*** 964,969 ****
--- 964,970 ----
  
  static char *get_th(char *num, int type);
  static char *str_numth(char *dest, char *num, int type);
+ static int add_era_to_partial_year(int year);
  static int	strspace_len(char *str);
  static int	strdigits_len(char *str);
  static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
*************** is_next_separator(FormatNode *n)
*** 1968,1973 ****
--- 1969,1995 ----
  	return TRUE;				/* some non-digit input (separator) */
  }
  
+ 
+ static int
+ add_era_to_partial_year(int year)
+ {
+ 	/* Force 0-69 into the 2000's */
+ 	if (year < 70)
+ 		return year + 2000;
+ 	/* Force 70-99 into the 1900's */
+ 	else if (year >= 70 && year < 100)
+ 		return year + 1900;
+ 	/* Force 100-499 into the 2000's */
+ 	else if (year >= 100 && year < 500)
+ 		return year + 2000;
+ 	/* Force 500-999 into the 1000's */
+ 	else if (year >= 500 && year < 1000)
+ 		return year + 1000;
+ 	else
+ 		return year;
+ }
+ 
+ 
  static int
  strspace_len(char *str)
  {
*************** DCH_from_char(FormatNode *node, char *in
*** 2931,2972 ****
  			case DCH_YYY:
  			case DCH_IYY:
  				from_char_parse_int(&out->year, &s, n);
  				out->yysz = 3;
- 
- 				/*
- 				 * 3-digit year: '100' ... '999' = 1100 ... 1999 '000' ...
- 				 * '099' = 2000 ... 2099
- 				 */
- 				if (out->year >= 100)
- 					out->year += 1000;
- 				else
- 					out->year += 2000;
  				s += SKIP_THth(n->suffix);
  				break;
  			case DCH_YY:
  			case DCH_IY:
  				from_char_parse_int(&out->year, &s, n);
  				out->yysz = 2;
- 
- 				/*
- 				 * 2-digit year: '00' ... '69'	= 2000 ... 2069 '70' ... '99'
- 				 * = 1970 ... 1999
- 				 */
- 				if (out->year < 70)
- 					out->year += 2000;
- 				else
- 					out->year += 1900;
  				s += SKIP_THth(n->suffix);
  				break;
  			case DCH_Y:
  			case DCH_I:
  				from_char_parse_int(&out->year, &s, n);
  				out->yysz = 1;
- 
- 				/*
- 				 * 1-digit year: always +2000
- 				 */
- 				out->year += 2000;
  				s += SKIP_THth(n->suffix);
  				break;
  			case DCH_RM:
--- 2953,2974 ----
  			case DCH_YYY:
  			case DCH_IYY:
  				from_char_parse_int(&out->year, &s, n);
+ 				out->year = add_era_to_partial_year(out->year);
  				out->yysz = 3;
  				s += SKIP_THth(n->suffix);
  				break;
  			case DCH_YY:
  			case DCH_IY:
  				from_char_parse_int(&out->year, &s, n);
+ 				out->year = add_era_to_partial_year(out->year);
  				out->yysz = 2;
  				s += SKIP_THth(n->suffix);
  				break;
  			case DCH_Y:
  			case DCH_I:
  				from_char_parse_int(&out->year, &s, n);
+ 				out->year = add_era_to_partial_year(out->year);
  				out->yysz = 1;
  				s += SKIP_THth(n->suffix);
  				break;
  			case DCH_RM:
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to