*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.105 2004/12/31 21:59:42 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.106 2005/04/19 03:13:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
* UNKNOWN as the canonical spelling.
*
* During GUC initialization, since the timezone library isn't
- * set up yet, pg_get_current_timezone will return NULL and we
+ * 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.
*/
- const char *curzone = pg_get_current_timezone();
+ const char *curzone = pg_get_timezone_name(global_timezone);
if (curzone)
value = curzone;
else
{
/*
- * Otherwise assume it is a timezone name.
- *
- * We have to actually apply the change before we can have any
- * hope of checking it. So, save the old value in case we
- * have to back out. We have to copy since
- * pg_get_current_timezone returns a pointer to its static
- * state.
- *
- * This would all get a lot simpler if the TZ library had a
- * better API that would let us look up and test a timezone
- * name without making it the default.
+ * Otherwise assume it is a timezone name, and try to load it.
*/
- const char *cur_tz;
- char *save_tz;
- bool known,
- acceptable;
+ pg_tz *new_tz;
- cur_tz = pg_get_current_timezone();
- if (cur_tz)
- save_tz = pstrdup(cur_tz);
- else
- save_tz = NULL;
+ new_tz = pg_tzset(value);
- known = pg_tzset(value);
- acceptable = known ? tz_acceptable() : false;
+ 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 (doit && known && acceptable)
+ if (!tz_acceptable(new_tz))
{
- /* Keep the changed TZ */
- HasCTZSet = false;
+ 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;
}
- else
+
+ if (doit)
{
- /*
- * Revert to prior TZ setting; note we haven't changed
- * HasCTZSet in this path, so if we were previously using
- * a fixed offset, we still are.
- */
- if (save_tz)
- pg_tzset(save_tz);
- else
- {
- /*
- * TZ library wasn't initialized yet. Annoyingly, we
- * will come here during startup because guc-file.l
- * checks the value with doit = false before actually
- * applying. The best approach seems to be as follows:
- *
- * 1. known && acceptable: leave the setting in place,
- * since we'll apply it soon anyway. This is mainly
- * so that any log messages printed during this
- * interval are timestamped with the user's requested
- * timezone.
- *
- * 2. known && !acceptable: revert to GMT for lack of any
- * better idea. (select_default_timezone() may get
- * called later to undo this.)
- *
- * 3. !known: no need to do anything since TZ library did
- * not change its state.
- *
- * Again, this should all go away sometime soon.
- */
- if (known && !acceptable)
- pg_tzset("GMT");
- }
- /* Complain if it was bad */
- if (!known)
- {
- ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized time zone name: \"%s\"",
- value)));
- return NULL;
- }
- if (!acceptable)
- {
- 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;
- }
+ /* Save the changed TZ */
+ global_timezone = new_tz;
+ HasCTZSet = false;
}
}
}
IntervalPGetDatum(&interval)));
}
else
- tzn = pg_get_current_timezone();
+ tzn = pg_get_timezone_name(global_timezone);
if (tzn != NULL)
return tzn;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/syslogger.c,v 1.14 2005/03/12 01:54:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/syslogger.c,v 1.15 2005/04/19 03:13:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
if (strchr(Log_filename, '%'))
{
/* treat it as a strftime pattern */
- tm = pg_localtime(×tamp);
+ tm = pg_localtime(×tamp, global_timezone);
pg_strftime(filename + len, MAXPGPATH - len, Log_filename, tm);
}
else
*/
rotinterval = Log_RotationAge * 60; /* convert to seconds */
now = time(NULL);
- tm = pg_localtime(&now);
+ tm = pg_localtime(&now, global_timezone);
now += tm->tm_gmtoff;
now -= now % rotinterval;
now += rotinterval;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.137 2005/01/11 18:33:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.138 2005/04/19 03:13:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
res = pg_next_dst_boundary(&prevtime,
&before_gmtoff, &before_isdst,
&boundary,
- &after_gmtoff, &after_isdst);
+ &after_gmtoff, &after_isdst,
+ global_timezone);
if (res < 0)
goto overflow; /* failure? */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.127 2004/12/31 22:01:22 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.128 2005/04/19 03:13:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
time -= CTimeZone;
if ((!HasCTZSet) && (tzp != NULL))
- tx = pg_localtime(&time);
+ tx = pg_localtime(&time,global_timezone);
else
tx = pg_gmtime(&time);
gettimeofday(&tp, &tpz);
tt = (pg_time_t) tp.tv_sec;
pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
- pg_localtime(&tt));
+ pg_localtime(&tt,global_timezone));
snprintf(buf, sizeof(buf), templ, tp.tv_usec);
len = VARHDRSZ + strlen(buf);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.118 2005/04/01 14:25:23 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.119 2005/04/19 03:13:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
utime = (pg_time_t) dt;
if ((Timestamp) utime == dt)
{
- struct pg_tm *tx = pg_localtime(&utime);
+ struct pg_tm *tx = pg_localtime(&utime, global_timezone);
tm->tm_year = tx->tm_year + 1900;
tm->tm_mon = tx->tm_mon + 1;
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.6 2004/12/31 22:03:19 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.7 2005/04/19 03:13:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
const char *tm_zone;
};
-extern struct pg_tm *pg_localtime(const pg_time_t *timep);
+typedef struct pg_tz pg_tz;
+
+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,
long int *before_gmtoff,
int *before_isdst,
pg_time_t *boundary,
long int *after_gmtoff,
- int *after_isdst);
+ int *after_isdst,
+ const pg_tz *tz);
extern size_t pg_strftime(char *s, size_t max, const char *format,
const struct pg_tm * tm);
extern void pg_timezone_initialize(void);
-extern bool pg_tzset(const char *tzname);
-extern bool tz_acceptable(void);
-extern const char *select_default_timezone(void);
-extern const char *pg_get_current_timezone(void);
+extern pg_tz *pg_tzset(const char *tzname);
+extern bool tz_acceptable(pg_tz *tz);
+extern const char *pg_get_timezone_name(pg_tz *tz);
+extern pg_tz *global_timezone;
#endif /* _PGTIME_H */
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.9 2004/11/01 21:34:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.10 2005/04/19 03:13:59 momjian Exp $
*/
/*
#include <fcntl.h>
-#include "pgtz.h"
#include "private.h"
+#include "pgtz.h"
#include "tzfile.h"
*/
#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
-struct ttinfo
-{ /* time type information */
- long tt_gmtoff; /* UTC offset in seconds */
- int tt_isdst; /* used to set tm_isdst */
- int tt_abbrind; /* abbreviation list index */
- int tt_ttisstd; /* TRUE if transition is std time */
- int tt_ttisgmt; /* TRUE if transition is UTC */
-};
-
-struct lsinfo
-{ /* leap second information */
- pg_time_t ls_trans; /* transition time */
- long ls_corr; /* correction to apply */
-};
-
-#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
-
-struct state
-{
- int leapcnt;
- int timecnt;
- int typecnt;
- int charcnt;
- pg_time_t ats[TZ_MAX_TIMES];
- unsigned char types[TZ_MAX_TIMES];
- struct ttinfo ttis[TZ_MAX_TYPES];
- char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
- (2 * (TZ_STRLEN_MAX + 1)))];
- struct lsinfo lsis[TZ_MAX_LEAPS];
-};
-
struct rule
{
int r_type; /* type of rule--see below */
static const char *getrule(const char *strp, struct rule * rulep);
static void gmtload(struct state * sp);
static void gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp);
-static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp);
+static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp, const pg_tz *tz);
static void timesub(const pg_time_t *timep, long offset,
const struct state * sp, struct pg_tm * tmp);
static pg_time_t transtime(pg_time_t janfirst, int year,
- const struct rule * rulep, long offset);
-static int tzload(const char *name, struct state * sp);
-static int tzparse(const char *name, struct state * sp, int lastditch);
+ const struct rule * rulep, long offset);
+int tzparse(const char *name, struct state * sp, int lastditch);
-static struct state lclmem;
+/* GMT timezone */
static struct state gmtmem;
-#define lclptr (&lclmem)
#define gmtptr (&gmtmem)
-static char lcl_TZname[TZ_STRLEN_MAX + 1];
-static int lcl_is_set = 0;
+
static int gmt_is_set = 0;
/*
return result;
}
-static int
+int
tzload(register const char *name, register struct state * sp)
{
register const char *p;
* appropriate.
*/
-static int
+int
tzparse(const char *name, register struct state * sp, const int lastditch)
{
const char *stdname;
}
-bool
-pg_tzset(const char *name)
-{
- if (lcl_is_set && strcmp(lcl_TZname, name) == 0)
- return true; /* no change */
-
- if (strlen(name) >= sizeof(lcl_TZname))
- return false; /* not gonna fit */
-
- if (tzload(name, lclptr) != 0)
- {
- if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
- {
- /* Unknown timezone. Fail our call instead of loading GMT! */
- return false;
- }
- }
-
- strcpy(lcl_TZname, name);
- lcl_is_set = true;
-
- return true;
-}
-
/*
* The easy way to behave "as if no library function calls" localtime
* is to not call it--so we drop its guts into "localsub", which can be
* The unused offset argument is for the benefit of mktime variants.
*/
static void
-localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp)
+localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp, const pg_tz *tz)
{
- register struct state *sp;
+ register const struct state *sp;
register const struct ttinfo *ttisp;
register int i;
const pg_time_t t = *timep;
- sp = lclptr;
+ sp = &tz->state;
if (sp->timecnt == 0 || t < sp->ats[0])
{
i = 0;
struct pg_tm *
-pg_localtime(const pg_time_t *timep)
+pg_localtime(const pg_time_t *timep, const pg_tz *tz)
{
- localsub(timep, 0L, &tm);
+ localsub(timep, 0L, &tm, tz);
return &tm;
}
int *before_isdst,
pg_time_t *boundary,
long int *after_gmtoff,
- int *after_isdst)
+ int *after_isdst,
+ const pg_tz *tz)
{
- register struct state *sp;
+ register const struct state *sp;
register const struct ttinfo *ttisp;
int i;
int j;
const pg_time_t t = *timep;
- sp = lclptr;
+ sp = &tz->state;
if (sp->timecnt == 0)
{
/* non-DST zone, use lowest-numbered standard type */
* Return the name of the current timezone
*/
const char *
-pg_get_current_timezone(void)
+pg_get_timezone_name(pg_tz *tz)
{
- if (lcl_is_set)
- return lcl_TZname;
+ if (tz)
+ return tz->TZname;
return NULL;
}
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.29 2004/12/31 22:03:59 pgsql Exp $
+ * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.30 2005/04/19 03:13:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "utils/datetime.h"
#include "utils/elog.h"
#include "utils/guc.h"
+#include "utils/hsearch.h"
+
+/* Current global timezone */
+pg_tz *global_timezone = NULL;
static char tzdir[MAXPGPATH];
static int done_tzdir = 0;
static const char *identify_system_timezone(void);
+static const char *select_default_timezone(void);
+static bool set_global_timezone(const char *tzname);
/*
struct tm *systm;
struct pg_tm *pgtm;
char cbuf[TZ_STRLEN_MAX + 1];
+ pg_tz *tz;
- if (!pg_tzset(tzname))
+ tz = pg_tzset(tzname);
+ if (!tz)
return -1; /* can't handle the TZ name at all */
/* Reject if leap seconds involved */
- if (!tz_acceptable())
+ if (!tz_acceptable(tz))
{
elog(DEBUG4, "Reject TZ \"%s\": uses leap seconds", tzname);
return -1;
for (i = 0; i < tt->n_test_times; i++)
{
pgtt = (pg_time_t) (tt->test_times[i]);
- pgtm = pg_localtime(&pgtt);
+ pgtm = pg_localtime(&pgtt, tz);
if (!pgtm)
return -1; /* probably shouldn't happen */
systm = localtime(&(tt->test_times[i]));
#endif /* WIN32 */
+
+/*
+ * We keep loaded timezones in a hashtable so we don't have to
+ * load and parse the TZ definition file every time it is selected.
+ */
+static HTAB *timezone_cache = NULL;
+static bool
+init_timezone_hashtable(void)
+{
+ HASHCTL hash_ctl;
+
+ MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+
+ hash_ctl.keysize = TZ_STRLEN_MAX;
+ hash_ctl.entrysize = sizeof(pg_tz);
+
+ timezone_cache = hash_create("Timezones",
+ 31,
+ &hash_ctl,
+ HASH_ELEM);
+ if (!timezone_cache)
+ return false;
+
+ return true;
+}
+
+/*
+ * Load a timezone from file or from cache.
+ * Does not verify that the timezone is acceptable!
+ */
+struct pg_tz *
+pg_tzset(const char *name)
+{
+ pg_tz *tzp;
+ pg_tz tz;
+
+ if (strlen(name) >= TZ_STRLEN_MAX)
+ return NULL; /* not going to fit */
+
+ if (!timezone_cache)
+ if (!init_timezone_hashtable())
+ return NULL;
+
+ tzp = (pg_tz *)hash_search(timezone_cache,
+ name,
+ HASH_FIND,
+ NULL);
+ if (tzp)
+ /* Timezone found in cache, nothing more to do */
+ return tzp;
+
+ if (tzload(name, &tz.state) != 0)
+ {
+ if (name[0] == ':' || tzparse(name, &tz.state, FALSE) != 0)
+ /* Unknown timezone. Fail our call instead of loading GMT! */
+ return NULL;
+ }
+
+ strcpy(tz.TZname, name);
+
+ /* Save timezone in the cache */
+ tzp = hash_search(timezone_cache,
+ name,
+ HASH_ENTER,
+ NULL);
+
+ if (!tzp)
+ return NULL;
+
+ strcpy(tzp->TZname, tz.TZname);
+ memcpy(&tzp->state, &tz.state, sizeof(tz.state));
+
+ return tzp;
+}
+
+
/*
* Check whether timezone is acceptable.
*
* it can restore the old value of TZ if we don't like the new one.
*/
bool
-tz_acceptable(void)
+tz_acceptable(pg_tz *tz)
{
struct pg_tm *tt;
pg_time_t time2000;
* any other result has to be due to leap seconds.
*/
time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400;
- tt = pg_localtime(&time2000);
+ tt = pg_localtime(&time2000, tz);
if (!tt || tt->tm_sec != 0)
return false;
return true;
}
+
+/*
+ * Set the global timezone. Verify that it's acceptable first.
+ */
+static bool
+set_global_timezone(const char *tzname)
+{
+ pg_tz *tznew;
+
+ if (!tzname || !tzname[0])
+ return false;
+
+ tznew = pg_tzset(tzname);
+ if (!tznew)
+ return false;
+
+ if (!tz_acceptable(tznew))
+ return false;
+
+ global_timezone = tznew;
+ return true;
+}
+
/*
* Identify a suitable default timezone setting based on the environment,
* and make it active.
* from the behavior of the system timezone library. When all else fails,
* fall back to GMT.
*/
-const char *
+static const char *
select_default_timezone(void)
{
const char *def_tz;
def_tz = getenv("TZ");
- if (def_tz && pg_tzset(def_tz) && tz_acceptable())
+ if (set_global_timezone(def_tz))
return def_tz;
def_tz = identify_system_timezone();
- if (def_tz && pg_tzset(def_tz) && tz_acceptable())
+ if (set_global_timezone(def_tz))
return def_tz;
- if (pg_tzset("GMT") && tz_acceptable())
+ if (set_global_timezone("GMT"))
return "GMT";
ereport(FATAL,
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.10 2004/12/31 22:03:59 pgsql Exp $
+ * $PostgreSQL: pgsql/src/timezone/pgtz.h,v 1.11 2005/04/19 03:13:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef _PGTZ_H
#define _PGTZ_H
+#include "tzfile.h"
+
#define TZ_STRLEN_MAX 255
extern char *pg_TZDIR(void);
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+struct ttinfo
+{ /* time type information */
+ long tt_gmtoff; /* UTC offset in seconds */
+ int tt_isdst; /* used to set tm_isdst */
+ int tt_abbrind; /* abbreviation list index */
+ int tt_ttisstd; /* TRUE if transition is std time */
+ int tt_ttisgmt; /* TRUE if transition is UTC */
+};
+
+struct lsinfo
+{ /* leap second information */
+ pg_time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+};
+
+struct state
+{
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ pg_time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */),
+ (2 * (TZ_STRLEN_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+};
+
+
+struct pg_tz {
+ char TZname[TZ_STRLEN_MAX + 1];
+ struct state state;
+};
+
+int tzload(const char *name, struct state * sp);
+int tzparse(const char *name, struct state * sp, int lastditch);
+
#endif /* _PGTZ_H */
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.5 2004/08/29 05:07:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.6 2005/04/19 03:13:59 momjian Exp $
*/
#include "postgres.h"
#include <fcntl.h>
#include <locale.h>
-#include "pgtz.h"
#include "private.h"
+#include "pgtz.h"
#include "tzfile.h"
* 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/timezone/zic.c,v 1.13 2004/09/27 19:16:03 momjian Exp $
+ * $PostgreSQL: pgsql/src/timezone/zic.c,v 1.14 2005/04/19 03:13:59 momjian Exp $
*/
#include "postgres.h"
#include <limits.h>
#include <locale.h>
-#include "pgtz.h"
#include "private.h"
+#include "pgtz.h"
#include "tzfile.h"
#ifdef HAVE_SYS_STAT_H