Re: Rectifying wrong Date outputs

From: Bruce Momjian <bruce(at)momjian(dot)us>
To: Piyush Newe <piyush(dot)newe(at)enterprisedb(dot)com>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: Rectifying wrong Date outputs
Date: 2011-09-07 13:51:04
Message-ID: 201109071351.p87Dp4l26991@momjian.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers


Applied, with a function rename. The only odd case we have left is:

test=> select to_date('079', 'YYY');
to_date
------------
1979-01-01
(1 row)

(Note the zero is ignored.) I can't see an easy way to fix this and
continue to be easily documented.

---------------------------------------------------------------------------

Bruce Momjian wrote:
> Bruce Momjian wrote:
> > 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
>
> In an attempt to make the to_date/to_timestamp behavior documentable, I
> have modified the patch to have dates adjust toward the year 2020, and
> added code so if four digits are supplied, we don't do any adjustment.
> Here is the current odd behavior, which is fixed by the patch:
>
> test=> select to_date('222', 'YYY');
> to_date
> ------------
> 2222-01-01
> (1 row)
>
> test=> select to_date('0222', 'YYY');
> to_date
> ------------
> 2222-01-01
> (1 row)
>
> If they supply a full 4-digit year, it seems we should honor that, even
> for YYY. YYYY still does no adjustment, and I doubt we want to change
> that:
>
> test=> select to_date('222', 'YYYY');
> to_date
> ------------
> 0222-01-01
> (1 row)
>
> test=> select to_date('0222', 'YYYY');
> to_date
> ------------
> 0222-01-01
> (1 row)
>
> --
> Bruce Momjian <bruce(at)momjian(dot)us> http://momjian.us
> EnterpriseDB http://enterprisedb.com
>
> + It's impossible for everything to be true. +

[ text/x-diff is unsupported, treating like TEXT/PLAIN ]

> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
> new file mode 100644
> index c03dd6c..282bb0d
> *** a/doc/src/sgml/func.sgml
> --- b/doc/src/sgml/func.sgml
> *************** SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1
> *** 5550,5555 ****
> --- 5550,5564 ----
>
> <listitem>
> <para>
> + If the year format specification is less than four digits, e.g.
> + <literal>YYY</>, and the supplied year is less than four digits,
> + the year will be adjusted to be nearest to year 2020, e.g.
> + <literal>95</> becomes 1995.
> + </para>
> + </listitem>
> +
> + <listitem>
> + <para>
> The <literal>YYYY</literal> conversion from string to <type>timestamp</type> or
> <type>date</type> has a restriction when processing years with more than 4 digits. You must
> use some non-digit character or template after <literal>YYYY</literal>,
> diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
> new file mode 100644
> index 726a1f4..1a3ec1c
> *** 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,1999 ----
> return TRUE; /* some non-digit input (separator) */
> }
>
> +
> + static int
> + add_era_to_partial_year(int year)
> + {
> + /*
> + * Adjust all dates toward 2020; this is effectively what happens
> + * when we assume '70' is 1970 and '69' is 2069.
> + */
> + /* 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-519 into the 2000's */
> + else if (year >= 100 && year < 519)
> + return year + 2000;
> + /* Force 520-999 into the 1000's */
> + else if (year >= 520 && year < 1000)
> + return year + 1000;
> + else
> + return year;
> + }
> +
> +
> static int
> strspace_len(char *str)
> {
> *************** DCH_from_char(FormatNode *node, char *in
> *** 2930,2972 ****
> break;
> 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:
> --- 2956,2978 ----
> break;
> case DCH_YYY:
> case DCH_IYY:
> ! if (from_char_parse_int(&out->year, &s, n) < 4)
> ! out->year = add_era_to_partial_year(out->year);
> out->yysz = 3;
> s += SKIP_THth(n->suffix);
> break;
> case DCH_YY:
> case DCH_IY:
> ! if (from_char_parse_int(&out->year, &s, n) < 4)
> ! out->year = add_era_to_partial_year(out->year);
> out->yysz = 2;
> s += SKIP_THth(n->suffix);
> break;
> case DCH_Y:
> case DCH_I:
> ! if (from_char_parse_int(&out->year, &s, n) < 4)
> ! 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(at)postgresql(dot)org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers

--
Bruce Momjian <bruce(at)momjian(dot)us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ It's impossible for everything to be true. +

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Robert Haas 2011-09-07 13:58:46 Re: [v9.2] Fix Leaky View Problem
Previous Message Robert Haas 2011-09-07 13:46:19 Re: FATAL: lock AccessShareLock on object 0/1260/0 is already held