Re: Rectifying wrong Date outputs

Поиск
Список
Период
Сортировка
От Bruce Momjian
Тема Re: Rectifying wrong Date outputs
Дата
Msg-id 201109071351.p87Dp4l26991@momjian.us
обсуждение исходный текст
Ответ на Re: Rectifying wrong Date outputs  (Bruce Momjian <bruce@momjian.us>)
Список 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@momjian.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@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers

--  Bruce Momjian  <bruce@momjian.us>        http://momjian.us EnterpriseDB
http://enterprisedb.com
 + It's impossible for everything to be true. +


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Robert Haas
Дата:
Сообщение: Re: FATAL: lock AccessShareLock on object 0/1260/0 is already held
Следующее
От: Robert Haas
Дата:
Сообщение: Re: [v9.2] Fix Leaky View Problem