diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 7add658d6abe79e1b516473600bea01ab70552f5..e7762040626328e3c0d282a2b97ebd08cb0ea14f 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.105 2004/12/31 21:59:42 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.106 2005/04/19 03:13:58 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -315,13 +315,13 @@ assign_timezone(const char *value, bool doit, GucSource source)
 			 * UNKNOWN as the canonical spelling.
 			 *
 			 * During GUC initialization, since the timezone library isn't
-			 * set up yet, pg_get_current_timezone will return NULL and we
+			 * set up yet, pg_get_timezone_name will return NULL and we
 			 * will leave the setting as UNKNOWN.  If this isn't
 			 * overridden from the config file then
 			 * pg_timezone_initialize() will eventually select a default
 			 * value from the environment.
 			 */
-			const char *curzone = pg_get_current_timezone();
+			const char *curzone = pg_get_timezone_name(global_timezone);
 
 			if (curzone)
 				value = curzone;
@@ -329,90 +329,36 @@ assign_timezone(const char *value, bool doit, GucSource source)
 		else
 		{
 			/*
-			 * Otherwise assume it is a timezone name.
-			 *
-			 * We have to actually apply the change before we can have any
-			 * hope of checking it.  So, save the old value in case we
-			 * have to back out.  We have to copy since
-			 * pg_get_current_timezone returns a pointer to its static
-			 * state.
-			 *
-			 * This would all get a lot simpler if the TZ library had a
-			 * better API that would let us look up and test a timezone
-			 * name without making it the default.
+			 * Otherwise assume it is a timezone name, and try to load it.
 			 */
-			const char *cur_tz;
-			char	   *save_tz;
-			bool		known,
-						acceptable;
+			pg_tz *new_tz;
 
-			cur_tz = pg_get_current_timezone();
-			if (cur_tz)
-				save_tz = pstrdup(cur_tz);
-			else
-				save_tz = NULL;
+			new_tz = pg_tzset(value);
 
-			known = pg_tzset(value);
-			acceptable = known ? tz_acceptable() : false;
+			if (!new_tz)
+			{
+				ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("unrecognized time zone name: \"%s\"",
+								value)));
+				return NULL;
+			}
 
-			if (doit && known && acceptable)
+			if (!tz_acceptable(new_tz))
 			{
-				/* Keep the changed TZ */
-				HasCTZSet = false;
+				ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("time zone \"%s\" appears to use leap seconds",
+								value),
+						 errdetail("PostgreSQL does not support leap seconds.")));
+				return NULL;
 			}
-			else
+
+			if (doit)
 			{
-				/*
-				 * Revert to prior TZ setting; note we haven't changed
-				 * HasCTZSet in this path, so if we were previously using
-				 * a fixed offset, we still are.
-				 */
-				if (save_tz)
-					pg_tzset(save_tz);
-				else
-				{
-					/*
-					 * TZ library wasn't initialized yet.  Annoyingly, we
-					 * will come here during startup because guc-file.l
-					 * checks the value with doit = false before actually
-					 * applying. The best approach seems to be as follows:
-					 *
-					 * 1. known && acceptable: leave the setting in place,
-					 * since we'll apply it soon anyway.  This is mainly
-					 * so that any log messages printed during this
-					 * interval are timestamped with the user's requested
-					 * timezone.
-					 *
-					 * 2. known && !acceptable: revert to GMT for lack of any
-					 * better idea.  (select_default_timezone() may get
-					 * called later to undo this.)
-					 *
-					 * 3. !known: no need to do anything since TZ library did
-					 * not change its state.
-					 *
-					 * Again, this should all go away sometime soon.
-					 */
-					if (known && !acceptable)
-						pg_tzset("GMT");
-				}
-				/* Complain if it was bad */
-				if (!known)
-				{
-					ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("unrecognized time zone name: \"%s\"",
-									value)));
-					return NULL;
-				}
-				if (!acceptable)
-				{
-					ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					errmsg("time zone \"%s\" appears to use leap seconds",
-						   value),
-							 errdetail("PostgreSQL does not support leap seconds.")));
-					return NULL;
-				}
+				/* Save the changed TZ */
+				global_timezone = new_tz;
+				HasCTZSet = false;
 			}
 		}
 	}
@@ -459,7 +405,7 @@ show_timezone(void)
 										  IntervalPGetDatum(&interval)));
 	}
 	else
-		tzn = pg_get_current_timezone();
+		tzn = pg_get_timezone_name(global_timezone);
 
 	if (tzn != NULL)
 		return tzn;
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index d019127c790b17e9b9e586bf3b4331e13b8f1fc3..1899d8f21a1c5630558ecb538147eb83b93deaaa 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -18,7 +18,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/postmaster/syslogger.c,v 1.14 2005/03/12 01:54:44 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/syslogger.c,v 1.15 2005/04/19 03:13:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -833,7 +833,7 @@ logfile_getname(pg_time_t timestamp)
 	if (strchr(Log_filename, '%'))
 	{
 		/* treat it as a strftime pattern */
-		tm = pg_localtime(&timestamp);
+		tm = pg_localtime(&timestamp, global_timezone);
 		pg_strftime(filename + len, MAXPGPATH - len, Log_filename, tm);
 	}
 	else 
@@ -868,7 +868,7 @@ set_next_rotation_time(void)
 	 */
 	rotinterval = Log_RotationAge * 60; /* convert to seconds */
 	now = time(NULL);
-	tm = pg_localtime(&now);
+	tm = pg_localtime(&now, global_timezone);
 	now += tm->tm_gmtoff;
 	now -= now % rotinterval;
 	now += rotinterval;
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 47dc28d13261274880a64185a504cedd0835a861..0ac92c304b0d073b32612b8ad4dc3b947c64cc05 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.137 2005/01/11 18:33:45 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.138 2005/04/19 03:13:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1634,7 +1634,8 @@ DetermineLocalTimeZone(struct pg_tm * tm)
 	res = pg_next_dst_boundary(&prevtime,
 							   &before_gmtoff, &before_isdst,
 							   &boundary,
-							   &after_gmtoff, &after_isdst);
+							   &after_gmtoff, &after_isdst,
+		                       global_timezone);
 	if (res < 0)
 		goto overflow;			/* failure? */
 
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
index 4e2069f5fb6cee19c5430a0856ec3701a192bed5..631c700c5ac481cab9de7033a5fca0ab64090bdc 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.127 2004/12/31 22:01:22 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.128 2005/04/19 03:13:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -192,7 +192,7 @@ abstime2tm(AbsoluteTime _time, int *tzp, struct pg_tm * tm, char **tzn)
 		time -= CTimeZone;
 
 	if ((!HasCTZSet) && (tzp != NULL))
-		tx = pg_localtime(&time);
+		tx = pg_localtime(&time,global_timezone);
 	else
 		tx = pg_gmtime(&time);
 
@@ -1677,7 +1677,7 @@ timeofday(PG_FUNCTION_ARGS)
 	gettimeofday(&tp, &tpz);
 	tt = (pg_time_t) tp.tv_sec;
 	pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
-				pg_localtime(&tt));
+				pg_localtime(&tt,global_timezone));
 	snprintf(buf, sizeof(buf), templ, tp.tv_usec);
 
 	len = VARHDRSZ + strlen(buf);
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 1f82b2fec9c0a1eb376bf82a96ad71686629c801..1da473dbfbe5179fad5742cb3cbfc0512af2ddd8 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.118 2005/04/01 14:25:23 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.119 2005/04/19 03:13:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1078,7 +1078,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);
+		struct pg_tm *tx = pg_localtime(&utime, global_timezone);
 
 		tm->tm_year = tx->tm_year + 1900;
 		tm->tm_mon = tx->tm_mon + 1;
diff --git a/src/include/pgtime.h b/src/include/pgtime.h
index 8b38945f4cb7784cece2ad18eff6e3c036306b41..b3322234e19ce26939d09f6c4ab9f7a0ef824151 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.6 2004/12/31 22:03:19 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/include/pgtime.h,v 1.7 2005/04/19 03:13:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,21 +37,24 @@ struct pg_tm
 	const char *tm_zone;
 };
 
-extern struct pg_tm *pg_localtime(const pg_time_t *timep);
+typedef struct pg_tz pg_tz;
+
+extern struct pg_tm *pg_localtime(const pg_time_t *timep, const pg_tz *tz);
 extern struct pg_tm *pg_gmtime(const pg_time_t *timep);
 extern int	pg_next_dst_boundary(const pg_time_t *timep,
 								 long int *before_gmtoff,
 								 int *before_isdst,
 								 pg_time_t *boundary,
 								 long int *after_gmtoff,
-								 int *after_isdst);
+								 int *after_isdst,
+	                             const pg_tz *tz);
 extern size_t pg_strftime(char *s, size_t max, const char *format,
 			const struct pg_tm * tm);
 
 extern void pg_timezone_initialize(void);
-extern bool pg_tzset(const char *tzname);
-extern bool tz_acceptable(void);
-extern const char *select_default_timezone(void);
-extern const char *pg_get_current_timezone(void);
+extern pg_tz *pg_tzset(const char *tzname);
+extern bool tz_acceptable(pg_tz *tz);
+extern const char *pg_get_timezone_name(pg_tz *tz);
 
+extern pg_tz *global_timezone;
 #endif   /* _PGTIME_H */
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index 8e64f065b451982c224ce31ddf76ec49f9cdf566..28b8f289a23c4104764dc08531070f698c7940a2 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -3,7 +3,7 @@
  * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.9 2004/11/01 21:34:44 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.10 2005/04/19 03:13:59 momjian Exp $
  */
 
 /*
@@ -16,8 +16,8 @@
 
 #include <fcntl.h>
 
-#include "pgtz.h"
 #include "private.h"
+#include "pgtz.h"
 #include "tzfile.h"
 
 
@@ -58,37 +58,6 @@ static const char gmt[] = "GMT";
  */
 #define TZDEFRULESTRING ",M4.1.0,M10.5.0"
 
-struct ttinfo
-{								/* time type information */
-	long		tt_gmtoff;		/* UTC offset in seconds */
-	int			tt_isdst;		/* used to set tm_isdst */
-	int			tt_abbrind;		/* abbreviation list index */
-	int			tt_ttisstd;		/* TRUE if transition is std time */
-	int			tt_ttisgmt;		/* TRUE if transition is UTC */
-};
-
-struct lsinfo
-{								/* leap second information */
-	pg_time_t	ls_trans;		/* transition time */
-	long		ls_corr;		/* correction to apply */
-};
-
-#define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))
-
-struct state
-{
-	int			leapcnt;
-	int			timecnt;
-	int			typecnt;
-	int			charcnt;
-	pg_time_t	ats[TZ_MAX_TIMES];
-	unsigned char types[TZ_MAX_TIMES];
-	struct ttinfo ttis[TZ_MAX_TYPES];
-	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
-										  (2 * (TZ_STRLEN_MAX + 1)))];
-	struct lsinfo lsis[TZ_MAX_LEAPS];
-};
-
 struct rule
 {
 	int			r_type;			/* type of rule--see below */
@@ -115,22 +84,19 @@ static const char *getoffset(const char *strp, long *offsetp);
 static const char *getrule(const char *strp, struct rule * rulep);
 static void gmtload(struct state * sp);
 static void gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp);
-static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp);
+static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz);
 static void timesub(const pg_time_t *timep, long offset,
 		const struct state * sp, struct pg_tm * tmp);
 static pg_time_t transtime(pg_time_t janfirst, int year,
-		  const struct rule * rulep, long offset);
-static int	tzload(const char *name, struct state * sp);
-static int	tzparse(const char *name, struct state * sp, int lastditch);
+						   const struct rule * rulep, long offset);
+int	tzparse(const char *name, struct state * sp, int lastditch);
 
-static struct state lclmem;
+/* GMT timezone */
 static struct state gmtmem;
 
-#define lclptr		(&lclmem)
 #define gmtptr		(&gmtmem)
 
-static char lcl_TZname[TZ_STRLEN_MAX + 1];
-static int	lcl_is_set = 0;
+
 static int	gmt_is_set = 0;
 
 /*
@@ -156,7 +122,7 @@ detzcode(const char *codep)
 	return result;
 }
 
-static int
+int
 tzload(register const char *name, register struct state * sp)
 {
 	register const char *p;
@@ -589,7 +555,7 @@ transtime(const pg_time_t janfirst, const int year,
  * appropriate.
  */
 
-static int
+int
 tzparse(const char *name, register struct state * sp, const int lastditch)
 {
 	const char *stdname;
@@ -839,30 +805,6 @@ gmtload(struct state * sp)
 }
 
 
-bool
-pg_tzset(const char *name)
-{
-	if (lcl_is_set && strcmp(lcl_TZname, name) == 0)
-		return true;			/* no change */
-
-	if (strlen(name) >= sizeof(lcl_TZname))
-		return false;			/* not gonna fit */
-
-	if (tzload(name, lclptr) != 0)
-	{
-		if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
-		{
-			/* Unknown timezone. Fail our call instead of loading GMT! */
-			return false;
-		}
-	}
-
-	strcpy(lcl_TZname, name);
-	lcl_is_set = true;
-
-	return true;
-}
-
 /*
  * The easy way to behave "as if no library function calls" localtime
  * is to not call it--so we drop its guts into "localsub", which can be
@@ -872,14 +814,14 @@ pg_tzset(const char *name)
  * The unused offset argument is for the benefit of mktime variants.
  */
 static void
-localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp)
+localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp, const pg_tz *tz)
 {
-	register struct state *sp;
+ 	register const struct state *sp;
 	register const struct ttinfo *ttisp;
 	register int i;
 	const pg_time_t t = *timep;
 
-	sp = lclptr;
+	sp = &tz->state;
 	if (sp->timecnt == 0 || t < sp->ats[0])
 	{
 		i = 0;
@@ -906,9 +848,9 @@ localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp)
 
 
 struct pg_tm *
-pg_localtime(const pg_time_t *timep)
+pg_localtime(const pg_time_t *timep, const pg_tz *tz)
 {
-	localsub(timep, 0L, &tm);
+	localsub(timep, 0L, &tm, tz);
 	return &tm;
 }
 
@@ -1084,15 +1026,16 @@ pg_next_dst_boundary(const pg_time_t *timep,
 					 int *before_isdst,
 					 pg_time_t *boundary,
 					 long int *after_gmtoff,
-					 int *after_isdst)
+					 int *after_isdst,
+	                 const pg_tz *tz)
 {
-	register struct state *sp;
+	register const struct state *sp;
 	register const struct ttinfo *ttisp;
 	int i;
 	int j;
 	const pg_time_t t = *timep;
 
-	sp = lclptr;
+	sp = &tz->state;
 	if (sp->timecnt == 0)
 	{
 		/* non-DST zone, use lowest-numbered standard type */
@@ -1158,9 +1101,9 @@ pg_next_dst_boundary(const pg_time_t *timep,
  * Return the name of the current timezone
  */
 const char *
-pg_get_current_timezone(void)
+pg_get_timezone_name(pg_tz *tz)
 {
-	if (lcl_is_set)
-		return lcl_TZname;
+	if (tz)
+		return tz->TZname;
 	return NULL;
 }
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c
index 599aaac4efccfc83889314a422c892d70f74188e..f228ed5bacfd491e6b61d4b0049696e0b014be0e 100644
--- a/src/timezone/pgtz.c
+++ b/src/timezone/pgtz.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.29 2004/12/31 22:03:59 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.30 2005/04/19 03:13:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,12 +26,18 @@
 #include "utils/datetime.h"
 #include "utils/elog.h"
 #include "utils/guc.h"
+#include "utils/hsearch.h"
+
+/* Current global timezone */
+pg_tz *global_timezone = NULL;
 
 
 static char tzdir[MAXPGPATH];
 static int	done_tzdir = 0;
 
 static const char *identify_system_timezone(void);
+static const char *select_default_timezone(void);
+static bool set_global_timezone(const char *tzname);
 
 
 /*
@@ -156,12 +162,14 @@ score_timezone(const char *tzname, struct tztry * tt)
 	struct tm  *systm;
 	struct pg_tm *pgtm;
 	char		cbuf[TZ_STRLEN_MAX + 1];
+	pg_tz       *tz;
 
-	if (!pg_tzset(tzname))
+	tz = pg_tzset(tzname);
+	if (!tz)
 		return -1;				/* can't handle the TZ name at all */
 
 	/* Reject if leap seconds involved */
-	if (!tz_acceptable())
+	if (!tz_acceptable(tz))
 	{
 		elog(DEBUG4, "Reject TZ \"%s\": uses leap seconds", tzname);
 		return -1;
@@ -171,7 +179,7 @@ score_timezone(const char *tzname, struct tztry * tt)
 	for (i = 0; i < tt->n_test_times; i++)
 	{
 		pgtt = (pg_time_t) (tt->test_times[i]);
-		pgtm = pg_localtime(&pgtt);
+		pgtm = pg_localtime(&pgtt, tz);
 		if (!pgtm)
 			return -1;			/* probably shouldn't happen */
 		systm = localtime(&(tt->test_times[i]));
@@ -957,6 +965,82 @@ identify_system_timezone(void)
 #endif   /* WIN32 */
 
 
+
+/*
+ * We keep loaded timezones in a hashtable so we don't have to
+ * load and parse the TZ definition file every time it is selected.
+ */
+static HTAB *timezone_cache = NULL;
+static bool
+init_timezone_hashtable(void)
+{
+	HASHCTL		hash_ctl;
+
+	MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+
+	hash_ctl.keysize = TZ_STRLEN_MAX;
+	hash_ctl.entrysize = sizeof(pg_tz);
+
+	timezone_cache = hash_create("Timezones",
+								 31,
+								 &hash_ctl,
+								 HASH_ELEM);
+	if (!timezone_cache)
+		return false;
+
+	return true;
+}
+
+/*
+ * Load a timezone from file or from cache.
+ * Does not verify that the timezone is acceptable!
+ */
+struct pg_tz *
+pg_tzset(const char *name)
+{
+	pg_tz *tzp;
+	pg_tz tz;
+	
+	if (strlen(name) >= TZ_STRLEN_MAX)
+		return NULL;			/* not going to fit */
+
+	if (!timezone_cache)
+		if (!init_timezone_hashtable())
+			return NULL;
+
+	tzp = (pg_tz *)hash_search(timezone_cache,
+							  name,
+							  HASH_FIND,
+							  NULL);
+	if (tzp)
+		/* Timezone found in cache, nothing more to do */
+		return tzp;
+
+	if (tzload(name, &tz.state) != 0)
+	{
+		if (name[0] == ':' || tzparse(name, &tz.state, FALSE) != 0)
+			/* Unknown timezone. Fail our call instead of loading GMT! */
+			return NULL;
+	}
+
+	strcpy(tz.TZname, name);
+
+	/* Save timezone in the cache */
+	tzp = hash_search(timezone_cache,
+					  name,
+					  HASH_ENTER,
+					  NULL);
+	
+	if (!tzp)
+		return NULL;
+	
+	strcpy(tzp->TZname, tz.TZname);
+	memcpy(&tzp->state, &tz.state, sizeof(tz.state));
+
+	return tzp;
+}
+
+
 /*
  * Check whether timezone is acceptable.
  *
@@ -968,7 +1052,7 @@ identify_system_timezone(void)
  * it can restore the old value of TZ if we don't like the new one.
  */
 bool
-tz_acceptable(void)
+tz_acceptable(pg_tz *tz)
 {
 	struct pg_tm *tt;
 	pg_time_t	time2000;
@@ -979,13 +1063,36 @@ tz_acceptable(void)
 	 * any other result has to be due to leap seconds.
 	 */
 	time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400;
-	tt = pg_localtime(&time2000);
+	tt = pg_localtime(&time2000, tz);
 	if (!tt || tt->tm_sec != 0)
 		return false;
 
 	return true;
 }
 
+
+/*
+ * Set the global timezone. Verify that it's acceptable first.
+ */
+static bool
+set_global_timezone(const char *tzname)
+{
+	pg_tz *tznew;
+
+	if (!tzname || !tzname[0])
+		return false;
+	
+	tznew = pg_tzset(tzname);
+	if (!tznew)
+		return false;
+
+	if (!tz_acceptable(tznew))
+		return false;
+
+	global_timezone = tznew;
+	return true;
+}
+
 /*
  * Identify a suitable default timezone setting based on the environment,
  * and make it active.
@@ -995,20 +1102,20 @@ tz_acceptable(void)
  * from the behavior of the system timezone library.  When all else fails,
  * fall back to GMT.
  */
-const char *
+static const char *
 select_default_timezone(void)
 {
 	const char *def_tz;
 
 	def_tz = getenv("TZ");
-	if (def_tz && pg_tzset(def_tz) && tz_acceptable())
+	if (set_global_timezone(def_tz))
 		return def_tz;
 
 	def_tz = identify_system_timezone();
-	if (def_tz && pg_tzset(def_tz) && tz_acceptable())
+	if (set_global_timezone(def_tz))
 		return def_tz;
 
-	if (pg_tzset("GMT") && tz_acceptable())
+	if (set_global_timezone("GMT"))
 		return "GMT";
 
 	ereport(FATAL,
diff --git a/src/timezone/pgtz.h b/src/timezone/pgtz.h
index 6c05198aed74966741aa6770afde5611601bb7e0..e325dec48328e3125f3a9cd49309956c8b2cd5ad 100644
--- a/src/timezone/pgtz.h
+++ b/src/timezone/pgtz.h
@@ -9,15 +9,57 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.10 2004/12/31 22:03:59 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.11 2005/04/19 03:13:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef _PGTZ_H
 #define _PGTZ_H
 
+#include "tzfile.h"
+
 #define TZ_STRLEN_MAX 255
 
 extern char *pg_TZDIR(void);
 
+#define BIGGEST(a, b)	(((a) > (b)) ? (a) : (b))
+
+struct ttinfo
+{								/* time type information */
+	long		tt_gmtoff;		/* UTC offset in seconds */
+	int			tt_isdst;		/* used to set tm_isdst */
+	int			tt_abbrind;		/* abbreviation list index */
+	int			tt_ttisstd;		/* TRUE if transition is std time */
+	int			tt_ttisgmt;		/* TRUE if transition is UTC */
+};
+
+struct lsinfo
+{								/* leap second information */
+	pg_time_t	ls_trans;		/* transition time */
+	long		ls_corr;		/* correction to apply */
+};
+
+struct state
+{
+	int			leapcnt;
+	int			timecnt;
+	int			typecnt;
+	int			charcnt;
+	pg_time_t	ats[TZ_MAX_TIMES];
+	unsigned char types[TZ_MAX_TIMES];
+	struct ttinfo ttis[TZ_MAX_TYPES];
+	char		chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */),
+										  (2 * (TZ_STRLEN_MAX + 1)))];
+	struct lsinfo lsis[TZ_MAX_LEAPS];
+};
+
+
+struct pg_tz {
+	char TZname[TZ_STRLEN_MAX + 1];
+	struct state state;
+};
+
+int	tzload(const char *name, struct state * sp);
+int	tzparse(const char *name, struct state * sp, int lastditch);
+
 #endif   /* _PGTZ_H */
diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c
index c23ae7cb6fc2e35bb40ed1c9f4d4a682c59b6b4d..cc45527fe4ee0e53d17aa2e86b2c6034e6f6ee3f 100644
--- a/src/timezone/strftime.c
+++ b/src/timezone/strftime.c
@@ -15,7 +15,7 @@
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.5 2004/08/29 05:07:02 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.6 2005/04/19 03:13:59 momjian Exp $
  */
 
 #include "postgres.h"
@@ -23,8 +23,8 @@
 #include <fcntl.h>
 #include <locale.h>
 
-#include "pgtz.h"
 #include "private.h"
+#include "pgtz.h"
 #include "tzfile.h"
 
 
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index 5cc243299818d35fce524f5bc11f441ae8889d7e..1d3695b65910922d5a98f72eceaedec8dd605375 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -3,7 +3,7 @@
  * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/timezone/zic.c,v 1.13 2004/09/27 19:16:03 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/timezone/zic.c,v 1.14 2005/04/19 03:13:59 momjian Exp $
  */
 
 #include "postgres.h"
@@ -14,8 +14,8 @@
 #include <limits.h>
 #include <locale.h>
 
-#include "pgtz.h"
 #include "private.h"
+#include "pgtz.h"
 #include "tzfile.h"
 
 #ifdef HAVE_SYS_STAT_H