make_interval ??
Hello
we have defined interface date, time, timestamp constructors.
There is a question if we would to have some similar for interval type?
As different from time, timestamp there we can use a zero as defaults.
So constructor should to look like:
CREATE OR REPLACE FUNCTION make_interval(years int DEFAULT 0, months int
DEFAULT 0, ...)
and usage:
SELECT make_interval(years := 2)
SELECT make_interval(days := 14)
Is there a interest for this (or similar) function?
Regards
Pavel
Pavel,
So constructor should to look like:
CREATE OR REPLACE FUNCTION make_interval(years int DEFAULT 0, months int
DEFAULT 0, ...)and usage:
SELECT make_interval(years := 2)
SELECT make_interval(days := 14)Is there a interest for this (or similar) function?
It would certainly make our Python users happy.
And for that matter would get rid of this kind of stupid thing in stored
procedure code:
time_ahead := ( interval '1 minute' * var_skip );
So, +1 for the feature.
--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Import Notes
Reply to msg id not found: WMdccb84e9878fdfc7c68ddaff1738d58d73d060a90335c6dd1be19c62fe52e0fbec0252e1b44e1e32fe299919bd11519d@asav-1.01.com
On 21/12/13 06:29, Josh Berkus wrote:
Pavel,
So constructor should to look like:
CREATE OR REPLACE FUNCTION make_interval(years int DEFAULT 0, months int
DEFAULT 0, ...)and usage:
SELECT make_interval(years := 2)
SELECT make_interval(days := 14)Is there a interest for this (or similar) function?
It would certainly make our Python users happy.
And for that matter would get rid of this kind of stupid thing in stored
procedure code:time_ahead := ( interval '1 minute' * var_skip );
So, +1 for the feature.
What about leap years?
Cheers,
Gavin
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 12/20/2013 03:09 PM, Gavin Flower wrote:
What about leap years?
What about them?
--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Import Notes
Reply to msg id not found: WMa6d4ab07aa72a23fd3c5a06c5095c628f97dd7c94fe8badc9752f418f7772424bbe31751a6d769872bb45332742f8347@asav-1.01.com
On 21/12/13 13:40, Josh Berkus wrote:
On 12/20/2013 03:09 PM, Gavin Flower wrote:
What about leap years?
What about them?
some years have 365 days others have 366, so how any days in an interval
of 2 years?, 4 years?
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 12/20/2013 04:44 PM, Gavin Flower wrote:
On 21/12/13 13:40, Josh Berkus wrote:
On 12/20/2013 03:09 PM, Gavin Flower wrote:
What about leap years?
What about them?
some years have 365 days others have 366, so how any days in an interval
of 2 years?, 4 years?
Your question isn't relevant to this patch. It's not defining the
interval type, just creating an alternate constructor for it.
(the answer is, it depends on what timestamp you're adding it to ...)
--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Import Notes
Reply to msg id not found: WMde4ec2f1130fb1dffcc3ecfc21a72299f724010e50554972a7b8a43db7b596795137acfc3aa9298b8f0c728e46b1797b@asav-2.01.com
Hello
here is patch
postgres=# select make_interval(years := 1, months := 6);
make_interval
---------------
1 year 6 mons
(1 row)
postgres=# select make_interval(weeks := 3);
make_interval
---------------
21 days
(1 row)
postgres=# select make_interval(days := 10);
make_interval
---------------
10 days
(1 row)
postgres=# select make_interval(hours := 2, mins := 10, secs := 25.33);
make_interval
---------------
02:10:25.33
(1 row)
Regards
Pavel
2013/12/21 Josh Berkus <josh@agliodbs.com>
Show quoted text
On 12/20/2013 04:44 PM, Gavin Flower wrote:
On 21/12/13 13:40, Josh Berkus wrote:
On 12/20/2013 03:09 PM, Gavin Flower wrote:
What about leap years?
What about them?
some years have 365 days others have 366, so how any days in an interval
of 2 years?, 4 years?Your question isn't relevant to this patch. It's not defining the
interval type, just creating an alternate constructor for it.(the answer is, it depends on what timestamp you're adding it to ...)
--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
make_timestamp-2013-12-21-01.patchtext/x-patch; charset=US-ASCII; name=make_timestamp-2013-12-21-01.patchDownload
commit a49f69a9b1fb3337a8bf5cf3f198b6505c8f4115
Author: Pavel Stehule <pavel.stehule@gooddata.com>
Date: Thu Dec 12 18:08:47 2013 +0100
initial implementation make_timestamp
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index a411e3a..cf7fcfe 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -6714,6 +6714,32 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
<row>
<entry>
<indexterm>
+ <primary>make_interval</primary>
+ </indexterm>
+ <literal>
+ <function>
+ make_interval(<parameter>years</parameter> <type>int</type> DEFAULT 0,
+ <parameter>months</parameter> <type>int</type> DEFAULT 0,
+ <parameter>weeks</parameter> <type>int</type> DEFAULT 0,
+ <parameter>days</parameter> <type>int</type> DEFAULT 0,
+ <parameter>hours</parameter> <type>int</type> DEFAULT 0,
+ <parameter>mins</parameter> <type>int</type> DEFAULT 0,
+ <parameter>secs</parameter> <type>double precision</type> DEFAULT 0.0)
+ </function>
+ </literal>
+ </entry>
+ <entry><type>interval</type></entry>
+ <entry>
+ Create interval from years, months, weeks, days, hours, minutes and
+ seconds fields
+ </entry>
+ <entry><literal>make_interval(days := 10)</literal></entry>
+ <entry><literal>10 days</literal></entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>make_time</primary>
</indexterm>
<literal>
@@ -6735,6 +6761,78 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
<row>
<entry>
<indexterm>
+ <primary>make_timetz</primary>
+ </indexterm>
+ <literal>
+ <function>
+ make_timetz(<parameter>hour</parameter> <type>int</type>,
+ <parameter>min</parameter> <type>int</type>,
+ <parameter>sec</parameter> <type>double precision</type>,
+ <optional> <parameter>timezone</parameter> <type>text</type> </optional>)
+ </function>
+ </literal>
+ </entry>
+ <entry><type>time with time zone</type></entry>
+ <entry>
+ Create time with time zone from hour, minute and seconds fields
+ </entry>
+ <entry><literal>make_timetz(8, 15, 23.5)</literal></entry>
+ <entry><literal>08:15:23.5+01</literal></entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>make_timestamp</primary>
+ </indexterm>
+ <literal>
+ <function>
+ make_timestamp(<parameter>year</parameter> <type>int</type>,
+ <parameter>month</parameter> <type>int</type>,
+ <parameter>day</parameter> <type>int</type>,
+ <parameter>hour</parameter> <type>int</type>,
+ <parameter>min</parameter> <type>int</type>,
+ <parameter>sec</parameter> <type>double precision</type>)
+ </function>
+ </literal>
+ </entry>
+ <entry><type>timestamp</type></entry>
+ <entry>
+ Create timestamp from year, month, day, hour, minute and seconds fields
+ </entry>
+ <entry><literal>make_timestamp(1-23, 7, 15, 8, 15, 23.5)</literal></entry>
+ <entry><literal>2013-07-15 08:15:23.5</literal></entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>make_timestamptz</primary>
+ </indexterm>
+ <literal>
+ <function>
+ make_timestamptz(<parameter>year</parameter> <type>int</type>,
+ <parameter>month</parameter> <type>int</type>,
+ <parameter>day</parameter> <type>int</type>,
+ <parameter>hour</parameter> <type>int</type>,
+ <parameter>min</parameter> <type>int</type>,
+ <parameter>sec</parameter> <type>double precision</type>,
+ <optional> <parameter>timezone</parameter> <type>text</type> </optional>)
+ </function>
+ </literal>
+ </entry>
+ <entry><type>timestamp with time zone</type></entry>
+ <entry>
+ Create timestamp with time zone from year, month, day, hour, minute
+ and seconds fields
+ </entry>
+ <entry><literal>make_timestamp(1-23, 7, 15, 8, 15, 23.5)</literal></entry>
+ <entry><literal>2013-07-15 08:15:23.5+01</literal></entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>now</primary>
</indexterm>
<literal><function>now()</function></literal>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 575a40f..ebe1bff 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -796,3 +796,9 @@ CREATE OR REPLACE FUNCTION
CREATE OR REPLACE FUNCTION
json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100 AS 'json_populate_recordset';
+
+CREATE OR REPLACE FUNCTION
+ make_interval(years int4 DEFAULT 0, months int4 DEFAULT 0, weeks int4 DEFAULT 0,
+ days int4 DEFAULT 0, hours int4 DEFAULT 0, mins int4 DEFAULT 0,
+ secs double precision DEFAULT 0.0)
+ RETURNS interval STRICT IMMUTABLE LANGUAGE internal AS 'make_interval';
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index fe091da..fedf261 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -1246,38 +1246,113 @@ timetypmodout(PG_FUNCTION_ARGS)
}
/*
- * make_time - time constructor
+ * time constructor used for make_time and make_timetz
*/
-Datum
-make_time(PG_FUNCTION_ARGS)
+static TimeADT
+make_time_internal(int hour, int min, double sec)
{
- 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 ||
+ if (hour < 0 || min < 0 || min > MINS_PER_HOUR - 1 ||
sec < 0 || sec > SECS_PER_MINUTE ||
- tm_hour > HOURS_PER_DAY ||
+ hour > HOURS_PER_DAY ||
/* test for > 24:00:00 */
- (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0)))
+ (hour == HOURS_PER_DAY && (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)));
+ hour, min, sec)));
/* This should match tm2time */
#ifdef HAVE_INT64_TIMESTAMP
- time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
+ time = (((hour * MINS_PER_HOUR + 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;
+ time = ((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE) + sec;
#endif
+ return time;
+}
+
+/*
+ * make_time - time constructor
+ */
+Datum
+make_time(PG_FUNCTION_ARGS)
+{
+ TimeADT time;
+
+ time = make_time_internal(PG_GETARG_INT32(0), /* hour */
+ PG_GETARG_INT32(1), /* min */
+ PG_GETARG_FLOAT8(2)); /* sec */
+
PG_RETURN_TIMEADT(time);
}
+/*
+ * make_timetz - timetz constructor
+ */
+Datum
+make_timetz(PG_FUNCTION_ARGS)
+{
+ TimeADT time;
+
+ time = make_time_internal(PG_GETARG_INT32(0), /* hour */
+ PG_GETARG_INT32(1), /* min */
+ PG_GETARG_FLOAT8(2)); /* sec */
+
+ PG_RETURN_DATUM(DirectFunctionCall1(time_timetz,
+ TimeADTGetDatum(time)));
+}
+
+/*
+ * make_timetz_at_timezone_text - timetz constructor
+ */
+Datum
+make_timetz_at_timezone(PG_FUNCTION_ARGS)
+{
+ TimeADT t;
+ TimeTzADT *result;
+ text *zone;
+ int tz;
+ char tzname[TZ_STRLEN_MAX + 1];
+ char *lowzone;
+ int type,
+ val;
+
+ t = make_time_internal(PG_GETARG_INT32(0), /* hour */
+ PG_GETARG_INT32(1), /* min */
+ PG_GETARG_FLOAT8(2)); /* sec */
+
+ zone = PG_GETARG_TEXT_PP(3);
+
+ text_to_cstring_buffer(zone, tzname, sizeof(tzname));
+ if (DecodeTimezone(tzname, &tz) != 0)
+ {
+ lowzone = downcase_truncate_identifier(tzname,
+ strlen(tzname),
+ false);
+ type = DecodeSpecial(0, lowzone, &val);
+
+ if (type == TZ || type == DTZ)
+ tz = val * MINS_PER_HOUR;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("time zone \"%s\" not recognized", tzname)));
+ tz = 0; /* keep compiler quiet */
+ }
+ }
+
+ result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+
+ result->time = t;
+ result->zone = tz;
+
+ PG_RETURN_TIMETZADT_P(result);
+}
/* time_transform()
* Flatten calls to time_scale() and timetz_scale() that solely represent
@@ -2679,6 +2754,9 @@ timetz_part(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+
+
+
/* timetz_zone()
* Encode time with time zone type with specified time zone.
* Applies DST rules as of the current date.
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 1c8291c..1017ce7 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -40,7 +40,6 @@ static int DecodeNumberField(int len, char *str,
struct pg_tm * tm, fsec_t *fsec, bool *is2digits);
static int DecodeTime(char *str, int fmask, int range,
int *tmask, struct pg_tm * tm, fsec_t *fsec);
-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);
@@ -1000,7 +999,6 @@ DecodeDateTime(char **field, int *ftype, int nf,
val = strtoi(field[i], &cp, 10);
if (errno == ERANGE)
return DTERR_FIELD_OVERFLOW;
-
/*
* only a few kinds are allowed to have an embedded
* decimal
@@ -2707,7 +2705,7 @@ DecodeNumberField(int len, char *str, int fmask,
*
* NB: this must *not* ereport on failure; see commands/variable.c.
*/
-static int
+int
DecodeTimezone(char *str, int *tzp)
{
int tz;
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index c3c71b7..ec0dbf7 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -477,6 +477,181 @@ timestamptz_in(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMPTZ(result);
}
+/*
+ * used as workhorse of make_timestamp and make_timestamptz
+ */
+static Timestamp
+make_timestamp_internal(int year, int month, int day,
+ int hour, int min, double sec)
+{
+ struct pg_tm tm;
+ TimeOffset date;
+ TimeOffset time;
+ int dterr;
+ Timestamp result;
+
+ tm.tm_year = year;
+ tm.tm_mon = month;
+ tm.tm_mday = day;
+
+ /*
+ * 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",
+ year, month, day)));
+
+ 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",
+ year, month, day)));
+
+ date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
+
+ /* This should match the checks in DecodeTimeOnly */
+ if (hour < 0 || min < 0 || min > MINS_PER_HOUR - 1 ||
+ sec < 0 || sec > SECS_PER_MINUTE ||
+ hour > HOURS_PER_DAY ||
+ /* test for > 24:00:00 */
+ (hour == HOURS_PER_DAY && (min > 0 || sec > 0)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
+ errmsg("time field value out of range: %d:%02d:%02g",
+ hour, min, sec)));
+
+ /* This should match tm2time */
+#ifdef HAVE_INT64_TIMESTAMP
+ time = (((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
+ * USECS_PER_SEC) + rint(sec * USECS_PER_SEC);
+
+ result = date * USECS_PER_DAY + time;
+ /* check for major overflow */
+ if ((result - time) / USECS_PER_DAY != date)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
+ year, month, day,
+ hour, min, sec)));
+
+ /* check for just-barely overflow (okay except time-of-day wraps) */
+ /* caution: we want to allow 1999-12-31 24:00:00 */
+ if ((result < 0 && date > 0) ||
+ (result > 0 && date < -1))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
+ year, month, day,
+ hour, min, sec)));
+#else
+ time = ((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE) + sec;
+ result = date * SECS_PER_DAY + time;
+#endif
+
+ return result;
+}
+
+/*
+ * make_timestamp() - timestamp constructor
+ */
+Datum
+make_timestamp(PG_FUNCTION_ARGS)
+{
+ Timestamp result;
+
+ result = make_timestamp_internal(PG_GETARG_INT32(0), /* year */
+ PG_GETARG_INT32(1), /* month */
+ PG_GETARG_INT32(2), /* mday */
+ PG_GETARG_INT32(3), /* hour */
+ PG_GETARG_INT32(4), /* min */
+ PG_GETARG_FLOAT8(5)); /* sec */
+
+ PG_RETURN_TIMESTAMP(result);
+}
+
+/*
+ * make_timestamptz() - timestamp with time zone constructor
+ */
+Datum
+make_timestamptz(PG_FUNCTION_ARGS)
+{
+ Timestamp result;
+
+ result = make_timestamp_internal(PG_GETARG_INT32(0), /* year */
+ PG_GETARG_INT32(1), /* month */
+ PG_GETARG_INT32(2), /* mday */
+ PG_GETARG_INT32(3), /* hour */
+ PG_GETARG_INT32(4), /* min */
+ PG_GETARG_FLOAT8(5)); /* sec */
+
+ PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result));
+}
+
+Datum
+make_timestamptz_at_timezone(PG_FUNCTION_ARGS)
+{
+ Timestamp timestamp;
+ text *zone;
+ int tz;
+ char tzname[TZ_STRLEN_MAX + 1];
+ char *lowzone;
+ int type,
+ val;
+ struct pg_tm tt,
+ *tm = &tt;
+ fsec_t fsec;
+
+ timestamp = make_timestamp_internal(PG_GETARG_INT32(0), /* year */
+ PG_GETARG_INT32(1), /* month */
+ PG_GETARG_INT32(2), /* mday */
+ PG_GETARG_INT32(3), /* hour */
+ PG_GETARG_INT32(4), /* min */
+ PG_GETARG_FLOAT8(5)); /* sec */
+
+ if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+
+ zone = PG_GETARG_TEXT_PP(6);
+
+ text_to_cstring_buffer(zone, tzname, sizeof(tzname));
+
+ if (DecodeTimezone(tzname, &tz) != 0)
+ {
+ lowzone = downcase_truncate_identifier(tzname,
+ strlen(tzname),
+ false);
+ type = DecodeSpecial(0, lowzone, &val);
+
+ if (type == TZ || type == DTZ)
+ tz = val * MINS_PER_HOUR;
+ else
+ {
+ pg_tz *tzp;
+
+ tzp = pg_tzset(tzname);
+
+ if (tzp)
+ tz = DetermineTimeZoneOffset(tm, tzp);
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("time zone \"%s\" not recognized", tzname)));
+ tz = 0; /* keep compiler quiet */
+ }
+ }
+ }
+
+ PG_RETURN_TIMESTAMPTZ((TimestampTz) dt2local(timestamp, -tz));
+}
+
/* timestamptz_out()
* Convert a timestamp to external form.
*/
@@ -1214,6 +1389,40 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
}
}
+/*
+ * make_interval - numeric Interval constructor
+ */
+Datum
+make_interval(PG_FUNCTION_ARGS)
+{
+ Interval *result;
+
+ int32 years = PG_GETARG_INT32(0);
+ int32 months = PG_GETARG_INT32(1);
+ int32 weeks = PG_GETARG_INT32(2);
+ int32 days = PG_GETARG_INT32(3);
+ int32 hours = PG_GETARG_INT32(4);
+ int32 mins= PG_GETARG_INT32(5);
+ double secs = PG_GETARG_FLOAT8(6);
+
+ result = (Interval *) palloc(sizeof(Interval));
+ result->month = years * MONTHS_PER_YEAR + months;
+ result->day = weeks * 7 + days;
+
+#ifdef HAVE_INT64_TIMESTAMP
+ result->time = (((((hours * INT64CONST(60)) +
+ mins) * INT64CONST(60)) +
+ secs) * USECS_PER_SEC);
+#else
+ result->time = (((hours * (double) MINS_PER_HOUR) +
+ mins) * (double) SECS_PER_MINUTE) +
+ secs;
+#endif
+
+ AdjustIntervalForTypmod(result, -1);
+
+ PG_RETURN_INTERVAL_P(result);
+}
/* EncodeSpecialTimestamp()
* Convert reserved timestamp data type to string.
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 0117500..667b44b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4675,11 +4675,23 @@ 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_ ));
+/* date, time, timestamp constructors */
+DATA(insert OID = 3969 ( 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,mday}" _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_ ));
+DATA(insert OID = 3970 ( 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");
+DATA(insert OID = 3971 ( make_timetz PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 1266 "23 23 701" _null_ _null_ "{hour,min,sec}" _null_ make_timetz _null_ _null_ _null_ ));
+DESCR("construct time with time zone");
+DATA(insert OID = 3972 ( make_timetz PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 1266 "23 23 701 25" _null_ _null_ "{hour,min,sec,timezone}" _null_ make_timetz_at_timezone _null_ _null_ _null_ ));
+DESCR("construct time with time zone");
+DATA(insert OID = 3973 ( make_timestamp PGNSP PGUID 12 1 0 0 0 f f f f t f i 6 0 1114 "23 23 23 23 23 701" _null_ _null_ "{year,month,mday,hour,min,sec}" _null_ make_timestamp _null_ _null_ _null_ ));
+DESCR("construct timestamp");
+DATA(insert OID = 3974 ( make_timestamptz PGNSP PGUID 12 1 0 0 0 f f f f t f s 6 0 1184 "23 23 23 23 23 701" _null_ _null_ "{year,month,mday,hour,min,sec}" _null_ make_timestamptz _null_ _null_ _null_ ));
+DESCR("construct timestamp with time zone");
+DATA(insert OID = 3975 ( make_timestamptz PGNSP PGUID 12 1 0 0 0 f f f f t f s 7 0 1184 "23 23 23 23 23 701 25" _null_ _null_ "{year,month,mday,hour,min,sec,timezone}" _null_ make_timestamptz_at_timezone _null_ _null_ _null_ ));
+DESCR("construct timestamp with time zone");
+DATA(insert OID = 3976 ( make_interval PGNSP PGUID 12 1 0 0 0 f f f f t f i 7 0 1186 "23 23 23 23 23 23 701" _null_ _null_ "{years,months,weeks,days,hours,mins,secs}" _null_ make_interval _null_ _null_ _null_ ));
+DESCR("construct interval");
/* 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_ ));
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 83a5bea..96a25f3 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -156,6 +156,8 @@ 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 make_timetz(PG_FUNCTION_ARGS);
+extern Datum make_timetz_at_timezone(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 4e59e44..37f5667 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -276,6 +276,7 @@ extern int ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
extern int DecodeDateTime(char **field, int *ftype,
int nf, int *dtype,
struct pg_tm * tm, fsec_t *fsec, int *tzp);
+extern int DecodeTimezone(char *str, int *tzp);
extern int DecodeTimeOnly(char **field, int *ftype,
int nf, int *dtype,
struct pg_tm * tm, fsec_t *fsec, int *tzp);
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index e7a53ff..bf03203 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -121,6 +121,10 @@ extern Datum timestamp_gt_timestamptz(PG_FUNCTION_ARGS);
extern Datum timestamp_ge_timestamptz(PG_FUNCTION_ARGS);
extern Datum timestamp_cmp_timestamptz(PG_FUNCTION_ARGS);
+extern Datum make_timestamp(PG_FUNCTION_ARGS);
+extern Datum make_timestamptz(PG_FUNCTION_ARGS);
+extern Datum make_timestamptz_at_timezone(PG_FUNCTION_ARGS);
+
extern Datum timestamptz_eq_timestamp(PG_FUNCTION_ARGS);
extern Datum timestamptz_ne_timestamp(PG_FUNCTION_ARGS);
extern Datum timestamptz_lt_timestamp(PG_FUNCTION_ARGS);
@@ -151,6 +155,7 @@ extern Datum interval_larger(PG_FUNCTION_ARGS);
extern Datum interval_justify_interval(PG_FUNCTION_ARGS);
extern Datum interval_justify_hours(PG_FUNCTION_ARGS);
extern Datum interval_justify_days(PG_FUNCTION_ARGS);
+extern Datum make_interval(PG_FUNCTION_ARGS);
extern Datum timestamp_trunc(PG_FUNCTION_ARGS);
extern Datum interval_trunc(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index 3bf2211..cf7d5f0 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -800,3 +800,34 @@ select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) a
t
(1 row)
+-- numeric constructor
+select make_interval(years := 2);
+ make_interval
+---------------
+ @ 2 years
+(1 row)
+
+select make_interval(years := 1, months := 6);
+ make_interval
+-----------------
+ @ 1 year 6 mons
+(1 row)
+
+select make_interval(weeks := 3);
+ make_interval
+---------------
+ @ 21 days
+(1 row)
+
+select make_interval(days := 10);
+ make_interval
+---------------
+ @ 10 days
+(1 row)
+
+select make_interval(hours := 2, mins := 10, secs := 25.33);
+ make_interval
+------------------------------
+ @ 2 hours 10 mins 25.33 secs
+(1 row)
+
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index db2cfe6..a092fc2 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -1585,3 +1585,10 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
| 2001 1 1 1 1 1 1
(65 rows)
+-- timestamp numeric fields constructor
+SELECT make_timestamp(2014,12,28,6,30,45.887);
+ make_timestamp
+------------------------------
+ Sun Dec 28 06:30:45.887 2014
+(1 row)
+
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 9f4f7a4..1758966 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -1697,3 +1697,23 @@ SELECT * FROM TIMESTAMPTZ_TST ORDER BY a;
--Cleanup
DROP TABLE TIMESTAMPTZ_TST;
+-- timestamp numeric constructor
+SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33);
+ make_timestamptz
+---------------------------------
+ Sun Jul 15 08:15:55.33 1973 PDT
+(1 row)
+
+SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '+2');
+ make_timestamptz
+---------------------------------
+ Sat Jul 14 23:15:55.33 1973 PDT
+(1 row)
+
+-- should be true
+SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '+2') = '1973-07-15 08:15:55.33+02'::timestamptz;
+ ?column?
+----------
+ t
+(1 row)
+
diff --git a/src/test/regress/expected/timetz.out b/src/test/regress/expected/timetz.out
index 4391131..7b6868d 100644
--- a/src/test/regress/expected/timetz.out
+++ b/src/test/regress/expected/timetz.out
@@ -93,3 +93,56 @@ ERROR: operator does not exist: time with time zone + time with time zone
LINE 1: SELECT f1 + time with time zone '00:01' AS "Illegal" FROM TI...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
+-- check time with time zone numeric constructor
+SELECT make_timetz(8, 15, 55.333);
+ make_timetz
+-----------------
+ 08:15:55.333-08
+(1 row)
+
+SELECT make_timetz(8, 15, 55.333, 'HKT');
+ make_timetz
+-----------------
+ 08:15:55.333+08
+(1 row)
+
+SELECT make_timetz(8, 15, 55.333, '+1:30');
+ make_timetz
+--------------------
+ 08:15:55.333+01:30
+(1 row)
+
+SELECT make_timetz(8, 15, 55.333, '-1:30');
+ make_timetz
+--------------------
+ 08:15:55.333-01:30
+(1 row)
+
+-- should be true
+SELECT make_timetz(8, 15, 55.333) = '8:15:55.333'::timetz;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT make_timetz(8, 15, 55.333, 'HKT') = '8:15:55.333 HKT'::timetz;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT make_timetz(8, 15, 55.333, '+1:30') = '8:15:55.333 +1:30'::timetz;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT make_timetz(8, 15, 55.333, '-1:30') = '8:15:55.333 -1:30'::timetz;
+ ?column?
+----------
+ t
+(1 row)
+
+-- only abbrev are supported, should fails
+SELECT make_timetz(8, 15, 55.333, 'Europe/Prague');
+ERROR: time zone "Europe/Prague" not recognized
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index f1da4c2..9b68c0b 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -259,3 +259,10 @@ select interval '0:0:0.7', interval '@ 0.70 secs', interval '0.7 seconds';
-- check that '30 days' equals '1 month' according to the hash function
select '30 days'::interval = '1 month'::interval as t;
select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) as t;
+
+-- numeric constructor
+select make_interval(years := 2);
+select make_interval(years := 1, months := 6);
+select make_interval(weeks := 3);
+select make_interval(days := 10);
+select make_interval(hours := 2, mins := 10, secs := 25.33);
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index c4ed4ee..b22cd48 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -222,3 +222,6 @@ SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID')
SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
FROM TIMESTAMP_TBL;
+
+-- timestamp numeric fields constructor
+SELECT make_timestamp(2014,12,28,6,30,45.887);
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index 4eef62e..04cdae9 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -254,3 +254,10 @@ INSERT INTO TIMESTAMPTZ_TST VALUES(4, '1000000312 23:58:48 IST');
SELECT * FROM TIMESTAMPTZ_TST ORDER BY a;
--Cleanup
DROP TABLE TIMESTAMPTZ_TST;
+
+-- timestamp numeric constructor
+SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33);
+SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '+2');
+
+-- should be true
+SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '+2') = '1973-07-15 08:15:55.33+02'::timestamptz;
diff --git a/src/test/regress/sql/timetz.sql b/src/test/regress/sql/timetz.sql
index c41686a..5181d4b 100644
--- a/src/test/regress/sql/timetz.sql
+++ b/src/test/regress/sql/timetz.sql
@@ -40,3 +40,19 @@ SELECT f1 AS "Ten" FROM TIMETZ_TBL WHERE f1 >= '00:00-07';
-- where we do mixed-type arithmetic. - thomas 2000-12-02
SELECT f1 + time with time zone '00:01' AS "Illegal" FROM TIMETZ_TBL;
+
+-- check time with time zone numeric constructor
+SELECT make_timetz(8, 15, 55.333);
+SELECT make_timetz(8, 15, 55.333, 'HKT');
+SELECT make_timetz(8, 15, 55.333, '+1:30');
+SELECT make_timetz(8, 15, 55.333, '-1:30');
+
+-- should be true
+SELECT make_timetz(8, 15, 55.333) = '8:15:55.333'::timetz;
+SELECT make_timetz(8, 15, 55.333, 'HKT') = '8:15:55.333 HKT'::timetz;
+SELECT make_timetz(8, 15, 55.333, '+1:30') = '8:15:55.333 +1:30'::timetz;
+SELECT make_timetz(8, 15, 55.333, '-1:30') = '8:15:55.333 -1:30'::timetz;
+
+-- only abbrev are supported, should fails
+SELECT make_timetz(8, 15, 55.333, 'Europe/Prague');
+