mirror of
https://github.com/postgres/postgres.git
synced 2025-08-21 10:42:50 +03:00
Remove hard-wired lists of timezone abbreviations in favor of providing
configuration files that can be altered by a DBA. The australian_timezones GUC setting disappears, replaced by a timezone_abbreviations setting (set this to 'Australia' to get the effect of australian_timezones). The list of zone names defined by default has undergone a bit of cleanup, too. Documentation still needs some work --- in particular, should we fix Table B-4, or just get rid of it? Joachim Wieland, with some editorializing by moi.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.27 2006/05/19 19:08:26 alvherre Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.28 2006/07/25 03:51:21 tgl Exp $
|
||||
*/
|
||||
|
||||
CREATE VIEW pg_roles AS
|
||||
@@ -186,6 +186,11 @@ CREATE RULE pg_settings_n AS
|
||||
|
||||
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
|
||||
|
||||
CREATE VIEW pg_timezonenames AS
|
||||
SELECT *
|
||||
FROM pg_timezonenames() AS T
|
||||
(name text, utc_offset interval, is_dst boolean);
|
||||
|
||||
-- Statistics views
|
||||
|
||||
CREATE VIEW pg_stat_all_tables AS
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.168 2006/07/14 05:28:28 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.169 2006/07/25 03:51:21 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -19,11 +19,15 @@
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/datetime.h"
|
||||
#include "utils/guc.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/tzparser.h"
|
||||
|
||||
|
||||
static int DecodeNumber(int flen, char *field, bool haveTextMonth,
|
||||
@@ -37,7 +41,7 @@ static int DecodeTime(char *str, int fmask, int *tmask,
|
||||
static int DecodeTimezone(char *str, int *tzp);
|
||||
static int DecodePosixTimezone(char *str, int *tzp);
|
||||
static int DecodeZicTimezone(char *str, int *tzp, struct pg_tm * tm);
|
||||
static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
|
||||
static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
|
||||
static int DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm);
|
||||
static void TrimTrailingZeros(char *str);
|
||||
|
||||
@@ -87,426 +91,92 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
|
||||
* which are 30 or 45 minutes away from an even hour, most are on an hour
|
||||
* boundary, and none on other boundaries.
|
||||
*
|
||||
* Let's include all strings from my current zic time zone database.
|
||||
* Not all of them are unique, or even very understandable, so we will
|
||||
* leave some commented out for now.
|
||||
* The static table contains no TZ or DTZ entries, rather those are loaded
|
||||
* from configuration files and stored in timezonetktbl, which has the same
|
||||
* format as the static datetktbl.
|
||||
*/
|
||||
static datetkn datetktbl[] = {
|
||||
static datetkn *timezonetktbl = NULL;
|
||||
|
||||
static int sztimezonetktbl = 0;
|
||||
|
||||
static const datetkn datetktbl[] = {
|
||||
/* text, token, lexval */
|
||||
{EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
|
||||
{"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */
|
||||
{"acsst", DTZ, POS(42)}, /* Cent. Australia */
|
||||
{"acst", DTZ, NEG(16)}, /* Atlantic/Porto Acre Summer Time */
|
||||
{"act", TZ, NEG(20)}, /* Atlantic/Porto Acre Time */
|
||||
{DA_D, ADBC, AD}, /* "ad" for years > 0 */
|
||||
{"adt", DTZ, NEG(12)}, /* Atlantic Daylight Time */
|
||||
{"aesst", DTZ, POS(44)}, /* E. Australia */
|
||||
{"aest", TZ, POS(40)}, /* Australia Eastern Std Time */
|
||||
{"aft", TZ, POS(18)}, /* Kabul */
|
||||
{"ahst", TZ, NEG(40)}, /* Alaska-Hawaii Std Time */
|
||||
{"akdt", DTZ, NEG(32)}, /* Alaska Daylight Time */
|
||||
{"akst", DTZ, NEG(36)}, /* Alaska Standard Time */
|
||||
{"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
|
||||
{"almst", TZ, POS(28)}, /* Almaty Savings Time */
|
||||
{"almt", TZ, POS(24)}, /* Almaty Time */
|
||||
{"am", AMPM, AM},
|
||||
{"amst", DTZ, POS(20)}, /* Armenia Summer Time (Yerevan) */
|
||||
#if 0
|
||||
{"amst", DTZ, NEG(12)}, /* Amazon Summer Time (Porto Velho) */
|
||||
#endif
|
||||
{"amt", TZ, POS(16)}, /* Armenia Time (Yerevan) */
|
||||
#if 0
|
||||
{"amt", TZ, NEG(16)}, /* Amazon Time (Porto Velho) */
|
||||
#endif
|
||||
{"anast", DTZ, POS(52)}, /* Anadyr Summer Time (Russia) */
|
||||
{"anat", TZ, POS(48)}, /* Anadyr Time (Russia) */
|
||||
{"apr", MONTH, 4},
|
||||
{"april", MONTH, 4},
|
||||
#if 0
|
||||
aqtst
|
||||
aqtt
|
||||
arst
|
||||
#endif
|
||||
{"art", TZ, NEG(12)}, /* Argentina Time */
|
||||
#if 0
|
||||
ashst
|
||||
ast /* Atlantic Standard Time, Arabia Standard
|
||||
* Time, Acre Standard Time */
|
||||
#endif
|
||||
{"ast", TZ, NEG(16)}, /* Atlantic Std Time (Canada) */
|
||||
{"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
|
||||
{"aug", MONTH, 8},
|
||||
{"august", MONTH, 8},
|
||||
{"awsst", DTZ, POS(36)}, /* W. Australia */
|
||||
{"awst", TZ, POS(32)}, /* W. Australia */
|
||||
{"awt", DTZ, NEG(12)},
|
||||
{"azost", DTZ, POS(0)}, /* Azores Summer Time */
|
||||
{"azot", TZ, NEG(4)}, /* Azores Time */
|
||||
{"azst", DTZ, POS(20)}, /* Azerbaijan Summer Time */
|
||||
{"azt", TZ, POS(16)}, /* Azerbaijan Time */
|
||||
{DB_C, ADBC, BC}, /* "bc" for years <= 0 */
|
||||
{"bdst", TZ, POS(8)}, /* British Double Summer Time */
|
||||
{"bdt", TZ, POS(24)}, /* Dacca */
|
||||
{"bnt", TZ, POS(32)}, /* Brunei Darussalam Time */
|
||||
{"bort", TZ, POS(32)}, /* Borneo Time (Indonesia) */
|
||||
#if 0
|
||||
bortst
|
||||
bost
|
||||
#endif
|
||||
{"bot", TZ, NEG(16)}, /* Bolivia Time */
|
||||
{"bra", TZ, NEG(12)}, /* Brazil Time */
|
||||
{"brst", DTZ, NEG(8)}, /* Brasilia Summer Time */
|
||||
{"brt", TZ, NEG(12)}, /* Brasilia Time */
|
||||
{"bst", DTZ, POS(4)}, /* British Summer Time */
|
||||
#if 0
|
||||
{"bst", TZ, NEG(12)}, /* Brazil Standard Time */
|
||||
{"bst", DTZ, NEG(44)}, /* Bering Summer Time */
|
||||
#endif
|
||||
{"bt", TZ, POS(12)}, /* Baghdad Time */
|
||||
{"btt", TZ, POS(24)}, /* Bhutan Time */
|
||||
{"cadt", DTZ, POS(42)}, /* Central Australian DST */
|
||||
{"cast", TZ, POS(38)}, /* Central Australian ST */
|
||||
{"cat", TZ, NEG(40)}, /* Central Alaska Time */
|
||||
{"cct", TZ, POS(32)}, /* China Coast Time */
|
||||
#if 0
|
||||
{"cct", TZ, POS(26)}, /* Indian Cocos (Island) Time */
|
||||
#endif
|
||||
{"cdt", DTZ, NEG(20)}, /* Central Daylight Time */
|
||||
{"cest", DTZ, POS(8)}, /* Central European Dayl.Time */
|
||||
{"cet", TZ, POS(4)}, /* Central European Time */
|
||||
{"cetdst", DTZ, POS(8)}, /* Central European Dayl.Time */
|
||||
{"chadt", DTZ, POS(55)}, /* Chatham Island Daylight Time (13:45) */
|
||||
{"chast", TZ, POS(51)}, /* Chatham Island Time (12:45) */
|
||||
#if 0
|
||||
ckhst
|
||||
#endif
|
||||
{"ckt", TZ, POS(48)}, /* Cook Islands Time */
|
||||
{"clst", DTZ, NEG(12)}, /* Chile Summer Time */
|
||||
{"clt", TZ, NEG(16)}, /* Chile Time */
|
||||
#if 0
|
||||
cost
|
||||
#endif
|
||||
{"cot", TZ, NEG(20)}, /* Columbia Time */
|
||||
{"cst", TZ, NEG(24)}, /* Central Standard Time */
|
||||
{DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */
|
||||
#if 0
|
||||
cvst
|
||||
#endif
|
||||
{"cvt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
|
||||
{"cxt", TZ, POS(28)}, /* Christmas Island Time (Indian Ocean) */
|
||||
{"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
|
||||
{"davt", TZ, POS(28)}, /* Davis Time (Antarctica) */
|
||||
{"ddut", TZ, POS(40)}, /* Dumont-d'Urville Time (Antarctica) */
|
||||
{"dec", MONTH, 12},
|
||||
{"december", MONTH, 12},
|
||||
{"dnt", TZ, POS(4)}, /* Dansk Normal Tid */
|
||||
{"dow", RESERV, DTK_DOW}, /* day of week */
|
||||
{"doy", RESERV, DTK_DOY}, /* day of year */
|
||||
{"dst", DTZMOD, 6},
|
||||
#if 0
|
||||
{"dusst", DTZ, POS(24)}, /* Dushanbe Summer Time */
|
||||
#endif
|
||||
{"easst", DTZ, NEG(20)}, /* Easter Island Summer Time */
|
||||
{"east", TZ, NEG(24)}, /* Easter Island Time */
|
||||
{"eat", TZ, POS(12)}, /* East Africa Time */
|
||||
#if 0
|
||||
{"east", DTZ, POS(16)}, /* Indian Antananarivo Savings Time */
|
||||
{"eat", TZ, POS(12)}, /* Indian Antananarivo Time */
|
||||
{"ect", TZ, NEG(16)}, /* Eastern Caribbean Time */
|
||||
{"ect", TZ, NEG(20)}, /* Ecuador Time */
|
||||
#endif
|
||||
{"edt", DTZ, NEG(16)}, /* Eastern Daylight Time */
|
||||
{"eest", DTZ, POS(12)}, /* Eastern Europe Summer Time */
|
||||
{"eet", TZ, POS(8)}, /* East. Europe, USSR Zone 1 */
|
||||
{"eetdst", DTZ, POS(12)}, /* Eastern Europe Daylight Time */
|
||||
{"egst", DTZ, POS(0)}, /* East Greenland Summer Time */
|
||||
{"egt", TZ, NEG(4)}, /* East Greenland Time */
|
||||
#if 0
|
||||
ehdt
|
||||
#endif
|
||||
{EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
|
||||
{"est", TZ, NEG(20)}, /* Eastern Standard Time */
|
||||
{"feb", MONTH, 2},
|
||||
{"february", MONTH, 2},
|
||||
{"fjst", DTZ, NEG(52)}, /* Fiji Summer Time (13 hour offset!) */
|
||||
{"fjt", TZ, NEG(48)}, /* Fiji Time */
|
||||
{"fkst", DTZ, NEG(12)}, /* Falkland Islands Summer Time */
|
||||
{"fkt", TZ, NEG(8)}, /* Falkland Islands Time */
|
||||
{"fnst", DTZ, NEG(4)}, /* Fernando de Noronha Summer Time */
|
||||
{"fnt", TZ, NEG(8)}, /* Fernando de Noronha Time */
|
||||
{"fri", DOW, 5},
|
||||
{"friday", DOW, 5},
|
||||
{"fst", TZ, POS(4)}, /* French Summer Time */
|
||||
{"fwt", DTZ, POS(8)}, /* French Winter Time */
|
||||
{"galt", TZ, NEG(24)}, /* Galapagos Time */
|
||||
{"gamt", TZ, NEG(36)}, /* Gambier Time */
|
||||
{"gest", DTZ, POS(20)}, /* Georgia Summer Time */
|
||||
{"get", TZ, POS(16)}, /* Georgia Time */
|
||||
{"gft", TZ, NEG(12)}, /* French Guiana Time */
|
||||
#if 0
|
||||
ghst
|
||||
#endif
|
||||
{"gilt", TZ, POS(48)}, /* Gilbert Islands Time */
|
||||
{"gmt", TZ, POS(0)}, /* Greenwich Mean Time */
|
||||
{"gst", TZ, POS(40)}, /* Guam Std Time, USSR Zone 9 */
|
||||
{"gyt", TZ, NEG(16)}, /* Guyana Time */
|
||||
{"h", UNITS, DTK_HOUR}, /* "hour" */
|
||||
#if 0
|
||||
hadt
|
||||
hast
|
||||
#endif
|
||||
{"hdt", DTZ, NEG(36)}, /* Hawaii/Alaska Daylight Time */
|
||||
#if 0
|
||||
hkst
|
||||
#endif
|
||||
{"hkt", TZ, POS(32)}, /* Hong Kong Time */
|
||||
#if 0
|
||||
{"hmt", TZ, POS(12)}, /* Hellas ? ? */
|
||||
hovst
|
||||
hovt
|
||||
#endif
|
||||
{"hst", TZ, NEG(40)}, /* Hawaii Std Time */
|
||||
#if 0
|
||||
hwt
|
||||
#endif
|
||||
{"ict", TZ, POS(28)}, /* Indochina Time */
|
||||
{"idle", TZ, POS(48)}, /* Intl. Date Line, East */
|
||||
{"idlw", TZ, NEG(48)}, /* Intl. Date Line, West */
|
||||
#if 0
|
||||
idt /* Israeli, Iran, Indian Daylight Time */
|
||||
#endif
|
||||
{LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
|
||||
{INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */
|
||||
{"iot", TZ, POS(20)}, /* Indian Chagos Time */
|
||||
{"irkst", DTZ, POS(36)}, /* Irkutsk Summer Time */
|
||||
{"irkt", TZ, POS(32)}, /* Irkutsk Time */
|
||||
{"irt", TZ, POS(14)}, /* Iran Time */
|
||||
#if 0
|
||||
isst
|
||||
#endif
|
||||
{"ist", TZ, POS(8)}, /* Israel */
|
||||
{"it", TZ, POS(14)}, /* Iran Time */
|
||||
{"j", UNITS, DTK_JULIAN},
|
||||
{"jan", MONTH, 1},
|
||||
{"january", MONTH, 1},
|
||||
{"javt", TZ, POS(28)}, /* Java Time (07:00? see JT) */
|
||||
{"jayt", TZ, POS(36)}, /* Jayapura Time (Indonesia) */
|
||||
{"jd", UNITS, DTK_JULIAN},
|
||||
{"jst", TZ, POS(36)}, /* Japan Std Time,USSR Zone 8 */
|
||||
{"jt", TZ, POS(30)}, /* Java Time (07:30? see JAVT) */
|
||||
{"jul", MONTH, 7},
|
||||
{"julian", UNITS, DTK_JULIAN},
|
||||
{"july", MONTH, 7},
|
||||
{"jun", MONTH, 6},
|
||||
{"june", MONTH, 6},
|
||||
{"kdt", DTZ, POS(40)}, /* Korea Daylight Time */
|
||||
{"kgst", DTZ, POS(24)}, /* Kyrgyzstan Summer Time */
|
||||
{"kgt", TZ, POS(20)}, /* Kyrgyzstan Time */
|
||||
{"kost", TZ, POS(48)}, /* Kosrae Time */
|
||||
{"krast", DTZ, POS(28)}, /* Krasnoyarsk Summer Time */
|
||||
{"krat", TZ, POS(32)}, /* Krasnoyarsk Standard Time */
|
||||
{"kst", TZ, POS(36)}, /* Korea Standard Time */
|
||||
{"lhdt", DTZ, POS(44)}, /* Lord Howe Daylight Time, Australia */
|
||||
{"lhst", TZ, POS(42)}, /* Lord Howe Standard Time, Australia */
|
||||
{"ligt", TZ, POS(40)}, /* From Melbourne, Australia */
|
||||
{"lint", TZ, POS(56)}, /* Line Islands Time (Kiribati; +14 hours!) */
|
||||
{"lkt", TZ, POS(24)}, /* Lanka Time */
|
||||
{"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
|
||||
{"magst", DTZ, POS(48)}, /* Magadan Summer Time */
|
||||
{"magt", TZ, POS(44)}, /* Magadan Time */
|
||||
{"mar", MONTH, 3},
|
||||
{"march", MONTH, 3},
|
||||
{"mart", TZ, NEG(38)}, /* Marquesas Time */
|
||||
{"mawt", TZ, POS(24)}, /* Mawson, Antarctica */
|
||||
{"may", MONTH, 5},
|
||||
{"mdt", DTZ, NEG(24)}, /* Mountain Daylight Time */
|
||||
{"mest", DTZ, POS(8)}, /* Middle Europe Summer Time */
|
||||
{"met", TZ, POS(4)}, /* Middle Europe Time */
|
||||
{"metdst", DTZ, POS(8)}, /* Middle Europe Daylight Time */
|
||||
{"mewt", TZ, POS(4)}, /* Middle Europe Winter Time */
|
||||
{"mez", TZ, POS(4)}, /* Middle Europe Zone */
|
||||
{"mht", TZ, POS(48)}, /* Kwajalein */
|
||||
{"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
|
||||
{"mmt", TZ, POS(26)}, /* Myanmar Time */
|
||||
{"mon", DOW, 1},
|
||||
{"monday", DOW, 1},
|
||||
#if 0
|
||||
most
|
||||
#endif
|
||||
{"mpt", TZ, POS(40)}, /* North Mariana Islands Time */
|
||||
{"msd", DTZ, POS(16)}, /* Moscow Summer Time */
|
||||
{"msk", TZ, POS(12)}, /* Moscow Time */
|
||||
{"mst", TZ, NEG(28)}, /* Mountain Standard Time */
|
||||
{"mt", TZ, POS(34)}, /* Moluccas Time */
|
||||
{"mut", TZ, POS(16)}, /* Mauritius Island Time */
|
||||
{"mvt", TZ, POS(20)}, /* Maldives Island Time */
|
||||
{"myt", TZ, POS(32)}, /* Malaysia Time */
|
||||
#if 0
|
||||
ncst
|
||||
#endif
|
||||
{"nct", TZ, POS(44)}, /* New Caledonia Time */
|
||||
{"ndt", DTZ, NEG(10)}, /* Nfld. Daylight Time */
|
||||
{"nft", TZ, NEG(14)}, /* Newfoundland Standard Time */
|
||||
{"nor", TZ, POS(4)}, /* Norway Standard Time */
|
||||
{"nov", MONTH, 11},
|
||||
{"november", MONTH, 11},
|
||||
{"novst", DTZ, POS(28)}, /* Novosibirsk Summer Time */
|
||||
{"novt", TZ, POS(24)}, /* Novosibirsk Standard Time */
|
||||
{NOW, RESERV, DTK_NOW}, /* current transaction time */
|
||||
{"npt", TZ, POS(23)}, /* Nepal Standard Time (GMT-5:45) */
|
||||
{"nst", TZ, NEG(14)}, /* Nfld. Standard Time */
|
||||
{"nt", TZ, NEG(44)}, /* Nome Time */
|
||||
{"nut", TZ, NEG(44)}, /* Niue Time */
|
||||
{"nzdt", DTZ, POS(52)}, /* New Zealand Daylight Time */
|
||||
{"nzst", TZ, POS(48)}, /* New Zealand Standard Time */
|
||||
{"nzt", TZ, POS(48)}, /* New Zealand Time */
|
||||
{"oct", MONTH, 10},
|
||||
{"october", MONTH, 10},
|
||||
{"omsst", DTZ, POS(28)}, /* Omsk Summer Time */
|
||||
{"omst", TZ, POS(24)}, /* Omsk Time */
|
||||
{"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
|
||||
{"pdt", DTZ, NEG(28)}, /* Pacific Daylight Time */
|
||||
#if 0
|
||||
pest
|
||||
#endif
|
||||
{"pet", TZ, NEG(20)}, /* Peru Time */
|
||||
{"petst", DTZ, POS(52)}, /* Petropavlovsk-Kamchatski Summer Time */
|
||||
{"pett", TZ, POS(48)}, /* Petropavlovsk-Kamchatski Time */
|
||||
{"pgt", TZ, POS(40)}, /* Papua New Guinea Time */
|
||||
{"phot", TZ, POS(52)}, /* Phoenix Islands (Kiribati) Time */
|
||||
#if 0
|
||||
phst
|
||||
#endif
|
||||
{"pht", TZ, POS(32)}, /* Phillipine Time */
|
||||
{"pkt", TZ, POS(20)}, /* Pakistan Time */
|
||||
{"pm", AMPM, PM},
|
||||
{"pmdt", DTZ, NEG(8)}, /* Pierre & Miquelon Daylight Time */
|
||||
#if 0
|
||||
pmst
|
||||
#endif
|
||||
{"pont", TZ, POS(44)}, /* Ponape Time (Micronesia) */
|
||||
{"pst", TZ, NEG(32)}, /* Pacific Standard Time */
|
||||
{"pwt", TZ, POS(36)}, /* Palau Time */
|
||||
{"pyst", DTZ, NEG(12)}, /* Paraguay Summer Time */
|
||||
{"pyt", TZ, NEG(16)}, /* Paraguay Time */
|
||||
{"ret", DTZ, POS(16)}, /* Reunion Island Time */
|
||||
{"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
|
||||
{"sadt", DTZ, POS(42)}, /* S. Australian Dayl. Time */
|
||||
#if 0
|
||||
samst
|
||||
samt
|
||||
#endif
|
||||
{"sast", TZ, POS(38)}, /* South Australian Std Time */
|
||||
{"sat", DOW, 6},
|
||||
{"saturday", DOW, 6},
|
||||
#if 0
|
||||
sbt
|
||||
#endif
|
||||
{"sct", DTZ, POS(16)}, /* Mahe Island Time */
|
||||
{"sep", MONTH, 9},
|
||||
{"sept", MONTH, 9},
|
||||
{"september", MONTH, 9},
|
||||
{"set", TZ, NEG(4)}, /* Seychelles Time ?? */
|
||||
#if 0
|
||||
sgt
|
||||
#endif
|
||||
{"sst", DTZ, POS(8)}, /* Swedish Summer Time */
|
||||
{"sun", DOW, 0},
|
||||
{"sunday", DOW, 0},
|
||||
{"swt", TZ, POS(4)}, /* Swedish Winter Time */
|
||||
#if 0
|
||||
syot
|
||||
#endif
|
||||
{"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
|
||||
{"tft", TZ, POS(20)}, /* Kerguelen Time */
|
||||
{"that", TZ, NEG(40)}, /* Tahiti Time */
|
||||
{"thu", DOW, 4},
|
||||
{"thur", DOW, 4},
|
||||
{"thurs", DOW, 4},
|
||||
{"thursday", DOW, 4},
|
||||
{"tjt", TZ, POS(20)}, /* Tajikistan Time */
|
||||
{"tkt", TZ, NEG(40)}, /* Tokelau Time */
|
||||
{"tmt", TZ, POS(20)}, /* Turkmenistan Time */
|
||||
{TODAY, RESERV, DTK_TODAY}, /* midnight */
|
||||
{TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
|
||||
#if 0
|
||||
tost
|
||||
#endif
|
||||
{"tot", TZ, POS(52)}, /* Tonga Time */
|
||||
#if 0
|
||||
tpt
|
||||
#endif
|
||||
{"truk", TZ, POS(40)}, /* Truk Time */
|
||||
{"tue", DOW, 2},
|
||||
{"tues", DOW, 2},
|
||||
{"tuesday", DOW, 2},
|
||||
{"tvt", TZ, POS(48)}, /* Tuvalu Time */
|
||||
#if 0
|
||||
uct
|
||||
#endif
|
||||
{"ulast", DTZ, POS(36)}, /* Ulan Bator Summer Time */
|
||||
{"ulat", TZ, POS(32)}, /* Ulan Bator Time */
|
||||
{"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
|
||||
{"ut", TZ, POS(0)},
|
||||
{"utc", TZ, POS(0)},
|
||||
{"uyst", DTZ, NEG(8)}, /* Uruguay Summer Time */
|
||||
{"uyt", TZ, NEG(12)}, /* Uruguay Time */
|
||||
{"uzst", DTZ, POS(24)}, /* Uzbekistan Summer Time */
|
||||
{"uzt", TZ, POS(20)}, /* Uzbekistan Time */
|
||||
{"vet", TZ, NEG(16)}, /* Venezuela Time */
|
||||
{"vlast", DTZ, POS(44)}, /* Vladivostok Summer Time */
|
||||
{"vlat", TZ, POS(40)}, /* Vladivostok Time */
|
||||
#if 0
|
||||
vust
|
||||
#endif
|
||||
{"vut", TZ, POS(44)}, /* Vanuata Time */
|
||||
{"wadt", DTZ, POS(32)}, /* West Australian DST */
|
||||
{"wakt", TZ, POS(48)}, /* Wake Time */
|
||||
#if 0
|
||||
warst
|
||||
#endif
|
||||
{"wast", TZ, POS(28)}, /* West Australian Std Time */
|
||||
{"wat", TZ, NEG(4)}, /* West Africa Time */
|
||||
{"wdt", DTZ, POS(36)}, /* West Australian DST */
|
||||
{"wed", DOW, 3},
|
||||
{"wednesday", DOW, 3},
|
||||
{"weds", DOW, 3},
|
||||
{"west", DTZ, POS(4)}, /* Western Europe Summer Time */
|
||||
{"wet", TZ, POS(0)}, /* Western Europe */
|
||||
{"wetdst", DTZ, POS(4)}, /* Western Europe Daylight Savings Time */
|
||||
{"wft", TZ, POS(48)}, /* Wallis and Futuna Time */
|
||||
{"wgst", DTZ, NEG(8)}, /* West Greenland Summer Time */
|
||||
{"wgt", TZ, NEG(12)}, /* West Greenland Time */
|
||||
{"wst", TZ, POS(32)}, /* West Australian Standard Time */
|
||||
{"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
|
||||
{"yakst", DTZ, POS(40)}, /* Yakutsk Summer Time */
|
||||
{"yakt", TZ, POS(36)}, /* Yakutsk Time */
|
||||
{"yapt", TZ, POS(40)}, /* Yap Time (Micronesia) */
|
||||
{"ydt", DTZ, NEG(32)}, /* Yukon Daylight Time */
|
||||
{"yekst", DTZ, POS(24)}, /* Yekaterinburg Summer Time */
|
||||
{"yekt", TZ, POS(20)}, /* Yekaterinburg Time */
|
||||
{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
|
||||
{"yst", TZ, NEG(36)}, /* Yukon Standard Time */
|
||||
{"z", TZ, POS(0)}, /* time zone tag per ISO-8601 */
|
||||
{"zp4", TZ, NEG(16)}, /* UTC +4 hours. */
|
||||
{"zp5", TZ, NEG(20)}, /* UTC +5 hours. */
|
||||
{"zp6", TZ, NEG(24)}, /* UTC +6 hours. */
|
||||
{ZULU, TZ, POS(0)}, /* UTC */
|
||||
};
|
||||
|
||||
static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
|
||||
|
||||
/* Used for SET australian_timezones to override North American ones */
|
||||
static datetkn australian_datetktbl[] = {
|
||||
{"acst", TZ, POS(38)}, /* Cent. Australia */
|
||||
{"cst", TZ, POS(42)}, /* Australia Central Std Time */
|
||||
{"east", TZ, POS(40)}, /* East Australian Std Time */
|
||||
{"est", TZ, POS(40)}, /* Australia Eastern Std Time */
|
||||
{"sat", TZ, POS(38)},
|
||||
};
|
||||
|
||||
static unsigned int australian_szdatetktbl = sizeof australian_datetktbl /
|
||||
sizeof australian_datetktbl[0];
|
||||
static int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
|
||||
|
||||
static datetkn deltatktbl[] = {
|
||||
/* text, token, lexval */
|
||||
@@ -576,11 +246,11 @@ static datetkn deltatktbl[] = {
|
||||
{"yrs", UNITS, DTK_YEAR}, /* "years" relative */
|
||||
};
|
||||
|
||||
static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
|
||||
static int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
|
||||
|
||||
static datetkn *datecache[MAXDATEFIELDS] = {NULL};
|
||||
static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
|
||||
|
||||
static datetkn *deltacache[MAXDATEFIELDS] = {NULL};
|
||||
static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
|
||||
|
||||
|
||||
/*
|
||||
@@ -2998,21 +2668,15 @@ int
|
||||
DecodeSpecial(int field, char *lowtoken, int *val)
|
||||
{
|
||||
int type;
|
||||
datetkn *tp;
|
||||
const datetkn *tp;
|
||||
|
||||
if (datecache[field] != NULL &&
|
||||
strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)
|
||||
tp = datecache[field];
|
||||
else
|
||||
tp = datecache[field];
|
||||
if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
|
||||
{
|
||||
tp = NULL;
|
||||
if (Australian_timezones)
|
||||
tp = datebsearch(lowtoken, australian_datetktbl,
|
||||
australian_szdatetktbl);
|
||||
if (!tp)
|
||||
tp = datebsearch(lowtoken, timezonetktbl, sztimezonetktbl);
|
||||
if (tp == NULL)
|
||||
tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
|
||||
}
|
||||
datecache[field] = tp;
|
||||
if (tp == NULL)
|
||||
{
|
||||
type = UNKNOWN_FIELD;
|
||||
@@ -3020,6 +2684,7 @@ DecodeSpecial(int field, char *lowtoken, int *val)
|
||||
}
|
||||
else
|
||||
{
|
||||
datecache[field] = tp;
|
||||
type = tp->type;
|
||||
switch (type)
|
||||
{
|
||||
@@ -3391,20 +3056,20 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm,
|
||||
|
||||
/* DecodeUnits()
|
||||
* Decode text string using lookup table.
|
||||
* This routine supports time interval decoding.
|
||||
* This routine supports time interval decoding
|
||||
* (hence, it need not recognize timezone names).
|
||||
*/
|
||||
int
|
||||
DecodeUnits(int field, char *lowtoken, int *val)
|
||||
{
|
||||
int type;
|
||||
datetkn *tp;
|
||||
const datetkn *tp;
|
||||
|
||||
if (deltacache[field] != NULL &&
|
||||
strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)
|
||||
tp = deltacache[field];
|
||||
else
|
||||
tp = deltacache[field];
|
||||
if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
|
||||
{
|
||||
tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
|
||||
deltacache[field] = tp;
|
||||
}
|
||||
if (tp == NULL)
|
||||
{
|
||||
type = UNKNOWN_FIELD;
|
||||
@@ -3412,6 +3077,7 @@ DecodeUnits(int field, char *lowtoken, int *val)
|
||||
}
|
||||
else
|
||||
{
|
||||
deltacache[field] = tp;
|
||||
type = tp->type;
|
||||
if (type == TZ || type == DTZ)
|
||||
*val = FROMVAL(tp);
|
||||
@@ -3477,10 +3143,10 @@ DateTimeParseError(int dterr, const char *str, const char *datatype)
|
||||
* Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
|
||||
* is WAY faster than the generic bsearch().
|
||||
*/
|
||||
static datetkn *
|
||||
datebsearch(char *key, datetkn *base, unsigned int nel)
|
||||
static const datetkn *
|
||||
datebsearch(const char *key, const datetkn *base, int nel)
|
||||
{
|
||||
datetkn *last = base + nel - 1,
|
||||
const datetkn *last = base + nel - 1,
|
||||
*position;
|
||||
int result;
|
||||
|
||||
@@ -4082,30 +3748,15 @@ EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
|
||||
} /* EncodeInterval() */
|
||||
|
||||
|
||||
/* GUC assign_hook for australian_timezones */
|
||||
bool
|
||||
ClearDateCache(bool newval, bool doit, GucSource source)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (doit)
|
||||
{
|
||||
for (i = 0; i < MAXDATEFIELDS; i++)
|
||||
datecache[i] = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've been burnt by stupid errors in the ordering of the datetkn tables
|
||||
* once too often. Arrange to check them during postmaster start.
|
||||
*/
|
||||
static bool
|
||||
CheckDateTokenTable(const char *tablename, datetkn *base, unsigned int nel)
|
||||
CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
|
||||
{
|
||||
bool ok = true;
|
||||
unsigned int i;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < nel; i++)
|
||||
{
|
||||
@@ -4131,8 +3782,135 @@ CheckDateTokenTables(void)
|
||||
|
||||
ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
|
||||
ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
|
||||
ok &= CheckDateTokenTable("australian_datetktbl",
|
||||
australian_datetktbl,
|
||||
australian_szdatetktbl);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function gets called during timezone config file load or reload
|
||||
* to create the final array of timezone tokens. The argument array
|
||||
* is already sorted in name order. This data is in a temporary memory
|
||||
* context and must be copied to somewhere permanent.
|
||||
*/
|
||||
void
|
||||
InstallTimeZoneAbbrevs(tzEntry *abbrevs, int n)
|
||||
{
|
||||
datetkn *newtbl;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Copy the data into TopMemoryContext and convert to datetkn format.
|
||||
*/
|
||||
newtbl = (datetkn *) MemoryContextAlloc(TopMemoryContext,
|
||||
n * sizeof(datetkn));
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN);
|
||||
newtbl[i].type = abbrevs[i].is_dst ? DTZ : TZ;
|
||||
TOVAL(&newtbl[i], abbrevs[i].offset / 60);
|
||||
}
|
||||
|
||||
/* Check the ordering, if testing */
|
||||
Assert(CheckDateTokenTable("timezone offset", newtbl, n));
|
||||
|
||||
/* Now safe to replace existing table (if any) */
|
||||
if (timezonetktbl)
|
||||
pfree(timezonetktbl);
|
||||
timezonetktbl = newtbl;
|
||||
sztimezonetktbl = n;
|
||||
|
||||
/* clear date cache in case it contains any stale timezone names */
|
||||
for (i = 0; i < MAXDATEFIELDS; i++)
|
||||
datecache[i] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This set-returning function reads all the available time zone abbreviations
|
||||
* and returns a set of (name, utc_offset, is_dst).
|
||||
*/
|
||||
Datum
|
||||
pg_timezonenames(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext *funcctx;
|
||||
int *pindex;
|
||||
Datum result;
|
||||
Interval *resInterval;
|
||||
HeapTuple tuple;
|
||||
Datum values[3];
|
||||
bool nulls[3];
|
||||
char buffer[TOKMAXLEN + 1];
|
||||
unsigned char *p;
|
||||
struct pg_tm tm;
|
||||
|
||||
/* stuff done only on the first call of the function */
|
||||
if (SRF_IS_FIRSTCALL())
|
||||
{
|
||||
TupleDesc tupdesc;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* create a function context for cross-call persistence */
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
|
||||
/*
|
||||
* switch to memory context appropriate for multiple function
|
||||
* calls
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
|
||||
/* allocate memory for user context */
|
||||
pindex = (int *) palloc(sizeof(int));
|
||||
*pindex = 0;
|
||||
funcctx->user_fctx = (void *) pindex;
|
||||
|
||||
/*
|
||||
* build tupdesc for result tuples. This must match the
|
||||
* definition of the pg_timezonenames view in system_views.sql
|
||||
*/
|
||||
tupdesc = CreateTemplateTupleDesc(3, false);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
|
||||
TEXTOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset",
|
||||
INTERVALOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_dst",
|
||||
BOOLOID, -1, 0);
|
||||
|
||||
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
/* stuff done on every call of the function */
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
pindex = (int *) funcctx->user_fctx;
|
||||
|
||||
if (*pindex >= sztimezonetktbl)
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
|
||||
MemSet(nulls, 0, sizeof(nulls));
|
||||
|
||||
/*
|
||||
* Convert name to text, using upcasing conversion that is the inverse
|
||||
* of what ParseDateTime() uses.
|
||||
*/
|
||||
strncpy(buffer, timezonetktbl[*pindex].token, TOKMAXLEN);
|
||||
buffer[TOKMAXLEN] = '\0'; /* may not be null-terminated */
|
||||
for (p = (unsigned char *) buffer; *p; p++)
|
||||
*p = pg_toupper(*p);
|
||||
|
||||
values[0] = DirectFunctionCall1(textin, CStringGetDatum(buffer));
|
||||
|
||||
MemSet(&tm, 0, sizeof(struct pg_tm));
|
||||
tm.tm_min = (-1) * FROMVAL(&timezonetktbl[*pindex]);
|
||||
resInterval = (Interval *) palloc(sizeof(Interval));
|
||||
tm2interval(&tm, 0, resInterval);
|
||||
values[1] = IntervalPGetDatum(resInterval);
|
||||
|
||||
Assert(timezonetktbl[*pindex].type == DTZ ||
|
||||
timezonetktbl[*pindex].type == TZ);
|
||||
values[2] = BoolGetDatum(timezonetktbl[*pindex].type == DTZ);
|
||||
|
||||
(*pindex)++;
|
||||
|
||||
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
|
||||
result = HeapTupleGetDatum(tuple);
|
||||
|
||||
SRF_RETURN_NEXT(funcctx, result);
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
# Makefile for utils/misc
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $PostgreSQL: pgsql/src/backend/utils/misc/Makefile,v 1.25 2006/03/07 01:03:12 tgl Exp $
|
||||
# $PostgreSQL: pgsql/src/backend/utils/misc/Makefile,v 1.26 2006/07/25 03:51:21 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
|
||||
|
||||
override CPPFLAGS := -I$(srcdir) $(CPPFLAGS)
|
||||
|
||||
OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o
|
||||
OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o
|
||||
|
||||
# This location might depend on the installation directories. Therefore
|
||||
# we can't subsitute it into pg_config.h.
|
||||
|
@@ -10,7 +10,7 @@
|
||||
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.328 2006/07/14 14:52:25 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.329 2006/07/25 03:51:21 tgl Exp $
|
||||
*
|
||||
*--------------------------------------------------------------------
|
||||
*/
|
||||
@@ -26,6 +26,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
#include "access/gin.h"
|
||||
#include "access/twophase.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/namespace.h"
|
||||
@@ -57,7 +58,7 @@
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/pg_locale.h"
|
||||
#include "utils/ps_status.h"
|
||||
#include "access/gin.h"
|
||||
#include "utils/tzparser.h"
|
||||
|
||||
#ifndef PG_KRB_SRVTAB
|
||||
#define PG_KRB_SRVTAB ""
|
||||
@@ -131,6 +132,7 @@ static bool assign_log_stats(bool newval, bool doit, GucSource source);
|
||||
static bool assign_transaction_read_only(bool newval, bool doit, GucSource source);
|
||||
static const char *assign_canonical_path(const char *newval, bool doit, GucSource source);
|
||||
static const char *assign_backslash_quote(const char *newval, bool doit, GucSource source);
|
||||
static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source);
|
||||
|
||||
static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source);
|
||||
static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source);
|
||||
@@ -163,8 +165,6 @@ bool log_btree_build_stats = false;
|
||||
|
||||
bool SQL_inheritance = true;
|
||||
|
||||
bool Australian_timezones = false;
|
||||
|
||||
bool Password_encryption = true;
|
||||
|
||||
bool default_with_oids = false;
|
||||
@@ -216,6 +216,7 @@ static char *timezone_string;
|
||||
static char *XactIsoLevel_string;
|
||||
static char *data_directory;
|
||||
static char *custom_variable_classes;
|
||||
static char *timezone_abbreviations;
|
||||
static int max_function_args;
|
||||
static int max_index_keys;
|
||||
static int max_identifier_length;
|
||||
@@ -806,15 +807,6 @@ static struct config_bool ConfigureNamesBool[] =
|
||||
&SQL_inheritance,
|
||||
true, NULL, NULL
|
||||
},
|
||||
{
|
||||
{"australian_timezones", PGC_USERSET, CLIENT_CONN_LOCALE,
|
||||
gettext_noop("Interprets ACST, CST, EST, and SAT as Australian time zones."),
|
||||
gettext_noop("Otherwise they are interpreted as North/South American "
|
||||
"time zones and Saturday.")
|
||||
},
|
||||
&Australian_timezones,
|
||||
false, ClearDateCache, NULL
|
||||
},
|
||||
{
|
||||
{"password_encryption", PGC_USERSET, CONN_AUTH_SECURITY,
|
||||
gettext_noop("Encrypt passwords."),
|
||||
@@ -2077,6 +2069,14 @@ static struct config_string ConfigureNamesString[] =
|
||||
&timezone_string,
|
||||
"UNKNOWN", assign_timezone, show_timezone
|
||||
},
|
||||
{
|
||||
{"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
|
||||
gettext_noop("Selects a file of timezone abbreviations"),
|
||||
NULL,
|
||||
},
|
||||
&timezone_abbreviations,
|
||||
"Default", assign_timezone_abbreviations, NULL
|
||||
},
|
||||
|
||||
{
|
||||
{"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
|
||||
@@ -6101,6 +6101,30 @@ assign_backslash_quote(const char *newval, bool doit, GucSource source)
|
||||
return newval;
|
||||
}
|
||||
|
||||
static const char *
|
||||
assign_timezone_abbreviations(const char *newval, bool doit, GucSource source)
|
||||
{
|
||||
/* Loading abbrev file is expensive, so only do it when value changes */
|
||||
if (timezone_abbreviations == NULL ||
|
||||
strcmp(timezone_abbreviations, newval) != 0)
|
||||
{
|
||||
int elevel;
|
||||
|
||||
/*
|
||||
* If reading config file, only the postmaster should bleat loudly
|
||||
* about problems. Otherwise, it's just this one process doing it,
|
||||
* and we use WARNING message level.
|
||||
*/
|
||||
if (source == PGC_S_FILE)
|
||||
elevel = IsUnderPostmaster ? DEBUG2 : LOG;
|
||||
else
|
||||
elevel = WARNING;
|
||||
if (!load_tzoffsets(newval, doit, elevel))
|
||||
return NULL;
|
||||
}
|
||||
return newval;
|
||||
}
|
||||
|
||||
static bool
|
||||
assign_tcp_keepalives_idle(int newval, bool doit, GucSource source)
|
||||
{
|
||||
|
@@ -397,7 +397,13 @@
|
||||
#datestyle = 'iso, mdy'
|
||||
#timezone = unknown # actually, defaults to TZ
|
||||
# environment setting
|
||||
#australian_timezones = off
|
||||
#timezone_abbreviations = 'Default' # select the set of available timezone
|
||||
# abbreviations. Currently, there are
|
||||
# Default
|
||||
# Australia
|
||||
# India
|
||||
# However you can also create your own
|
||||
# file in share/timezonesets/.
|
||||
#extra_float_digits = 0 # min -15, max 2
|
||||
#client_encoding = sql_ascii # actually, defaults to database
|
||||
# encoding
|
||||
|
461
src/backend/utils/misc/tzparser.c
Normal file
461
src/backend/utils/misc/tzparser.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tzparser.c
|
||||
* Functions for parsing timezone offset files
|
||||
*
|
||||
* Note: we generally should not throw any errors in this file, but instead
|
||||
* try to return an error code. This is not completely bulletproof at
|
||||
* present --- in particular out-of-memory will throw an error. Could
|
||||
* probably fix with PG_TRY if necessary.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/tzparser.c,v 1.1 2006/07/25 03:51:21 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "storage/fd.h"
|
||||
#include "utils/datetime.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/tzparser.h"
|
||||
|
||||
|
||||
#define WHITESPACE " \t\n\r"
|
||||
|
||||
static int tz_elevel; /* to avoid passing this around a lot */
|
||||
|
||||
static bool validateTzEntry(tzEntry *tzentry);
|
||||
static bool splitTzLine(const char *filename, int lineno,
|
||||
char *line, tzEntry *tzentry);
|
||||
static int addToArray(tzEntry **base, int *arraysize, int n,
|
||||
tzEntry *entry, bool override);
|
||||
static int ParseTzFile(const char *filename, int depth,
|
||||
tzEntry **base, int *arraysize, int n);
|
||||
|
||||
|
||||
/*
|
||||
* Apply additional validation checks to a tzEntry
|
||||
*
|
||||
* Returns TRUE if OK, else false
|
||||
*/
|
||||
static bool
|
||||
validateTzEntry(tzEntry *tzentry)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
/*
|
||||
* Check restrictions imposed by datetkntbl storage format (see datetime.c)
|
||||
*/
|
||||
if (strlen(tzentry->abbrev) > TOKMAXLEN)
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("time zone abbreviation \"%s\" is too long (maximum %d characters) in time zone file \"%s\", line %d",
|
||||
tzentry->abbrev, TOKMAXLEN,
|
||||
tzentry->filename, tzentry->lineno)));
|
||||
return false;
|
||||
}
|
||||
if (tzentry->offset % 900 != 0)
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("time zone offset %d is not a multiple of 900 sec (15 min) in time zone file \"%s\", line %d",
|
||||
tzentry->offset,
|
||||
tzentry->filename, tzentry->lineno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity-check the offset: shouldn't exceed 14 hours
|
||||
*/
|
||||
if (tzentry->offset > 14*60*60 ||
|
||||
tzentry->offset < -14*60*60)
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("time zone offset %d is out of range in time zone file \"%s\", line %d",
|
||||
tzentry->offset,
|
||||
tzentry->filename, tzentry->lineno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert abbrev to lowercase (must match datetime.c's conversion)
|
||||
*/
|
||||
for (p = (unsigned char *) tzentry->abbrev; *p; p++)
|
||||
*p = pg_tolower(*p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to parse the line as a timezone abbrev spec (name, offset, dst)
|
||||
*
|
||||
* Returns TRUE if OK, else false; data is stored in *tzentry
|
||||
*/
|
||||
static bool
|
||||
splitTzLine(const char *filename, int lineno, char *line, tzEntry *tzentry)
|
||||
{
|
||||
char *abbrev;
|
||||
char *offset;
|
||||
char *offset_endptr;
|
||||
char *remain;
|
||||
char *is_dst;
|
||||
|
||||
tzentry->lineno = lineno;
|
||||
tzentry->filename = filename;
|
||||
|
||||
abbrev = strtok(line, WHITESPACE);
|
||||
if (!abbrev)
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("missing time zone abbreviation in time zone file \"%s\", line %d",
|
||||
filename, lineno)));
|
||||
return false;
|
||||
}
|
||||
tzentry->abbrev = abbrev;
|
||||
|
||||
offset = strtok(NULL, WHITESPACE);
|
||||
if (!offset)
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("missing time zone offset in time zone file \"%s\", line %d",
|
||||
filename, lineno)));
|
||||
return false;
|
||||
}
|
||||
tzentry->offset = strtol(offset, &offset_endptr, 10);
|
||||
if (offset_endptr == offset || *offset_endptr != '\0')
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid number for time zone offset in time zone file \"%s\", line %d",
|
||||
filename, lineno)));
|
||||
return false;
|
||||
}
|
||||
|
||||
is_dst = strtok(NULL, WHITESPACE);
|
||||
if (is_dst && pg_strcasecmp(is_dst, "D") == 0)
|
||||
{
|
||||
tzentry->is_dst = true;
|
||||
remain = strtok(NULL, WHITESPACE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* there was no 'D' dst specifier */
|
||||
tzentry->is_dst = false;
|
||||
remain = is_dst;
|
||||
}
|
||||
|
||||
if (!remain) /* no more non-whitespace chars */
|
||||
return true;
|
||||
|
||||
if (remain[0] != '#') /* must be a comment */
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid syntax in time zone file \"%s\", line %d",
|
||||
filename, lineno)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert entry into sorted array
|
||||
*
|
||||
* *base: base address of array (changeable if must enlarge array)
|
||||
* *arraysize: allocated length of array (changeable if must enlarge array)
|
||||
* n: current number of valid elements in array
|
||||
* entry: new data to insert
|
||||
* override: TRUE if OK to override
|
||||
*
|
||||
* Returns the new array length (new value for n), or -1 if error
|
||||
*/
|
||||
static int
|
||||
addToArray(tzEntry **base, int *arraysize, int n,
|
||||
tzEntry *entry, bool override)
|
||||
{
|
||||
tzEntry* arrayptr;
|
||||
int low;
|
||||
int high;
|
||||
|
||||
/*
|
||||
* Search the array for a duplicate; as a useful side effect, the array
|
||||
* is maintained in sorted order. We use strcmp() to ensure we match
|
||||
* the sort order datetime.c expects.
|
||||
*/
|
||||
arrayptr = *base;
|
||||
low = 0;
|
||||
high = n-1;
|
||||
while (low <= high)
|
||||
{
|
||||
int mid = (low + high) >> 1;
|
||||
tzEntry *midptr = arrayptr + mid;
|
||||
int cmp;
|
||||
|
||||
cmp = strcmp(entry->abbrev, midptr->abbrev);
|
||||
if (cmp < 0)
|
||||
high = mid - 1;
|
||||
else if (cmp > 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Found a duplicate entry; complain unless it's the same.
|
||||
*/
|
||||
if (midptr->offset == entry->offset &&
|
||||
midptr->is_dst == entry->is_dst)
|
||||
{
|
||||
/* return unchanged array */
|
||||
return n;
|
||||
}
|
||||
if (override)
|
||||
{
|
||||
/* same abbrev but something is different, override */
|
||||
midptr->offset = entry->offset;
|
||||
midptr->is_dst = entry->is_dst;
|
||||
return n;
|
||||
}
|
||||
/* same abbrev but something is different, complain */
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("time zone abbreviation \"%s\" is multiply defined",
|
||||
entry->abbrev),
|
||||
errdetail("Time zone file \"%s\", line %d conflicts with file \"%s\", line %d.",
|
||||
midptr->filename, midptr->lineno,
|
||||
entry->filename, entry->lineno)));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No match, insert at position "low".
|
||||
*/
|
||||
if (n >= *arraysize)
|
||||
{
|
||||
*arraysize *= 2;
|
||||
*base = (tzEntry *) repalloc(*base, *arraysize * sizeof(tzEntry));
|
||||
}
|
||||
|
||||
arrayptr = *base + low;
|
||||
|
||||
memmove(arrayptr + 1, arrayptr, (n - low) * sizeof(tzEntry));
|
||||
|
||||
memcpy(arrayptr, entry, sizeof(tzEntry));
|
||||
|
||||
/* Must dup the abbrev to ensure it survives */
|
||||
arrayptr->abbrev = pstrdup(entry->abbrev);
|
||||
|
||||
return n+1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a single timezone abbrev file --- can recurse to handle @INCLUDE
|
||||
*
|
||||
* filename: user-specified file name (does not include path)
|
||||
* depth: current recursion depth
|
||||
* *base: array for results (changeable if must enlarge array)
|
||||
* *arraysize: allocated length of array (changeable if must enlarge array)
|
||||
* n: current number of valid elements in array
|
||||
*
|
||||
* Returns the new array length (new value for n), or -1 if error
|
||||
*/
|
||||
static int
|
||||
ParseTzFile(const char *filename, int depth,
|
||||
tzEntry **base, int *arraysize, int n)
|
||||
{
|
||||
char share_path[MAXPGPATH];
|
||||
char file_path[MAXPGPATH];
|
||||
FILE *tzFile;
|
||||
char tzbuf[1024];
|
||||
char *line;
|
||||
tzEntry tzentry;
|
||||
int lineno = 0;
|
||||
bool override = false;
|
||||
const char *p;
|
||||
|
||||
/*
|
||||
* We enforce that the filename is all alpha characters. This may be
|
||||
* overly restrictive, but we don't want to allow access to anything
|
||||
* outside the timezonesets directory, so for instance '/' *must* be
|
||||
* rejected.
|
||||
*/
|
||||
for (p = filename; *p; p++)
|
||||
{
|
||||
if (!isalpha((unsigned char) *p))
|
||||
{
|
||||
/* at level 0, we need no ereport since guc.c will say enough */
|
||||
if (depth > 0)
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid time zone file name \"%s\"",
|
||||
filename)));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The maximal recursion depth is a pretty arbitrary setting.
|
||||
* It is hard to imagine that someone needs more than 3 levels so stick
|
||||
* with this conservative setting until someone complains.
|
||||
*/
|
||||
if (depth > 3)
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("time zone file recursion limit exceeded in file \"%s\"",
|
||||
filename)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_share_path(my_exec_path, share_path);
|
||||
snprintf(file_path, sizeof(file_path), "%s/timezonesets/%s",
|
||||
share_path, filename);
|
||||
tzFile = AllocateFile(file_path, "r");
|
||||
if (!tzFile)
|
||||
{
|
||||
/* at level 0, if file doesn't exist, guc.c's complaint is enough */
|
||||
if (errno != ENOENT || depth > 0)
|
||||
ereport(tz_elevel,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read time zone file \"%s\": %m",
|
||||
filename)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!feof(tzFile))
|
||||
{
|
||||
lineno++;
|
||||
if (fgets(tzbuf, sizeof(tzbuf), tzFile) == NULL)
|
||||
{
|
||||
if (ferror(tzFile))
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read time zone file \"%s\": %m",
|
||||
filename)));
|
||||
return -1;
|
||||
}
|
||||
/* else we're at EOF after all */
|
||||
break;
|
||||
}
|
||||
if (strlen(tzbuf) == sizeof(tzbuf)-1)
|
||||
{
|
||||
/* the line is too long for tzbuf */
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||
errmsg("line is too long in time zone file \"%s\", line %d",
|
||||
filename, lineno)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* skip over whitespace */
|
||||
line = tzbuf;
|
||||
while (*line && isspace((unsigned char) *line))
|
||||
line++;
|
||||
|
||||
if (*line == '\0') /* empty line */
|
||||
continue;
|
||||
if (*line == '#') /* comment line */
|
||||
continue;
|
||||
|
||||
if (pg_strncasecmp(line, "@INCLUDE", strlen("@INCLUDE")) == 0)
|
||||
{
|
||||
/* pstrdup so we can use filename in result data structure */
|
||||
char* includeFile = pstrdup(line + strlen("@INCLUDE"));
|
||||
|
||||
includeFile = strtok(includeFile, WHITESPACE);
|
||||
if (!includeFile || !*includeFile)
|
||||
{
|
||||
ereport(tz_elevel,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("@INCLUDE without filename in time zone file \"%s\", line %d",
|
||||
filename, lineno)));
|
||||
return -1;
|
||||
}
|
||||
n = ParseTzFile(includeFile, depth + 1,
|
||||
base, arraysize, n);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pg_strncasecmp(line, "@OVERRIDE", strlen("@OVERRIDE")) == 0)
|
||||
{
|
||||
override = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!splitTzLine(filename, lineno, line, &tzentry))
|
||||
return -1;
|
||||
if (!validateTzEntry(&tzentry))
|
||||
return -1;
|
||||
n = addToArray(base, arraysize, n, &tzentry, override);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
FreeFile(tzFile);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* load_tzoffsets --- read and parse the specified timezone offset file
|
||||
*
|
||||
* filename: name specified by user
|
||||
* doit: whether to actually apply the new values, or just check
|
||||
* elevel: elog reporting level (will be less than ERROR)
|
||||
*
|
||||
* Returns TRUE if OK, FALSE if not; should avoid erroring out
|
||||
*/
|
||||
bool
|
||||
load_tzoffsets(const char *filename, bool doit, int elevel)
|
||||
{
|
||||
MemoryContext tmpContext;
|
||||
MemoryContext oldContext;
|
||||
tzEntry *array;
|
||||
int arraysize;
|
||||
int n;
|
||||
|
||||
tz_elevel = elevel;
|
||||
|
||||
/*
|
||||
* Create a temp memory context to work in. This makes it easy to
|
||||
* clean up afterwards.
|
||||
*/
|
||||
tmpContext = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"TZParserMemory",
|
||||
ALLOCSET_SMALL_MINSIZE,
|
||||
ALLOCSET_SMALL_INITSIZE,
|
||||
ALLOCSET_SMALL_MAXSIZE);
|
||||
oldContext = MemoryContextSwitchTo(tmpContext);
|
||||
|
||||
/* Initialize array at a reasonable size */
|
||||
arraysize = 128;
|
||||
array = (tzEntry *) palloc(arraysize * sizeof(tzEntry));
|
||||
|
||||
/* Parse the file(s) */
|
||||
n = ParseTzFile(filename, 0, &array, &arraysize, 0);
|
||||
|
||||
/* If no errors and we should apply the result, pass it to datetime.c */
|
||||
if (n >= 0 && doit)
|
||||
InstallTimeZoneAbbrevs(array, n);
|
||||
|
||||
/* Clean up */
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
MemoryContextDelete(tmpContext);
|
||||
|
||||
return (n >= 0);
|
||||
}
|
Reference in New Issue
Block a user