diff --git a/doc/src/sgml/datetime.sgml b/doc/src/sgml/datetime.sgml
index 0e42e34fd69a67df6834b3012dbef3408231fab2..9610dc36c5cada51e4d6c12f98bb37c2fcf75e24 100644
--- a/doc/src/sgml/datetime.sgml
+++ b/doc/src/sgml/datetime.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.45 2005/01/09 18:58:10 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.46 2005/06/15 00:34:08 momjian Exp $
 -->
 
  <appendix id="datetime-appendix">
@@ -990,7 +990,9 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.45 2005/01/09 18:58:10 tgl Exp
    <para>
     <xref linkend="datetime-timezone-set-table"> shows the time zone
     names recognized by <productname>PostgreSQL</productname> as valid
-    settings for the <xref linkend="guc-timezone"> parameter.  Note that
+    settings for the <xref linkend="guc-timezone"> parameter, and as
+	parameters to the <literal>AT TIME ZONE function</> (see 
+	<xref linkend="functions-datetime-zoneconvert">).  Note that
     these names are conceptually as well as practically different from
     the names shown in <xref linkend="datetime-timezone-input-table">:
     most of these names imply a local daylight-savings time rule, whereas
@@ -1004,7 +1006,7 @@ $PostgreSQL: pgsql/doc/src/sgml/datetime.sgml,v 2.45 2005/01/09 18:58:10 tgl Exp
    </para>
 
     <table id="datetime-timezone-set-table">
-     <title>Time Zone Names for Setting <varname>timezone</></title>
+     <title>Time Zone Names for Setting <varname>timezone</> and <literal>AT TIME ZONE</></title>
      <tgroup cols="1">
       <thead>
        <row>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4fe5e1d6fca6ff6f20a23436c1d6090668d47087..38e2c4a7001b958b664136e3837459ab815a8543 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.256 2005/06/14 23:47:39 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.257 2005/06/15 00:34:08 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -5679,10 +5679,7 @@ SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40');
     specified either as a text string (e.g., <literal>'PST'</literal>)
     or as an interval (e.g., <literal>INTERVAL '-08:00'</literal>).
     In the text case, the available zone names are those shown in
-    <xref linkend="datetime-timezone-input-table">.  (It would be useful
-    to support the more general names shown in
-    <xref linkend="datetime-timezone-set-table">, but this is not yet
-    implemented.)
+    <xref linkend="datetime-timezone-set-table">.
    </para>
 
    <para>
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 5371c64250a7b24f5763c3a108a174f5dd5ba477..abc6155594f137f242268fb8deff5d36c26da894 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.109 2005/05/26 02:04:13 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.110 2005/06/15 00:34:08 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 #include <ctype.h>
 #include <limits.h>
 #include <float.h>
+#include <time.h> 
 
 #include "access/hash.h"
 #include "libpq/pqformat.h"
@@ -724,7 +725,7 @@ timestamp_date(PG_FUNCTION_ARGS)
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		PG_RETURN_NULL();
 
-	if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+	if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -767,7 +768,7 @@ timestamptz_date(PG_FUNCTION_ARGS)
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		PG_RETURN_NULL();
 
-	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -1327,7 +1328,7 @@ timestamp_time(PG_FUNCTION_ARGS)
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		PG_RETURN_NULL();
 
-	if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+	if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -1364,7 +1365,7 @@ timestamptz_time(PG_FUNCTION_ARGS)
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		PG_RETURN_NULL();
 
-	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -2247,7 +2248,7 @@ timestamptz_timetz(PG_FUNCTION_ARGS)
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		PG_RETURN_NULL();
 
-	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -2463,53 +2464,60 @@ timetz_part(PG_FUNCTION_ARGS)
 
 /* timetz_zone()
  * Encode time with time zone type with specified time zone.
+ * Applies DST rules as of the current date.
  */
 Datum
 timetz_zone(PG_FUNCTION_ARGS)
 {
 	text	   *zone = PG_GETARG_TEXT_P(0);
-	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
+	TimeTzADT  *t = PG_GETARG_TIMETZADT_P(1);
 	TimeTzADT  *result;
 	int			tz;
-	int			type,
-				val;
-	char	   *lowzone;
-
-	lowzone = downcase_truncate_identifier(VARDATA(zone),
-										   VARSIZE(zone) - VARHDRSZ,
-										   false);
-
-	type = DecodeSpecial(0, lowzone, &val);
+	char        tzname[TZ_STRLEN_MAX];
+	int         len;
+	pg_tz	   *tzp;
+	struct pg_tm *tm;
+	pg_time_t   now;
+
+	/* Find the specified timezone */ 
+	len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ);
+	memcpy(tzname,VARDATA(zone),len);
+	tzname[len]=0;
+	tzp = pg_tzset(tzname);
+	if (!tzp) {
+		ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+		 	 errmsg("time zone \"%s\" not recognized", tzname)));
+		PG_RETURN_NULL();
+	}
 
-	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+	/* Get the offset-from-GMT that is valid today for the selected zone */
+	if ((now = time(NULL)) < 0 ||
+	    (tm = pg_localtime(&now, tzp)) == NULL) {
+	   	ereport(ERROR,
+	   		(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("could not determine current time")));
+		PG_RETURN_NULL();
+	}
 
-	if (type == TZ || type == DTZ)
-	{
-		tz = val * 60;
+	result = (TimeTzADT *)palloc(sizeof(TimeTzADT));
+	
+	tz = -tm->tm_gmtoff;
 #ifdef HAVE_INT64_TIMESTAMP
-		result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
-		while (result->time < INT64CONST(0))
-			result->time += USECS_PER_DAY;
-		while (result->time >= USECS_PER_DAY)
-			result->time -= USECS_PER_DAY;
+	result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
+	while (result->time < INT64CONST(0))
+		result->time += USECS_PER_DAY;
+	while (result->time >= USECS_PER_DAY)
+		result->time -= USECS_PER_DAY;
 #else
-		result->time = time->time + (time->zone - tz);
-		while (result->time < 0)
-			result->time += SECS_PER_DAY;
-		while (result->time >= SECS_PER_DAY)
-			result->time -= SECS_PER_DAY;
+	result->time = t->time + (t->zone - tz);
+	while (result->time < 0)
+		result->time += SECS_PER_DAY;
+	while (result->time >= SECS_PER_DAY)
+		result->time -= SECS_PER_DAY;
 #endif
 
-		result->zone = tz;
-	}
-	else
-	{
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("time zone \"%s\" not recognized", lowzone)));
-
-		PG_RETURN_NULL();
-	}
+	result->zone = tz;
 
 	PG_RETURN_TIMETZADT_P(result);
 }	/* timetz_zone() */
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 68a73bbb6fc793866818e41bf8e6c6f08619ec9f..e4de64444eae73e631f6f4d069fa28aef825973f 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1,7 +1,7 @@
 /* -----------------------------------------------------------------------
  * formatting.c
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.87 2005/05/25 21:40:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.88 2005/06/15 00:34:08 momjian Exp $
  *
  *
  *	 Portions Copyright (c) 1999-2005, PostgreSQL Global Development Group
@@ -2910,7 +2910,7 @@ timestamp_to_char(PG_FUNCTION_ARGS)
 
 	ZERO_tmtc(&tmtc);
 
-	if (timestamp2tm(dt, NULL, tmtcTm(&tmtc), &tmtcFsec(&tmtc), NULL) != 0)
+	if (timestamp2tm(dt, NULL, tmtcTm(&tmtc), &tmtcFsec(&tmtc), NULL, NULL) != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -2935,7 +2935,7 @@ timestamptz_to_char(PG_FUNCTION_ARGS)
 
 	ZERO_tmtc(&tmtc);
 
-	if (timestamp2tm(dt, &tz, tmtcTm(&tmtc), &tmtcFsec(&tmtc), &tmtcTzn(&tmtc)) != 0)
+	if (timestamp2tm(dt, &tz, tmtcTm(&tmtc), &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
index bebd3f9d3256096719094b43502e3cf8341cd2ef..d712d9f8b46e42b9118bd9cae39381b0894b8236 100644
--- a/src/backend/utils/adt/nabstime.c
+++ b/src/backend/utils/adt/nabstime.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.132 2005/05/26 02:04:13 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.133 2005/06/15 00:34:08 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -556,7 +556,7 @@ timestamp_abstime(PG_FUNCTION_ARGS)
 		result = NOSTART_ABSTIME;
 	else if (TIMESTAMP_IS_NOEND(timestamp))
 		result = NOEND_ABSTIME;
-	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)
+	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
 	{
 		tz = DetermineLocalTimeZone(tm);
 		result = tm2abstime(tm, tz);
@@ -632,7 +632,7 @@ timestamptz_abstime(PG_FUNCTION_ARGS)
 		result = NOSTART_ABSTIME;
 	else if (TIMESTAMP_IS_NOEND(timestamp))
 		result = NOEND_ABSTIME;
-	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)
+	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
 		result = tm2abstime(tm, 0);
 	else
 	{
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 37308d7451a769316f4040e65103a77744d0c899..035a422bfcc33361afb4ef2769e4107c434a7096 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.125 2005/06/14 21:04:40 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.126 2005/06/15 00:34:09 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -142,7 +142,7 @@ timestamp_out(PG_FUNCTION_ARGS)
 
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		EncodeSpecialTimestamp(timestamp, buf);
-	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)
+	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
 		EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf);
 	else
 		ereport(ERROR,
@@ -178,7 +178,7 @@ timestamp_recv(PG_FUNCTION_ARGS)
 	/* rangecheck: see if timestamp_out would like it */
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		 /* ok */ ;
-	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -381,7 +381,7 @@ timestamptz_out(PG_FUNCTION_ARGS)
 
 	if (TIMESTAMP_NOT_FINITE(dt))
 		EncodeSpecialTimestamp(dt, buf);
-	else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)
+	else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0)
 		EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf);
 	else
 		ereport(ERROR,
@@ -419,7 +419,7 @@ timestamptz_recv(PG_FUNCTION_ARGS)
 	/* rangecheck: see if timestamptz_out would like it */
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		 /* ok */ ;
-	else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+	else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
@@ -984,9 +984,12 @@ dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
  * Returns:
  *	 0 on success
  *	-1 on out of range
+ *
+ * If attimezone is NULL, the global timezone (including possblly brute forced
+ * timezone) will be used.
  */
 int
-timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn)
+timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn, pg_tz *attimezone)
 {
 	Timestamp date;
 	Timestamp	time;
@@ -997,7 +1000,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn
 	 * specified. Go ahead and rotate to the local time zone since we will
 	 * later bypass any calls which adjust the tm fields.
 	 */
-	if (HasCTZSet && (tzp != NULL))
+	if ((attimezone==NULL) && HasCTZSet && (tzp != NULL))
 	{
 #ifdef HAVE_INT64_TIMESTAMP
 		dt -= CTimeZone * USECS_PER_SEC;
@@ -1050,7 +1053,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn
 	 * We have a brute force time zone per SQL99? Then use it without
 	 * change since we have already rotated to the time zone.
 	 */
-	if (HasCTZSet)
+	if ((attimezone==NULL) && HasCTZSet)
 	{
 		*tzp = CTimeZone;
 		tm->tm_isdst = 0;
@@ -1081,7 +1084,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn
 	utime = (pg_time_t) dt;
 	if ((Timestamp) utime == dt)
 	{
-		struct pg_tm *tx = pg_localtime(&utime, global_timezone);
+		struct pg_tm *tx = pg_localtime(&utime, (attimezone!=NULL)?attimezone:global_timezone);
 
 		tm->tm_year = tx->tm_year + 1900;
 		tm->tm_mon = tx->tm_mon + 1;
@@ -1926,7 +1929,7 @@ timestamp_pl_interval(PG_FUNCTION_ARGS)
 					   *tm = &tt;
 			fsec_t		fsec;
 
-			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 				ereport(ERROR,
 						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 						 errmsg("timestamp out of range")));
@@ -2005,7 +2008,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
 					   *tm = &tt;
 			fsec_t		fsec;
 
-			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 				ereport(ERROR,
 						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 						 errmsg("timestamp out of range")));
@@ -2332,8 +2335,8 @@ timestamp_age(PG_FUNCTION_ARGS)
 
 	result = (Interval *) palloc(sizeof(Interval));
 
-	if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0 &&
-		timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0)
+	if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 &&
+		timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0)
 	{
 		fsec = (fsec1 - fsec2);
 		tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
@@ -2446,8 +2449,8 @@ timestamptz_age(PG_FUNCTION_ARGS)
 
 	result = (Interval *) palloc(sizeof(Interval));
 
-	if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn) == 0 &&
-		timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn) == 0)
+	if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn, NULL) == 0 &&
+		timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn, NULL) == 0)
 	{
 		fsec = fsec1 - fsec2;
 		tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
@@ -2750,7 +2753,7 @@ timestamp_trunc(PG_FUNCTION_ARGS)
 
 	if (type == UNITS)
 	{
-		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
@@ -2881,7 +2884,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
 
 	if (type == UNITS)
 	{
-		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
@@ -3271,7 +3274,7 @@ timestamp_part(PG_FUNCTION_ARGS)
 
 	if (type == UNITS)
 	{
-		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
@@ -3405,7 +3408,7 @@ timestamp_part(PG_FUNCTION_ARGS)
 					 * convert to timestamptz to produce consistent
 					 * results
 					 */
-					if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+					if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 						ereport(ERROR,
 						   (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 							errmsg("timestamp out of range")));
@@ -3425,7 +3428,7 @@ timestamp_part(PG_FUNCTION_ARGS)
 					break;
 				}
 			case DTK_DOW:
-				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 							 errmsg("timestamp out of range")));
@@ -3433,7 +3436,7 @@ timestamp_part(PG_FUNCTION_ARGS)
 				break;
 
 			case DTK_DOY:
-				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 							 errmsg("timestamp out of range")));
@@ -3496,7 +3499,7 @@ timestamptz_part(PG_FUNCTION_ARGS)
 
 	if (type == UNITS)
 	{
-		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
@@ -3631,7 +3634,7 @@ timestamptz_part(PG_FUNCTION_ARGS)
 				break;
 
 			case DTK_DOW:
-				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 							 errmsg("timestamp out of range")));
@@ -3639,7 +3642,7 @@ timestamptz_part(PG_FUNCTION_ARGS)
 				break;
 
 			case DTK_DOY:
-				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 							 errmsg("timestamp out of range")));
@@ -3815,38 +3818,40 @@ timestamp_zone(PG_FUNCTION_ARGS)
 {
 	text	   *zone = PG_GETARG_TEXT_P(0);
 	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
-	TimestampTz result;
+	Timestamp result;
 	int			tz;
-	int			type,
-				val;
-	char	   *lowzone;
+	pg_tz      *tzp;
+	char        tzname[TZ_STRLEN_MAX+1];
+	int         len;
+	struct pg_tm tm;
+	fsec_t      fsec;
 
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		PG_RETURN_TIMESTAMPTZ(timestamp);
 
-	lowzone = downcase_truncate_identifier(VARDATA(zone),
-										   VARSIZE(zone) - VARHDRSZ,
-										   false);
-
-	type = DecodeSpecial(0, lowzone, &val);
-
-	if (type == TZ || type == DTZ)
-	{
-		tz = -(val * 60);
-
-		result = dt2local(timestamp, tz);
-	}
-	else
-	{
+	/* Find the specified timezone? */
+	len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ);
+	memcpy(tzname,VARDATA(zone),len);
+	tzname[len] = 0;
+	tzp = pg_tzset(tzname);
+	if (!tzp) {
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("time zone \"%s\" not recognized",
-						lowzone)));
-
+				 errmsg("time zone \"%s\" not recognised",
+				        tzname)));
 		PG_RETURN_NULL();
 	}
 
-	PG_RETURN_TIMESTAMPTZ(result);
+	/* Apply the timezone change */
+	if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
+	    tm2timestamp(&tm, fsec, NULL, &result) != 0) {
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("could not convert to time zone \"%s\"",
+				        tzname)));
+		PG_RETURN_NULL();
+	}
+	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result));
 }	/* timestamp_zone() */
 
 /* timestamp_izone()
@@ -3906,7 +3911,7 @@ timestamp2timestamptz(Timestamp timestamp)
 
 	else
 	{
-		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
+		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) !=0)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
@@ -3941,7 +3946,7 @@ timestamptz_timestamp(PG_FUNCTION_ARGS)
 
 	else
 	{
-		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
+		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) !=0)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
@@ -3950,7 +3955,6 @@ timestamptz_timestamp(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
 	}
-
 	PG_RETURN_TIMESTAMP(result);
 }
 
@@ -3966,31 +3970,34 @@ timestamptz_zone(PG_FUNCTION_ARGS)
 	Timestamp	result;
 
 	int			tz;
-	int			type,
-				val;
-	char	   *lowzone;
+	pg_tz	   *tzp;
+	char        tzname[TZ_STRLEN_MAX];
+	int         len;
+	struct pg_tm tm;
+	fsec_t      fsec = 0;
 
 	if (TIMESTAMP_NOT_FINITE(timestamp))
 		PG_RETURN_NULL();
 
-	lowzone = downcase_truncate_identifier(VARDATA(zone),
-										   VARSIZE(zone) - VARHDRSZ,
-										   false);
-
-	type = DecodeSpecial(0, lowzone, &val);
+	/* Find the specified zone */
+	len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ);
+	memcpy(tzname,VARDATA(zone),len);
+	tzname[len] = 0;
+	tzp = pg_tzset(tzname);
 
-	if (type == TZ || type == DTZ)
-	{
-		tz = val * 60;
+	if (!tzp) {
+		ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("time zone \"%s\" not recognized", tzname)));
 
-		result = dt2local(timestamp, tz);
+		PG_RETURN_NULL();
 	}
-	else
-	{
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("time zone \"%s\" not recognized", lowzone)));
 
+	if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
+	    tm2timestamp(&tm, fsec, NULL, &result)) { 
+		ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			errmsg("could not to convert to time zone \"%s\"", tzname)));
 		PG_RETURN_NULL();
 	}
 
diff --git a/src/include/pgtime.h b/src/include/pgtime.h
index b3322234e19ce26939d09f6c4ab9f7a0ef824151..c36a871fb33c6aca3156d836ffcb9d5448a93e76 100644
--- a/src/include/pgtime.h
+++ b/src/include/pgtime.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/include/pgtime.h,v 1.7 2005/04/19 03:13:59 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/include/pgtime.h,v 1.8 2005/06/15 00:34:09 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,4 +57,8 @@ extern bool tz_acceptable(pg_tz *tz);
 extern const char *pg_get_timezone_name(pg_tz *tz);
 
 extern pg_tz *global_timezone;
+
+/* Maximum length of a timezone name */
+#define TZ_STRLEN_MAX 255
+
 #endif   /* _PGTIME_H */
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index 782b75ec8dfd5e01d45623e1bd9d36687ab808c5..7475dacbe7c0532cbff949076f813bdeb71f2cd6 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -6,7 +6,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/timestamp.h,v 1.44 2005/06/14 21:04:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.45 2005/06/15 00:34:10 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -264,7 +264,7 @@ extern TimestampTz StartTime;
 
 extern int	tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, Timestamp *dt);
 extern int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm,
-			 fsec_t *fsec, char **tzn);
+			 fsec_t *fsec, char **tzn, pg_tz *attimezone);
 extern void dt2time(Timestamp dt, int *hour, int *min, int *sec, fsec_t *fsec);
 
 extern int	interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec);
diff --git a/src/timezone/pgtz.h b/src/timezone/pgtz.h
index e325dec48328e3125f3a9cd49309956c8b2cd5ad..a6e6bbf97f61a965376208fdfc92821b51fc6e52 100644
--- a/src/timezone/pgtz.h
+++ b/src/timezone/pgtz.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.11 2005/04/19 03:13:59 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.12 2005/06/15 00:34:11 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,8 +18,6 @@
 
 #include "tzfile.h"
 
-#define TZ_STRLEN_MAX 255
-
 extern char *pg_TZDIR(void);
 
 #define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))