make_interval ??

Started by Pavel Stehuleabout 12 years ago7 messages
#1Pavel Stehule
pavel.stehule@gmail.com

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

#2Josh Berkus
josh@agliodbs.com
In reply to: Pavel Stehule (#1)
Re: make_interval ??

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

#3Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Josh Berkus (#2)
Re: make_interval ??

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

#4Josh Berkus
josh@agliodbs.com
In reply to: Pavel Stehule (#1)
Re: make_interval ??

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

#5Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Josh Berkus (#4)
Re: make_interval ??

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

#6Josh Berkus
josh@agliodbs.com
In reply to: Pavel Stehule (#1)
Re: make_interval ??

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

#7Pavel Stehule
pavel.stehule@gmail.com
In reply to: Josh Berkus (#6)
1 attachment(s)
Re: make_interval ??

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');
+