diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 8e353aaf702..072c96d44f4 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -1,5 +1,5 @@ @@ -1823,7 +1823,8 @@ January 8 04:05:06 1999 PST Where: quantity is a number (possibly signed); - unit is second, + unit is microsecond, + millisecond, second, minute, hour, day, week, month, year, decade, century, millennium, diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 63bc43debf9..bfb9564397f 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.160.2.2 2005/12/01 17:56:43 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.160.2.3 2007/05/29 04:59:44 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -1240,6 +1240,7 @@ DecodeDateTime(char **field, int *ftype, int nf, #else *fsec = frac; #endif + tmask = DTK_ALL_SECS_M; } break; @@ -2000,6 +2001,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, #else *fsec = frac; #endif + tmask = DTK_ALL_SECS_M; } break; @@ -3117,6 +3119,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, #else *fsec += (val + fval) * 1e-6; #endif + tmask = DTK_M(MICROSECOND); break; case DTK_MILLISEC: @@ -3125,6 +3128,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, #else *fsec += (val + fval) * 1e-3; #endif + tmask = DTK_M(MILLISECOND); break; case DTK_SECOND: @@ -3134,7 +3138,15 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, #else *fsec += fval; #endif - tmask = DTK_M(SECOND); + /* + * If any subseconds were specified, consider + * this microsecond and millisecond input as + * well. + */ + if (fval == 0) + tmask = DTK_M(SECOND); + else + tmask = DTK_ALL_SECS_M; break; case DTK_MINUTE: diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 48b158e050b..e8e26533766 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.57 2005/10/15 02:49:46 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.57.2.1 2007/05/29 04:59:44 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -99,17 +99,19 @@ #define HOUR 10 #define MINUTE 11 #define SECOND 12 -#define DOY 13 -#define DOW 14 -#define UNITS 15 -#define ADBC 16 +#define MILLISECOND 13 +#define MICROSECOND 14 +#define DOY 15 +#define DOW 16 +#define UNITS 17 +#define ADBC 18 /* these are only for relative dates */ -#define AGO 17 -#define ABS_BEFORE 18 -#define ABS_AFTER 19 +#define AGO 19 +#define ABS_BEFORE 20 +#define ABS_AFTER 21 /* generic fields to help with parsing */ -#define ISODATE 20 -#define ISOTIME 21 +#define ISODATE 22 +#define ISOTIME 23 /* reserved for unrecognized string values */ #define UNKNOWN_FIELD 31 @@ -172,8 +174,10 @@ #define DTK_M(t) (0x01 << (t)) +/* Convenvience: a second, plus any fractional component */ +#define DTK_ALL_SECS_M (DTK_M(SECOND) | DTK_M(MILLISECOND) | DTK_M(MICROSECOND)) #define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)) -#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND)) +#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M) #define MAXDATELEN 51 /* maximum possible length of an input date * string (not counting tr. null) */ diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index d07dd3013dc..17b18a1a0f9 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -241,3 +241,26 @@ SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as @ 7 mons 6 days 5 hours 4 mins 3 secs (1 row) +-- test fractional second input, and detection of duplicate units +SET DATESTYLE = 'ISO'; +SELECT '1 millisecond'::interval, '1 microsecond'::interval, + '500 seconds 99 milliseconds 51 microseconds'::interval; + interval | interval | interval +--------------+-----------------+----------------- + 00:00:00.001 | 00:00:00.000001 | 00:08:20.099051 +(1 row) + +SELECT '3 days 5 milliseconds'::interval; + interval +--------------------- + 3 days 00:00:00.005 +(1 row) + +SELECT '1 second 2 seconds'::interval; -- error +ERROR: invalid input syntax for type interval: "1 second 2 seconds" +SELECT '10 milliseconds 20 milliseconds'::interval; -- error +ERROR: invalid input syntax for type interval: "10 milliseconds 20 milliseconds" +SELECT '5.5 seconds 3 milliseconds'::interval; -- error +ERROR: invalid input syntax for type interval: "5.5 seconds 3 milliseconds" +SELECT '1:20:05 5 microseconds'::interval; -- error +ERROR: invalid input syntax for type interval: "1:20:05 5 microseconds" diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 25e58659e7d..920ce8a09a4 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -76,3 +76,13 @@ select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds"; SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as "7 mons 6 days 5 hours 4 mins 3 seconds"; +-- test fractional second input, and detection of duplicate units +SET DATESTYLE = 'ISO'; +SELECT '1 millisecond'::interval, '1 microsecond'::interval, + '500 seconds 99 milliseconds 51 microseconds'::interval; +SELECT '3 days 5 milliseconds'::interval; + +SELECT '1 second 2 seconds'::interval; -- error +SELECT '10 milliseconds 20 milliseconds'::interval; -- error +SELECT '5.5 seconds 3 milliseconds'::interval; -- error +SELECT '1:20:05 5 microseconds'::interval; -- error \ No newline at end of file