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(×tamp, global_timezone); - pg_strftime(filename + len, MAXPGPATH - len, Log_filename, tm); + pg_strftime(filename + len, MAXPGPATH - len, Log_filename, + pg_localtime(×tamp, 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, ×tamp) != 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, ×tamp) != 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, ×tamptz) != 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); } }