diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 1525d2a..46140ef 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -475,6 +475,79 @@ timestamptz_in(PG_FUNCTION_ARGS)
 	PG_RETURN_TIMESTAMPTZ(result);
 }
 
+/* to_timestamp(double precision)
+ * Convert UNIX epoch to timestamptz.
+ */
+Datum
+unixtime_timestamptz(PG_FUNCTION_ARGS)
+{
+	float8		seconds = PG_GETARG_FLOAT8(0);
+	TimestampTz result;
+
+	int			is_inf = is_infinite(seconds);
+
+	static const float8 epoch_lbound = (float8)SECS_PER_DAY * -UNIX_EPOCH_JDATE;
+	static const float8 epoch_ubound = (float8)SECS_PER_DAY *
+										 (JULIAN_MAX4STAMPS - UNIX_EPOCH_JDATE);
+
+	if(isnan(seconds))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid value for UNIX epoch \"%f\"", seconds),
+				 errhint("Valid units for this parameter are finite floats, \"Infinity\" and \"-Infinity\"")));
+
+	if (is_inf < 0)
+		TIMESTAMP_NOBEGIN(result);
+
+	else if (is_inf > 0)
+		TIMESTAMP_NOEND(result);
+
+	else
+	{
+		if (seconds < epoch_lbound)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+					 errmsg("UNIX epoch out of range: \"%lf\"", seconds),
+					 errhint("Minimal UNIX epoch value is \"%lf\"", epoch_lbound)));
+
+		if (seconds >= epoch_ubound)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+					 errmsg("UNIX epoch out of range: \"%lf\"", seconds),
+					 errhint("Maximal UNIX epoch value is \"%lf\"", epoch_ubound)));
+
+		/* Convert seconds to Postgres' epoch */
+		seconds -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
+
+#ifdef HAVE_INT64_TIMESTAMP
+		/*
+		 * Here should be just "result = seconds * USECS_PER_SEC;",
+		 * but for big values it leads loosing precision.
+		 * Do as many operations as possible as integers.
+		 */
+		{
+			int64		seconds_int = (int64)seconds;
+			fsec_t		msec = (seconds - seconds_int) * USECS_PER_SEC;
+
+			result = (seconds_int * USECS_PER_SEC) + msec;
+		}
+#else
+		result = seconds;
+#endif
+		/*
+		 * There can't be overflow. Just check timestamp for correctness again
+		 * because of possible loosing precision on float8-int86 conversion.
+		 */
+		if (!IS_VALID_TIMESTAMP(result))
+			ereport(ERROR,
+					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+					 errmsg("UNIX epoch out of range: \"%lf\"",
+							PG_GETARG_FLOAT8(0))));
+	}
+
+	PG_RETURN_TIMESTAMP(result);
+}
+
 /*
  * Try to parse a timezone specification, and return its timezone offset value
  * if it's acceptable.  Otherwise, an error is thrown.
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 62b9125..7f3433a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1202,7 +1202,7 @@ DATA(insert OID = 1154 (  timestamptz_lt   PGNSP PGUID 12 1 0 0 0 f f f t t f i
 DATA(insert OID = 1155 (  timestamptz_le   PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_le _null_ _null_ _null_ ));
 DATA(insert OID = 1156 (  timestamptz_ge   PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_ge _null_ _null_ _null_ ));
 DATA(insert OID = 1157 (  timestamptz_gt   PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "1184 1184" _null_ _null_ _null_ _null_ _null_ timestamp_gt _null_ _null_ _null_ ));
-DATA(insert OID = 1158 (  to_timestamp	   PGNSP PGUID 14 1 0 0 0 f f f f t f i s 1 0 1184 "701" _null_ _null_ _null_ _null_ _null_ "select (''epoch''::pg_catalog.timestamptz + $1 * ''1 second''::pg_catalog.interval)" _null_ _null_ _null_ ));
+DATA(insert OID = 1158 (  to_timestamp	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 1184 "701" _null_ _null_ _null_ _null_ _null_ unixtime_timestamptz _null_ _null_ _null_ ));
 DESCR("convert UNIX epoch to timestamptz");
 DATA(insert OID = 3995 (  timestamp_zone_transform PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ timestamp_zone_transform _null_ _null_ _null_ ));
 DESCR("transform a time zone adjustment");
diff --git a/src/include/datatype/timestamp.h b/src/include/datatype/timestamp.h
index 12a2a8a..9b21ba8 100644
--- a/src/include/datatype/timestamp.h
+++ b/src/include/datatype/timestamp.h
@@ -105,6 +105,20 @@ typedef struct
 #define USECS_PER_MINUTE INT64CONST(60000000)
 #define USECS_PER_SEC	INT64CONST(1000000)
 
+#ifdef HAVE_INT64_TIMESTAMP
+#define MIN_TIMESTAMP	INT64CONST(-211813488000000000)
+/* == (0 - POSTGRES_EPOCH_JDATE) * 86400 * USECS_PER_SEC */
+#define MAX_TIMESTAMP	INT64CONST(9223371331200000000)
+/* == (JULIAN_MAX4STAMPS - POSTGRES_EPOCH_JDATE) * 86400 * USECS_PER_SEC */
+#else
+#define MIN_TIMESTAMP	-211813488000.0
+/* == (0 - POSTGRES_EPOCH_JDATE) * 86400 */
+#define MAX_TIMESTAMP	9223371331200.0
+/* == (JULIAN_MAX4STAMPS - POSTGRES_EPOCH_JDATE) * 86400 */
+#endif
+
+#define IS_VALID_TIMESTAMP(j)  ((MIN_TIMESTAMP <= (j)) && ((j) < MAX_TIMESTAMP))
+
 /*
  * We allow numeric timezone offsets up to 15:59:59 either way from Greenwich.
  * Currently, the record holders for wackiest offsets in actual use are zones
@@ -156,6 +170,7 @@ typedef struct
 #define JULIAN_MINMONTH (11)
 #define JULIAN_MINDAY (24)
 #define JULIAN_MAXYEAR (5874898)
+#define JULIAN_MAXYEAR4STAMPS (294277)
 
 #define IS_VALID_JULIAN(y,m,d) \
 	(((y) > JULIAN_MINYEAR \
@@ -164,10 +179,22 @@ typedef struct
 		   || ((m) == JULIAN_MINMONTH && (d) >= JULIAN_MINDAY)))) \
 	 && (y) < JULIAN_MAXYEAR)
 
+#define IS_VALID_JULIAN4STAMPS(y,m,d) \
+	(((y) > JULIAN_MINYEAR \
+	  || ((y) == JULIAN_MINYEAR && \
+		  ((m) > JULIAN_MINMONTH \
+		   || ((m) == JULIAN_MINMONTH && (d) >= JULIAN_MINDAY)))) \
+	 && (y) < JULIAN_MAXYEAR4STAMPS)
+
 #define JULIAN_MAX (2147483494) /* == date2j(JULIAN_MAXYEAR, 1, 1) */
+#define JULIAN_MAX4STAMPS (109203528) /* == date2j(JULIAN_MAXYEAR4STAMPS, 1, 1) */
 
 /* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */
 #define UNIX_EPOCH_JDATE		2440588 /* == date2j(1970, 1, 1) */
 #define POSTGRES_EPOCH_JDATE	2451545 /* == date2j(2000, 1, 1) */
 
+#define IS_VALID_DATE(j) \
+	((-POSTGRES_EPOCH_JDATE <= (j)) && \
+	 ((j) < (JULIAN_MAX - POSTGRES_EPOCH_JDATE)))
+
 #endif   /* DATATYPE_TIMESTAMP_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index fbead3a..e18211d 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -124,6 +124,7 @@ 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 unixtime_timestamptz(PG_FUNCTION_ARGS);
 
 extern Datum timestamptz_eq_timestamp(PG_FUNCTION_ARGS);
 extern Datum timestamptz_ne_timestamp(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 40a2254..8d7f981 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2232,6 +2232,66 @@ SELECT make_timestamptz(2007, 12, 9, 3, 0, 0, 'VET');
  Sun Dec 09 07:30:00 2007 UTC
 (1 row)
 
+SELECT to_timestamp(         0);          -- 1970-01-01 00:00:00+00
+         to_timestamp         
+------------------------------
+ Thu Jan 01 00:00:00 1970 UTC
+(1 row)
+
+SELECT to_timestamp( 946684800);          -- 2000-01-01 00:00:00+00
+         to_timestamp         
+------------------------------
+ Sat Jan 01 00:00:00 2000 UTC
+(1 row)
+
+SELECT to_timestamp(1262349296.7890123);  -- 2010-01-01 12:34:56.789012+00
+            to_timestamp             
+-------------------------------------
+ Fri Jan 01 12:34:56.789012 2010 UTC
+(1 row)
+
+-- corner cases
+SELECT to_timestamp(-1e20::float8);       -- Error:  UNIX epoch out of range
+ERROR:  UNIX epoch out of range: "-100000000000000000000.000000"
+HINT:  Minimal UNIX epoch value is "-210866803200.000000"
+SELECT to_timestamp(-210866803200.0625);  -- Error:  UNIX epoch out of range
+ERROR:  UNIX epoch out of range: "-210866803200.062500"
+HINT:  Minimal UNIX epoch value is "-210866803200.000000"
+SELECT to_timestamp(-210866803200);       --   4714-11-24 00:00:00+00 BC
+          to_timestamp           
+---------------------------------
+ Mon Nov 24 00:00:00 4714 UTC BC
+(1 row)
+
+-- Maximal allowed input value
+SELECT to_timestamp(9224318015999.875);   -- 294276-12-31 23:59:59.875+00
+            to_timestamp            
+------------------------------------
+ Sun Dec 31 23:59:59.875 294276 UTC
+(1 row)
+
+SELECT to_timestamp(9224318016000);       -- Error:  UNIX epoch out of range
+ERROR:  UNIX epoch out of range: "9224318016000.000000"
+HINT:  Maximal UNIX epoch value is "9224318016000.000000"
+SELECT to_timestamp(1e20::float8);        -- Error:  UNIX epoch out of range
+ERROR:  UNIX epoch out of range: "100000000000000000000.000000"
+HINT:  Maximal UNIX epoch value is "9224318016000.000000"
+-- infinity values
+SELECT to_timestamp(' Infinity'::float);
+ to_timestamp 
+--------------
+ infinity
+(1 row)
+
+SELECT to_timestamp('-Infinity'::float);
+ to_timestamp 
+--------------
+ -infinity
+(1 row)
+
+SELECT to_timestamp('NaN'::float);
+ERROR:  invalid value for UNIX epoch "nan"
+HINT:  Valid units for this parameter are finite floats, "Infinity" and "-Infinity"
 SET TimeZone to 'Europe/Moscow';
 SELECT '2011-03-26 21:00:00 UTC'::timestamptz;
          timestamptz          
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index f4b455e..fdad178 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -379,6 +379,23 @@ SELECT '2007-12-09 04:00:00'::timestamp AT TIME ZONE 'VET';
 SELECT make_timestamptz(2007, 12, 9, 2, 0, 0, 'VET');
 SELECT make_timestamptz(2007, 12, 9, 3, 0, 0, 'VET');
 
+SELECT to_timestamp(         0);          -- 1970-01-01 00:00:00+00
+SELECT to_timestamp( 946684800);          -- 2000-01-01 00:00:00+00
+SELECT to_timestamp(1262349296.7890123);  -- 2010-01-01 12:34:56.789012+00
+-- corner cases
+SELECT to_timestamp(-1e20::float8);       -- Error:  UNIX epoch out of range
+SELECT to_timestamp(-210866803200.0625);  -- Error:  UNIX epoch out of range
+SELECT to_timestamp(-210866803200);       --   4714-11-24 00:00:00+00 BC
+-- Maximal allowed input value
+SELECT to_timestamp(9224318015999.875);   -- 294276-12-31 23:59:59.875+00
+SELECT to_timestamp(9224318016000);       -- Error:  UNIX epoch out of range
+SELECT to_timestamp(1e20::float8);        -- Error:  UNIX epoch out of range
+-- infinity values
+SELECT to_timestamp(' Infinity'::float);
+SELECT to_timestamp('-Infinity'::float);
+SELECT to_timestamp('NaN'::float);
+
+
 SET TimeZone to 'Europe/Moscow';
 
 SELECT '2011-03-26 21:00:00 UTC'::timestamptz;
