Thread: Datetime patch

Datetime patch

From
greg@turnstep.com
Date:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
NotDashEscaped: You need GnuPG to verify this message



Here is another attempt at the datetime fix, to stop the ambiguity
for dates like "01-13-2003". I put the > 12 month validation in because
otherwise it is still read in as a "month", but it rolls over.
In other words, '2003-14-03' becomes '2004-02-03'



--
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200306301327





Index: datetime.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
retrieving revision 1.106
diff -c -r1.106 datetime.c
*** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
--- datetime.c    30 Jun 2003 17:26:22 -0000
***************
*** 2379,2440 ****
       ***/
      else if (flen >= 4)
      {
!         *tmask = DTK_M(YEAR);

!         /* already have a year? then see if we can substitute... */
!         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
!             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
          {
!             tm->tm_mday = tm->tm_year;
!             *tmask = DTK_M(DAY);
          }
!
!         tm->tm_year = val;
      }
!
!     /* already have year? then could be month */
!     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
      }
!     /* no year and EuroDates enabled? then could be day */
!     else if ((EuroDates || (fmask & DTK_M(MONTH)))
!              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     else if ((!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     else if ((!(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!
!     /*
!      * Check for 2 or 4 or more digits, but currently we reach here only
!      * if two digits. - thomas 2000-03-28
!      */
!     else if (!(fmask & DTK_M(YEAR))
!              && ((flen >= 4) || (flen == 2)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!
!         /* adjust ONLY if exactly two digits... */
!         *is2digits = (flen == 2);
      }
      else
!         return -1;

      return 0;
  }    /* DecodeNumber() */
--- 2379,2447 ----
       ***/
      else if (flen >= 4)
      {
!         /*
!          * If no month or day, start of YYYY-MM-DD
!          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
!          * If neither case, throw an error
!          */

!         if ((!(fmask & DTK_M(YEAR)))
!             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
!                 ||
!                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
          {
!             *tmask = DTK_M(YEAR);
!             tm->tm_year = val;
          }
!         else
!             return -1;
      }
!     /* If we already have a day AND month, must be a 2-digit year */
!     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!         *is2digits = TRUE;
      }
!
!     /* If we already have a year and a month, must be day */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
!         return -1;
!     /* If have year or day, set the month */
!     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
      {
+         /* Do not allow month to roll over */
+         if (val > 12)
+             return -1;
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     /* If have a month, set the day */
!     else if ((fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* If using EuroDates, this must be the day, otherwise month */
!     else if (EuroDates)
      {
!         *tmask = DTK_M(DAY);
!         tm->tm_mday = val;
      }
      else
!     {
!         /* Do not allow month to roll over */
!         if (val > 12)
!             return -1;
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
!     }

      return 0;
  }    /* DecodeNumber() */




-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iD8DBQE/AHNDvJuQZxSWSsgRApuiAKDB5CcBTxwQBnOdRkzPpLCOKHMmNgCeLJ//
Nz5sB+KyQUWv+MEVLXztBJw=
=kXb4
-----END PGP SIGNATURE-----




Re: Datetime patch

From
Peter Eisentraut
Date:
greg@turnstep.com writes:

> !     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
> !         return -1;

I believe this conditional is incorrect.  The second part should be
DTK_M(DAY).

--
Peter Eisentraut   peter_e@gmx.net


Re: Datetime patch

From
greg@turnstep.com
Date:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
NotDashEscaped: You need GnuPG to verify this message


> I believe this conditional is incorrect.  The second part should be DTK_M(DAY).

Thank you - that is correct. Revised patch below.




Index: datetime.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
retrieving revision 1.106
diff -c -r1.106 datetime.c
*** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
--- datetime.c    30 Jun 2003 17:26:22 -0000
***************
*** 2379,2440 ****
       ***/
      else if (flen >= 4)
      {
!         *tmask = DTK_M(YEAR);

!         /* already have a year? then see if we can substitute... */
!         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
!             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
          {
!             tm->tm_mday = tm->tm_year;
!             *tmask = DTK_M(DAY);
          }
!
!         tm->tm_year = val;
      }
!
!     /* already have year? then could be month */
!     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
      }
!     /* no year and EuroDates enabled? then could be day */
!     else if ((EuroDates || (fmask & DTK_M(MONTH)))
!              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     else if ((!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     else if ((!(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!
!     /*
!      * Check for 2 or 4 or more digits, but currently we reach here only
!      * if two digits. - thomas 2000-03-28
!      */
!     else if (!(fmask & DTK_M(YEAR))
!              && ((flen >= 4) || (flen == 2)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!
!         /* adjust ONLY if exactly two digits... */
!         *is2digits = (flen == 2);
      }
      else
!         return -1;

      return 0;
  }    /* DecodeNumber() */
--- 2379,2447 ----
       ***/
      else if (flen >= 4)
      {
!         /*
!          * If no month or day, start of YYYY-MM-DD
!          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
!          * If neither case, throw an error
!          */

!         if ((!(fmask & DTK_M(YEAR)))
!             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
!                 ||
!                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
          {
!             *tmask = DTK_M(YEAR);
!             tm->tm_year = val;
          }
!         else
!             return -1;
      }
!     /* If we already have a day AND month, must be a 2-digit year */
!     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!         *is2digits = TRUE;
      }
!
!     /* If we already have a year and a month, must be day */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
!         return -1;
!     /* If have year or day, set the month */
!     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
      {
+         /* Do not allow month to roll over */
+         if (val > 12)
+             return -1;
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     /* If have a month, set the day */
!     else if ((fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* If using EuroDates, this must be the day, otherwise month */
!     else if (EuroDates)
      {
!         *tmask = DTK_M(DAY);
!         tm->tm_mday = val;
      }
      else
!     {
!         /* Do not allow month to roll over */
!         if (val > 12)
!             return -1;
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
NotDashEscaped: You need GnuPG to verify this message


> I believe this conditional is incorrect.  The second part should be DTK_M(DAY).

Thank you - that is correct. Revised patch below.




Index: datetime.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
retrieving revision 1.106
diff -c -r1.106 datetime.c
*** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
--- datetime.c    30 Jun 2003 17:26:22 -0000
***************
*** 2379,2440 ****
       ***/
      else if (flen >= 4)
      {
!         *tmask = DTK_M(YEAR);

!         /* already have a year? then see if we can substitute... */
!         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
!             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
          {
!             tm->tm_mday = tm->tm_year;
!             *tmask = DTK_M(DAY);
          }
!
!         tm->tm_year = val;
      }
!
!     /* already have year? then could be month */
!     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
      }
!     /* no year and EuroDates enabled? then could be day */
!     else if ((EuroDates || (fmask & DTK_M(MONTH)))
!              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     else if ((!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     else if ((!(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!
!     /*
!      * Check for 2 or 4 or more digits, but currently we reach here only
!      * if two digits. - thomas 2000-03-28
!      */
!     else if (!(fmask & DTK_M(YEAR))
!              && ((flen >= 4) || (flen == 2)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!
!         /* adjust ONLY if exactly two digits... */
!         *is2digits = (flen == 2);
      }
      else
!         return -1;

      return 0;
  }    /* DecodeNumber() */
--- 2379,2447 ----
       ***/
      else if (flen >= 4)
      {
!         /*
!          * If no month or day, start of YYYY-MM-DD
!          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
!          * If neither case, throw an error
!          */

!         if ((!(fmask & DTK_M(YEAR)))
!             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
!                 ||
!                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
          {
!             *tmask = DTK_M(YEAR);
!             tm->tm_year = val;
          }
!         else
!             return -1;
      }
!     /* If we already have a day AND month, must be a 2-digit year */
!     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!         *is2digits = TRUE;
      }
!
!     /* If we already have a year and a month, must be day */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
!         return -1;
!     /* If have year or day, set the month */
!     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
      {
+         /* Do not allow month to roll over */
+         if (val > 12)
+             return -1;
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     /* If have a month, set the day */
!     else if ((fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* If using EuroDates, this must be the day, otherwise month */
!     else if (EuroDates)
      {
!         *tmask = DTK_M(DAY);
!         tm->tm_mday = val;
      }
      else
!     {
!         /* Do not allow month to roll over */
!         if (val > 12)
!             return -1;
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
!     }

      return 0;
  }    /* DecodeNumber() */





--
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200307010916
-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iD8DBQE/AYp/vJuQZxSWSsgRAvaLAKCG9si7HhJLOnVIbhPKCo/bP6HMOACfRtlq
DivxA+RBcivkseNS/yvzc/0=
=mris
-----END PGP SIGNATURE-----

!     }

      return 0;
  }    /* DecodeNumber() */





--
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200307010916
-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iD8DBQE/AYp/vJuQZxSWSsgRAvaLAKCG9si7HhJLOnVIbhPKCo/bP6HMOACfRtlq
DivxA+RBcivkseNS/yvzc/0=
=mris
-----END PGP SIGNATURE-----




Re: Datetime patch

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

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


greg@turnstep.com wrote:
[ There is text before PGP section. ]
>
[ PGP not available, raw data follows ]
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> NotDashEscaped: You need GnuPG to verify this message
>
>
> > I believe this conditional is incorrect.  The second part should be DTK_M(DAY).
>
> Thank you - that is correct. Revised patch below.
>
>
>
>
> Index: datetime.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
> retrieving revision 1.106
> diff -c -r1.106 datetime.c
> *** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
> --- datetime.c    30 Jun 2003 17:26:22 -0000
> ***************
> *** 2379,2440 ****
>        ***/
>       else if (flen >= 4)
>       {
> !         *tmask = DTK_M(YEAR);
>
> !         /* already have a year? then see if we can substitute... */
> !         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
> !             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
>           {
> !             tm->tm_mday = tm->tm_year;
> !             *tmask = DTK_M(DAY);
>           }
> !
> !         tm->tm_year = val;
>       }
> !
> !     /* already have year? then could be month */
> !     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
>       }
> !     /* no year and EuroDates enabled? then could be day */
> !     else if ((EuroDates || (fmask & DTK_M(MONTH)))
> !              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     else if ((!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     else if ((!(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !
> !     /*
> !      * Check for 2 or 4 or more digits, but currently we reach here only
> !      * if two digits. - thomas 2000-03-28
> !      */
> !     else if (!(fmask & DTK_M(YEAR))
> !              && ((flen >= 4) || (flen == 2)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !
> !         /* adjust ONLY if exactly two digits... */
> !         *is2digits = (flen == 2);
>       }
>       else
> !         return -1;
>
>       return 0;
>   }    /* DecodeNumber() */
> --- 2379,2447 ----
>        ***/
>       else if (flen >= 4)
>       {
> !         /*
> !          * If no month or day, start of YYYY-MM-DD
> !          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
> !          * If neither case, throw an error
> !          */
>
> !         if ((!(fmask & DTK_M(YEAR)))
> !             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
> !                 ||
> !                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
>           {
> !             *tmask = DTK_M(YEAR);
> !             tm->tm_year = val;
>           }
> !         else
> !             return -1;
>       }
> !     /* If we already have a day AND month, must be a 2-digit year */
> !     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !         *is2digits = TRUE;
>       }
> !
> !     /* If we already have a year and a month, must be day */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
> !         return -1;
> !     /* If have year or day, set the month */
> !     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
>       {
> +         /* Do not allow month to roll over */
> +         if (val > 12)
> +             return -1;
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     /* If have a month, set the day */
> !     else if ((fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* If using EuroDates, this must be the day, otherwise month */
> !     else if (EuroDates)
>       {
> !         *tmask = DTK_M(DAY);
> !         tm->tm_mday = val;
>       }
>       else
> !     {
> !         /* Do not allow month to roll over */
> !         if (val > 12)
> !             return -1;
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;-----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> NotDashEscaped: You need GnuPG to verify this message
>
>
> > I believe this conditional is incorrect.  The second part should be DTK_M(DAY).
>
> Thank you - that is correct. Revised patch below.
>
>
>
>
> Index: datetime.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
> retrieving revision 1.106
> diff -c -r1.106 datetime.c
> *** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
> --- datetime.c    30 Jun 2003 17:26:22 -0000
> ***************
> *** 2379,2440 ****
>        ***/
>       else if (flen >= 4)
>       {
> !         *tmask = DTK_M(YEAR);
>
> !         /* already have a year? then see if we can substitute... */
> !         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
> !             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
>           {
> !             tm->tm_mday = tm->tm_year;
> !             *tmask = DTK_M(DAY);
>           }
> !
> !         tm->tm_year = val;
>       }
> !
> !     /* already have year? then could be month */
> !     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
>       }
> !     /* no year and EuroDates enabled? then could be day */
> !     else if ((EuroDates || (fmask & DTK_M(MONTH)))
> !              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     else if ((!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     else if ((!(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !
> !     /*
> !      * Check for 2 or 4 or more digits, but currently we reach here only
> !      * if two digits. - thomas 2000-03-28
> !      */
> !     else if (!(fmask & DTK_M(YEAR))
> !              && ((flen >= 4) || (flen == 2)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !
> !         /* adjust ONLY if exactly two digits... */
> !         *is2digits = (flen == 2);
>       }
>       else
> !         return -1;
>
>       return 0;
>   }    /* DecodeNumber() */
> --- 2379,2447 ----
>        ***/
>       else if (flen >= 4)
>       {
> !         /*
> !          * If no month or day, start of YYYY-MM-DD
> !          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
> !          * If neither case, throw an error
> !          */
>
> !         if ((!(fmask & DTK_M(YEAR)))
> !             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
> !                 ||
> !                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
>           {
> !             *tmask = DTK_M(YEAR);
> !             tm->tm_year = val;
>           }
> !         else
> !             return -1;
>       }
> !     /* If we already have a day AND month, must be a 2-digit year */
> !     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !         *is2digits = TRUE;
>       }
> !
> !     /* If we already have a year and a month, must be day */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
> !         return -1;
> !     /* If have year or day, set the month */
> !     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
>       {
> +         /* Do not allow month to roll over */
> +         if (val > 12)
> +             return -1;
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     /* If have a month, set the day */
> !     else if ((fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* If using EuroDates, this must be the day, otherwise month */
> !     else if (EuroDates)
>       {
> !         *tmask = DTK_M(DAY);
> !         tm->tm_mday = val;
>       }
>       else
> !     {
> !         /* Do not allow month to roll over */
> !         if (val > 12)
> !             return -1;
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
> !     }
>
>       return 0;
>   }    /* DecodeNumber() */
>
>
>
>
>
> --
> Greg Sabino Mullane greg@turnstep.com
> PGP Key: 0x14964AC8 200307010916
> -----BEGIN PGP SIGNATURE-----
> Comment: http://www.turnstep.com/pgp.html
>
> iD8DBQE/AYp/vJuQZxSWSsgRAvaLAKCG9si7HhJLOnVIbhPKCo/bP6HMOACfRtlq
> DivxA+RBcivkseNS/yvzc/0=
> =mris
> -----END PGP SIGNATURE-----
>
> !     }
>
>       return 0;
>   }    /* DecodeNumber() */
>
>
>
>
>
> --
> Greg Sabino Mullane greg@turnstep.com
> PGP Key: 0x14964AC8 200307010916
> -----BEGIN PGP SIGNATURE-----
> Comment: http://www.turnstep.com/pgp.html
>
> iD8DBQE/AYp/vJuQZxSWSsgRAvaLAKCG9si7HhJLOnVIbhPKCo/bP6HMOACfRtlq
> DivxA+RBcivkseNS/yvzc/0=
> =mris
> -----END PGP SIGNATURE-----
>
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: if posting/reading through Usenet, please send an appropriate
>       subscribe-nomail command to majordomo@postgresql.org so that your
>       message can get through to the mailing list cleanly
>
[ Decrypting message... End of raw data. ]

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: Datetime patch

From
Bruce Momjian
Date:
Greg, does this patch still reject dates with month/days out of range?
I see some of the range checks are removed.  Where those checks used
only for the month/date detection?

For example, I don't see the <= 31 checks in there anymore.

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

greg@turnstep.com wrote:
[ There is text before PGP section. ]
>
[ PGP not available, raw data follows ]
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> NotDashEscaped: You need GnuPG to verify this message
>
>
> > I believe this conditional is incorrect.  The second part should be DTK_M(DAY).
>
> Thank you - that is correct. Revised patch below.
>
>
>
>
> Index: datetime.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
> retrieving revision 1.106
> diff -c -r1.106 datetime.c
> *** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
> --- datetime.c    30 Jun 2003 17:26:22 -0000
> ***************
> *** 2379,2440 ****
>        ***/
>       else if (flen >= 4)
>       {
> !         *tmask = DTK_M(YEAR);
>
> !         /* already have a year? then see if we can substitute... */
> !         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
> !             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
>           {
> !             tm->tm_mday = tm->tm_year;
> !             *tmask = DTK_M(DAY);
>           }
> !
> !         tm->tm_year = val;
>       }
> !
> !     /* already have year? then could be month */
> !     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
>       }
> !     /* no year and EuroDates enabled? then could be day */
> !     else if ((EuroDates || (fmask & DTK_M(MONTH)))
> !              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     else if ((!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     else if ((!(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !
> !     /*
> !      * Check for 2 or 4 or more digits, but currently we reach here only
> !      * if two digits. - thomas 2000-03-28
> !      */
> !     else if (!(fmask & DTK_M(YEAR))
> !              && ((flen >= 4) || (flen == 2)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !
> !         /* adjust ONLY if exactly two digits... */
> !         *is2digits = (flen == 2);
>       }
>       else
> !         return -1;
>
>       return 0;
>   }    /* DecodeNumber() */
> --- 2379,2447 ----
>        ***/
>       else if (flen >= 4)
>       {
> !         /*
> !          * If no month or day, start of YYYY-MM-DD
> !          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
> !          * If neither case, throw an error
> !          */
>
> !         if ((!(fmask & DTK_M(YEAR)))
> !             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
> !                 ||
> !                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
>           {
> !             *tmask = DTK_M(YEAR);
> !             tm->tm_year = val;
>           }
> !         else
> !             return -1;
>       }
> !     /* If we already have a day AND month, must be a 2-digit year */
> !     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !         *is2digits = TRUE;
>       }
> !
> !     /* If we already have a year and a month, must be day */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
> !         return -1;
> !     /* If have year or day, set the month */
> !     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
>       {
> +         /* Do not allow month to roll over */
> +         if (val > 12)
> +             return -1;
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     /* If have a month, set the day */
> !     else if ((fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* If using EuroDates, this must be the day, otherwise month */
> !     else if (EuroDates)
>       {
> !         *tmask = DTK_M(DAY);
> !         tm->tm_mday = val;
>       }
>       else
> !     {
> !         /* Do not allow month to roll over */
> !         if (val > 12)
> !             return -1;
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;-----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> NotDashEscaped: You need GnuPG to verify this message
>
>
> > I believe this conditional is incorrect.  The second part should be DTK_M(DAY).
>
> Thank you - that is correct. Revised patch below.
>
>
>
>
> Index: datetime.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
> retrieving revision 1.106
> diff -c -r1.106 datetime.c
> *** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
> --- datetime.c    30 Jun 2003 17:26:22 -0000
> ***************
> *** 2379,2440 ****
>        ***/
>       else if (flen >= 4)
>       {
> !         *tmask = DTK_M(YEAR);
>
> !         /* already have a year? then see if we can substitute... */
> !         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
> !             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
>           {
> !             tm->tm_mday = tm->tm_year;
> !             *tmask = DTK_M(DAY);
>           }
> !
> !         tm->tm_year = val;
>       }
> !
> !     /* already have year? then could be month */
> !     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
>       }
> !     /* no year and EuroDates enabled? then could be day */
> !     else if ((EuroDates || (fmask & DTK_M(MONTH)))
> !              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     else if ((!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     else if ((!(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !
> !     /*
> !      * Check for 2 or 4 or more digits, but currently we reach here only
> !      * if two digits. - thomas 2000-03-28
> !      */
> !     else if (!(fmask & DTK_M(YEAR))
> !              && ((flen >= 4) || (flen == 2)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !
> !         /* adjust ONLY if exactly two digits... */
> !         *is2digits = (flen == 2);
>       }
>       else
> !         return -1;
>
>       return 0;
>   }    /* DecodeNumber() */
> --- 2379,2447 ----
>        ***/
>       else if (flen >= 4)
>       {
> !         /*
> !          * If no month or day, start of YYYY-MM-DD
> !          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
> !          * If neither case, throw an error
> !          */
>
> !         if ((!(fmask & DTK_M(YEAR)))
> !             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
> !                 ||
> !                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
>           {
> !             *tmask = DTK_M(YEAR);
> !             tm->tm_year = val;
>           }
> !         else
> !             return -1;
>       }
> !     /* If we already have a day AND month, must be a 2-digit year */
> !     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !         *is2digits = TRUE;
>       }
> !
> !     /* If we already have a year and a month, must be day */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
> !         return -1;
> !     /* If have year or day, set the month */
> !     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
>       {
> +         /* Do not allow month to roll over */
> +         if (val > 12)
> +             return -1;
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     /* If have a month, set the day */
> !     else if ((fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* If using EuroDates, this must be the day, otherwise month */
> !     else if (EuroDates)
>       {
> !         *tmask = DTK_M(DAY);
> !         tm->tm_mday = val;
>       }
>       else
> !     {
> !         /* Do not allow month to roll over */
> !         if (val > 12)
> !             return -1;
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
> !     }
>
>       return 0;
>   }    /* DecodeNumber() */
>
>
>
>
>
> --
> Greg Sabino Mullane greg@turnstep.com
> PGP Key: 0x14964AC8 200307010916
> -----BEGIN PGP SIGNATURE-----
> Comment: http://www.turnstep.com/pgp.html
>
> iD8DBQE/AYp/vJuQZxSWSsgRAvaLAKCG9si7HhJLOnVIbhPKCo/bP6HMOACfRtlq
> DivxA+RBcivkseNS/yvzc/0=
> =mris
> -----END PGP SIGNATURE-----
>
> !     }
>
>       return 0;
>   }    /* DecodeNumber() */
>
>
>
>
>
> --
> Greg Sabino Mullane greg@turnstep.com
> PGP Key: 0x14964AC8 200307010916
> -----BEGIN PGP SIGNATURE-----
> Comment: http://www.turnstep.com/pgp.html
>
> iD8DBQE/AYp/vJuQZxSWSsgRAvaLAKCG9si7HhJLOnVIbhPKCo/bP6HMOACfRtlq
> DivxA+RBcivkseNS/yvzc/0=
> =mris
> -----END PGP SIGNATURE-----
>
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: if posting/reading through Usenet, please send an appropriate
>       subscribe-nomail command to majordomo@postgresql.org so that your
>       message can get through to the mailing list cleanly
>
[ Decrypting message... End of raw data. ]

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: Datetime patch

From
greg@turnstep.com
Date:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


> Greg, does this patch still reject dates with month/days out of range?
> I see some of the range checks are removed.  Where those checks used
> only for the month/date detection?

Yes and yes. The range rejection in the previous code only allowed an
invalid date to be detected earlier than it would have otherwise. It is
the responsibility of the code following it to determine the validity of
the date we construct here.

Some very minimal checking could be put into place, but it would just be
a small subset of the full checking that occurs later on, so it seemed
better to leave all that logic in one place.


- --
Greg Sabino Mullane greg@turnstep.com
PGP Key: 0x14964AC8 200307251104
-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iD8DBQE/IUhQvJuQZxSWSsgRAthUAJ42qjxGOVRX/Ak/RF5z0KlZu2CUQwCfeYoy
IxRZhzqzNWJM8NeqFKLGesE=
=4rCE
-----END PGP SIGNATURE-----



Re: Datetime patch

From
Bruce Momjian
Date:
OK, I tested the patch and found that it still does proper date validity
checking:

    test=> select '2003-09-31'::date;
    ERROR:  Bad date external representation '2003-09-31'

I did find the following change in the regression tests:

  INSERT INTO TIMESTAMP_TBL VALUES ('02-10-1997 17:32:01 PST');
  INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 PST');
  INSERT INTO TIMESTAMP_TBL VALUES ('97FEB10 5:32:01PM UTC');
+ ERROR:  Bad timestamp external representation '97FEB10 5:32:01PM UTC'
  INSERT INTO TIMESTAMP_TBL VALUES ('97/02/10 17:32:01 UTC');
+ ERROR:  Bad timestamp external representation '97/02/10 17:32:01 UTC'
  INSERT INTO TIMESTAMP_TBL VALUES ('97.041 17:32:01 UTC');
+ ERROR:  Bad timestamp external representation '97.041 17:32:01 UTC'

I didn't know we supported the first format, though it looks nice.

I don't think we want to support the second format, so the error is
correct.

The last format looks like it is year.days_from_year_start, which I also
didn't know we supported, but again looks interesting, if strange.

Comments?

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

greg@turnstep.com wrote:
[ There is text before PGP section. ]
>
[ PGP not available, raw data follows ]
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> NotDashEscaped: You need GnuPG to verify this message
>
>
> > I believe this conditional is incorrect.  The second part should be DTK_M(DAY).
>
> Thank you - that is correct. Revised patch below.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: datetime.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
retrieving revision 1.106
diff -c -r1.106 datetime.c
*** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
--- datetime.c    30 Jun 2003 17:26:22 -0000
***************
*** 2379,2440 ****
       ***/
      else if (flen >= 4)
      {
!         *tmask = DTK_M(YEAR);

!         /* already have a year? then see if we can substitute... */
!         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
!             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
          {
!             tm->tm_mday = tm->tm_year;
!             *tmask = DTK_M(DAY);
          }
!
!         tm->tm_year = val;
      }
!
!     /* already have year? then could be month */
!     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
      }
!     /* no year and EuroDates enabled? then could be day */
!     else if ((EuroDates || (fmask & DTK_M(MONTH)))
!              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     else if ((!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     else if ((!(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!
!     /*
!      * Check for 2 or 4 or more digits, but currently we reach here only
!      * if two digits. - thomas 2000-03-28
!      */
!     else if (!(fmask & DTK_M(YEAR))
!              && ((flen >= 4) || (flen == 2)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!
!         /* adjust ONLY if exactly two digits... */
!         *is2digits = (flen == 2);
      }
      else
!         return -1;

      return 0;
  }    /* DecodeNumber() */
--- 2379,2447 ----
       ***/
      else if (flen >= 4)
      {
!         /*
!          * If no month or day, start of YYYY-MM-DD
!          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
!          * If neither case, throw an error
!          */

!         if ((!(fmask & DTK_M(YEAR)))
!             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
!                 ||
!                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
          {
!             *tmask = DTK_M(YEAR);
!             tm->tm_year = val;
          }
!         else
!             return -1;
      }
!     /* If we already have a day AND month, must be a 2-digit year */
!     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!         *is2digits = TRUE;
      }
!
!     /* If we already have a year and a month, must be day */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
!         return -1;
!     /* If have year or day, set the month */
!     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
      {
+         /* Do not allow month to roll over */
+         if (val > 12)
+             return -1;
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     /* If have a month, set the day */
!     else if ((fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* If using EuroDates, this must be the day, otherwise month */
!     else if (EuroDates)
      {
!         *tmask = DTK_M(DAY);
!         tm->tm_mday = val;
      }
      else
!     {
!         /* Do not allow month to roll over */
!         if (val > 12)
!             return -1;
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
NotDashEscaped: You need GnuPG to verify this message


> I believe this conditional is incorrect.  The second part should be DTK_M(DAY).

Thank you - that is correct. Revised patch below.




Index: datetime.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
retrieving revision 1.106
diff -c -r1.106 datetime.c
*** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
--- datetime.c    30 Jun 2003 17:26:22 -0000
***************
*** 2379,2440 ****
       ***/
      else if (flen >= 4)
      {
!         *tmask = DTK_M(YEAR);

!         /* already have a year? then see if we can substitute... */
!         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
!             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
          {
!             tm->tm_mday = tm->tm_year;
!             *tmask = DTK_M(DAY);
          }
!
!         tm->tm_year = val;
      }
!
!     /* already have year? then could be month */
!     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
      }
!     /* no year and EuroDates enabled? then could be day */
!     else if ((EuroDates || (fmask & DTK_M(MONTH)))
!              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     else if ((!(fmask & DTK_M(MONTH)))
!              && ((val >= 1) && (val <= 12)))
      {
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     else if ((!(fmask & DTK_M(DAY)))
!              && ((val >= 1) && (val <= 31)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!
!     /*
!      * Check for 2 or 4 or more digits, but currently we reach here only
!      * if two digits. - thomas 2000-03-28
!      */
!     else if (!(fmask & DTK_M(YEAR))
!              && ((flen >= 4) || (flen == 2)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!
!         /* adjust ONLY if exactly two digits... */
!         *is2digits = (flen == 2);
      }
      else
!         return -1;

      return 0;
  }    /* DecodeNumber() */
--- 2379,2447 ----
       ***/
      else if (flen >= 4)
      {
!         /*
!          * If no month or day, start of YYYY-MM-DD
!          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
!          * If neither case, throw an error
!          */

!         if ((!(fmask & DTK_M(YEAR)))
!             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
!                 ||
!                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
          {
!             *tmask = DTK_M(YEAR);
!             tm->tm_year = val;
          }
!         else
!             return -1;
      }
!     /* If we already have a day AND month, must be a 2-digit year */
!     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
      {
!         *tmask = DTK_M(YEAR);
!         tm->tm_year = val;
!         *is2digits = TRUE;
      }
!
!     /* If we already have a year and a month, must be day */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
!     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(DAY)))
!         return -1;
!     /* If have year or day, set the month */
!     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
      {
+         /* Do not allow month to roll over */
+         if (val > 12)
+             return -1;
          *tmask = DTK_M(MONTH);
          tm->tm_mon = val;
      }
!     /* If have a month, set the day */
!     else if ((fmask & DTK_M(MONTH)))
      {
          *tmask = DTK_M(DAY);
          tm->tm_mday = val;
      }
!     /* If using EuroDates, this must be the day, otherwise month */
!     else if (EuroDates)
      {
!         *tmask = DTK_M(DAY);
!         tm->tm_mday = val;
      }
      else
!     {
!         /* Do not allow month to roll over */
!         if (val > 12)
!             return -1;
!         *tmask = DTK_M(MONTH);
!         tm->tm_mon = val;
!     }

      return 0;
  }    /* DecodeNumber() */

Re: Datetime patch

From
Tom Lane
Date:
greg@turnstep.com writes:
> Some very minimal checking could be put into place, but it would just be
> a small subset of the full checking that occurs later on, so it seemed
> better to leave all that logic in one place.

Then why not move the rangechecks on month to the full check code too?
(It sure looks like you've lost the defense against month=0, btw.)

BTW, it seems to me that part of the original thread about this issue
included a demonstration that Postgres would sometimes take out-of-range
dates.  That may have just been with respect to to_date() ... but are
you sure there are no other paths for parsing a date spec in the main
date/time code?

            regards, tom lane

Re: Datetime patch

From
Bruce Momjian
Date:
I did a little more looking at this...

>     test=> select '2003-09-31'::date;
>     ERROR:  Bad date external representation '2003-09-31'
>
> I did find the following change in the regression tests:
>
>   INSERT INTO TIMESTAMP_TBL VALUES ('02-10-1997 17:32:01 PST');
>   INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 PST');
>   INSERT INTO TIMESTAMP_TBL VALUES ('97FEB10 5:32:01PM UTC');
> + ERROR:  Bad timestamp external representation '97FEB10 5:32:01PM UTC'

We still accept 10FEB97, which is a more common format.  Seems this now
fails because the year/day detection is not gone, which is fine, I
think.  This works as expected:

    test=> select '01FEB03'::date;
        date
    ------------
     2003-02-01
    (1 row)

>   INSERT INTO TIMESTAMP_TBL VALUES ('97/02/10 17:32:01 UTC');
> + ERROR:  Bad timestamp external representation '97/02/10 17:32:01 UTC'

Again, this one should fail.

Seems these are all related to two-digit years.  This works when
specifying the year as 1997:

test=> select '97/02/10 17:32:01 UTC'::date;
ERROR:  Bad date external representation '97/02/10 17:32:01 UTC'
test=> select '1997/02/10 17:32:01 UTC'::date;
    date
------------
 1997-02-10
(1 row)

>   INSERT INTO TIMESTAMP_TBL VALUES ('97.041 17:32:01 UTC');
> + ERROR:  Bad timestamp external representation '97.041 17:32:01 UTC'

I just played around with this one and got it working:

    test=> select '2004.041'::date;
        date
    ------------
     2004-02-10
    (1 row)

    test=> select '2004.001'::date;
        date
    ------------
     2004-01-01
    (1 row)

Seems you now need a 4-digit year to specify this format.  Also
interesting is that the day must be three digits.

As part of the patch application, I will modify the years to have the
leading '19' so the regression tests will pass.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: Datetime patch

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
>> INSERT INTO TIMESTAMP_TBL VALUES ('97/02/10 17:32:01 UTC');
>> + ERROR:  Bad timestamp external representation '97/02/10 17:32:01 UTC'

> Again, this one should fail.

It should?  I think you're gonna have a lot of unhappy users if there's
no way to persuade Postgres to take that.  This is the same point we
were discussing on the phone earlier.

It might be that the cleanest way to do things is to extend the
input-side DateStyle to a three-way switch, "US" (accept mm/dd/yy)
"Euro" (accept dd/mm/yy) and a third case that accepts yy/mm/dd.
But I do not believe we can get away with deciding that common date
formats aren't common.

            regards, tom lane

Re: Datetime patch

From
Bruce Momjian
Date:
Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> >> INSERT INTO TIMESTAMP_TBL VALUES ('97/02/10 17:32:01 UTC');
> >> + ERROR:  Bad timestamp external representation '97/02/10 17:32:01 UTC'
>
> > Again, this one should fail.
>
> It should?  I think you're gonna have a lot of unhappy users if there's
> no way to persuade Postgres to take that.  This is the same point we
> were discussing on the phone earlier.
>
> It might be that the cleanest way to do things is to extend the
> input-side DateStyle to a three-way switch, "US" (accept mm/dd/yy)
> "Euro" (accept dd/mm/yy) and a third case that accepts yy/mm/dd.
> But I do not believe we can get away with deciding that common date
> formats aren't common.

I have never seen YY/MM/DD, only YYYY-MM-DD.  The huge problem is
deciding out how to decode 03-02-01.  I think we have to require the
century for those.  The big point is that yy-mm-dd only works for years
> 31.  For current dates, you can't specify it because it is already
taken as month first or day first, so I don't see how anyone could be
already using such a format for input.

If we go with a three-way, I am afraid things get confusing because you
have a xx/xx/xx date input that is year first, while I think we have to
insist on xxxx/xx/xx for dates.  We can try it and see what reports we
get.  I don't even know what we would call such a three-way because I
have never seen dates in that format.

If that is the only issue, I can ask on general, but I doubt someone is
going to pipe up.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: Datetime patch

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> I have never seen YY/MM/DD, only YYYY-MM-DD.

You have apparently forgotten what was standard practice just a few
years ago.

> The huge problem is
> deciding out how to decode 03-02-01.  I think we have to require the
> century for those.

No, the entire point is to drive it off datestyle, *not* off the input
value ranges.

> If that is the only issue, I can ask on general, but I doubt someone is
> going to pipe up.

I really dislike the idea that we are going to legislate this behavior
in a three-person discussion on -patches.  The people who will be
screaming about it don't read -patches.

            regards, tom lane

Re: Datetime patch

From
Bruce Momjian
Date:
The newest version of this patch was applied by Tom.  Thanks.

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

greg@turnstep.com wrote:
[ There is text before PGP section. ]
>
[ PGP not available, raw data follows ]
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> NotDashEscaped: You need GnuPG to verify this message
>
>
>
> Here is another attempt at the datetime fix, to stop the ambiguity
> for dates like "01-13-2003". I put the > 12 month validation in because
> otherwise it is still read in as a "month", but it rolls over.
> In other words, '2003-14-03' becomes '2004-02-03'
>
>
>
> --
> Greg Sabino Mullane greg@turnstep.com
> PGP Key: 0x14964AC8 200306301327
>
>
>
>
>
> Index: datetime.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/datetime.c,v
> retrieving revision 1.106
> diff -c -r1.106 datetime.c
> *** datetime.c    25 Jun 2003 21:14:14 -0000    1.106
> --- datetime.c    30 Jun 2003 17:26:22 -0000
> ***************
> *** 2379,2440 ****
>        ***/
>       else if (flen >= 4)
>       {
> !         *tmask = DTK_M(YEAR);
>
> !         /* already have a year? then see if we can substitute... */
> !         if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
> !             && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
>           {
> !             tm->tm_mday = tm->tm_year;
> !             *tmask = DTK_M(DAY);
>           }
> !
> !         tm->tm_year = val;
>       }
> !
> !     /* already have year? then could be month */
> !     else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
>       }
> !     /* no year and EuroDates enabled? then could be day */
> !     else if ((EuroDates || (fmask & DTK_M(MONTH)))
> !              && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     else if ((!(fmask & DTK_M(MONTH)))
> !              && ((val >= 1) && (val <= 12)))
>       {
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     else if ((!(fmask & DTK_M(DAY)))
> !              && ((val >= 1) && (val <= 31)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !
> !     /*
> !      * Check for 2 or 4 or more digits, but currently we reach here only
> !      * if two digits. - thomas 2000-03-28
> !      */
> !     else if (!(fmask & DTK_M(YEAR))
> !              && ((flen >= 4) || (flen == 2)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !
> !         /* adjust ONLY if exactly two digits... */
> !         *is2digits = (flen == 2);
>       }
>       else
> !         return -1;
>
>       return 0;
>   }    /* DecodeNumber() */
> --- 2379,2447 ----
>        ***/
>       else if (flen >= 4)
>       {
> !         /*
> !          * If no month or day, start of YYYY-MM-DD
> !          * If have month AND day, end of MM-DD-YYYY or DD-MM-YYYY
> !          * If neither case, throw an error
> !          */
>
> !         if ((!(fmask & DTK_M(YEAR)))
> !             && (((!(fmask & DTK_M(DAY))) && (!(fmask & DTK_M(MONTH))))
> !                 ||
> !                 ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))))
>           {
> !             *tmask = DTK_M(YEAR);
> !             tm->tm_year = val;
>           }
> !         else
> !             return -1;
>       }
> !     /* If we already have a day AND month, must be a 2-digit year */
> !     else if ((fmask & DTK_M(DAY)) && (fmask & DTK_M(MONTH)))
>       {
> !         *tmask = DTK_M(YEAR);
> !         tm->tm_year = val;
> !         *is2digits = TRUE;
>       }
> !
> !     /* If we already have a year and a month, must be day */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* A year and a day is currently an error, as YYYY-DD-MM is not allowed */
> !     else if ((fmask & DTK_M(YEAR)) && (fmask & DTK_M(MONTH)))
> !         return -1;
> !     /* If have year or day, set the month */
> !     else if ((fmask & DTK_M(YEAR)) || (fmask & DTK_M(DAY)))
>       {
> +         /* Do not allow month to roll over */
> +         if (val > 12)
> +             return -1;
>           *tmask = DTK_M(MONTH);
>           tm->tm_mon = val;
>       }
> !     /* If have a month, set the day */
> !     else if ((fmask & DTK_M(MONTH)))
>       {
>           *tmask = DTK_M(DAY);
>           tm->tm_mday = val;
>       }
> !     /* If using EuroDates, this must be the day, otherwise month */
> !     else if (EuroDates)
>       {
> !         *tmask = DTK_M(DAY);
> !         tm->tm_mday = val;
>       }
>       else
> !     {
> !         /* Do not allow month to roll over */
> !         if (val > 12)
> !             return -1;
> !         *tmask = DTK_M(MONTH);
> !         tm->tm_mon = val;
> !     }
>
>       return 0;
>   }    /* DecodeNumber() */
>
>
>
>
> -----BEGIN PGP SIGNATURE-----
> Comment: http://www.turnstep.com/pgp.html
>
> iD8DBQE/AHNDvJuQZxSWSsgRApuiAKDB5CcBTxwQBnOdRkzPpLCOKHMmNgCeLJ//
> Nz5sB+KyQUWv+MEVLXztBJw=
> =kXb4
> -----END PGP SIGNATURE-----
>
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 8: explain analyze is your friend
>
[ Decrypting message... End of raw data. ]

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073