From: Tom Lane Date: Sun, 17 Nov 2013 20:06:50 +0000 (-0500) Subject: Add make_date() and make_time() functions. X-Git-Tag: REL9_4_BETA1~930 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f901bb50e33ad95593bb68f7b3b55eb2e47607dc;p=postgresql Add make_date() and make_time() functions. Pavel Stehule, reviewed by Jeevan Chalke and Atri Sharma --- diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 89f08aff97..a5c808effa 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -6690,6 +6690,48 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); + + + + make_date + + + + make_date(year int, + month int, + day int) + + + + date + + Create date from year, month and day fields + + make_date(2013, 7, 15) + 2013-07-15 + + + + + + make_time + + + + make_time(hour int, + min int, + sec double precision) + + + + time + + Create time from hour, minute and seconds fields + + make_time(8, 15, 23.5) + 08:15:23.5 + + diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 8677520cb6..fe091daec8 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -235,6 +235,43 @@ date_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +/* + * make_date - date constructor + */ +Datum +make_date(PG_FUNCTION_ARGS) +{ + struct pg_tm tm; + DateADT date; + int dterr; + + tm.tm_year = PG_GETARG_INT32(0); + tm.tm_mon = PG_GETARG_INT32(1); + tm.tm_mday = PG_GETARG_INT32(2); + + /* + * Note: we'll reject zero or negative year values. Perhaps negatives + * should be allowed to represent BC years? + */ + dterr = ValidateDate(DTK_DATE_M, false, false, false, &tm); + + if (dterr != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), + errmsg("date field value out of range: %d-%02d-%02d", + tm.tm_year, tm.tm_mon, tm.tm_mday))); + + if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range: %d-%02d-%02d", + tm.tm_year, tm.tm_mon, tm.tm_mday))); + + date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE; + + PG_RETURN_DATEADT(date); +} + /* * Convert reserved date values to string. */ @@ -1208,6 +1245,39 @@ timetypmodout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(anytime_typmodout(false, typmod)); } +/* + * make_time - time constructor + */ +Datum +make_time(PG_FUNCTION_ARGS) +{ + int tm_hour = PG_GETARG_INT32(0); + int tm_min = PG_GETARG_INT32(1); + double sec = PG_GETARG_FLOAT8(2); + TimeADT time; + + /* This should match the checks in DecodeTimeOnly */ + if (tm_hour < 0 || tm_min < 0 || tm_min > MINS_PER_HOUR - 1 || + sec < 0 || sec > SECS_PER_MINUTE || + tm_hour > HOURS_PER_DAY || + /* test for > 24:00:00 */ + (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0))) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), + errmsg("time field value out of range: %d:%02d:%02g", + tm_hour, tm_min, sec))); + + /* This should match tm2time */ +#ifdef HAVE_INT64_TIMESTAMP + time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE) + * USECS_PER_SEC) + rint(sec * USECS_PER_SEC); +#else + time = ((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE) + sec; +#endif + + PG_RETURN_TIMEADT(time); +} + /* time_transform() * Flatten calls to time_scale() and timetz_scale() that solely represent diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 1b8f109992..1c8291c8c5 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -44,8 +44,6 @@ static int DecodeTimezone(char *str, int *tzp); static const datetkn *datebsearch(const char *key, const datetkn *base, int nel); static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, struct pg_tm * tm); -static int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, - struct pg_tm * tm); static void TrimTrailingZeros(char *str); static void AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros); @@ -2270,7 +2268,7 @@ DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, * Check valid year/month/day values, handle BC and DOY cases * Return 0 if okay, a DTERR code if not. */ -static int +int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, struct pg_tm * tm) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index b0286157f7..2d80cc30b7 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201311162 +#define CATALOG_VERSION_NO 201311171 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c5d5d29155..6da4a50efe 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4673,6 +4673,12 @@ DESCR("int8range constructor"); DATA(insert OID = 3946 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 3926 "20 20 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ )); DESCR("int8range constructor"); +/* date, time constructors */ +DATA(insert OID = 3846 ( make_date PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1082 "23 23 23" _null_ _null_ "{year,month,day}" _null_ make_date _null_ _null_ _null_ )); +DESCR("construct date"); +DATA(insert OID = 3847 ( make_time PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1083 "23 23 701" _null_ _null_ "{hour,min,sec}" _null_ make_time _null_ _null_ _null_ )); +DESCR("construct time"); + /* spgist support functions */ DATA(insert OID = 4001 ( spggettuple PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ spggettuple _null_ _null_ _null_ )); DESCR("spgist(internal)"); diff --git a/src/include/utils/date.h b/src/include/utils/date.h index 7c3a1bec23..83a5beacf0 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -97,6 +97,7 @@ extern Datum date_in(PG_FUNCTION_ARGS); extern Datum date_out(PG_FUNCTION_ARGS); extern Datum date_recv(PG_FUNCTION_ARGS); extern Datum date_send(PG_FUNCTION_ARGS); +extern Datum make_date(PG_FUNCTION_ARGS); extern Datum date_eq(PG_FUNCTION_ARGS); extern Datum date_ne(PG_FUNCTION_ARGS); extern Datum date_lt(PG_FUNCTION_ARGS); @@ -154,6 +155,7 @@ extern Datum time_recv(PG_FUNCTION_ARGS); extern Datum time_send(PG_FUNCTION_ARGS); extern Datum timetypmodin(PG_FUNCTION_ARGS); extern Datum timetypmodout(PG_FUNCTION_ARGS); +extern Datum make_time(PG_FUNCTION_ARGS); extern Datum time_transform(PG_FUNCTION_ARGS); extern Datum time_scale(PG_FUNCTION_ARGS); extern Datum time_eq(PG_FUNCTION_ARGS); diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 3cd921a0db..4e59e445ee 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -294,6 +294,9 @@ extern void EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz extern void EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str); extern void EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str); +extern int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, + struct pg_tm * tm); + extern int DecodeSpecial(int field, char *lowtoken, int *val); extern int DecodeUnits(int field, char *lowtoken, int *val); diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out index b603745077..8923f6090a 100644 --- a/src/test/regress/expected/date.out +++ b/src/test/regress/expected/date.out @@ -1184,3 +1184,29 @@ select isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today' f | f | t (1 row) +-- test constructors +select make_date(2013, 7, 15); + make_date +------------ + 07-15-2013 +(1 row) + +select make_time(8, 20, 0.0); + make_time +----------- + 08:20:00 +(1 row) + +-- should fail +select make_date(2013, 2, 30); +ERROR: date field value out of range: 2013-02-30 +select make_date(2013, 13, 1); +ERROR: date field value out of range: 2013-13-01 +select make_date(2013, 11, -1); +ERROR: date field value out of range: 2013-11--1 +select make_date(-44, 3, 15); -- perhaps we should allow this sometime? +ERROR: date field value out of range: -44-03-15 +select make_time(10, 55, 100.1); +ERROR: time field value out of range: 10:55:100.1 +select make_time(24, 0, 2.1); +ERROR: time field value out of range: 24:00:2.1 diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql index d179ddf09b..a62e92a77e 100644 --- a/src/test/regress/sql/date.sql +++ b/src/test/regress/sql/date.sql @@ -276,3 +276,14 @@ select 'infinity'::date, '-infinity'::date; select 'infinity'::date > 'today'::date as t; select '-infinity'::date < 'today'::date as t; select isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today'::date); + +-- test constructors +select make_date(2013, 7, 15); +select make_time(8, 20, 0.0); +-- should fail +select make_date(2013, 2, 30); +select make_date(2013, 13, 1); +select make_date(2013, 11, -1); +select make_date(-44, 3, 15); -- perhaps we should allow this sometime? +select make_time(10, 55, 100.1); +select make_time(24, 0, 2.1);