Switch over to using the src/timezone functions for formatting timestamps
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Aug 2007 01:26:54 +0000 (01:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 4 Aug 2007 01:26:54 +0000 (01:26 +0000)
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.

16 files changed:
doc/src/sgml/config.sgml
doc/src/sgml/datatype.sgml
src/backend/access/transam/xlog.c
src/backend/commands/variable.c
src/backend/postmaster/syslogger.c
src/backend/utils/adt/date.c
src/backend/utils/adt/datetime.c
src/backend/utils/adt/formatting.c
src/backend/utils/adt/nabstime.c
src/backend/utils/adt/timestamp.c
src/backend/utils/error/elog.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/commands/variable.h
src/include/pgtime.h
src/timezone/pgtz.c

index 4099fbc32b7bd8960ed06838de68b9299509a531..f635f2b56614ba4fa8296220e5b06b31198bb838 100644 (file)
@@ -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>
index f3bc210ce096e601b5e5bd794b7a0648e49b9665..8545af1cc01226dd13efb397656aedc6e9e28bee 100644 (file)
@@ -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>
 
index 4527ce1b180330b5d170a56b287da30b0b247fdb..25f4b37a7c003c5bad28965b687f47c34eb12a2a 100644 (file)
@@ -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
index 07d3b22705d331c1950bb1b48696f143dad4e535..941ec888037661ac4d6996b803e2114861fed1b4 100644 (file)
@@ -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;
index 786a358e4063d932efe2b7d8cb4561f9b6679809..e23019afc13ca644c56179b6e0620b7a3c0737c9 100644 (file)
@@ -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;
index 4257ede403ff2e3aa57505ad1ec3a2ebec167d28..79c0fb589b7ef9d05533273775ded3901777546c 100644 (file)
@@ -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));
 
index 07798c55e708fb246f5e8d4cc23983ac4bc720e6..31ebad0bef4b62179d6c0871cd7ff73ffef8c480 100644 (file)
@@ -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;
        }
 
index 8d0f4d96d41e6c844dd74d942f5452f27c31fa09..2298b247841869b5bd17b81e4e44aa5707f8c137 100644 (file)
@@ -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,
index 72781eac66a3ae0a29e2cb99e1d6ba92854055ef..a2f5c555ffaee49d24b4e264775b6c8f07a857ba 100644 (file)
@@ -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);
index ff850b551be6f88a27d3abb59880e55713f9c160..c28cfff6ccc4ab1ef20ef4acd13ce1cc918b6be0 100644 (file)
@@ -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,
index 60f3620c29a43a95bf41772ad510a21c04e47e91..e4832f373ef475e5a4d7c9730966761a747b5206 100644 (file)
@@ -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;
index 19fd266f97245cca8cafaa9ce426a915721eac18..ec336871824957ce06f3a75cb03df4237d623ad4 100644 (file)
@@ -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,
index c87e4baf43dfe1004cc17b4c9ca783f659c90743..ef6cae299cbea7517d0cb2b6fb05c3c40cb4813a 100644 (file)
 #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
index ed94a5d5216d4b662657f4fccfc0f92eb70477a8..31ae2cf8eb5be1e551d37d580498c5a1db42e37d 100644 (file)
@@ -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);
index 319929f08e25d4a485a58dad525a2a86fbc6326c..8f126831264f534232916610cb3f81e7d6d197fe 100644 (file)
@@ -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
index 49a583dfd2a2e7b01fc973a484da2a8a39846554..211f999a56ebd11d5bb5306e2c8e524df15020a8 100644 (file)
 #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);
        }
 }