Commit bdd6b622 authored by Tom Lane's avatar Tom Lane

Switch over to using the src/timezone functions for formatting timestamps

displayed in the postmaster log.  This avoids Windows-specific problems with
localized time zone names that are in the wrong encoding, and generally seems
like a good idea to forestall other potential platform-dependent issues.
To preserve the existing behavior that all backends will log in the same time
zone, create a new GUC variable log_timezone that can only be changed on a
system-wide basis, and reference log-related calculations to that zone instead
of the TimeZone variable.

This fixes the issue reported by Hiroshi Saito that timestamps printed by
xlog.c startup could be improperly localized on Windows.  We still need a
simpler patch for that problem in the back branches, however.
parent 73852bd5
<!-- $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"> <chapter Id="runtime-config">
<title>Server Configuration</title> <title>Server Configuration</title>
...@@ -2311,13 +2311,15 @@ SELECT * FROM parent WHERE key = 2400; ...@@ -2311,13 +2311,15 @@ SELECT * FROM parent WHERE key = 2400;
When <varname>redirect_stderr</varname> is enabled, this parameter When <varname>redirect_stderr</varname> is enabled, this parameter
sets the file names of the created log files. The value sets the file names of the created log files. The value
is treated as a <systemitem>strftime</systemitem> pattern, is treated as a <systemitem>strftime</systemitem> pattern,
so <literal>%</literal>-escapes so <literal>%</literal>-escapes can be used to specify time-varying
can be used to specify time-varying file names. 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, If no <literal>%</literal>-escapes are present,
<productname>PostgreSQL</productname> will <productname>PostgreSQL</productname> will append the epoch of the new
append the epoch of the new log file's open time. For example, log file's creation time. For example, if
if <varname>log_filename</varname> were <literal>server_log</literal>, then the <varname>log_filename</varname> were <literal>server_log</literal>,
chosen file name would be <literal>server_log.1093827753</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. for a log starting at Sun Aug 29 19:02:33 2004 MST.
This parameter can only be set in the <filename>postgresql.conf</> This parameter can only be set in the <filename>postgresql.conf</>
file or on the server command line. file or on the server command line.
...@@ -2884,7 +2886,7 @@ SELECT * FROM parent WHERE key = 2400; ...@@ -2884,7 +2886,7 @@ SELECT * FROM parent WHERE key = 2400;
</row> </row>
<row> <row>
<entry><literal>%t</literal></entry> <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> <entry>no</entry>
</row> </row>
<row> <row>
...@@ -2909,7 +2911,7 @@ SELECT * FROM parent WHERE key = 2400; ...@@ -2909,7 +2911,7 @@ SELECT * FROM parent WHERE key = 2400;
</row> </row>
<row> <row>
<entry><literal>%s</literal></entry> <entry><literal>%s</literal></entry>
<entry>Session start time stamp</entry> <entry>Process start time stamp</entry>
<entry>no</entry> <entry>no</entry>
</row> </row>
<row> <row>
...@@ -2935,7 +2937,7 @@ SELECT * FROM parent WHERE key = 2400; ...@@ -2935,7 +2937,7 @@ SELECT * FROM parent WHERE key = 2400;
The <literal>%c</> escape prints a quasi-unique session identifier, The <literal>%c</> escape prints a quasi-unique session identifier,
consisting of two 4-byte hexadecimal numbers (without leading zeros) 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 process ID, so <literal>%c</> can also be used as a space saving way
of printing those items. of printing those items.
</para> </para>
...@@ -3036,6 +3038,25 @@ SELECT * FROM parent WHERE key = 2400; ...@@ -3036,6 +3038,25 @@ SELECT * FROM parent WHERE key = 2400;
</listitem> </listitem>
</varlistentry> </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> </variablelist>
</sect2> </sect2>
</sect1> </sect1>
...@@ -3822,9 +3843,9 @@ SET XML OPTION { DOCUMENT | CONTENT }; ...@@ -3822,9 +3843,9 @@ SET XML OPTION { DOCUMENT | CONTENT };
<listitem> <listitem>
<para> <para>
Sets the time zone for displaying and interpreting time stamps. 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 the system environment specifies as the time zone. See <xref
linkend="datatype-datetime"> for more linkend="datatype-timezones"> for more
information. information.
</para> </para>
</listitem> </listitem>
......
<!-- $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"> <chapter id="datatype">
<title id="datatype-title">Data Types</title> <title id="datatype-title">Data Types</title>
...@@ -2234,7 +2234,8 @@ January 8 04:05:06 1999 PST ...@@ -2234,7 +2234,8 @@ January 8 04:05:06 1999 PST
savings transition-date rules as well. The recognized abbreviations savings transition-date rules as well. The recognized abbreviations
are listed in the <literal>pg_timezone_abbrevs</> view (see <xref are listed in the <literal>pg_timezone_abbrevs</> view (see <xref
linkend="view-pg-timezone-abbrevs">). You cannot set the 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 zone abbreviation, but you can use abbreviations in
date/time input values and with the <literal>AT TIME ZONE</> date/time input values and with the <literal>AT TIME ZONE</>
operator. operator.
...@@ -2316,6 +2317,8 @@ January 8 04:05:06 1999 PST ...@@ -2316,6 +2317,8 @@ January 8 04:05:06 1999 PST
behavior of the C library function <literal>localtime()</>. The behavior of the C library function <literal>localtime()</>. The
default time zone is selected as the closest match among default time zone is selected as the closest match among
<productname>PostgreSQL</productname>'s known time zones. <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> </para>
</listitem> </listitem>
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -437,7 +437,7 @@ static void writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
uint32 endLogId, uint32 endLogSeg); uint32 endLogId, uint32 endLogSeg);
static void WriteControlFile(void); static void WriteControlFile(void);
static void ReadControlFile(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); static void issue_xlog_fsync(void);
#ifdef WAL_DEBUG #ifdef WAL_DEBUG
...@@ -4266,13 +4266,13 @@ BootStrapXLOG(void) ...@@ -4266,13 +4266,13 @@ BootStrapXLOG(void)
} }
static char * static char *
str_time(time_t tnow) str_time(pg_time_t tnow)
{ {
static char buf[128]; static char buf[128];
strftime(buf, sizeof(buf), pg_strftime(buf, sizeof(buf),
"%Y-%m-%d %H:%M:%S %Z", "%Y-%m-%d %H:%M:%S %Z",
localtime(&tnow)); pg_localtime(&tnow, log_timezone));
return buf; return buf;
} }
...@@ -6290,7 +6290,7 @@ pg_start_backup(PG_FUNCTION_ARGS) ...@@ -6290,7 +6290,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
char *backupidstr; char *backupidstr;
XLogRecPtr checkpointloc; XLogRecPtr checkpointloc;
XLogRecPtr startpoint; XLogRecPtr startpoint;
time_t stamp_time; pg_time_t stamp_time;
char strfbuf[128]; char strfbuf[128];
char xlogfilename[MAXFNAMELEN]; char xlogfilename[MAXFNAMELEN];
uint32 _logId; uint32 _logId;
...@@ -6368,16 +6368,11 @@ pg_start_backup(PG_FUNCTION_ARGS) ...@@ -6368,16 +6368,11 @@ pg_start_backup(PG_FUNCTION_ARGS)
XLByteToSeg(startpoint, _logId, _logSeg); XLByteToSeg(startpoint, _logId, _logSeg);
XLogFileName(xlogfilename, ThisTimeLineID, _logId, _logSeg); XLogFileName(xlogfilename, ThisTimeLineID, _logId, _logSeg);
/* /* Use the log timezone here, not the session timezone */
* We deliberately use strftime/localtime not the src/timezone stamp_time = (pg_time_t) time(NULL);
* functions, so that backup labels will consistently be recorded in pg_strftime(strfbuf, sizeof(strfbuf),
* the same timezone regardless of TimeZone setting. This matches "%Y-%m-%d %H:%M:%S %Z",
* elog.c's practice. pg_localtime(&stamp_time, log_timezone));
*/
stamp_time = time(NULL);
strftime(strfbuf, sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z",
localtime(&stamp_time));
/* /*
* Check for existing backup label --- implies a backup is already * Check for existing backup label --- implies a backup is already
...@@ -6455,7 +6450,7 @@ pg_stop_backup(PG_FUNCTION_ARGS) ...@@ -6455,7 +6450,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
text *result; text *result;
XLogRecPtr startpoint; XLogRecPtr startpoint;
XLogRecPtr stoppoint; XLogRecPtr stoppoint;
time_t stamp_time; pg_time_t stamp_time;
char strfbuf[128]; char strfbuf[128];
char histfilepath[MAXPGPATH]; char histfilepath[MAXPGPATH];
char startxlogfilename[MAXFNAMELEN]; char startxlogfilename[MAXFNAMELEN];
...@@ -6489,16 +6484,11 @@ pg_stop_backup(PG_FUNCTION_ARGS) ...@@ -6489,16 +6484,11 @@ pg_stop_backup(PG_FUNCTION_ARGS)
XLByteToSeg(stoppoint, _logId, _logSeg); XLByteToSeg(stoppoint, _logId, _logSeg);
XLogFileName(stopxlogfilename, ThisTimeLineID, _logId, _logSeg); XLogFileName(stopxlogfilename, ThisTimeLineID, _logId, _logSeg);
/* /* Use the log timezone here, not the session timezone */
* We deliberately use strftime/localtime not the src/timezone functions, stamp_time = (pg_time_t) time(NULL);
* so that backup labels will consistently be recorded in the same pg_strftime(strfbuf, sizeof(strfbuf),
* timezone regardless of TimeZone setting. This matches elog.c's "%Y-%m-%d %H:%M:%S %Z",
* practice. pg_localtime(&stamp_time, log_timezone));
*/
stamp_time = time(NULL);
strftime(strfbuf, sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z",
localtime(&stamp_time));
/* /*
* Open the existing label file * Open the existing label file
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -344,7 +344,7 @@ assign_timezone(const char *value, bool doit, GucSource source)
*/ */
if (doit) if (doit)
{ {
const char *curzone = pg_get_timezone_name(global_timezone); const char *curzone = pg_get_timezone_name(session_timezone);
if (curzone) if (curzone)
value = curzone; value = curzone;
...@@ -381,7 +381,7 @@ assign_timezone(const char *value, bool doit, GucSource source) ...@@ -381,7 +381,7 @@ assign_timezone(const char *value, bool doit, GucSource source)
if (doit) if (doit)
{ {
/* Save the changed TZ */ /* Save the changed TZ */
global_timezone = new_tz; session_timezone = new_tz;
HasCTZSet = false; HasCTZSet = false;
} }
} }
...@@ -434,7 +434,112 @@ show_timezone(void) ...@@ -434,7 +434,112 @@ show_timezone(void)
IntervalPGetDatum(&interval))); IntervalPGetDatum(&interval)));
} }
else 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) if (tzn != NULL)
return tzn; return tzn;
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1038,7 +1038,6 @@ logfile_getname(pg_time_t timestamp)
{ {
char *filename; char *filename;
int len; int len;
struct pg_tm *tm;
filename = palloc(MAXPGPATH); filename = palloc(MAXPGPATH);
...@@ -1049,8 +1048,8 @@ logfile_getname(pg_time_t timestamp) ...@@ -1049,8 +1048,8 @@ logfile_getname(pg_time_t timestamp)
if (strchr(Log_filename, '%')) if (strchr(Log_filename, '%'))
{ {
/* treat it as a strftime pattern */ /* treat it as a strftime pattern */
tm = pg_localtime(&timestamp, global_timezone); pg_strftime(filename + len, MAXPGPATH - len, Log_filename,
pg_strftime(filename + len, MAXPGPATH - len, Log_filename, tm); pg_localtime(&timestamp, log_timezone));
} }
else else
{ {
...@@ -1079,12 +1078,12 @@ set_next_rotation_time(void) ...@@ -1079,12 +1078,12 @@ set_next_rotation_time(void)
/* /*
* The requirements here are to choose the next time > now that is a * The requirements here are to choose the next time > now that is a
* "multiple" of the log rotation interval. "Multiple" can be interpreted * "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. * GMT.
*/ */
rotinterval = Log_RotationAge * SECS_PER_MINUTE; /* convert to seconds */ rotinterval = Log_RotationAge * SECS_PER_MINUTE; /* convert to seconds */
now = time(NULL); now = (pg_time_t) time(NULL);
tm = pg_localtime(&now, global_timezone); tm = pg_localtime(&now, log_timezone);
now += tm->tm_gmtoff; now += tm->tm_gmtoff;
now -= now % rotinterval; now -= now % rotinterval;
now += rotinterval; now += rotinterval;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -360,7 +360,7 @@ date2timestamptz(DateADT dateVal)
tm->tm_hour = 0; tm->tm_hour = 0;
tm->tm_min = 0; tm->tm_min = 0;
tm->tm_sec = 0; tm->tm_sec = 0;
tz = DetermineTimeZoneOffset(tm, global_timezone); tz = DetermineTimeZoneOffset(tm, session_timezone);
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC; result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
...@@ -2239,7 +2239,7 @@ time_timetz(PG_FUNCTION_ARGS) ...@@ -2239,7 +2239,7 @@ time_timetz(PG_FUNCTION_ARGS)
GetCurrentDateTime(tm); GetCurrentDateTime(tm);
time2tm(time, tm, &fsec); time2tm(time, tm, &fsec);
tz = DetermineTimeZoneOffset(tm, global_timezone); tz = DetermineTimeZoneOffset(tm, session_timezone);
result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -1325,7 +1325,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
if (fmask & DTK_M(DTZMOD)) if (fmask & DTK_M(DTZMOD))
return DTERR_BAD_FORMAT; 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) ...@@ -1361,7 +1361,7 @@ DetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp)
after_isdst; after_isdst;
int res; int res;
if (tzp == global_timezone && HasCTZSet) if (tzp == session_timezone && HasCTZSet)
{ {
tm->tm_isdst = 0; /* for lack of a better idea */ tm->tm_isdst = 0; /* for lack of a better idea */
return CTimeZone; return CTimeZone;
...@@ -2033,7 +2033,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, ...@@ -2033,7 +2033,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
tmp->tm_hour = tm->tm_hour; tmp->tm_hour = tm->tm_hour;
tmp->tm_min = tm->tm_min; tmp->tm_min = tm->tm_min;
tmp->tm_sec = tm->tm_sec; tmp->tm_sec = tm->tm_sec;
*tzp = DetermineTimeZoneOffset(tmp, global_timezone); *tzp = DetermineTimeZoneOffset(tmp, session_timezone);
tm->tm_isdst = tmp->tm_isdst; tm->tm_isdst = tmp->tm_isdst;
} }
......
/* ----------------------------------------------------------------------- /* -----------------------------------------------------------------------
* formatting.c * 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 * Portions Copyright (c) 1999-2007, PostgreSQL Global Development Group
...@@ -3286,7 +3286,7 @@ to_timestamp(PG_FUNCTION_ARGS) ...@@ -3286,7 +3286,7 @@ to_timestamp(PG_FUNCTION_ARGS)
do_to_timestamp(date_txt, fmt, &tm, &fsec); 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) if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
ereport(ERROR, ereport(ERROR,
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -112,7 +112,7 @@ abstime2tm(AbsoluteTime _time, int *tzp, struct pg_tm * tm, char **tzn)
time -= CTimeZone; time -= CTimeZone;
if (!HasCTZSet && tzp != NULL) if (!HasCTZSet && tzp != NULL)
tx = pg_localtime(&time, global_timezone); tx = pg_localtime(&time, session_timezone);
else else
tx = pg_gmtime(&time); tx = pg_gmtime(&time);
...@@ -474,7 +474,7 @@ timestamp_abstime(PG_FUNCTION_ARGS) ...@@ -474,7 +474,7 @@ timestamp_abstime(PG_FUNCTION_ARGS)
result = NOEND_ABSTIME; result = NOEND_ABSTIME;
else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
{ {
tz = DetermineTimeZoneOffset(tm, global_timezone); tz = DetermineTimeZoneOffset(tm, session_timezone);
result = tm2abstime(tm, tz); result = tm2abstime(tm, tz);
} }
else else
...@@ -1591,7 +1591,7 @@ timeofday(PG_FUNCTION_ARGS) ...@@ -1591,7 +1591,7 @@ timeofday(PG_FUNCTION_ARGS)
gettimeofday(&tp, NULL); gettimeofday(&tp, NULL);
tt = (pg_time_t) tp.tv_sec; tt = (pg_time_t) tp.tv_sec;
pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z", 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); snprintf(buf, sizeof(buf), templ, tp.tv_usec);
len = VARHDRSZ + strlen(buf); len = VARHDRSZ + strlen(buf);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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: ...@@ -1494,7 +1494,7 @@ recalc_t:
if ((Timestamp) utime == dt) if ((Timestamp) utime == dt)
{ {
struct pg_tm *tx = pg_localtime(&utime, struct pg_tm *tx = pg_localtime(&utime,
attimezone ? attimezone : global_timezone); attimezone ? attimezone : session_timezone);
tm->tm_year = tx->tm_year + 1900; tm->tm_year = tx->tm_year + 1900;
tm->tm_mon = tx->tm_mon + 1; tm->tm_mon = tx->tm_mon + 1;
...@@ -2675,7 +2675,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS) ...@@ -2675,7 +2675,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) 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]); 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) if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
ereport(ERROR, ereport(ERROR,
...@@ -2699,7 +2699,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS) ...@@ -2699,7 +2699,7 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS)
julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day; julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); 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) if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
ereport(ERROR, ereport(ERROR,
...@@ -3546,7 +3546,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS) ...@@ -3546,7 +3546,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
} }
if (redotz) if (redotz)
tz = DetermineTimeZoneOffset(tm, global_timezone); tz = DetermineTimeZoneOffset(tm, session_timezone);
if (tm2timestamp(tm, fsec, &tz, &result) != 0) if (tm2timestamp(tm, fsec, &tz, &result) != 0)
ereport(ERROR, ereport(ERROR,
...@@ -4010,7 +4010,7 @@ timestamp_part(PG_FUNCTION_ARGS) ...@@ -4010,7 +4010,7 @@ timestamp_part(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp 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) if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
ereport(ERROR, ereport(ERROR,
...@@ -4549,7 +4549,7 @@ timestamp2timestamptz(Timestamp timestamp) ...@@ -4549,7 +4549,7 @@ timestamp2timestamptz(Timestamp timestamp)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp 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) if (tm2timestamp(tm, fsec, &tz, &result) != 0)
ereport(ERROR, ereport(ERROR,
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1495,33 +1495,18 @@ log_line_prefix(StringInfo buf)
break; break;
case 'm': case 'm':
{ {
/* struct timeval tv;
* Note: for %m, %t, and %s we deliberately use the C pg_time_t stamp_time;
* 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;
char strfbuf[128], char strfbuf[128],
msbuf[8]; msbuf[8];
struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
stamp_time = tv.tv_sec; stamp_time = (pg_time_t) tv.tv_sec;
strftime(strfbuf, sizeof(strfbuf), pg_strftime(strfbuf, sizeof(strfbuf),
/* leave room for milliseconds... */ /* leave room for milliseconds... */
/* Win32 timezone names are too long so don't print them */ "%Y-%m-%d %H:%M:%S %Z",
#ifndef WIN32 pg_localtime(&stamp_time, log_timezone));
"%Y-%m-%d %H:%M:%S %Z",
#else
"%Y-%m-%d %H:%M:%S ",
#endif
localtime(&stamp_time));
/* 'paste' milliseconds into place... */ /* 'paste' milliseconds into place... */
sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000)); sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000));
...@@ -1532,32 +1517,23 @@ log_line_prefix(StringInfo buf) ...@@ -1532,32 +1517,23 @@ log_line_prefix(StringInfo buf)
break; break;
case 't': case 't':
{ {
time_t stamp_time = time(NULL); pg_time_t stamp_time = (pg_time_t) time(NULL);
char strfbuf[128]; char strfbuf[128];
strftime(strfbuf, sizeof(strfbuf), pg_strftime(strfbuf, sizeof(strfbuf),
/* Win32 timezone names are too long so don't print them */ "%Y-%m-%d %H:%M:%S %Z",
#ifndef WIN32 pg_localtime(&stamp_time, log_timezone));
"%Y-%m-%d %H:%M:%S %Z",
#else
"%Y-%m-%d %H:%M:%S",
#endif
localtime(&stamp_time));
appendStringInfoString(buf, strfbuf); appendStringInfoString(buf, strfbuf);
} }
break; break;
case 's': case 's':
{ {
pg_time_t stamp_time = (pg_time_t) MyStartTime;
char strfbuf[128]; char strfbuf[128];
strftime(strfbuf, sizeof(strfbuf), pg_strftime(strfbuf, sizeof(strfbuf),
/* Win32 timezone names are too long so don't print them */ "%Y-%m-%d %H:%M:%S %Z",
#ifndef WIN32 pg_localtime(&stamp_time, log_timezone));
"%Y-%m-%d %H:%M:%S %Z",
#else
"%Y-%m-%d %H:%M:%S",
#endif
localtime(&MyStartTime));
appendStringInfoString(buf, strfbuf); appendStringInfoString(buf, strfbuf);
} }
break; break;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * 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; ...@@ -255,6 +255,7 @@ static char *server_encoding_string;
static char *server_version_string; static char *server_version_string;
static int server_version_num; static int server_version_num;
static char *timezone_string; static char *timezone_string;
static char *log_timezone_string;
static char *timezone_abbreviations_string; static char *timezone_abbreviations_string;
static char *XactIsoLevel_string; static char *XactIsoLevel_string;
static char *data_directory; static char *data_directory;
...@@ -1984,6 +1985,14 @@ static struct config_string ConfigureNamesString[] = ...@@ -1984,6 +1985,14 @@ static struct config_string ConfigureNamesString[] =
"", NULL, NULL "", 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, {"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
......
...@@ -347,6 +347,8 @@ ...@@ -347,6 +347,8 @@
#log_temp_files = -1 # Log temporary files equal or larger #log_temp_files = -1 # Log temporary files equal or larger
# than specified number of kilobytes. # than specified number of kilobytes.
# -1 disables; 0 logs all temp files # -1 disables; 0 logs all temp files
#log_timezone = unknown # actually, defaults to TZ
# environment setting
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# RUNTIME STATISTICS # RUNTIME STATISTICS
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 #ifndef VARIABLE_H
#define VARIABLE_H #define VARIABLE_H
...@@ -18,6 +18,9 @@ extern const char *assign_datestyle(const char *value, ...@@ -18,6 +18,9 @@ extern const char *assign_datestyle(const char *value,
extern const char *assign_timezone(const char *value, extern const char *assign_timezone(const char *value,
bool doit, GucSource source); bool doit, GucSource source);
extern const char *show_timezone(void); 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, extern const char *assign_XactIsoLevel(const char *value,
bool doit, GucSource source); bool doit, GucSource source);
extern const char *show_XactIsoLevel(void); extern const char *show_XactIsoLevel(void);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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); ...@@ -62,7 +62,8 @@ extern pg_tzenum *pg_tzenumerate_start(void);
extern pg_tz *pg_tzenumerate_next(pg_tzenum *dir); extern pg_tz *pg_tzenumerate_next(pg_tzenum *dir);
extern void pg_tzenumerate_end(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) */ /* Maximum length of a timezone name (not including trailing null) */
#define TZ_STRLEN_MAX 255 #define TZ_STRLEN_MAX 255
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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 @@ ...@@ -27,8 +27,11 @@
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/hsearch.h" #include "utils/hsearch.h"
/* Current global timezone */ /* Current session timezone (controlled by TimeZone GUC) */
pg_tz *global_timezone = NULL; pg_tz *session_timezone = NULL;
/* Current log timezone (controlled by log_timezone GUC) */
pg_tz *log_timezone = NULL;
static char tzdir[MAXPGPATH]; static char tzdir[MAXPGPATH];
...@@ -38,8 +41,8 @@ static bool scan_directory_ci(const char *dirname, ...@@ -38,8 +41,8 @@ static bool scan_directory_ci(const char *dirname,
const char *fname, int fnamelen, const char *fname, int fnamelen,
char *canonname, int canonnamelen); char *canonname, int canonnamelen);
static const char *identify_system_timezone(void); static const char *identify_system_timezone(void);
static const char *select_default_timezone(void); static pg_tz *get_pg_tz_for_zone(const char *tzname);
static bool set_global_timezone(const char *tzname); static pg_tz *select_default_timezone(void);
/* /*
...@@ -1196,51 +1199,51 @@ tz_acceptable(pg_tz *tz) ...@@ -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 static pg_tz *
set_global_timezone(const char *tzname) get_pg_tz_for_zone(const char *tzname)
{ {
pg_tz *tznew; pg_tz *tz;
if (!tzname || !tzname[0]) if (!tzname || !tzname[0])
return false; return NULL;
tznew = pg_tzset(tzname); tz = pg_tzset(tzname);
if (!tznew) if (!tz)
return false; return NULL;
if (!tz_acceptable(tznew)) if (!tz_acceptable(tz))
return false; return NULL;
global_timezone = tznew; return tz;
return true;
} }
/* /*
* Identify a suitable default timezone setting based on the environment, * Identify a suitable default timezone setting based on the environment.
* and make it active.
* *
* We first look to the TZ environment variable. If not found or not * 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 * 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, * from the behavior of the system timezone library. When all else fails,
* fall back to GMT. * fall back to GMT.
*/ */
static const char * static pg_tz *
select_default_timezone(void) select_default_timezone(void)
{ {
const char *def_tz; pg_tz *def_tz;
def_tz = getenv("TZ"); def_tz = get_pg_tz_for_zone(getenv("TZ"));
if (set_global_timezone(def_tz)) if (def_tz)
return def_tz; return def_tz;
def_tz = identify_system_timezone(); def_tz = get_pg_tz_for_zone(identify_system_timezone());
if (set_global_timezone(def_tz)) if (def_tz)
return def_tz; return def_tz;
if (set_global_timezone("GMT")) def_tz = get_pg_tz_for_zone("GMT");
return "GMT"; if (def_tz)
return def_tz;
ereport(FATAL, ereport(FATAL,
(errmsg("could not select a suitable default timezone"), (errmsg("could not select a suitable default timezone"),
...@@ -1253,19 +1256,34 @@ select_default_timezone(void) ...@@ -1253,19 +1256,34 @@ select_default_timezone(void)
* *
* This is called after initial loading of postgresql.conf. If no TimeZone * This is called after initial loading of postgresql.conf. If no TimeZone
* setting was found therein, we try to derive one from the environment. * setting was found therein, we try to derive one from the environment.
* Likewise for log_timezone.
*/ */
void void
pg_timezone_initialize(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) if (pg_strcasecmp(GetConfigOption("timezone"), "UNKNOWN") == 0)
{ {
const char *def_tz;
/* Select setting */ /* Select setting */
def_tz = select_default_timezone(); 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() */ /* 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);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment