Обсуждение: Datetime patch
-----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-----
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
-----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-----
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
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
-----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-----
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() */
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
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
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
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
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
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