diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index adac32931df89a9f2b6bb356baac10274bda8bf7..6b4db09605b5f08a287eb9fa2b4cf72df5ae132d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.135 2007/08/02 23:39:43 adunstan Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.136 2007/08/04 01:26:53 tgl Exp $ -->
 
 <chapter Id="runtime-config">
   <title>Server Configuration</title>
@@ -2311,13 +2311,15 @@ SELECT * FROM parent WHERE key = 2400;
         When <varname>redirect_stderr</varname> is enabled, this parameter
         sets the file names of the created log files.  The value
         is treated as a <systemitem>strftime</systemitem> pattern,
-        so <literal>%</literal>-escapes
-        can be used to specify time-varying file names.
+        so <literal>%</literal>-escapes can be used to specify time-varying
+        file names.  (Note that if there are
+        any time-zone-dependent <literal>%</literal>-escapes, the computation
+        is done in the zone specified by <xref linkend="guc-log-timezone">.)
         If no <literal>%</literal>-escapes are present,
-        <productname>PostgreSQL</productname> will
-        append the epoch of the new log file's open time.  For example,
-        if <varname>log_filename</varname> were <literal>server_log</literal>, then the
-        chosen file name would be <literal>server_log.1093827753</literal>
+        <productname>PostgreSQL</productname> will append the epoch of the new
+        log file's creation time.  For example, if
+        <varname>log_filename</varname> were <literal>server_log</literal>,
+        then the chosen file name would be <literal>server_log.1093827753</>
         for a log starting at Sun Aug 29 19:02:33 2004 MST.
         This parameter can only be set in the <filename>postgresql.conf</>
         file or on the server command line.
@@ -2884,7 +2886,7 @@ SELECT * FROM parent WHERE key = 2400;
             </row>
             <row>
              <entry><literal>%t</literal></entry>
-             <entry>Time stamp without milliseconds (no timezone either on Windows)</entry>
+             <entry>Time stamp without milliseconds</entry>
              <entry>no</entry>
             </row>
             <row>
@@ -2909,7 +2911,7 @@ SELECT * FROM parent WHERE key = 2400;
             </row>
             <row>
              <entry><literal>%s</literal></entry>
-             <entry>Session start time stamp</entry>
+             <entry>Process start time stamp</entry>
              <entry>no</entry>
             </row>
             <row>
@@ -2935,7 +2937,7 @@ SELECT * FROM parent WHERE key = 2400;
 
          The <literal>%c</> escape prints a quasi-unique session identifier,
          consisting of two 4-byte hexadecimal numbers (without leading zeros)
-         separated by a dot.  The numbers are the session start time and the
+         separated by a dot.  The numbers are the process start time and the
          process ID, so <literal>%c</> can also be used as a space saving way
          of printing those items.
        </para>
@@ -3036,6 +3038,25 @@ SELECT * FROM parent WHERE key = 2400;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-log-timezone" xreflabel="log_timezone">
+      <term><varname>log_timezone</varname> (<type>string</type>)</term>
+      <indexterm>
+       <primary><varname>log_timezone</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Sets the time zone used for timestamps written in the log.
+        Unlike <xref linkend="guc-timezone">, this value is cluster-wide,
+        so that all sessions will report timestamps consistently.
+        The default is <literal>unknown</>, which means to use whatever
+        the system environment specifies as the time zone.  See <xref
+        linkend="datatype-timezones"> for more information.
+        This parameter can only be set in the <filename>postgresql.conf</>
+        file or on the server command line.
+       </para>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
    </sect1>
@@ -3822,9 +3843,9 @@ SET XML OPTION { DOCUMENT | CONTENT };
       <listitem>
        <para>
         Sets the time zone for displaying and interpreting time stamps.
-        The default is <literal>'unknown'</>, which means to use whatever
+        The default is <literal>unknown</>, which means to use whatever
         the system environment specifies as the time zone.  See <xref
-        linkend="datatype-datetime"> for more
+        linkend="datatype-timezones"> for more
         information.
        </para>
       </listitem>
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 64b53d1e83d30a6f38269613245845ce789c90ea..99cc22958f262c4f4a765a62380e3c1915e1d472 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.205 2007/07/27 10:37:52 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.206 2007/08/04 01:26:53 tgl Exp $ -->
 
  <chapter id="datatype">
   <title id="datatype-title">Data Types</title>
@@ -2234,7 +2234,8 @@ January 8 04:05:06 1999 PST
         savings transition-date rules as well.  The recognized abbreviations
         are listed in the <literal>pg_timezone_abbrevs</> view (see <xref
         linkend="view-pg-timezone-abbrevs">).  You cannot set the
-        configuration parameter <xref linkend="guc-timezone"> using a time
+        configuration parameters <xref linkend="guc-timezone"> or
+        <xref linkend="guc-log-timezone"> using a time
         zone abbreviation, but you can use abbreviations in
         date/time input values and with the <literal>AT TIME ZONE</>
         operator.
@@ -2316,6 +2317,8 @@ January 8 04:05:06 1999 PST
         behavior of the C library function <literal>localtime()</>.  The
         default time zone is selected as the closest match among
         <productname>PostgreSQL</productname>'s known time zones.
+        (These rules are also used to choose the default value of
+        <xref linkend="guc-log-timezone">, if it is not specified.)
        </para>
       </listitem>
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 4c7024baa38f14fedf33cd67fd050cc3ec98ea02..626143ff19bb62b678e38ce82ce2bd529acecab9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.276 2007/08/01 22:45:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.277 2007/08/04 01:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -437,7 +437,7 @@ static void writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
 					 uint32 endLogId, uint32 endLogSeg);
 static void WriteControlFile(void);
 static void ReadControlFile(void);
-static char *str_time(time_t tnow);
+static char *str_time(pg_time_t tnow);
 static void issue_xlog_fsync(void);
 
 #ifdef WAL_DEBUG
@@ -4266,13 +4266,13 @@ BootStrapXLOG(void)
 }
 
 static char *
-str_time(time_t tnow)
+str_time(pg_time_t tnow)
 {
 	static char buf[128];
 
-	strftime(buf, sizeof(buf),
-			 "%Y-%m-%d %H:%M:%S %Z",
-			 localtime(&tnow));
+	pg_strftime(buf, sizeof(buf),
+				"%Y-%m-%d %H:%M:%S %Z",
+				pg_localtime(&tnow, log_timezone));
 
 	return buf;
 }
@@ -6290,7 +6290,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
 	char	   *backupidstr;
 	XLogRecPtr	checkpointloc;
 	XLogRecPtr	startpoint;
-	time_t		stamp_time;
+	pg_time_t	stamp_time;
 	char		strfbuf[128];
 	char		xlogfilename[MAXFNAMELEN];
 	uint32		_logId;
@@ -6368,16 +6368,11 @@ pg_start_backup(PG_FUNCTION_ARGS)
 		XLByteToSeg(startpoint, _logId, _logSeg);
 		XLogFileName(xlogfilename, ThisTimeLineID, _logId, _logSeg);
 
-		/*
-		 * We deliberately use strftime/localtime not the src/timezone
-		 * functions, so that backup labels will consistently be recorded in
-		 * the same timezone regardless of TimeZone setting.  This matches
-		 * elog.c's practice.
-		 */
-		stamp_time = time(NULL);
-		strftime(strfbuf, sizeof(strfbuf),
-				 "%Y-%m-%d %H:%M:%S %Z",
-				 localtime(&stamp_time));
+		/* Use the log timezone here, not the session timezone */
+		stamp_time = (pg_time_t) time(NULL);
+		pg_strftime(strfbuf, sizeof(strfbuf),
+					"%Y-%m-%d %H:%M:%S %Z",
+					pg_localtime(&stamp_time, log_timezone));
 
 		/*
 		 * Check for existing backup label --- implies a backup is already
@@ -6455,7 +6450,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 	text	   *result;
 	XLogRecPtr	startpoint;
 	XLogRecPtr	stoppoint;
-	time_t		stamp_time;
+	pg_time_t	stamp_time;
 	char		strfbuf[128];
 	char		histfilepath[MAXPGPATH];
 	char		startxlogfilename[MAXFNAMELEN];
@@ -6489,16 +6484,11 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 	XLByteToSeg(stoppoint, _logId, _logSeg);
 	XLogFileName(stopxlogfilename, ThisTimeLineID, _logId, _logSeg);
 
-	/*
-	 * We deliberately use strftime/localtime not the src/timezone functions,
-	 * so that backup labels will consistently be recorded in the same
-	 * timezone regardless of TimeZone setting.  This matches elog.c's
-	 * practice.
-	 */
-	stamp_time = time(NULL);
-	strftime(strfbuf, sizeof(strfbuf),
-			 "%Y-%m-%d %H:%M:%S %Z",
-			 localtime(&stamp_time));
+	/* Use the log timezone here, not the session timezone */
+	stamp_time = (pg_time_t) time(NULL);
+	pg_strftime(strfbuf, sizeof(strfbuf),
+				"%Y-%m-%d %H:%M:%S %Z",
+				pg_localtime(&stamp_time, log_timezone));
 
 	/*
 	 * Open the existing label file
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 1f6839cbd17b9157bd87d1ca9af15409388c26ad..8a04a975ca14a833d41e43f9240a3cd92c77c99d 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.120 2007/01/05 22:19:27 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.121 2007/08/04 01:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -344,7 +344,7 @@ assign_timezone(const char *value, bool doit, GucSource source)
 			 */
 			if (doit)
 			{
-				const char *curzone = pg_get_timezone_name(global_timezone);
+				const char *curzone = pg_get_timezone_name(session_timezone);
 
 				if (curzone)
 					value = curzone;
@@ -381,7 +381,7 @@ assign_timezone(const char *value, bool doit, GucSource source)
 			if (doit)
 			{
 				/* Save the changed TZ */
-				global_timezone = new_tz;
+				session_timezone = new_tz;
 				HasCTZSet = false;
 			}
 		}
@@ -434,7 +434,112 @@ show_timezone(void)
 											  IntervalPGetDatum(&interval)));
 	}
 	else
-		tzn = pg_get_timezone_name(global_timezone);
+		tzn = pg_get_timezone_name(session_timezone);
+
+	if (tzn != NULL)
+		return tzn;
+
+	return "unknown";
+}
+
+
+/*
+ * LOG_TIMEZONE
+ *
+ * For log_timezone, we don't support the interval-based methods of setting a
+ * zone, which are only there for SQL spec compliance not because they're
+ * actually useful.
+ */
+
+/*
+ * assign_log_timezone: GUC assign_hook for log_timezone
+ */
+const char *
+assign_log_timezone(const char *value, bool doit, GucSource source)
+{
+	char	   *result;
+
+	if (pg_strcasecmp(value, "UNKNOWN") == 0)
+	{
+		/*
+		 * UNKNOWN is the value shown as the "default" for log_timezone in
+		 * guc.c.  We interpret it as being a complete no-op; we don't
+		 * change the timezone setting.  Note that if there is a known
+		 * timezone setting, we will return that name rather than UNKNOWN
+		 * as the canonical spelling.
+		 *
+		 * During GUC initialization, since the timezone library isn't 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.
+		 */
+		if (doit)
+		{
+			const char *curzone = pg_get_timezone_name(log_timezone);
+
+			if (curzone)
+				value = curzone;
+		}
+	}
+	else
+	{
+		/*
+		 * Otherwise assume it is a timezone name, and try to load it.
+		 */
+		pg_tz	   *new_tz;
+
+		new_tz = pg_tzset(value);
+
+		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 (!tz_acceptable(new_tz))
+		{
+			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;
+		}
+
+		if (doit)
+		{
+			/* Save the changed TZ */
+			log_timezone = new_tz;
+		}
+	}
+
+	/*
+	 * If we aren't going to do the assignment, just return OK indicator.
+	 */
+	if (!doit)
+		return value;
+
+	/*
+	 * Prepare the canonical string to return.	GUC wants it malloc'd.
+	 */
+	result = strdup(value);
+
+	return result;
+}
+
+/*
+ * show_log_timezone: GUC show_hook for log_timezone
+ */
+const char *
+show_log_timezone(void)
+{
+	const char *tzn;
+
+	tzn = pg_get_timezone_name(log_timezone);
 
 	if (tzn != NULL)
 		return tzn;
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 00480600690db1e31b125623a4f0423106ef1781..cd3793497bd03d9f07889768dfaf2994f446048d 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.35 2007/08/02 23:39:44 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/syslogger.c,v 1.36 2007/08/04 01:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1038,7 +1038,6 @@ logfile_getname(pg_time_t timestamp)
 {
 	char	   *filename;
 	int			len;
-	struct pg_tm *tm;
 
 	filename = palloc(MAXPGPATH);
 
@@ -1049,8 +1048,8 @@ logfile_getname(pg_time_t timestamp)
 	if (strchr(Log_filename, '%'))
 	{
 		/* treat it as a strftime pattern */
-		tm = pg_localtime(&timestamp, global_timezone);
-		pg_strftime(filename + len, MAXPGPATH - len, Log_filename, tm);
+		pg_strftime(filename + len, MAXPGPATH - len, Log_filename,
+					pg_localtime(&timestamp, log_timezone));
 	}
 	else
 	{
@@ -1079,12 +1078,12 @@ set_next_rotation_time(void)
 	/*
 	 * The requirements here are to choose the next time > now that is a
 	 * "multiple" of the log rotation interval.  "Multiple" can be interpreted
-	 * fairly loosely.	In this version we align to local time rather than
+	 * fairly loosely.	In this version we align to log_timezone rather than
 	 * GMT.
 	 */
 	rotinterval = Log_RotationAge * SECS_PER_MINUTE;	/* convert to seconds */
-	now = time(NULL);
-	tm = pg_localtime(&now, global_timezone);
+	now = (pg_time_t) time(NULL);
+	tm = pg_localtime(&now, log_timezone);
 	now += tm->tm_gmtoff;
 	now -= now % rotinterval;
 	now += rotinterval;
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index efc965ae4c36818967da557e0c210356f3f470ac..c3cc78977ff2de07194fe657c0996e94773d6b2a 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.134 2007/07/06 04:15:58 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.135 2007/08/04 01:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -360,7 +360,7 @@ date2timestamptz(DateADT dateVal)
 	tm->tm_hour = 0;
 	tm->tm_min = 0;
 	tm->tm_sec = 0;
-	tz = DetermineTimeZoneOffset(tm, global_timezone);
+	tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 #ifdef HAVE_INT64_TIMESTAMP
 	result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
@@ -2239,7 +2239,7 @@ time_timetz(PG_FUNCTION_ARGS)
 
 	GetCurrentDateTime(tm);
 	time2tm(time, tm, &fsec);
-	tz = DetermineTimeZoneOffset(tm, global_timezone);
+	tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
 
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index ddd4fa6167decd219221ee046abaf17a1d1050a0..742a328b280fbc19a763cb28f91ee85a681137cf 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.181 2007/06/12 15:58:32 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.182 2007/08/04 01:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1325,7 +1325,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
 			if (fmask & DTK_M(DTZMOD))
 				return DTERR_BAD_FORMAT;
 
-			*tzp = DetermineTimeZoneOffset(tm, global_timezone);
+			*tzp = DetermineTimeZoneOffset(tm, session_timezone);
 		}
 	}
 
@@ -1361,7 +1361,7 @@ DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp)
 				after_isdst;
 	int			res;
 
-	if (tzp == global_timezone && HasCTZSet)
+	if (tzp == session_timezone && HasCTZSet)
 	{
 		tm->tm_isdst = 0;		/* for lack of a better idea */
 		return CTimeZone;
@@ -2033,7 +2033,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 		tmp->tm_hour = tm->tm_hour;
 		tmp->tm_min = tm->tm_min;
 		tmp->tm_sec = tm->tm_sec;
-		*tzp = DetermineTimeZoneOffset(tmp, global_timezone);
+		*tzp = DetermineTimeZoneOffset(tmp, session_timezone);
 		tm->tm_isdst = tmp->tm_isdst;
 	}
 
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 0b54f72e6e0eb2d0736794563e80b95c0c8bc301..f9ccd461d87145eb8fe8dc8ebf529bfc5e8d3012 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.130 2007/06/29 01:51:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.131 2007/08/04 01:26:53 tgl Exp $
  *
  *
  *	 Portions Copyright (c) 1999-2007, PostgreSQL Global Development Group
@@ -3286,7 +3286,7 @@ to_timestamp(PG_FUNCTION_ARGS)
 
 	do_to_timestamp(date_txt, fmt, &tm, &fsec);
 
-	tz = DetermineTimeZoneOffset(&tm, global_timezone);
+	tz = DetermineTimeZoneOffset(&tm, session_timezone);
 
 	if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
 		ereport(ERROR,
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
index 3ec098a39f3ddd855a42aea78678e9ce8a857e32..903ca5d6adf15096ba9b2008f8e7bde99eb63eca 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.150 2007/02/27 23:48:08 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.151 2007/08/04 01:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -112,7 +112,7 @@ abstime2tm(AbsoluteTime _time, int *tzp, struct pg_tm * tm, char **tzn)
 		time -= CTimeZone;
 
 	if (!HasCTZSet && tzp != NULL)
-		tx = pg_localtime(&time, global_timezone);
+		tx = pg_localtime(&time, session_timezone);
 	else
 		tx = pg_gmtime(&time);
 
@@ -474,7 +474,7 @@ timestamp_abstime(PG_FUNCTION_ARGS)
 		result = NOEND_ABSTIME;
 	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
 	{
-		tz = DetermineTimeZoneOffset(tm, global_timezone);
+		tz = DetermineTimeZoneOffset(tm, session_timezone);
 		result = tm2abstime(tm, tz);
 	}
 	else
@@ -1591,7 +1591,7 @@ timeofday(PG_FUNCTION_ARGS)
 	gettimeofday(&tp, NULL);
 	tt = (pg_time_t) tp.tv_sec;
 	pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
-				pg_localtime(&tt, global_timezone));
+				pg_localtime(&tt, session_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 73b8fbfa3084e5385e076c96780c5526f9492265..8d2c9318d86dc342d1875708284bb5db97145e38 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.180 2007/07/18 03:13:13 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.181 2007/08/04 01:26:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1494,7 +1494,7 @@ recalc_t:
 	if ((Timestamp) utime == dt)
 	{
 		struct pg_tm *tx = pg_localtime(&utime,
-								  attimezone ? attimezone : global_timezone);
+								  attimezone ? attimezone : session_timezone);
 
 		tm->tm_year = tx->tm_year + 1900;
 		tm->tm_mon = tx->tm_mon + 1;
@@ -2675,7 +2675,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
 			if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
 				tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
 
-			tz = DetermineTimeZoneOffset(tm, global_timezone);
+			tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
 				ereport(ERROR,
@@ -2699,7 +2699,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
 			julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
 			j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
 
-			tz = DetermineTimeZoneOffset(tm, global_timezone);
+			tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
 				ereport(ERROR,
@@ -3546,7 +3546,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
 		}
 
 		if (redotz)
-			tz = DetermineTimeZoneOffset(tm, global_timezone);
+			tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
 			ereport(ERROR,
@@ -4010,7 +4010,7 @@ timestamp_part(PG_FUNCTION_ARGS)
 								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 								 errmsg("timestamp out of range")));
 
-					tz = DetermineTimeZoneOffset(tm, global_timezone);
+					tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 					if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
 						ereport(ERROR,
@@ -4549,7 +4549,7 @@ timestamp2timestamptz(Timestamp timestamp)
 					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 					 errmsg("timestamp out of range")));
 
-		tz = DetermineTimeZoneOffset(tm, global_timezone);
+		tz = DetermineTimeZoneOffset(tm, session_timezone);
 
 		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
 			ereport(ERROR,
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 78993a24ebe8fe7b2164474811c3715558684ab8..d51dd0bf1af250cf3bd4b2320b597a801e1dde09 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -42,7 +42,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.191 2007/08/02 23:39:44 adunstan Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.192 2007/08/04 01:26:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1495,33 +1495,18 @@ log_line_prefix(StringInfo buf)
 				break;
 			case 'm':
 				{
-					/*
-					 * Note: for %m, %t, and %s we deliberately use the C
-					 * library's strftime/localtime, and not the equivalent
-					 * functions from src/timezone.  This ensures that all
-					 * backends will report log entries in the same timezone,
-					 * namely whatever C-library setting they inherit from the
-					 * postmaster.	If we used src/timezone then local
-					 * settings of the TimeZone GUC variable would confuse the
-					 * log.
-					 */
-					time_t		stamp_time;
+					struct timeval tv;
+					pg_time_t	stamp_time;
 					char		strfbuf[128],
 								msbuf[8];
-					struct timeval tv;
 
 					gettimeofday(&tv, NULL);
-					stamp_time = tv.tv_sec;
+					stamp_time = (pg_time_t) tv.tv_sec;
 
-					strftime(strfbuf, sizeof(strfbuf),
-					/* leave room for milliseconds... */
-					/* Win32 timezone names are too long so don't print them */
-#ifndef WIN32
-							 "%Y-%m-%d %H:%M:%S     %Z",
-#else
-							 "%Y-%m-%d %H:%M:%S     ",
-#endif
-							 localtime(&stamp_time));
+					pg_strftime(strfbuf, sizeof(strfbuf),
+								/* leave room for milliseconds... */
+								"%Y-%m-%d %H:%M:%S     %Z",
+								pg_localtime(&stamp_time, log_timezone));
 
 					/* 'paste' milliseconds into place... */
 					sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000));
@@ -1532,32 +1517,23 @@ log_line_prefix(StringInfo buf)
 				break;
 			case 't':
 				{
-					time_t		stamp_time = time(NULL);
+					pg_time_t	stamp_time = (pg_time_t) time(NULL);
 					char		strfbuf[128];
 
-					strftime(strfbuf, sizeof(strfbuf),
-					/* Win32 timezone names are too long so don't print them */
-#ifndef WIN32
-							 "%Y-%m-%d %H:%M:%S %Z",
-#else
-							 "%Y-%m-%d %H:%M:%S",
-#endif
-							 localtime(&stamp_time));
+					pg_strftime(strfbuf, sizeof(strfbuf),
+								"%Y-%m-%d %H:%M:%S %Z",
+								pg_localtime(&stamp_time, log_timezone));
 					appendStringInfoString(buf, strfbuf);
 				}
 				break;
 			case 's':
 				{
+					pg_time_t	stamp_time = (pg_time_t) MyStartTime;
 					char		strfbuf[128];
 
-					strftime(strfbuf, sizeof(strfbuf),
-					/* Win32 timezone names are too long so don't print them */
-#ifndef WIN32
-							 "%Y-%m-%d %H:%M:%S %Z",
-#else
-							 "%Y-%m-%d %H:%M:%S",
-#endif
-							 localtime(&MyStartTime));
+					pg_strftime(strfbuf, sizeof(strfbuf),
+								"%Y-%m-%d %H:%M:%S %Z",
+								pg_localtime(&stamp_time, log_timezone));
 					appendStringInfoString(buf, strfbuf);
 				}
 				break;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c30d8b50a05f4061a2f422448bbfbce1a3d7681b..fc35b14cf3f3b58d80b23c6ab7e343cfe9b483a4 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.408 2007/08/01 22:45:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.409 2007/08/04 01:26:54 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -255,6 +255,7 @@ static char *server_encoding_string;
 static char *server_version_string;
 static int	server_version_num;
 static char *timezone_string;
+static char *log_timezone_string;
 static char *timezone_abbreviations_string;
 static char *XactIsoLevel_string;
 static char *data_directory;
@@ -1984,6 +1985,14 @@ static struct config_string ConfigureNamesString[] =
 		"", NULL, NULL
 	},
 
+	{
+		{"log_timezone", PGC_SIGHUP, LOGGING_WHAT,
+			gettext_noop("Sets the time zone to use in log messages."),
+			NULL
+		},
+		&log_timezone_string,
+		"UNKNOWN", assign_log_timezone, show_log_timezone
+	},
 
 	{
 		{"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c87e4baf43dfe1004cc17b4c9ca783f659c90743..ef6cae299cbea7517d0cb2b6fb05c3c40cb4813a 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -347,6 +347,8 @@
 #log_temp_files = -1			# Log temporary files equal or larger
 					# than specified number of kilobytes.
 					# -1 disables;  0 logs all temp files
+#log_timezone = unknown			# actually, defaults to TZ 
+					# environment setting
 
 #---------------------------------------------------------------------------
 # RUNTIME STATISTICS
diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h
index b1b308fbd0ea5ad090c9f8dc6ee149e261142e6e..ba26233e7dec02ffec74905fec0435d15b36666f 100644
--- a/src/include/commands/variable.h
+++ b/src/include/commands/variable.h
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/variable.h,v 1.29 2007/01/05 22:19:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/variable.h,v 1.30 2007/08/04 01:26:54 tgl Exp $
  */
 #ifndef VARIABLE_H
 #define VARIABLE_H
@@ -18,6 +18,9 @@ extern const char *assign_datestyle(const char *value,
 extern const char *assign_timezone(const char *value,
 				bool doit, GucSource source);
 extern const char *show_timezone(void);
+extern const char *assign_log_timezone(const char *value,
+				bool doit, GucSource source);
+extern const char *show_log_timezone(void);
 extern const char *assign_XactIsoLevel(const char *value,
 					bool doit, GucSource source);
 extern const char *show_XactIsoLevel(void);
diff --git a/src/include/pgtime.h b/src/include/pgtime.h
index f9056c90e0a09963a1ca9e2e5eb5692ca54e5954..b1ff5eac44d813e70e43d6a9d5c0b1ed853e7967 100644
--- a/src/include/pgtime.h
+++ b/src/include/pgtime.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/include/pgtime.h,v 1.15 2007/01/05 22:19:50 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/include/pgtime.h,v 1.16 2007/08/04 01:26:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,7 +62,8 @@ extern pg_tzenum *pg_tzenumerate_start(void);
 extern pg_tz *pg_tzenumerate_next(pg_tzenum *dir);
 extern void pg_tzenumerate_end(pg_tzenum *dir);
 
-extern pg_tz *global_timezone;
+extern pg_tz *session_timezone;
+extern pg_tz *log_timezone;
 
 /* Maximum length of a timezone name (not including trailing null) */
 #define TZ_STRLEN_MAX 255
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c
index ac97e9815300a453e85df92435546a83517f5286..26ff1ea1129db64803b2cd432f7854e65358a037 100644
--- a/src/timezone/pgtz.c
+++ b/src/timezone/pgtz.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.51 2007/05/31 15:13:06 petere Exp $
+ *	  $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.52 2007/08/04 01:26:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,8 +27,11 @@
 #include "utils/guc.h"
 #include "utils/hsearch.h"
 
-/* Current global timezone */
-pg_tz	   *global_timezone = NULL;
+/* Current session timezone (controlled by TimeZone GUC) */
+pg_tz	   *session_timezone = NULL;
+
+/* Current log timezone (controlled by log_timezone GUC) */
+pg_tz	   *log_timezone = NULL;
 
 
 static char tzdir[MAXPGPATH];
@@ -38,8 +41,8 @@ static bool scan_directory_ci(const char *dirname,
 							  const char *fname, int fnamelen,
 							  char *canonname, int canonnamelen);
 static const char *identify_system_timezone(void);
-static const char *select_default_timezone(void);
-static bool set_global_timezone(const char *tzname);
+static pg_tz *get_pg_tz_for_zone(const char *tzname);
+static pg_tz *select_default_timezone(void);
 
 
 /*
@@ -1196,51 +1199,51 @@ tz_acceptable(pg_tz *tz)
 
 
 /*
- * Set the global timezone. Verify that it's acceptable first.
+ * Get a pg_tz struct for the given timezone name.  Returns NULL if name
+ * is invalid or not an "acceptable" zone.
  */
-static bool
-set_global_timezone(const char *tzname)
+static pg_tz *
+get_pg_tz_for_zone(const char *tzname)
 {
-	pg_tz	   *tznew;
+	pg_tz	   *tz;
 
 	if (!tzname || !tzname[0])
-		return false;
+		return NULL;
 
-	tznew = pg_tzset(tzname);
-	if (!tznew)
-		return false;
+	tz = pg_tzset(tzname);
+	if (!tz)
+		return NULL;
 
-	if (!tz_acceptable(tznew))
-		return false;
+	if (!tz_acceptable(tz))
+		return NULL;
 
-	global_timezone = tznew;
-	return true;
+	return tz;
 }
 
 /*
- * Identify a suitable default timezone setting based on the environment,
- * and make it active.
+ * Identify a suitable default timezone setting based on the environment.
  *
  * We first look to the TZ environment variable.  If not found or not
  * recognized by our own code, we see if we can identify the timezone
  * from the behavior of the system timezone library.  When all else fails,
  * fall back to GMT.
  */
-static const char *
+static pg_tz *
 select_default_timezone(void)
 {
-	const char *def_tz;
+	pg_tz	   *def_tz;
 
-	def_tz = getenv("TZ");
-	if (set_global_timezone(def_tz))
+	def_tz = get_pg_tz_for_zone(getenv("TZ"));
+	if (def_tz)
 		return def_tz;
 
-	def_tz = identify_system_timezone();
-	if (set_global_timezone(def_tz))
+	def_tz = get_pg_tz_for_zone(identify_system_timezone());
+	if (def_tz)
 		return def_tz;
 
-	if (set_global_timezone("GMT"))
-		return "GMT";
+	def_tz = get_pg_tz_for_zone("GMT");
+	if (def_tz)
+		return def_tz;
 
 	ereport(FATAL,
 			(errmsg("could not select a suitable default timezone"),
@@ -1253,19 +1256,34 @@ select_default_timezone(void)
  *
  * This is called after initial loading of postgresql.conf.  If no TimeZone
  * setting was found therein, we try to derive one from the environment.
+ * Likewise for log_timezone.
  */
 void
 pg_timezone_initialize(void)
 {
-	/* Do we need to try to figure the timezone? */
+	pg_tz	   *def_tz = NULL;
+
+	/* Do we need to try to figure the session timezone? */
 	if (pg_strcasecmp(GetConfigOption("timezone"), "UNKNOWN") == 0)
 	{
-		const char *def_tz;
-
 		/* Select setting */
 		def_tz = select_default_timezone();
+		session_timezone = def_tz;
+		/* Tell GUC about the value. Will redundantly call pg_tzset() */
+		SetConfigOption("timezone", pg_get_timezone_name(def_tz),
+						PGC_POSTMASTER, PGC_S_ARGV);
+	}
+
+	/* What about the log timezone? */
+	if (pg_strcasecmp(GetConfigOption("log_timezone"), "UNKNOWN") == 0)
+	{
+		/* Select setting, but don't duplicate work */
+		if (!def_tz)
+			def_tz = select_default_timezone();
+		log_timezone = def_tz;
 		/* Tell GUC about the value. Will redundantly call pg_tzset() */
-		SetConfigOption("timezone", def_tz, PGC_POSTMASTER, PGC_S_ARGV);
+		SetConfigOption("log_timezone", pg_get_timezone_name(def_tz),
+						PGC_POSTMASTER, PGC_S_ARGV);
 	}
 }