Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff -u -r -b -B postgresql-9.2.1a/doc/src/sgml/config.sgml postgresql-9.2.1/doc/src/sgml/config.sgml
- --- postgresql-9.2.1a/doc/src/sgml/config.sgml Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/doc/src/sgml/config.sgml Sat Dec 15 03:45:00 2012
- @@ -4119,10 +4119,9 @@
- Sets the time zone used for timestamps written in the server log.
- Unlike <xref linkend="guc-timezone">, this value is cluster-wide,
- so that all sessions will report timestamps consistently.
- - The built-in default is <literal>GMT</>, but that is typically
- - overridden in <filename>postgresql.conf</>; <application>initdb</>
- - will install a setting there corresponding to its system environment.
- - See <xref linkend="datatype-timezones"> for more information.
- + If not explicitly set, the server initializes this variable to the
- + time zone specified by its system environment. 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>
- @@ -5195,10 +5194,9 @@
- <listitem>
- <para>
- Sets the time zone for displaying and interpreting time stamps.
- - The built-in default is <literal>GMT</>, but that is typically
- - overridden in <filename>postgresql.conf</>; <application>initdb</>
- - will install a setting there corresponding to its system environment.
- - See <xref linkend="datatype-timezones"> for more information.
- + If not explicitly set, the server initializes this variable to the
- + time zone specified by its system environment. See <xref
- + linkend="datatype-timezones"> for more information.
- </para>
- </listitem>
- </varlistentry>
- diff -u -r -b -B postgresql-9.2.1a/doc/src/sgml/datatype.sgml postgresql-9.2.1/doc/src/sgml/datatype.sgml
- --- postgresql-9.2.1a/doc/src/sgml/datatype.sgml Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/doc/src/sgml/datatype.sgml Sat Dec 15 03:45:00 2012
- @@ -2322,7 +2322,7 @@
- but continue to be prone to arbitrary changes, particularly with
- respect to daylight-savings rules.
- <productname>PostgreSQL</productname> uses the widely-used
- - <literal>zoneinfo</> (Olson) time zone database for information about
- + <literal>zoneinfo</> time zone database for information about
- historical time zone rules. For times in the future, the assumption
- is that the latest known rules for a given time zone will
- continue to be observed indefinitely far into the future.
- @@ -2468,11 +2468,28 @@
- The <xref linkend="guc-timezone"> configuration parameter can
- be set in the file <filename>postgresql.conf</>, or in any of the
- other standard ways described in <xref linkend="runtime-config">.
- - There are also some special ways to set it:
- + There are also several special ways to set it:
- <itemizedlist>
- <listitem>
- <para>
- + If <varname>timezone</> is not specified in
- + <filename>postgresql.conf</> or as a server command-line option,
- + the server attempts to use the value of the <envar>TZ</envar>
- + environment variable as the default time zone. If <envar>TZ</envar>
- + is not defined or is not any of the time zone names known to
- + <productname>PostgreSQL</productname>, the server attempts to
- + determine the operating system's default time zone by checking the
- + 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 not specified.)
- + </para>
- + </listitem>
- +
- + <listitem>
- + <para>
- The <acronym>SQL</acronym> command <command>SET TIME ZONE</command>
- sets the time zone for the session. This is an alternative spelling
- of <command>SET TIMEZONE TO</> with a more SQL-spec-compatible syntax.
- diff -u -r -b -B postgresql-9.2.1a/doc/src/sgml/ref/set.sgml postgresql-9.2.1/doc/src/sgml/ref/set.sgml
- --- postgresql-9.2.1a/doc/src/sgml/ref/set.sgml Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/doc/src/sgml/ref/set.sgml Sat Dec 15 03:45:00 2012
- @@ -239,7 +239,9 @@
- <listitem>
- <para>
- Set the time zone to your local time zone (that is, the
- - server's default value of <varname>timezone</>).
- + server's default value of <varname>timezone</>; if this
- + has not been explicitly set anywhere, it will be the zone that
- + the server's operating system defaults to).
- </para>
- </listitem>
- </varlistentry>
- diff -u -r -b -B postgresql-9.2.1a/src/backend/bootstrap/bootstrap.c postgresql-9.2.1/src/backend/bootstrap/bootstrap.c
- --- postgresql-9.2.1a/src/backend/bootstrap/bootstrap.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/bootstrap/bootstrap.c Sat Dec 15 03:45:00 2012
- @@ -341,6 +341,10 @@
- {
- if (!SelectConfigFiles(userDoption, progname))
- proc_exit(1);
- + /* If timezone is not set, determine what the OS uses */
- + pg_timezone_initialize();
- + /* If timezone_abbreviations is not set, select default */
- + pg_timezone_abbrev_initialize();
- }
- /* Validate we have been given a reasonable-looking DataDir */
- diff -u -r -b -B postgresql-9.2.1a/src/backend/commands/variable.c postgresql-9.2.1/src/backend/commands/variable.c
- --- postgresql-9.2.1a/src/backend/commands/variable.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/commands/variable.c Sat Dec 15 03:45:00 2012
- @@ -259,6 +259,23 @@
- char *endptr;
- double hours;
- + if (*newval == NULL)
- + {
- + /*
- + * The boot_val given for TimeZone in guc.c is NULL. When we see this
- + * we just do nothing. If this isn't overridden from the config file
- + * then pg_timezone_initialize() will eventually select a default
- + * value from the environment. This hack has two purposes: to avoid
- + * wasting cycles loading values that might soon be overridden from
- + * the config file, and to avoid trying to read the timezone files
- + * during InitializeGUCOptions(). The latter doesn't work in an
- + * EXEC_BACKEND subprocess because my_exec_path hasn't been set yet
- + * and so we can't locate PGSHAREDIR.
- + */
- + Assert(source == PGC_S_DEFAULT);
- + return true;
- + }
- +
- /*
- * Initialize the "extra" struct that will be passed to assign_timezone.
- * We don't want to change any of the three global variables except as
- @@ -357,7 +374,7 @@
- return false;
- }
- - if (!pg_tz_acceptable(new_tz))
- + if (!tz_acceptable(new_tz))
- {
- GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
- *newval);
- @@ -410,6 +427,10 @@
- {
- timezone_extra *myextra = (timezone_extra *) extra;
- + /* Do nothing for the boot_val default of NULL */
- + if (!myextra)
- + return;
- +
- session_timezone = myextra->session_timezone;
- CTimeZone = myextra->CTimeZone;
- HasCTZSet = myextra->HasCTZSet;
- @@ -469,8 +490,20 @@
- {
- pg_tz *new_tz;
- + if (*newval == NULL)
- + {
- /*
- - * Assume it is a timezone name, and try to load it.
- + * The boot_val given for log_timezone in guc.c is NULL. When we see
- + * this we just do nothing. If this isn't overridden from the config
- + * file then pg_timezone_initialize() will eventually select a default
- + * value from the environment.
- + */
- + Assert(source == PGC_S_DEFAULT);
- + return true;
- + }
- +
- + /*
- + * Otherwise assume it is a timezone name, and try to load it.
- */
- new_tz = pg_tzset(*newval);
- @@ -480,7 +513,7 @@
- return false;
- }
- - if (!pg_tz_acceptable(new_tz))
- + if (!tz_acceptable(new_tz))
- {
- GUC_check_errmsg("time zone \"%s\" appears to use leap seconds",
- *newval);
- @@ -505,6 +538,10 @@
- void
- assign_log_timezone(const char *newval, void *extra)
- {
- + /* Do nothing for the boot_val default of NULL */
- + if (!extra)
- + return;
- +
- log_timezone = *((pg_tz **) extra);
- }
- diff -u -r -b -B postgresql-9.2.1a/src/backend/postmaster/postmaster.c postgresql-9.2.1/src/backend/postmaster/postmaster.c
- --- postgresql-9.2.1a/src/backend/postmaster/postmaster.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/postmaster/postmaster.c Sat Dec 15 03:45:00 2012
- @@ -824,6 +824,21 @@
- CreateDataDirLockFile(true);
- /*
- + * If timezone is not set, determine what the OS uses. (In theory this
- + * should be done during GUC initialization, but because it can take as
- + * much as several seconds, we delay it until after we've created the
- + * postmaster.pid file. This prevents problems with boot scripts that
- + * expect the pidfile to appear quickly. Also, we avoid problems with
- + * trying to locate the timezone files too early in initialization.)
- + */
- + pg_timezone_initialize();
- +
- + /*
- + * Likewise, init timezone_abbreviations if not already set.
- + */
- + pg_timezone_abbrev_initialize();
- +
- + /*
- * Initialize SSL library, if specified.
- */
- #ifdef USE_SSL
- diff -u -r -b -B postgresql-9.2.1a/src/backend/tcop/postgres.c postgresql-9.2.1/src/backend/tcop/postgres.c
- --- postgresql-9.2.1a/src/backend/tcop/postgres.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/tcop/postgres.c Sat Dec 15 03:45:00 2012
- @@ -3575,6 +3575,10 @@
- {
- if (!SelectConfigFiles(userDoption, progname))
- proc_exit(1);
- + /* If timezone is not set, determine what the OS uses */
- + pg_timezone_initialize();
- + /* If timezone_abbreviations is not set, select default */
- + pg_timezone_abbrev_initialize();
- }
- /*
- diff -u -r -b -B postgresql-9.2.1a/src/backend/utils/error/elog.c postgresql-9.2.1/src/backend/utils/error/elog.c
- --- postgresql-9.2.1a/src/backend/utils/error/elog.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/utils/error/elog.c Sat Dec 15 15:05:08 2012
- @@ -1851,20 +1851,24 @@
- {
- struct timeval tv;
- pg_time_t stamp_time;
- + pg_tz *tz;
- char msbuf[8];
- gettimeofday(&tv, NULL);
- stamp_time = (pg_time_t) tv.tv_sec;
- /*
- - * Note: we expect that guc.c will ensure that log_timezone is set up (at
- - * least with a minimal GMT value) before Log_line_prefix can become
- - * nonempty or CSV mode can be selected.
- + * Normally we print log timestamps in log_timezone, but during startup we
- + * could get here before that's set. If so, fall back to gmt_timezone
- + * (which guc.c ensures is set up before Log_line_prefix can become
- + * nonempty).
- */
- + tz = log_timezone ? log_timezone : gmt_timezone;
- +
- pg_strftime(formatted_log_time, FORMATTED_TS_LEN,
- /* leave room for milliseconds... */
- "%Y-%m-%d %H:%M:%S %Z",
- - pg_localtime(&stamp_time, log_timezone));
- + pg_localtime(&stamp_time, tz));
- /* 'paste' milliseconds into place... */
- sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000));
- @@ -1878,15 +1882,19 @@
- setup_formatted_start_time(void)
- {
- pg_time_t stamp_time = (pg_time_t) MyStartTime;
- + pg_tz *tz;
- /*
- - * Note: we expect that guc.c will ensure that log_timezone is set up (at
- - * least with a minimal GMT value) before Log_line_prefix can become
- - * nonempty or CSV mode can be selected.
- + * Normally we print log timestamps in log_timezone, but during startup we
- + * could get here before that's set. If so, fall back to gmt_timezone
- + * (which guc.c ensures is set up before Log_line_prefix can become
- + * nonempty).
- */
- + tz = log_timezone ? log_timezone : gmt_timezone;
- +
- pg_strftime(formatted_start_time, FORMATTED_TS_LEN,
- "%Y-%m-%d %H:%M:%S %Z",
- - pg_localtime(&stamp_time, log_timezone));
- + pg_localtime(&stamp_time, tz));
- }
- /*
- @@ -1985,11 +1993,14 @@
- case 't':
- {
- pg_time_t stamp_time = (pg_time_t) time(NULL);
- + pg_tz *tz;
- char strfbuf[128];
- + tz = log_timezone ? log_timezone : gmt_timezone;
- +
- pg_strftime(strfbuf, sizeof(strfbuf),
- "%Y-%m-%d %H:%M:%S %Z",
- - pg_localtime(&stamp_time, log_timezone));
- + pg_localtime(&stamp_time, tz));
- appendStringInfoString(buf, strfbuf);
- }
- break;
- diff -u -r -b -B postgresql-9.2.1a/src/backend/utils/misc/guc-file.c postgresql-9.2.1/src/backend/utils/misc/guc-file.c
- --- postgresql-9.2.1a/src/backend/utils/misc/guc-file.c Wed Sep 19 18:03:44 2012
- +++ postgresql-9.2.1/src/backend/utils/misc/guc-file.c Sat Dec 15 03:54:30 2012
- @@ -1,6 +1,6 @@
- -#line 2 "guc-file.c"
- +#line 2 "src\\backend\\utils\\misc\\guc-file.c"
- -#line 4 "guc-file.c"
- +#line 4 "src\\backend\\utils\\misc\\guc-file.c"
- #define YY_INT_ALIGNED short int
- @@ -360,7 +360,7 @@
- /* Begin user sect3 */
- -#define GUC_yywrap(n) 1
- +#define GUC_yywrap() 1
- #define YY_SKIP_YYWRAP
- typedef unsigned char YY_CHAR;
- @@ -522,7 +522,7 @@
- #define YY_MORE_ADJ 0
- #define YY_RESTORE_YY_MORE_OFFSET
- char *GUC_yytext;
- -#line 1 "guc-file.l"
- +#line 1 "src\\backend\\utils\\misc\\guc-file.l"
- /* -*-pgsql-c-*- */
- /*
- * Scanner for the configuration file
- @@ -531,7 +531,7 @@
- *
- * src/backend/utils/misc/guc-file.l
- */
- -#line 11 "guc-file.l"
- +#line 11 "src\\backend\\utils\\misc\\guc-file.l"
- #include "postgres.h"
- @@ -576,7 +576,7 @@
- static char *GUC_scanstr(const char *s);
- #define YY_NO_INPUT 1
- -#line 580 "guc-file.c"
- +#line 580 "src\\backend\\utils\\misc\\guc-file.c"
- #define INITIAL 0
- @@ -761,10 +761,10 @@
- register char *yy_cp, *yy_bp;
- register int yy_act;
- -#line 86 "guc-file.l"
- +#line 86 "src\\backend\\utils\\misc\\guc-file.l"
- -#line 768 "guc-file.c"
- +#line 768 "src\\backend\\utils\\misc\\guc-file.c"
- if ( !(yy_init) )
- {
- @@ -846,65 +846,65 @@
- case 1:
- /* rule 1 can match eol */
- YY_RULE_SETUP
- -#line 88 "guc-file.l"
- +#line 88 "src\\backend\\utils\\misc\\guc-file.l"
- ConfigFileLineno++; return GUC_EOL;
- YY_BREAK
- case 2:
- YY_RULE_SETUP
- -#line 89 "guc-file.l"
- +#line 89 "src\\backend\\utils\\misc\\guc-file.l"
- /* eat whitespace */
- YY_BREAK
- case 3:
- YY_RULE_SETUP
- -#line 90 "guc-file.l"
- +#line 90 "src\\backend\\utils\\misc\\guc-file.l"
- /* eat comment (.* matches anything until newline) */
- YY_BREAK
- case 4:
- YY_RULE_SETUP
- -#line 92 "guc-file.l"
- +#line 92 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_ID;
- YY_BREAK
- case 5:
- YY_RULE_SETUP
- -#line 93 "guc-file.l"
- +#line 93 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_QUALIFIED_ID;
- YY_BREAK
- case 6:
- YY_RULE_SETUP
- -#line 94 "guc-file.l"
- +#line 94 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_STRING;
- YY_BREAK
- case 7:
- YY_RULE_SETUP
- -#line 95 "guc-file.l"
- +#line 95 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_UNQUOTED_STRING;
- YY_BREAK
- case 8:
- YY_RULE_SETUP
- -#line 96 "guc-file.l"
- +#line 96 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_INTEGER;
- YY_BREAK
- case 9:
- YY_RULE_SETUP
- -#line 97 "guc-file.l"
- +#line 97 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_REAL;
- YY_BREAK
- case 10:
- YY_RULE_SETUP
- -#line 98 "guc-file.l"
- +#line 98 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_EQUALS;
- YY_BREAK
- case 11:
- YY_RULE_SETUP
- -#line 100 "guc-file.l"
- +#line 100 "src\\backend\\utils\\misc\\guc-file.l"
- return GUC_ERROR;
- YY_BREAK
- case 12:
- YY_RULE_SETUP
- -#line 102 "guc-file.l"
- +#line 102 "src\\backend\\utils\\misc\\guc-file.l"
- YY_FATAL_ERROR( "flex scanner jammed" );
- YY_BREAK
- -#line 908 "guc-file.c"
- +#line 908 "src\\backend\\utils\\misc\\guc-file.c"
- case YY_STATE_EOF(INITIAL):
- yyterminate();
- @@ -1862,7 +1862,7 @@
- #define YYTABLES_NAME "yytables"
- -#line 102 "guc-file.l"
- +#line 102 "src\\backend\\utils\\misc\\guc-file.l"
- @@ -2038,6 +2038,7 @@
- if (context == PGC_SIGHUP)
- {
- InitializeGUCOptionsFromEnvironment();
- + pg_timezone_initialize();
- pg_timezone_abbrev_initialize();
- /* this selects SQL_ASCII in processes not connected to a database */
- SetConfigOption("client_encoding", GetDatabaseEncodingName(),
- diff -u -r -b -B postgresql-9.2.1a/src/backend/utils/misc/guc-file.l postgresql-9.2.1/src/backend/utils/misc/guc-file.l
- --- postgresql-9.2.1a/src/backend/utils/misc/guc-file.l Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/utils/misc/guc-file.l Sat Dec 15 03:45:00 2012
- @@ -273,6 +273,7 @@
- if (context == PGC_SIGHUP)
- {
- InitializeGUCOptionsFromEnvironment();
- + pg_timezone_initialize();
- pg_timezone_abbrev_initialize();
- /* this selects SQL_ASCII in processes not connected to a database */
- SetConfigOption("client_encoding", GetDatabaseEncodingName(),
- diff -u -r -b -B postgresql-9.2.1a/src/backend/utils/misc/guc.c postgresql-9.2.1/src/backend/utils/misc/guc.c
- --- postgresql-9.2.1a/src/backend/utils/misc/guc.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/utils/misc/guc.c Sat Dec 15 15:07:29 2012
- @@ -187,7 +187,6 @@
- static bool check_canonical_path(char **newval, void **extra, GucSource source);
- static bool check_timezone_abbreviations(char **newval, void **extra, GucSource source);
- static void assign_timezone_abbreviations(const char *newval, void *extra);
- -static void pg_timezone_abbrev_initialize(void);
- static const char *show_archive_command(void);
- static void assign_tcp_keepalives_idle(int newval, void *extra);
- static void assign_tcp_keepalives_interval(int newval, void *extra);
- @@ -2567,7 +2566,7 @@
- NULL
- },
- &log_timezone_string,
- - "GMT",
- + NULL,
- check_log_timezone, assign_log_timezone, show_log_timezone
- },
- @@ -2858,7 +2857,7 @@
- GUC_REPORT
- },
- &timezone_string,
- - "GMT",
- + NULL,
- check_timezone, assign_timezone, show_timezone
- },
- {
- @@ -3841,7 +3840,7 @@
- * Before log_line_prefix could possibly receive a nonempty setting, make
- * sure that timezone processing is minimally alive (see elog.c).
- */
- - pg_timezone_initialize();
- + pg_timezone_pre_initialize();
- /*
- * Build sorted array of all GUC variables.
- @@ -4142,15 +4141,6 @@
- SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE);
- /*
- - * If timezone_abbreviations wasn't set in the configuration file, install
- - * the default value. We do it this way because we can't safely install a
- - * "real" value until my_exec_path is set, which may not have happened
- - * when InitializeGUCOptions runs, so the bootstrap default value cannot
- - * be the real desired default.
- - */
- - pg_timezone_abbrev_initialize();
- -
- - /*
- * Figure out where pg_hba.conf is, and make sure the path is absolute.
- */
- if (HbaFileName)
- @@ -8521,11 +8511,8 @@
- * This is called after initial loading of postgresql.conf. If no
- * timezone_abbreviations setting was found therein, select default.
- * If a non-default value is already installed, nothing will happen.
- - *
- - * This can also be called from ProcessConfigFile to establish the default
- - * value after a postgresql.conf entry for it is removed.
- */
- -static void
- +void
- pg_timezone_abbrev_initialize(void)
- {
- SetConfigOption("timezone_abbreviations", "Default",
- diff -u -r -b -B postgresql-9.2.1a/src/backend/utils/misc/postgresql.conf.sample postgresql-9.2.1/src/backend/utils/misc/postgresql.conf.sample
- --- postgresql-9.2.1a/src/backend/utils/misc/postgresql.conf.sample Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/backend/utils/misc/postgresql.conf.sample Sat Dec 15 03:45:00 2012
- @@ -414,7 +414,7 @@
- #log_temp_files = -1 # log temporary files equal or larger
- # than the specified size in kilobytes;
- # -1 disables, 0 logs all temp files
- -#log_timezone = 'GMT'
- +#log_timezone = '(defaults to server environment setting)'
- #------------------------------------------------------------------------------
- @@ -495,7 +495,7 @@
- #datestyle = 'iso, mdy'
- #intervalstyle = 'postgres'
- -#timezone = 'GMT'
- +#timezone = '(defaults to server environment setting)'
- #timezone_abbreviations = 'Default' # Select the set of available time zone
- # abbreviations. Currently, there are
- # Default
- diff -u -r -b -B postgresql-9.2.1a/src/bin/initdb/.gitignore postgresql-9.2.1/src/bin/initdb/.gitignore
- --- postgresql-9.2.1a/src/bin/initdb/.gitignore Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/bin/initdb/.gitignore Sat Dec 15 03:45:01 2012
- @@ -1,5 +1,4 @@
- /encnames.c
- /pqsignal.c
- -/localtime.c
- /initdb
- diff -u -r -b -B postgresql-9.2.1a/src/bin/initdb/Makefile postgresql-9.2.1/src/bin/initdb/Makefile
- --- postgresql-9.2.1a/src/bin/initdb/Makefile Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/bin/initdb/Makefile Sat Dec 15 15:09:42 2012
- @@ -16,14 +16,14 @@
- top_builddir = ../../..
- include $(top_builddir)/src/Makefile.global
- -override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) -I$(top_srcdir)/src/timezone $(CPPFLAGS)
- +override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)
- # use system timezone data?
- ifneq (,$(with_system_tzdata))
- override CPPFLAGS += '-DSYSTEMTZDIR="$(with_system_tzdata)"'
- endif
- -OBJS= initdb.o findtimezone.o localtime.o encnames.o pqsignal.o $(WIN32RES)
- +OBJS= initdb.o encnames.o pqsignal.o $(WIN32RES)
- all: initdb
- @@ -40,11 +40,6 @@
- pqsignal.c: % : $(top_srcdir)/src/interfaces/libpq/%
- rm -f $@ && $(LN_S) $< .
- -# Likewise, pull in localtime.c from src/timezones
- -
- -localtime.c: % : $(top_srcdir)/src/timezone/%
- - rm -f $@ && $(LN_S) $< .
- -
- install: all installdirs
- $(INSTALL_PROGRAM) initdb$(X) '$(DESTDIR)$(bindir)/initdb$(X)'
- @@ -55,7 +50,7 @@
- rm -f '$(DESTDIR)$(bindir)/initdb$(X)'
- clean distclean maintainer-clean:
- - rm -f initdb$(X) $(OBJS) encnames.c pqsignal.c localtime.c
- + rm -f initdb$(X) $(OBJS) encnames.c pqsignal.c
- # ensure that changes in datadir propagate into object file
- Only in postgresql-9.2.1a/src/bin/initdb: findtimezone.c
- diff -u -r -b -B postgresql-9.2.1a/src/bin/initdb/initdb.c postgresql-9.2.1/src/bin/initdb/initdb.c
- --- postgresql-9.2.1a/src/bin/initdb/initdb.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/bin/initdb/initdb.c Sat Dec 15 15:16:01 2012
- @@ -61,9 +61,6 @@
- #include "getopt_long.h"
- #include "miscadmin.h"
- -/* Ideally this would be in a .h file, but it hardly seems worth the trouble */
- -extern const char *select_default_timezone(const char *share_path);
- -
- static const char *auth_methods_host[] = {"trust", "reject", "md5", "password", "ident", "radius",
- #ifdef ENABLE_GSS
- "gss",
- @@ -979,9 +976,8 @@
- setup_config(void)
- {
- char **conflines;
- - char repltok[TZ_STRLEN_MAX + 100];
- + char repltok[100];
- char path[MAXPGPATH];
- - const char *default_timezone;
- fputs(_("creating configuration files ... "), stdout);
- fflush(stdout);
- @@ -1044,17 +1040,6 @@
- "#default_text_search_config = 'pg_catalog.simple'",
- repltok);
- - default_timezone = select_default_timezone(share_path);
- - if (default_timezone)
- - {
- - snprintf(repltok, sizeof(repltok), "timezone = '%s'",
- - escape_quotes(default_timezone));
- - conflines = replace_token(conflines, "#timezone = 'GMT'", repltok);
- - snprintf(repltok, sizeof(repltok), "log_timezone = '%s'",
- - escape_quotes(default_timezone));
- - conflines = replace_token(conflines, "#log_timezone = 'GMT'", repltok);
- - }
- -
- snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
- writefile(path, conflines);
- @@ -2877,6 +2862,16 @@
- sprintf(pgdenv, "PGDATA=%s", pg_data);
- putenv(pgdenv);
- + /*
- + * Also ensure that TZ is set, so that we don't waste time identifying the
- + * system timezone each of the many times we start a standalone backend.
- + * It's okay to use a hard-wired value here because nothing done during
- + * initdb cares about the timezone setting.
- + */
- + putenv("TZ=GMT");
- +
- +
- +
- if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR,
- backend_exec)) < 0)
- {
- diff -u -r -b -B postgresql-9.2.1a/src/include/pgtime.h postgresql-9.2.1/src/include/pgtime.h
- --- postgresql-9.2.1a/src/include/pgtime.h Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/include/pgtime.h Sat Dec 15 03:45:28 2012
- @@ -40,11 +40,6 @@
- typedef struct pg_tz pg_tz;
- typedef struct pg_tzenum pg_tzenum;
- -/* Maximum length of a timezone name (not including trailing null) */
- -#define TZ_STRLEN_MAX 255
- -
- -/* these functions are in localtime.c */
- -
- extern struct pg_tm *pg_localtime(const pg_time_t *timep, const pg_tz *tz);
- extern struct pg_tm *pg_gmtime(const pg_time_t *timep);
- extern int pg_next_dst_boundary(const pg_time_t *timep,
- @@ -57,20 +52,22 @@
- extern size_t pg_strftime(char *s, size_t max, const char *format,
- const struct pg_tm * tm);
- +extern void pg_timezone_pre_initialize(void);
- +extern void pg_timezone_initialize(void);
- +extern pg_tz *pg_tzset(const char *tzname);
- +extern bool tz_acceptable(pg_tz *tz);
- extern bool pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff);
- extern const char *pg_get_timezone_name(pg_tz *tz);
- -extern bool pg_tz_acceptable(pg_tz *tz);
- -/* these functions and variables are in pgtz.c */
- +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 *session_timezone;
- extern pg_tz *log_timezone;
- +extern pg_tz *gmt_timezone;
- -extern void pg_timezone_initialize(void);
- -extern pg_tz *pg_tzset(const char *tzname);
- -
- -extern pg_tzenum *pg_tzenumerate_start(void);
- -extern pg_tz *pg_tzenumerate_next(pg_tzenum *dir);
- -extern void pg_tzenumerate_end(pg_tzenum *dir);
- +/* Maximum length of a timezone name (not including trailing null) */
- +#define TZ_STRLEN_MAX 255
- #endif /* _PGTIME_H */
- diff -u -r -b -B postgresql-9.2.1a/src/include/utils/guc.h postgresql-9.2.1/src/include/utils/guc.h
- --- postgresql-9.2.1a/src/include/utils/guc.h Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/include/utils/guc.h Sat Dec 15 03:45:28 2012
- @@ -333,6 +333,8 @@
- extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
- extern ArrayType *GUCArrayReset(ArrayType *array);
- +extern void pg_timezone_abbrev_initialize(void);
- +
- #ifdef EXEC_BACKEND
- extern void write_nondefault_variables(GucContext context);
- extern void read_nondefault_variables(void);
- diff -u -r -b -B postgresql-9.2.1a/src/timezone/localtime.c postgresql-9.2.1/src/timezone/localtime.c
- --- postgresql-9.2.1a/src/timezone/localtime.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/timezone/localtime.c Sat Dec 15 03:45:28 2012
- @@ -16,7 +16,6 @@
- #include <fcntl.h>
- -#include "datatype/timestamp.h"
- #include "private.h"
- #include "pgtz.h"
- #include "tzfile.h"
- @@ -735,7 +734,6 @@
- * can't assume pg_open_tzfile() is sane yet, and we don't care about
- * leap seconds anyway.
- */
- - sp->goback = sp->goahead = FALSE;
- load_result = -1;
- }
- else
- @@ -1479,29 +1477,3 @@
- return tz->TZname;
- return NULL;
- }
- -
- -/*
- - * Check whether timezone is acceptable.
- - *
- - * What we are doing here is checking for leap-second-aware timekeeping.
- - * We need to reject such TZ settings because they'll wreak havoc with our
- - * date/time arithmetic.
- - */
- -bool
- -pg_tz_acceptable(pg_tz *tz)
- -{
- - struct pg_tm *tt;
- - pg_time_t time2000;
- -
- - /*
- - * To detect leap-second timekeeping, run pg_localtime for what should be
- - * GMT midnight, 2000-01-01. Insist that the tm_sec value be zero; any
- - * other result has to be due to leap seconds.
- - */
- - time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
- - tt = pg_localtime(&time2000, tz);
- - if (!tt || tt->tm_sec != 0)
- - return false;
- -
- - return true;
- -}
- diff -u -r -b -B postgresql-9.2.1a/src/timezone/pgtz.c postgresql-9.2.1/src/timezone/pgtz.c
- --- postgresql-9.2.1a/src/timezone/pgtz.c Wed Sep 19 17:47:58 2012
- +++ postgresql-9.2.1/src/timezone/pgtz.c Sat Dec 15 04:16:13 2012
- @@ -10,6 +10,8 @@
- *
- *-------------------------------------------------------------------------
- */
- +#define NO_REDEFINE_TIMEFUNCS
- +
- #include "postgres.h"
- #include <ctype.h>
- @@ -20,9 +22,11 @@
- #include "miscadmin.h"
- #include "pgtz.h"
- #include "storage/fd.h"
- +#include "tzfile.h"
- +#include "utils/datetime.h"
- +#include "utils/guc.h"
- #include "utils/hsearch.h"
- -
- /* Current session timezone (controlled by TimeZone GUC) */
- pg_tz *session_timezone = NULL;
- @@ -29,10 +33,17 @@
- /* Current log timezone (controlled by log_timezone GUC) */
- pg_tz *log_timezone = NULL;
- +/* Fallback GMT timezone for last-ditch error message formatting */
- +pg_tz *gmt_timezone = NULL;
- +static pg_tz gmt_timezone_data;
- +
- 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 pg_tz *get_pg_tz_for_zone(const char *tzname);
- +static pg_tz *select_default_timezone(void);
- /*
- @@ -69,7 +80,7 @@
- * timezone database does not contain case-equivalent names).
- *
- * If "canonname" is not NULL, then on success the canonical spelling of the
- - * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
- + * given name is stored there (it is assumed to be > TZ_STRLEN_MAX bytes!).
- */
- int
- pg_open_tzfile(const char *name, char *canonname)
- @@ -165,6 +176,1054 @@
- /*
- + * The following block of code attempts to determine which timezone in our
- + * timezone database is the best match for the active system timezone.
- + *
- + * On most systems, we rely on trying to match the observable behavior of
- + * the C library's localtime() function. The database zone that matches
- + * furthest into the past is the one to use. Often there will be several
- + * zones with identical rankings (since the zic database assigns multiple
- + * names to many zones). We break ties arbitrarily by preferring shorter,
- + * then alphabetically earlier zone names.
- + *
- + * Win32's native knowledge about timezones appears to be too incomplete
- + * and too different from the zic database for the above matching strategy
- + * to be of any use. But there is just a limited number of timezones
- + * available, so we can rely on a handmade mapping table instead.
- + */
- +
- +#ifndef WIN32
- +
- +#define T_DAY ((time_t) (60*60*24))
- +#define T_WEEK ((time_t) (60*60*24*7))
- +#define T_MONTH ((time_t) (60*60*24*31))
- +
- +#define MAX_TEST_TIMES (52*100) /* 100 years */
- +
- +struct tztry
- +{
- + int n_test_times;
- + time_t test_times[MAX_TEST_TIMES];
- +};
- +
- +static void scan_available_timezones(char *tzdir, char *tzdirsub,
- + struct tztry * tt,
- + int *bestscore, char *bestzonename);
- +
- +
- +/*
- + * Get GMT offset from a system struct tm
- + */
- +static int
- +get_timezone_offset(struct tm * tm)
- +{
- +#if defined(HAVE_STRUCT_TM_TM_ZONE)
- + return tm->tm_gmtoff;
- +#elif defined(HAVE_INT_TIMEZONE)
- + return -TIMEZONE_GLOBAL;
- +#else
- +#error No way to determine TZ? Can this happen?
- +#endif
- +}
- +
- +/*
- + * Convenience subroutine to convert y/m/d to time_t (NOT pg_time_t)
- + */
- +static time_t
- +build_time_t(int year, int month, int day)
- +{
- + struct tm tm;
- +
- + memset(&tm, 0, sizeof(tm));
- + tm.tm_mday = day;
- + tm.tm_mon = month - 1;
- + tm.tm_year = year - 1900;
- +
- + return mktime(&tm);
- +}
- +
- +/*
- + * Does a system tm value match one we computed ourselves?
- + */
- +static bool
- +compare_tm(struct tm * s, struct pg_tm * p)
- +{
- + if (s->tm_sec != p->tm_sec ||
- + s->tm_min != p->tm_min ||
- + s->tm_hour != p->tm_hour ||
- + s->tm_mday != p->tm_mday ||
- + s->tm_mon != p->tm_mon ||
- + s->tm_year != p->tm_year ||
- + s->tm_wday != p->tm_wday ||
- + s->tm_yday != p->tm_yday ||
- + s->tm_isdst != p->tm_isdst)
- + return false;
- + return true;
- +}
- +
- +/*
- + * See how well a specific timezone setting matches the system behavior
- + *
- + * We score a timezone setting according to the number of test times it
- + * matches. (The test times are ordered later-to-earlier, but this routine
- + * doesn't actually know that; it just scans until the first non-match.)
- + *
- + * We return -1 for a completely unusable setting; this is worse than the
- + * score of zero for a setting that works but matches not even the first
- + * test time.
- + */
- +static int
- +score_timezone(const char *tzname, struct tztry * tt)
- +{
- + int i;
- + pg_time_t pgtt;
- + struct tm *systm;
- + struct pg_tm *pgtm;
- + char cbuf[TZ_STRLEN_MAX + 1];
- + pg_tz tz;
- +
- +
- + /*
- + * Load timezone directly. Don't use pg_tzset, because we don't want all
- + * timezones loaded in the cache at startup.
- + */
- + if (tzload(tzname, NULL, &tz.state, TRUE) != 0)
- + {
- + if (tzname[0] == ':' || tzparse(tzname, &tz.state, FALSE) != 0)
- + {
- + return -1; /* can't handle the TZ name at all */
- + }
- + }
- +
- + /* Reject if leap seconds involved */
- + if (!tz_acceptable(&tz))
- + {
- + elog(DEBUG4, "Reject TZ \"%s\": uses leap seconds", tzname);
- + return -1;
- + }
- +
- + /* Check for match at all the test times */
- + for (i = 0; i < tt->n_test_times; i++)
- + {
- + pgtt = (pg_time_t) (tt->test_times[i]);
- + pgtm = pg_localtime(&pgtt, &tz);
- + if (!pgtm)
- + return -1; /* probably shouldn't happen */
- + systm = localtime(&(tt->test_times[i]));
- + if (!systm)
- + {
- + elog(DEBUG4, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s, system had no data",
- + tzname, i, (long) pgtt,
- + pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
- + pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
- + pgtm->tm_isdst ? "dst" : "std");
- + return i;
- + }
- + if (!compare_tm(systm, pgtm))
- + {
- + elog(DEBUG4, "TZ \"%s\" scores %d: at %ld %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s",
- + tzname, i, (long) pgtt,
- + pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday,
- + pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec,
- + pgtm->tm_isdst ? "dst" : "std",
- + systm->tm_year + 1900, systm->tm_mon + 1, systm->tm_mday,
- + systm->tm_hour, systm->tm_min, systm->tm_sec,
- + systm->tm_isdst ? "dst" : "std");
- + return i;
- + }
- + if (systm->tm_isdst >= 0)
- + {
- + /* Check match of zone names, too */
- + if (pgtm->tm_zone == NULL)
- + return -1; /* probably shouldn't happen */
- + memset(cbuf, 0, sizeof(cbuf));
- + strftime(cbuf, sizeof(cbuf) - 1, "%Z", systm); /* zone abbr */
- + if (strcmp(cbuf, pgtm->tm_zone) != 0)
- + {
- + elog(DEBUG4, "TZ \"%s\" scores %d: at %ld \"%s\" versus \"%s\"",
- + tzname, i, (long) pgtt,
- + pgtm->tm_zone, cbuf);
- + return i;
- + }
- + }
- + }
- +
- + elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);
- + return i;
- +}
- +
- +
- +/*
- + * Try to identify a timezone name (in our terminology) that best matches the
- + * observed behavior of the system timezone library. We cannot assume that
- + * the system TZ environment setting (if indeed there is one) matches our
- + * terminology, so we ignore it and just look at what localtime() returns.
- + */
- +static const char *
- +identify_system_timezone(void)
- +{
- + static char resultbuf[TZ_STRLEN_MAX + 1];
- + time_t tnow;
- + time_t t;
- + struct tztry tt;
- + struct tm *tm;
- + int thisyear;
- + int bestscore;
- + char tmptzdir[MAXPGPATH];
- + int std_ofs;
- + char std_zone_name[TZ_STRLEN_MAX + 1],
- + dst_zone_name[TZ_STRLEN_MAX + 1];
- + char cbuf[TZ_STRLEN_MAX + 1];
- +
- + /* Initialize OS timezone library */
- + tzset();
- +
- + /*
- + * Set up the list of dates to be probed to see how well our timezone
- + * matches the system zone. We first probe January and July of the
- + * current year; this serves to quickly eliminate the vast majority of the
- + * TZ database entries. If those dates match, we probe every week for 100
- + * years backwards from the current July. (Weekly resolution is good
- + * enough to identify DST transition rules, since everybody switches on
- + * Sundays.) This is sufficient to cover most of the Unix time_t range,
- + * and we don't want to look further than that since many systems won't
- + * have sane TZ behavior further back anyway. The further back the zone
- + * matches, the better we score it. This may seem like a rather random
- + * way of doing things, but experience has shown that system-supplied
- + * timezone definitions are likely to have DST behavior that is right for
- + * the recent past and not so accurate further back. Scoring in this way
- + * allows us to recognize zones that have some commonality with the zic
- + * database, without insisting on exact match. (Note: we probe Thursdays,
- + * not Sundays, to avoid triggering DST-transition bugs in localtime
- + * itself.)
- + */
- + tnow = time(NULL);
- + tm = localtime(&tnow);
- + if (!tm)
- + return NULL; /* give up if localtime is broken... */
- + thisyear = tm->tm_year + 1900;
- +
- + t = build_time_t(thisyear, 1, 15);
- +
- + /*
- + * Round back to GMT midnight Thursday. This depends on the knowledge
- + * that the time_t origin is Thu Jan 01 1970. (With a different origin
- + * we'd be probing some other day of the week, but it wouldn't matter
- + * anyway unless localtime() had DST-transition bugs.)
- + */
- + t -= (t % T_WEEK);
- +
- + tt.n_test_times = 0;
- + tt.test_times[tt.n_test_times++] = t;
- +
- + t = build_time_t(thisyear, 7, 15);
- + t -= (t % T_WEEK);
- +
- + tt.test_times[tt.n_test_times++] = t;
- +
- + while (tt.n_test_times < MAX_TEST_TIMES)
- + {
- + t -= T_WEEK;
- + tt.test_times[tt.n_test_times++] = t;
- + }
- +
- + /* Search for the best-matching timezone file */
- + strcpy(tmptzdir, pg_TZDIR());
- + bestscore = -1;
- + resultbuf[0] = '\0';
- + scan_available_timezones(tmptzdir, tmptzdir + strlen(tmptzdir) + 1,
- + &tt,
- + &bestscore, resultbuf);
- + if (bestscore > 0)
- + {
- + /* Ignore zic's rather silly "Factory" time zone; use GMT instead */
- + if (strcmp(resultbuf, "Factory") == 0)
- + return NULL;
- + return resultbuf;
- + }
- +
- + /*
- + * Couldn't find a match in the database, so next we try constructed zone
- + * names (like "PST8PDT").
- + *
- + * First we need to determine the names of the local standard and daylight
- + * zones. The idea here is to scan forward from today until we have seen
- + * both zones, if both are in use.
- + */
- + memset(std_zone_name, 0, sizeof(std_zone_name));
- + memset(dst_zone_name, 0, sizeof(dst_zone_name));
- + std_ofs = 0;
- +
- + tnow = time(NULL);
- +
- + /*
- + * Round back to a GMT midnight so results don't depend on local time of
- + * day
- + */
- + tnow -= (tnow % T_DAY);
- +
- + /*
- + * We have to look a little further ahead than one year, in case today is
- + * just past a DST boundary that falls earlier in the year than the next
- + * similar boundary. Arbitrarily scan up to 14 months.
- + */
- + for (t = tnow; t <= tnow + T_MONTH * 14; t += T_MONTH)
- + {
- + tm = localtime(&t);
- + if (!tm)
- + continue;
- + if (tm->tm_isdst < 0)
- + continue;
- + if (tm->tm_isdst == 0 && std_zone_name[0] == '\0')
- + {
- + /* found STD zone */
- + memset(cbuf, 0, sizeof(cbuf));
- + strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
- + strcpy(std_zone_name, cbuf);
- + std_ofs = get_timezone_offset(tm);
- + }
- + if (tm->tm_isdst > 0 && dst_zone_name[0] == '\0')
- + {
- + /* found DST zone */
- + memset(cbuf, 0, sizeof(cbuf));
- + strftime(cbuf, sizeof(cbuf) - 1, "%Z", tm); /* zone abbr */
- + strcpy(dst_zone_name, cbuf);
- + }
- + /* Done if found both */
- + if (std_zone_name[0] && dst_zone_name[0])
- + break;
- + }
- +
- + /* We should have found a STD zone name by now... */
- + if (std_zone_name[0] == '\0')
- + {
- + ereport(LOG,
- + (errmsg("could not determine system time zone"),
- + errdetail("The PostgreSQL time zone will be set to \"%s\".",
- + "GMT"),
- + errhint("You can specify the correct timezone in postgresql.conf.")));
- + return NULL; /* go to GMT */
- + }
- +
- + /* If we found DST then try STD<ofs>DST */
- + if (dst_zone_name[0] != '\0')
- + {
- + snprintf(resultbuf, sizeof(resultbuf), "%s%d%s",
- + std_zone_name, -std_ofs / 3600, dst_zone_name);
- + if (score_timezone(resultbuf, &tt) > 0)
- + return resultbuf;
- + }
- +
- + /* Try just the STD timezone (works for GMT at least) */
- + strcpy(resultbuf, std_zone_name);
- + if (score_timezone(resultbuf, &tt) > 0)
- + return resultbuf;
- +
- + /* Try STD<ofs> */
- + snprintf(resultbuf, sizeof(resultbuf), "%s%d",
- + std_zone_name, -std_ofs / 3600);
- + if (score_timezone(resultbuf, &tt) > 0)
- + return resultbuf;
- +
- + /*
- + * Did not find the timezone. Fallback to use a GMT zone. Note that the
- + * zic timezone database names the GMT-offset zones in POSIX style: plus
- + * is west of Greenwich. It's unfortunate that this is opposite of SQL
- + * conventions. Should we therefore change the names? Probably not...
- + */
- + snprintf(resultbuf, sizeof(resultbuf), "Etc/GMT%s%d",
- + (-std_ofs > 0) ? "+" : "", -std_ofs / 3600);
- +
- + ereport(LOG,
- + (errmsg("could not recognize system time zone"),
- + errdetail("The PostgreSQL time zone will be set to \"%s\".",
- + resultbuf),
- + errhint("You can specify the correct timezone in postgresql.conf.")));
- + return resultbuf;
- +}
- +
- +/*
- + * Recursively scan the timezone database looking for the best match to
- + * the system timezone behavior.
- + *
- + * tzdir points to a buffer of size MAXPGPATH. On entry, it holds the
- + * pathname of a directory containing TZ files. We internally modify it
- + * to hold pathnames of sub-directories and files, but must restore it
- + * to its original contents before exit.
- + *
- + * tzdirsub points to the part of tzdir that represents the subfile name
- + * (ie, tzdir + the original directory name length, plus one for the
- + * first added '/').
- + *
- + * tt tells about the system timezone behavior we need to match.
- + *
- + * *bestscore and *bestzonename on entry hold the best score found so far
- + * and the name of the best zone. We overwrite them if we find a better
- + * score. bestzonename must be a buffer of length TZ_STRLEN_MAX + 1.
- + */
- +static void
- +scan_available_timezones(char *tzdir, char *tzdirsub, struct tztry * tt,
- + int *bestscore, char *bestzonename)
- +{
- + int tzdir_orig_len = strlen(tzdir);
- + DIR *dirdesc;
- + struct dirent *direntry;
- +
- + dirdesc = AllocateDir(tzdir);
- + if (!dirdesc)
- + {
- + ereport(LOG,
- + (errcode_for_file_access(),
- + errmsg("could not open directory \"%s\": %m", tzdir)));
- + return;
- + }
- +
- + while ((direntry = ReadDir(dirdesc, tzdir)) != NULL)
- + {
- + struct stat statbuf;
- +
- + /* Ignore . and .., plus any other "hidden" files */
- + if (direntry->d_name[0] == '.')
- + continue;
- +
- + snprintf(tzdir + tzdir_orig_len, MAXPGPATH - tzdir_orig_len,
- + "/%s", direntry->d_name);
- +
- + if (stat(tzdir, &statbuf) != 0)
- + {
- + ereport(LOG,
- + (errcode_for_file_access(),
- + errmsg("could not stat \"%s\": %m", tzdir)));
- + tzdir[tzdir_orig_len] = '\0';
- + continue;
- + }
- +
- + if (S_ISDIR(statbuf.st_mode))
- + {
- + /* Recurse into subdirectory */
- + scan_available_timezones(tzdir, tzdirsub, tt,
- + bestscore, bestzonename);
- + }
- + else
- + {
- + /* Load and test this file */
- + int score = score_timezone(tzdirsub, tt);
- +
- + if (score > *bestscore)
- + {
- + *bestscore = score;
- + strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
- + }
- + else if (score == *bestscore)
- + {
- + /* Consider how to break a tie */
- + if (strlen(tzdirsub) < strlen(bestzonename) ||
- + (strlen(tzdirsub) == strlen(bestzonename) &&
- + strcmp(tzdirsub, bestzonename) < 0))
- + strlcpy(bestzonename, tzdirsub, TZ_STRLEN_MAX + 1);
- + }
- + }
- +
- + /* Restore tzdir */
- + tzdir[tzdir_orig_len] = '\0';
- + }
- +
- + FreeDir(dirdesc);
- +}
- +#else /* WIN32 */
- +
- +static const struct
- +{
- + const char *stdname; /* Windows name of standard timezone */
- + const char *dstname; /* Windows name of daylight timezone */
- + const char *pgtzname; /* Name of pgsql timezone to map to */
- +} win32_tzmap[] =
- +
- +{
- + /*
- + * This list was built from the contents of the registry at
- + * HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time
- + * Zones on Windows 2003 R2.
- + *
- + * The zones have been matched to zic timezones by looking at the cities
- + * listed in the win32 display name (in the comment here) in most cases.
- + */
- + {
- + "Afghanistan Standard Time", "Afghanistan Daylight Time",
- + "Asia/Kabul"
- + }, /* (GMT+04:30) Kabul */
- + {
- + "Alaskan Standard Time", "Alaskan Daylight Time",
- + "US/Alaska"
- + }, /* (GMT-09:00) Alaska */
- + {
- + "Arab Standard Time", "Arab Daylight Time",
- + "Asia/Kuwait"
- + }, /* (GMT+03:00) Kuwait, Riyadh */
- + {
- + "Arabian Standard Time", "Arabian Daylight Time",
- + "Asia/Muscat"
- + }, /* (GMT+04:00) Abu Dhabi, Muscat */
- + {
- + "Arabic Standard Time", "Arabic Daylight Time",
- + "Asia/Baghdad"
- + }, /* (GMT+03:00) Baghdad */
- + {
- + "Argentina Standard Time", "Argentina Daylight Time",
- + "America/Buenos_Aires"
- + }, /* (GMT-03:00) Buenos Aires */
- + {
- + "Armenian Standard Time", "Armenian Daylight Time",
- + "Asia/Yerevan"
- + }, /* (GMT+04:00) Yerevan */
- + {
- + "Atlantic Standard Time", "Atlantic Daylight Time",
- + "Canada/Atlantic"
- + }, /* (GMT-04:00) Atlantic Time (Canada) */
- + {
- + "AUS Central Standard Time", "AUS Central Daylight Time",
- + "Australia/Darwin"
- + }, /* (GMT+09:30) Darwin */
- + {
- + "AUS Eastern Standard Time", "AUS Eastern Daylight Time",
- + "Australia/Canberra"
- + }, /* (GMT+10:00) Canberra, Melbourne, Sydney */
- + {
- + "Azerbaijan Standard Time", "Azerbaijan Daylight Time",
- + "Asia/Baku"
- + }, /* (GMT+04:00) Baku */
- + {
- + "Azores Standard Time", "Azores Daylight Time",
- + "Atlantic/Azores"
- + }, /* (GMT-01:00) Azores */
- + {
- + "Bangladesh Standard Time", "Bangladesh Daylight Time",
- + "Asia/Dhaka"
- + }, /* (GMT+06:00) Dhaka */
- + {
- + "Canada Central Standard Time", "Canada Central Daylight Time",
- + "Canada/Saskatchewan"
- + }, /* (GMT-06:00) Saskatchewan */
- + {
- + "Cape Verde Standard Time", "Cape Verde Daylight Time",
- + "Atlantic/Cape_Verde"
- + }, /* (GMT-01:00) Cape Verde Is. */
- + {
- + "Caucasus Standard Time", "Caucasus Daylight Time",
- + "Asia/Baku"
- + }, /* (GMT+04:00) Baku, Tbilisi, Yerevan */
- + {
- + "Cen. Australia Standard Time", "Cen. Australia Daylight Time",
- + "Australia/Adelaide"
- + }, /* (GMT+09:30) Adelaide */
- + {
- + "Central America Standard Time", "Central America Daylight Time",
- + "CST6CDT"
- + }, /* (GMT-06:00) Central America */
- + {
- + "Central Asia Standard Time", "Central Asia Daylight Time",
- + "Asia/Dhaka"
- + }, /* (GMT+06:00) Astana, Dhaka */
- + {
- + "Central Brazilian Standard Time", "Central Brazilian Daylight Time",
- + "America/Cuiaba"
- + }, /* (GMT-04:00) Cuiaba */
- + {
- + "Central Europe Standard Time", "Central Europe Daylight Time",
- + "Europe/Belgrade"
- + }, /* (GMT+01:00) Belgrade, Bratislava, Budapest,
- + * Ljubljana, Prague */
- + {
- + "Central European Standard Time", "Central European Daylight Time",
- + "Europe/Sarajevo"
- + }, /* (GMT+01:00) Sarajevo, Skopje, Warsaw,
- + * Zagreb */
- + {
- + "Central Pacific Standard Time", "Central Pacific Daylight Time",
- + "Pacific/Noumea"
- + }, /* (GMT+11:00) Magadan, Solomon Is., New
- + * Caledonia */
- + {
- + "Central Standard Time", "Central Daylight Time",
- + "US/Central"
- + }, /* (GMT-06:00) Central Time (US & Canada) */
- + {
- + "Central Standard Time (Mexico)", "Central Daylight Time (Mexico)",
- + "America/Mexico_City"
- + }, /* (GMT-06:00) Guadalajara, Mexico City,
- + * Monterrey - New */
- + {
- + "China Standard Time", "China Daylight Time",
- + "Asia/Hong_Kong"
- + }, /* (GMT+08:00) Beijing, Chongqing, Hong Kong,
- + * Urumqi */
- + {
- + "Dateline Standard Time", "Dateline Daylight Time",
- + "Etc/GMT+12"
- + }, /* (GMT-12:00) International Date Line West */
- + {
- + "E. Africa Standard Time", "E. Africa Daylight Time",
- + "Africa/Nairobi"
- + }, /* (GMT+03:00) Nairobi */
- + {
- + "E. Australia Standard Time", "E. Australia Daylight Time",
- + "Australia/Brisbane"
- + }, /* (GMT+10:00) Brisbane */
- + {
- + "E. Europe Standard Time", "E. Europe Daylight Time",
- + "Europe/Bucharest"
- + }, /* (GMT+02:00) Bucharest */
- + {
- + "E. South America Standard Time", "E. South America Daylight Time",
- + "America/Araguaina"
- + }, /* (GMT-03:00) Brasilia */
- + {
- + "Eastern Standard Time", "Eastern Daylight Time",
- + "US/Eastern"
- + }, /* (GMT-05:00) Eastern Time (US & Canada) */
- + {
- + "Egypt Standard Time", "Egypt Daylight Time",
- + "Africa/Cairo"
- + }, /* (GMT+02:00) Cairo */
- + {
- + "Ekaterinburg Standard Time", "Ekaterinburg Daylight Time",
- + "Asia/Yekaterinburg"
- + }, /* (GMT+05:00) Ekaterinburg */
- + {
- + "Fiji Standard Time", "Fiji Daylight Time",
- + "Pacific/Fiji"
- + }, /* (GMT+12:00) Fiji, Kamchatka, Marshall Is. */
- + {
- + "FLE Standard Time", "FLE Daylight Time",
- + "Europe/Helsinki"
- + }, /* (GMT+02:00) Helsinki, Kyiv, Riga, Sofia,
- + * Tallinn, Vilnius */
- + {
- + "Georgian Standard Time", "Georgian Daylight Time",
- + "Asia/Tbilisi"
- + }, /* (GMT+03:00) Tbilisi */
- + {
- + "GMT Standard Time", "GMT Daylight Time",
- + "Europe/London"
- + }, /* (GMT) Greenwich Mean Time : Dublin,
- + * Edinburgh, Lisbon, London */
- + {
- + "Greenland Standard Time", "Greenland Daylight Time",
- + "America/Godthab"
- + }, /* (GMT-03:00) Greenland */
- + {
- + "Greenwich Standard Time", "Greenwich Daylight Time",
- + "Africa/Casablanca"
- + }, /* (GMT) Casablanca, Monrovia */
- + {
- + "GTB Standard Time", "GTB Daylight Time",
- + "Europe/Athens"
- + }, /* (GMT+02:00) Athens, Istanbul, Minsk */
- + {
- + "Hawaiian Standard Time", "Hawaiian Daylight Time",
- + "US/Hawaii"
- + }, /* (GMT-10:00) Hawaii */
- + {
- + "India Standard Time", "India Daylight Time",
- + "Asia/Calcutta"
- + }, /* (GMT+05:30) Chennai, Kolkata, Mumbai, New
- + * Delhi */
- + {
- + "Iran Standard Time", "Iran Daylight Time",
- + "Asia/Tehran"
- + }, /* (GMT+03:30) Tehran */
- + {
- + "Jerusalem Standard Time", "Jerusalem Daylight Time",
- + "Asia/Jerusalem"
- + }, /* (GMT+02:00) Jerusalem */
- + {
- + "Jordan Standard Time", "Jordan Daylight Time",
- + "Asia/Amman"
- + }, /* (GMT+02:00) Amman */
- + {
- + "Kamchatka Standard Time", "Kamchatka Daylight Time",
- + "Asia/Kamchatka"
- + }, /* (GMT+12:00) Petropavlovsk-Kamchatsky */
- + {
- + "Korea Standard Time", "Korea Daylight Time",
- + "Asia/Seoul"
- + }, /* (GMT+09:00) Seoul */
- + {
- + "Mauritius Standard Time", "Mauritius Daylight Time",
- + "Indian/Mauritius"
- + }, /* (GMT+04:00) Port Louis */
- + {
- + "Mexico Standard Time", "Mexico Daylight Time",
- + "America/Mexico_City"
- + }, /* (GMT-06:00) Guadalajara, Mexico City,
- + * Monterrey */
- + {
- + "Mexico Standard Time 2", "Mexico Daylight Time 2",
- + "America/Chihuahua"
- + }, /* (GMT-07:00) Chihuahua, La Paz, Mazatlan */
- + {
- + "Mid-Atlantic Standard Time", "Mid-Atlantic Daylight Time",
- + "Atlantic/South_Georgia"
- + }, /* (GMT-02:00) Mid-Atlantic */
- + {
- + "Middle East Standard Time", "Middle East Daylight Time",
- + "Asia/Beirut"
- + }, /* (GMT+02:00) Beirut */
- + {
- + "Montevideo Standard Time", "Montevideo Daylight Time",
- + "America/Montevideo"
- + }, /* (GMT-03:00) Montevideo */
- + {
- + "Morocco Standard Time", "Morocco Daylight Time",
- + "Africa/Casablanca"
- + }, /* (GMT) Casablanca */
- + {
- + "Mountain Standard Time", "Mountain Daylight Time",
- + "US/Mountain"
- + }, /* (GMT-07:00) Mountain Time (US & Canada) */
- + {
- + "Mountain Standard Time (Mexico)", "Mountain Daylight Time (Mexico)",
- + "America/Chihuahua"
- + }, /* (GMT-07:00) Chihuahua, La Paz, Mazatlan -
- + * New */
- + {
- + "Myanmar Standard Time", "Myanmar Daylight Time",
- + "Asia/Rangoon"
- + }, /* (GMT+06:30) Rangoon */
- + {
- + "N. Central Asia Standard Time", "N. Central Asia Daylight Time",
- + "Asia/Novosibirsk"
- + }, /* (GMT+06:00) Novosibirsk */
- + {
- + "Namibia Standard Time", "Namibia Daylight Time",
- + "Africa/Windhoek"
- + }, /* (GMT+02:00) Windhoek */
- + {
- + "Nepal Standard Time", "Nepal Daylight Time",
- + "Asia/Katmandu"
- + }, /* (GMT+05:45) Kathmandu */
- + {
- + "New Zealand Standard Time", "New Zealand Daylight Time",
- + "Pacific/Auckland"
- + }, /* (GMT+12:00) Auckland, Wellington */
- + {
- + "Newfoundland Standard Time", "Newfoundland Daylight Time",
- + "Canada/Newfoundland"
- + }, /* (GMT-03:30) Newfoundland */
- + {
- + "North Asia East Standard Time", "North Asia East Daylight Time",
- + "Asia/Irkutsk"
- + }, /* (GMT+08:00) Irkutsk, Ulaan Bataar */
- + {
- + "North Asia Standard Time", "North Asia Daylight Time",
- + "Asia/Krasnoyarsk"
- + }, /* (GMT+07:00) Krasnoyarsk */
- + {
- + "Pacific SA Standard Time", "Pacific SA Daylight Time",
- + "America/Santiago"
- + }, /* (GMT-04:00) Santiago */
- + {
- + "Pacific Standard Time", "Pacific Daylight Time",
- + "US/Pacific"
- + }, /* (GMT-08:00) Pacific Time (US & Canada);
- + * Tijuana */
- + {
- + "Pacific Standard Time (Mexico)", "Pacific Daylight Time (Mexico)",
- + "America/Tijuana"
- + }, /* (GMT-08:00) Tijuana, Baja California */
- + {
- + "Pakistan Standard Time", "Pakistan Daylight Time",
- + "Asia/Karachi"
- + }, /* (GMT+05:00) Islamabad, Karachi */
- + {
- + "Paraguay Standard Time", "Paraguay Daylight Time",
- + "America/Asuncion"
- + }, /* (GMT-04:00) Asuncion */
- + {
- + "Romance Standard Time", "Romance Daylight Time",
- + "Europe/Brussels"
- + }, /* (GMT+01:00) Brussels, Copenhagen, Madrid,
- + * Paris */
- + {
- + "Russian Standard Time", "Russian Daylight Time",
- + "Europe/Moscow"
- + }, /* (GMT+03:00) Moscow, St. Petersburg,
- + * Volgograd */
- + {
- + "SA Eastern Standard Time", "SA Eastern Daylight Time",
- + "America/Buenos_Aires"
- + }, /* (GMT-03:00) Buenos Aires, Georgetown */
- + {
- + "SA Pacific Standard Time", "SA Pacific Daylight Time",
- + "America/Bogota"
- + }, /* (GMT-05:00) Bogota, Lima, Quito */
- + {
- + "SA Western Standard Time", "SA Western Daylight Time",
- + "America/Caracas"
- + }, /* (GMT-04:00) Caracas, La Paz */
- + {
- + "Samoa Standard Time", "Samoa Daylight Time",
- + "Pacific/Midway"
- + }, /* (GMT-11:00) Midway Island, Samoa */
- + {
- + "SE Asia Standard Time", "SE Asia Daylight Time",
- + "Asia/Bangkok"
- + }, /* (GMT+07:00) Bangkok, Hanoi, Jakarta */
- + {
- + "Malay Peninsula Standard Time", "Malay Peninsula Daylight Time",
- + "Asia/Kuala_Lumpur"
- + }, /* (GMT+08:00) Kuala Lumpur, Singapore */
- + {
- + "South Africa Standard Time", "South Africa Daylight Time",
- + "Africa/Harare"
- + }, /* (GMT+02:00) Harare, Pretoria */
- + {
- + "Sri Lanka Standard Time", "Sri Lanka Daylight Time",
- + "Asia/Colombo"
- + }, /* (GMT+06:00) Sri Jayawardenepura */
- + {
- + "Taipei Standard Time", "Taipei Daylight Time",
- + "Asia/Taipei"
- + }, /* (GMT+08:00) Taipei */
- + {
- + "Tasmania Standard Time", "Tasmania Daylight Time",
- + "Australia/Hobart"
- + }, /* (GMT+10:00) Hobart */
- + {
- + "Tokyo Standard Time", "Tokyo Daylight Time",
- + "Asia/Tokyo"
- + }, /* (GMT+09:00) Osaka, Sapporo, Tokyo */
- + {
- + "Tonga Standard Time", "Tonga Daylight Time",
- + "Pacific/Tongatapu"
- + }, /* (GMT+13:00) Nuku'alofa */
- + {
- + "Ulaanbaatar Standard Time", "Ulaanbaatar Daylight Time",
- + "Asia/Ulaanbaatar",
- + }, /* (GMT+08:00) Ulaanbaatar */
- + {
- + "US Eastern Standard Time", "US Eastern Daylight Time",
- + "US/Eastern"
- + }, /* (GMT-05:00) Indiana (East) */
- + {
- + "US Mountain Standard Time", "US Mountain Daylight Time",
- + "US/Arizona"
- + }, /* (GMT-07:00) Arizona */
- + {
- + "Coordinated Universal Time", "Coordinated Universal Time",
- + "UTC"
- + }, /* (GMT) Coordinated Universal Time */
- + {
- + "UTC+12", "UTC+12",
- + "Etc/GMT+12"
- + }, /* (GMT+12:00) Coordinated Universal Time+12 */
- + {
- + "UTC-02", "UTC-02",
- + "Etc/GMT-02"
- + }, /* (GMT-02:00) Coordinated Universal Time-02 */
- + {
- + "UTC-11", "UTC-11",
- + "Etc/GMT-11"
- + }, /* (GMT-11:00) Coordinated Universal Time-11 */
- + {
- + "Venezuela Standard Time", "Venezuela Daylight Time",
- + "America/Caracas",
- + }, /* (GMT-04:30) Caracas */
- + {
- + "Vladivostok Standard Time", "Vladivostok Daylight Time",
- + "Asia/Vladivostok"
- + }, /* (GMT+10:00) Vladivostok */
- + {
- + "W. Australia Standard Time", "W. Australia Daylight Time",
- + "Australia/Perth"
- + }, /* (GMT+08:00) Perth */
- +#ifdef NOT_USED
- + /* Could not find a match for this one (just a guess). Excluded for now. */
- + {
- + "W. Central Africa Standard Time", "W. Central Africa Daylight Time",
- + "WAT"
- + }, /* (GMT+01:00) West Central Africa */
- +#endif
- + {
- + "W. Europe Standard Time", "W. Europe Daylight Time",
- + "CET"
- + }, /* (GMT+01:00) Amsterdam, Berlin, Bern, Rome,
- + * Stockholm, Vienna */
- + {
- + "West Asia Standard Time", "West Asia Daylight Time",
- + "Asia/Karachi"
- + }, /* (GMT+05:00) Islamabad, Karachi, Tashkent */
- + {
- + "West Pacific Standard Time", "West Pacific Daylight Time",
- + "Pacific/Guam"
- + }, /* (GMT+10:00) Guam, Port Moresby */
- + {
- + "Yakutsk Standard Time", "Yakutsk Daylight Time",
- + "Asia/Yakutsk"
- + }, /* (GMT+09:00) Yakutsk */
- + {
- + NULL, NULL, NULL
- + }
- +};
- +
- +static const char *
- +identify_system_timezone(void)
- +{
- + int i;
- + char tzname[128];
- + char localtzname[256];
- + time_t t = time(NULL);
- + struct tm *tm = localtime(&t);
- + HKEY rootKey;
- + int idx;
- +
- + if (!tm)
- + {
- + ereport(LOG,
- + (errmsg("could not identify system time zone: localtime() failed"),
- + errdetail("The PostgreSQL time zone will be set to \"%s\".",
- + "GMT"),
- + errhint("You can specify the correct timezone in postgresql.conf.")));
- + return NULL; /* go to GMT */
- + }
- +
- + memset(tzname, 0, sizeof(tzname));
- + strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
- +
- + for (i = 0; win32_tzmap[i].stdname != NULL; i++)
- + {
- + if (strcmp(tzname, win32_tzmap[i].stdname) == 0 ||
- + strcmp(tzname, win32_tzmap[i].dstname) == 0)
- + {
- + elog(DEBUG4, "TZ \"%s\" matches system time zone \"%s\"",
- + win32_tzmap[i].pgtzname, tzname);
- + return win32_tzmap[i].pgtzname;
- + }
- + }
- +
- + /*
- + * Localized Windows versions return localized names for the timezone.
- + * Scan the registry to find the English name, and then try matching
- + * against our table again.
- + */
- + memset(localtzname, 0, sizeof(localtzname));
- + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
- + 0,
- + KEY_READ,
- + &rootKey) != ERROR_SUCCESS)
- + {
- + ereport(LOG,
- + (errmsg("could not open registry key to identify system time zone: error code %lu",
- + GetLastError()),
- + errdetail("The PostgreSQL time zone will be set to \"%s\".",
- + "GMT"),
- + errhint("You can specify the correct timezone in postgresql.conf.")));
- + return NULL; /* go to GMT */
- + }
- +
- + for (idx = 0;; idx++)
- + {
- + char keyname[256];
- + char zonename[256];
- + DWORD namesize;
- + FILETIME lastwrite;
- + HKEY key;
- + LONG r;
- +
- + memset(keyname, 0, sizeof(keyname));
- + namesize = sizeof(keyname);
- + if ((r = RegEnumKeyEx(rootKey,
- + idx,
- + keyname,
- + &namesize,
- + NULL,
- + NULL,
- + NULL,
- + &lastwrite)) != ERROR_SUCCESS)
- + {
- + if (r == ERROR_NO_MORE_ITEMS)
- + break;
- + ereport(LOG,
- + (errmsg_internal("could not enumerate registry subkeys to identify system time zone: %d", (int) r)));
- + break;
- + }
- +
- + if ((r = RegOpenKeyEx(rootKey, keyname, 0, KEY_READ, &key)) != ERROR_SUCCESS)
- + {
- + ereport(LOG,
- + (errmsg_internal("could not open registry subkey to identify system time zone: %d", (int) r)));
- + break;
- + }
- +
- + memset(zonename, 0, sizeof(zonename));
- + namesize = sizeof(zonename);
- + if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
- + {
- + ereport(LOG,
- + (errmsg_internal("could not query value for key \"std\" to identify system time zone \"%s\": %d",
- + keyname, (int) r)));
- + RegCloseKey(key);
- + continue; /* Proceed to look at the next timezone */
- + }
- + if (strcmp(tzname, zonename) == 0)
- + {
- + /* Matched zone */
- + strcpy(localtzname, keyname);
- + RegCloseKey(key);
- + break;
- + }
- + memset(zonename, 0, sizeof(zonename));
- + namesize = sizeof(zonename);
- + if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
- + {
- + ereport(LOG,
- + (errmsg_internal("could not query value for key \"dlt\" to identify system time zone \"%s\": %d",
- + keyname, (int) r)));
- + RegCloseKey(key);
- + continue; /* Proceed to look at the next timezone */
- + }
- + if (strcmp(tzname, zonename) == 0)
- + {
- + /* Matched DST zone */
- + strcpy(localtzname, keyname);
- + RegCloseKey(key);
- + break;
- + }
- +
- + RegCloseKey(key);
- + }
- +
- + RegCloseKey(rootKey);
- +
- + if (localtzname[0])
- + {
- + /* Found a localized name, so scan for that one too */
- + for (i = 0; win32_tzmap[i].stdname != NULL; i++)
- + {
- + if (strcmp(localtzname, win32_tzmap[i].stdname) == 0 ||
- + strcmp(localtzname, win32_tzmap[i].dstname) == 0)
- + {
- + elog(DEBUG4, "TZ \"%s\" matches localized system time zone \"%s\" (\"%s\")",
- + win32_tzmap[i].pgtzname, tzname, localtzname);
- + return win32_tzmap[i].pgtzname;
- + }
- + }
- + }
- +
- + ereport(LOG,
- + (errmsg("could not find a match for system time zone \"%s\"",
- + tzname),
- + errdetail("The PostgreSQL time zone will be set to \"%s\".",
- + "GMT"),
- + errhint("You can specify the correct timezone in postgresql.conf.")));
- + return NULL; /* go to GMT */
- +}
- +#endif /* WIN32 */
- +
- +
- +
- +/*
- * We keep loaded timezones in a hashtable so we don't have to
- * load and parse the TZ definition file every time one is selected.
- * Because we want timezone names to be found case-insensitively,
- @@ -203,18 +1262,8 @@
- /*
- * Load a timezone from file or from cache.
- * Does not verify that the timezone is acceptable!
- - *
- - * "GMT" is always interpreted as the tzparse() definition, without attempting
- - * to load a definition from the filesystem. This has a number of benefits:
- - * 1. It's guaranteed to succeed, so we don't have the failure mode wherein
- - * the bootstrap default timezone setting doesn't work (as could happen if
- - * the OS attempts to supply a leap-second-aware version of "GMT").
- - * 2. Because we aren't accessing the filesystem, we can safely initialize
- - * the "GMT" zone definition before my_exec_path is known.
- - * 3. It's quick enough that we don't waste much time when the bootstrap
- - * default timezone setting is later overridden from postgresql.conf.
- */
- -pg_tz *
- +struct pg_tz *
- pg_tzset(const char *name)
- {
- pg_tz_cache *tzp;
- @@ -251,20 +1300,7 @@
- return &tzp->tz;
- }
- - /*
- - * "GMT" is always sent to tzparse(), as per discussion above.
- - */
- - if (strcmp(uppername, "GMT") == 0)
- - {
- - if (tzparse(uppername, &tzstate, TRUE) != 0)
- - {
- - /* This really, really should not happen ... */
- - elog(ERROR, "could not initialize GMT time zone");
- - }
- - /* Use uppercase name as canonical */
- - strcpy(canonname, uppername);
- - }
- - else if (tzload(uppername, canonname, &tzstate, TRUE) != 0)
- + if (tzload(uppername, canonname, &tzstate, TRUE) != 0)
- {
- if (uppername[0] == ':' || tzparse(uppername, &tzstate, FALSE) != 0)
- {
- @@ -290,26 +1326,175 @@
- /*
- - * Initialize timezone library
- - *
- - * This is called before GUC variable initialization begins. Its purpose
- - * is to ensure that log_timezone has a valid value before any logging GUC
- - * variables could become set to values that require elog.c to provide
- - * timestamps (e.g., log_line_prefix). We may as well initialize
- - * session_timestamp to something valid, too.
- +* Check whether timezone is acceptable.
- +*
- +* What we are doing here is checking for leap-second-aware timekeeping.
- +* We need to reject such TZ settings because they'll wreak havoc with our
- +* date/time arithmetic.
- +*
- +* NB: this must NOT ereport(ERROR). The caller must get control back so that
- +* it can restore the old value of TZ if we don't like the new one.
- +*/
- +bool
- +tz_acceptable(pg_tz *tz)
- +{
- + struct pg_tm *tt;
- + pg_time_t time2000;
- +
- + /*
- + * To detect leap-second timekeeping, run pg_localtime for what should be
- + * GMT midnight, 2000-01-01. Insist that the tm_sec value be zero; any
- + * other result has to be due to leap seconds.
- */
- + time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
- + tt = pg_localtime(&time2000, tz);
- + if (!tt || tt->tm_sec != 0)
- + return false;
- +
- + return true;
- +}
- +
- +
- +/*
- +* Get a pg_tz struct for the given timezone name. Returns NULL if name
- +* is invalid or not an "acceptable" zone.
- +*/
- +static pg_tz *
- +get_pg_tz_for_zone(const char *tzname)
- +{
- + pg_tz *tz;
- +
- + if (!tzname || !tzname[0])
- + return NULL;
- +
- + tz = pg_tzset(tzname);
- + if (!tz)
- + return NULL;
- +
- + if (!tz_acceptable(tz))
- + return NULL;
- +
- + return tz;
- +}
- +
- +/*
- +* 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 pg_tz *
- +select_default_timezone(void)
- +{
- + pg_tz *def_tz;
- +
- + def_tz = get_pg_tz_for_zone(getenv("TZ"));
- + if (def_tz)
- + return def_tz;
- +
- + def_tz = get_pg_tz_for_zone(identify_system_timezone());
- + if (def_tz)
- + return def_tz;
- +
- + def_tz = get_pg_tz_for_zone("GMT");
- + if (def_tz)
- + return def_tz;
- +
- + ereport(FATAL,
- + (errmsg("could not select a suitable default time zone"),
- + errdetail("It appears that your GMT time zone uses leap seconds. PostgreSQL does not support leap seconds.")));
- + return NULL; /* keep compiler quiet */
- +}
- +
- +
- +/*
- +* Pre-initialize timezone library
- +*
- +* This is called before GUC variable initialization begins. Its purpose
- +* is to ensure that elog.c has a pgtz variable available to format timestamps
- +* with, in case log_line_prefix is set to a value requiring that. We cannot
- +* set log_timezone yet.
- +*/
- +void
- +pg_timezone_pre_initialize(void)
- +{
- + /*
- + * We can't use tzload() because we may not know where PGSHAREDIR is (in
- + * particular this is true in an EXEC_BACKEND subprocess). Since this
- + * timezone variable will only be used for emergency fallback purposes, it
- + * seems OK to just use the "lastditch" case provided by tzparse().
- + */
- + if (tzparse("GMT", &gmt_timezone_data.state, TRUE) != 0)
- + elog(FATAL, "could not initialize GMT time zone");
- + strcpy(gmt_timezone_data.TZname, "GMT");
- + gmt_timezone = &gmt_timezone_data;
- +}
- +
- +/*
- +* Initialize timezone library
- +*
- +* 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.
- +*
- +* Note: this is also called from ProcessConfigFile, to re-establish valid
- +* GUC settings if the GUCs have been reset to default following their
- +* removal from postgresql.conf.
- +*/
- void
- pg_timezone_initialize(void)
- {
- + pg_tz *def_tz = NULL;
- +
- + /*
- + * Make sure that session_timezone and log_timezone are set.
- + * (session_timezone could still be NULL even if a timezone value was set
- + * in postgresql.conf, if that setting was interval-based rather than
- + * timezone-based.)
- + */
- + if (!session_timezone)
- + {
- + def_tz = select_default_timezone();
- + session_timezone = def_tz;
- + }
- + if (!log_timezone)
- + {
- + /* Don't duplicate work */
- + if (!def_tz)
- + def_tz = select_default_timezone();
- + log_timezone = def_tz;
- + }
- +
- /*
- - * We may not yet know where PGSHAREDIR is (in particular this is true in
- - * an EXEC_BACKEND subprocess). So use "GMT", which pg_tzset forces to be
- - * interpreted without reference to the filesystem. This corresponds to
- - * the bootstrap default for these variables in guc.c, although in
- - * principle it could be different.
- + * Now, set the timezone and log_timezone GUCs if they're still default.
- + * (This will redundantly call pg_tzset().)
- + *
- + * We choose to label these values PGC_S_ENV_VAR, rather than
- + * PGC_S_DYNAMIC_DEFAULT which would be functionally equivalent, because
- + * they came either from getenv("TZ") or from libc behavior that's
- + * determined by process environment of some kind.
- + *
- + * Note: in the case where a setting has just been removed from
- + * postgresql.conf, this code will not do what you might expect, namely
- + * call select_default_timezone() and install that value as the setting.
- + * Rather, the previously active setting --- typically the one from
- + * postgresql.conf --- will be reinstalled, relabeled as PGC_S_ENV_VAR. If
- + * we did try to install the "correct" default value, the effect would be
- + * that each postmaster child would independently run an extremely
- + * expensive search of the timezone database, bringing the database to its
- + * knees for possibly multiple seconds. This is so unpleasant, and could
- + * so easily be triggered quite unintentionally, that it seems better to
- + * violate the principle of least astonishment.
- */
- - session_timezone = pg_tzset("GMT");
- - log_timezone = session_timezone;
- + if (GetConfigOptionResetString("timezone") == NULL)
- + SetConfigOption("timezone", pg_get_timezone_name(session_timezone),
- + PGC_POSTMASTER, PGC_S_ENV_VAR);
- +
- + if (GetConfigOptionResetString("log_timezone") == NULL)
- + SetConfigOption("log_timezone", pg_get_timezone_name(log_timezone),
- + PGC_POSTMASTER, PGC_S_ENV_VAR);
- }
- @@ -423,7 +1608,7 @@
- continue;
- }
- - if (!pg_tz_acceptable(&dir->tz))
- + if (!tz_acceptable(&dir->tz))
- {
- /* Ignore leap-second zones */
- continue;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement