diff options
Diffstat (limited to 'src/backend/utils/adt/datetime.c')
-rw-r--r-- | src/backend/utils/adt/datetime.c | 3587 |
1 files changed, 0 insertions, 3587 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c deleted file mode 100644 index 184ad15ff38..00000000000 --- a/src/backend/utils/adt/datetime.c +++ /dev/null @@ -1,3587 +0,0 @@ -/*------------------------------------------------------------------------- - * - * datetime.c - * Support functions for date/time types. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.92 2002/06/20 20:29:37 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <ctype.h> -#include <math.h> -#include <sys/types.h> -#include <errno.h> -#include <float.h> -#include <limits.h> - -#include "miscadmin.h" -#include "utils/guc.h" -#include "utils/datetime.h" - - -static int DecodeNumber(int flen, char *field, - int fmask, int *tmask, - struct tm * tm, fsec_t *fsec, int *is2digits); -static int DecodeNumberField(int len, char *str, - int fmask, int *tmask, - struct tm * tm, fsec_t *fsec, int *is2digits); -static int DecodeTime(char *str, int fmask, int *tmask, - struct tm * tm, fsec_t *fsec); -static int DecodeTimezone(char *str, int *tzp); -static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel); -static int DecodeDate(char *str, int fmask, int *tmask, struct tm * tm); -static int DecodePosixTimezone(char *str, int *val); -void TrimTrailingZeros(char *str); - - -int day_tab[2][13] = { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}}; - -char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; - -char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday", NULL}; - - -/***************************************************************************** - * PRIVATE ROUTINES * - *****************************************************************************/ - -/* definitions for squeezing values into "value" */ -#define ABS_SIGNBIT ((char) 0200) -#define VALMASK ((char) 0177) -#define NEG(n) ((n)|ABS_SIGNBIT) -#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c)) -#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */ -#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10)) - -/* - * datetktbl holds date/time keywords. Note that this table must be strictly - * ordered to allow an O(ln(N)) search algorithm. - * - * To keep this table reasonably small, we divide the lexval for TZ and DTZ - * entries by 10 and truncate the text field at MAXTOKLEN characters. - * the text field is not guaranteed to be NULL-terminated. - * - * Let's include all strings from my current zinc time zone database. - * Not all of them are unique, or even very understandable, so we will - * leave some commented out for now. - */ -static datetkn datetktbl[] = { -/* text, token, lexval */ - {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */ - {"acsst", DTZ, 63}, /* Cent. Australia */ - {"acst", DTZ, NEG(24)}, /* Atlantic/Porto Acre */ - {"act", TZ, NEG(30)}, /* Atlantic/Porto Acre */ - {DA_D, ADBC, AD}, /* "ad" for years >= 0 */ - {"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */ - {"adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */ - {"aesst", DTZ, 66}, /* E. Australia */ - {"aest", TZ, 60}, /* Australia Eastern Std Time */ - {"aft", TZ, 27}, /* Kabul */ - {"ahst", TZ, NEG(60)}, /* Alaska-Hawaii Std Time */ - {"akdt", DTZ, NEG(48)}, /* Alaska Daylight Time */ - {"akst", DTZ, NEG(54)}, /* Alaska Standard Time */ - {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */ - {"almt", TZ, 36}, /* Almaty Time */ - {"almst", TZ, 42}, /* Almaty Savings Time */ - {"am", AMPM, AM}, - {"amst", DTZ, 30}, /* Armenia Summer Time (Yerevan) */ - {"amt", TZ, 24}, /* Armenia Time (Yerevan) */ -#if 0 - {"amst", DTZ, NEG(18)}, /* Porto Velho */ -#endif - {"anast", DTZ, 78}, /* Anadyr Summer Time (Russia) */ - {"anat", TZ, 72}, /* Anadyr Time (Russia) */ -#if 0 -aqtst -aqtt -arst -#endif - {"art", TZ, NEG(18)}, /* Argentina Time */ -#if 0 -ashst -ast /* Atlantic Standard Time, Arabia Standard Time, Acre Standard Time */ -#endif - {"apr", MONTH, 4}, - {"april", MONTH, 4}, - {"ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */ - {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */ - {"aug", MONTH, 8}, - {"august", MONTH, 8}, - {"awsst", DTZ, 54}, /* W. Australia */ - {"awst", TZ, 48}, /* W. Australia */ - {"awt", DTZ, NEG(18)}, - {"azost", DTZ, 0}, /* Azores Summer Time */ - {"azot", TZ, NEG(6)}, /* Azores Time */ - {"azst", DTZ, 30}, /* Azerbaijan Summer Time */ - {"azt", TZ, 24}, /* Azerbaijan Time */ - {DB_C, ADBC, BC}, /* "bc" for years < 0 */ - {"bdst", TZ, 12}, /* British Double Summer Time */ - {"bdt", TZ, 36}, /* Dacca */ - {"bnt", TZ, 48}, /* Brunei Darussalam Time */ - {"bort", TZ, 48}, /* Borneo Time (Indonesia) */ -#if 0 -bortst -bost -#endif - {"bot", TZ, NEG(24)}, /* Bolivia Time */ - {"bra", TZ, NEG(18)}, /* Brazil Time */ -#if 0 -brst -brt -#endif - {"bst", DTZ, 6}, /* British Summer Time */ -#if 0 - {"bst", TZ, NEG(18)}, /* Brazil Standard Time */ - {"bst", DTZ, NEG(66)}, /* Bering Summer Time */ -#endif - {"bt", TZ, 18}, /* Baghdad Time */ - {"btt", TZ, 36}, /* Bhutan Time */ - {"cadt", DTZ, 63}, /* Central Australian DST */ - {"cast", TZ, 57}, /* Central Australian ST */ - {"cat", TZ, NEG(60)}, /* Central Alaska Time */ - {"cct", TZ, 48}, /* China Coast Time */ -#if 0 - {"cct", TZ, 39}, /* Indian Cocos (Island) Time */ -#endif - {"cdt", DTZ, NEG(30)}, /* Central Daylight Time */ - {"cest", DTZ, 12}, /* Central European Dayl.Time */ - {"cet", TZ, 6}, /* Central European Time */ - {"cetdst", DTZ, 12}, /* Central European Dayl.Time */ -#if 0 - {"chadt", DTZ, 82}, /* Chatham Island Daylight Time (13:45? set to 13:40) */ - {"chast", TZ, 76}, /* Chatham Island Time (12:45? set to 12:40) */ -ckhst -#endif - {"ckt", TZ, 72}, /* Cook Islands Time */ - {"clst", DTZ, NEG(18)}, /* Chile Summer Time */ - {"clt", TZ, NEG(24)}, /* Chile Time */ -#if 0 -cost -#endif - {"cot", TZ, NEG(30)}, /* Columbia Time */ - {"cst", TZ, NEG(36)}, /* Central Standard Time */ -#if 0 -cvst -#endif - {"cvt", TZ, 42}, /* Christmas Island Time (Indian Ocean) */ - {"cxt", TZ, 42}, /* Christmas Island Time (Indian Ocean) */ - {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */ - {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */ - {"davt", TZ, 42}, /* Davis Time (Antarctica) */ - {"ddut", TZ, 60}, /* Dumont-d'Urville Time (Antarctica) */ - {"dec", MONTH, 12}, - {"december", MONTH, 12}, - {"dnt", TZ, 6}, /* Dansk Normal Tid */ - {"dow", RESERV, DTK_DOW}, /* day of week */ - {"doy", RESERV, DTK_DOY}, /* day of year */ - {"dst", DTZMOD, 6}, -#if 0 - {"dusst", DTZ, 36}, /* Dushanbe Summer Time */ -#endif - {"easst", DTZ, NEG(30)}, /* Easter Island Summer Time */ - {"east", TZ, NEG(36)}, /* Easter Island Time */ - {"eat", TZ, 18}, /* East Africa Time */ -#if 0 - {"east", DTZ, 24}, /* Indian Antananarivo Savings Time */ - {"eat", TZ, 18}, /* Indian Antananarivo Time */ - {"ect", TZ, NEG(24)}, /* Eastern Caribbean Time */ - {"ect", TZ, NEG(30)}, /* Ecuador Time */ -#endif - {"edt", DTZ, NEG(24)}, /* Eastern Daylight Time */ - {"eest", DTZ, 18}, /* Eastern Europe Summer Time */ - {"eet", TZ, 12}, /* East. Europe, USSR Zone 1 */ - {"eetdst", DTZ, 18}, /* Eastern Europe Daylight Time */ - {"egst", DTZ, 0}, /* East Greenland Summer Time */ - {"egt", TZ, NEG(6)}, /* East Greenland Time */ -#if 0 -ehdt -#endif - {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */ - {"est", TZ, NEG(30)}, /* Eastern Standard Time */ - {"feb", MONTH, 2}, - {"february", MONTH, 2}, - {"fjst", DTZ, NEG(78)}, /* Fiji Summer Time (13 hour offset!) */ - {"fjt", TZ, NEG(72)}, /* Fiji Time */ - {"fkst", DTZ, NEG(18)}, /* Falkland Islands Summer Time */ - {"fkt", TZ, NEG(12)}, /* Falkland Islands Time */ -#if 0 -fnst -fnt -#endif - {"fri", DOW, 5}, - {"friday", DOW, 5}, - {"fst", TZ, 6}, /* French Summer Time */ - {"fwt", DTZ, 12}, /* French Winter Time */ - {"galt", TZ, NEG(36)}, /* Galapagos Time */ - {"gamt", TZ, NEG(54)}, /* Gambier Time */ - {"gest", DTZ, 30}, /* Georgia Summer Time */ - {"get", TZ, 24}, /* Georgia Time */ - {"gft", TZ, NEG(18)}, /* French Guiana Time */ -#if 0 -ghst -#endif - {"gilt", TZ, 72}, /* Gilbert Islands Time */ - {"gmt", TZ, 0}, /* Greenwish Mean Time */ - {"gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */ - {"gyt", TZ, NEG(24)}, /* Guyana Time */ - {"h", UNITS, DTK_HOUR}, /* "hour" */ -#if 0 -hadt -hast -#endif - {"hdt", DTZ, NEG(54)}, /* Hawaii/Alaska Daylight Time */ -#if 0 -hkst -#endif - {"hkt", TZ, 48}, /* Hong Kong Time */ -#if 0 - {"hmt", TZ, 18}, /* Hellas ? ? */ -hovst -hovt -#endif - {"hst", TZ, NEG(60)}, /* Hawaii Std Time */ -#if 0 -hwt -#endif - {"ict", TZ, 42}, /* Indochina Time */ - {"idle", TZ, 72}, /* Intl. Date Line, East */ - {"idlw", TZ, NEG(72)}, /* 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, 30}, /* Indian Chagos Time */ - {"irkst", DTZ, 54}, /* Irkutsk Summer Time */ - {"irkt", TZ, 48}, /* Irkutsk Time */ - {"irt", TZ, 21}, /* Iran Time */ -#if 0 -isst -#endif - {"ist", TZ, 12}, /* Israel */ - {"it", TZ, 21}, /* Iran Time */ - {"j", UNITS, DTK_JULIAN}, - {"jan", MONTH, 1}, - {"january", MONTH, 1}, - {"javt", TZ, 42}, /* Java Time (07:00? see JT) */ - {"jayt", TZ, 54}, /* Jayapura Time (Indonesia) */ - {"jd", UNITS, DTK_JULIAN}, - {"jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */ - {"jt", TZ, 45}, /* Java Time (07:30? see JAVT) */ - {"jul", MONTH, 7}, - {"julian", UNITS, DTK_JULIAN}, - {"july", MONTH, 7}, - {"jun", MONTH, 6}, - {"june", MONTH, 6}, - {"kdt", DTZ, 60}, /* Korea Daylight Time */ - {"kgst", DTZ, 36}, /* Kyrgyzstan Summer Time */ - {"kgt", TZ, 30}, /* Kyrgyzstan Time */ - {"kost", TZ, 72}, /* Kosrae Time */ - {"krast", DTZ, 42}, /* Krasnoyarsk Summer Time */ - {"krat", TZ, 48}, /* Krasnoyarsk Standard Time */ - {"kst", TZ, 54}, /* Korea Standard Time */ - {"lhdt", DTZ, 66}, /* Lord Howe Daylight Time, Australia */ - {"lhst", TZ, 63}, /* Lord Howe Standard Time, Australia */ - {"ligt", TZ, 60}, /* From Melbourne, Australia */ - {"lint", TZ, 84}, /* Line Islands Time (Kiribati; +14 hours!) */ - {"lkt", TZ, 36}, /* Lanka Time */ - {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */ - {"magst", DTZ, 72}, /* Magadan Summer Time */ - {"magt", TZ, 66}, /* Magadan Time */ - {"mar", MONTH, 3}, - {"march", MONTH, 3}, - {"mart", TZ, NEG(57)}, /* Marquesas Time */ - {"mawt", TZ, 36}, /* Mawson, Antarctica */ - {"may", MONTH, 5}, - {"mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */ - {"mest", DTZ, 12}, /* Middle Europe Summer Time */ - {"met", TZ, 6}, /* Middle Europe Time */ - {"metdst", DTZ, 12}, /* Middle Europe Daylight Time */ - {"mewt", TZ, 6}, /* Middle Europe Winter Time */ - {"mez", TZ, 6}, /* Middle Europe Zone */ - {"mht", TZ, 72}, /* Kwajalein */ - {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */ - {"mmt", TZ, 39}, /* Myannar Time */ - {"mon", DOW, 1}, - {"monday", DOW, 1}, -#if 0 -most -#endif - {"mpt", TZ, 60}, /* North Mariana Islands Time */ - {"msd", DTZ, 24}, /* Moscow Summer Time */ - {"msk", TZ, 18}, /* Moscow Time */ - {"mst", TZ, NEG(42)}, /* Mountain Standard Time */ - {"mt", TZ, 51}, /* Moluccas Time */ - {"mut", TZ, 24}, /* Mauritius Island Time */ - {"mvt", TZ, 30}, /* Maldives Island Time */ - {"myt", TZ, 48}, /* Malaysia Time */ -#if 0 -ncst -#endif - {"nct", TZ, 66}, /* New Caledonia Time */ - {"ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */ - {"nft", TZ, NEG(21)}, /* Newfoundland Standard Time */ - {"nor", TZ, 6}, /* Norway Standard Time */ - {"nov", MONTH, 11}, - {"november", MONTH, 11}, - {"novst", DTZ, 42}, /* Novosibirsk Summer Time */ - {"novt", TZ, 36}, /* Novosibirsk Standard Time */ - {NOW, RESERV, DTK_NOW}, /* current transaction time */ - {"nst", TZ, NEG(21)}, /* Nfld. Standard Time */ - {"nt", TZ, NEG(66)}, /* Nome Time */ - {"nut", TZ, NEG(66)}, /* Niue Time */ - {"nzdt", DTZ, 78}, /* New Zealand Daylight Time */ - {"nzst", TZ, 72}, /* New Zealand Standard Time */ - {"nzt", TZ, 72}, /* New Zealand Time */ - {"oct", MONTH, 10}, - {"october", MONTH, 10}, - {"omsst", DTZ, 42}, /* Omsk Summer Time */ - {"omst", TZ, 36}, /* Omsk Time */ - {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */ - {"pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */ -#if 0 -pest -#endif - {"pet", TZ, NEG(30)}, /* Peru Time */ - {"petst", DTZ, 78}, /* Petropavlovsk-Kamchatski Summer Time */ - {"pett", TZ, 72}, /* Petropavlovsk-Kamchatski Time */ - {"pgt", TZ, 60}, /* Papua New Guinea Time */ - {"phot", TZ, 78}, /* Phoenix Islands (Kiribati) Time */ -#if 0 -phst -#endif - {"pht", TZ, 48}, /* Phillipine Time */ - {"pkt", TZ, 30}, /* Pakistan Time */ - {"pm", AMPM, PM}, - {"pmdt", DTZ, NEG(12)}, /* Pierre & Miquelon Daylight Time */ -#if 0 -pmst -#endif - {"pont", TZ, 66}, /* Ponape Time (Micronesia) */ - {"pst", TZ, NEG(48)}, /* Pacific Standard Time */ - {"pwt", TZ, 54}, /* Palau Time */ - {"pyst", DTZ, NEG(18)}, /* Paraguay Summer Time */ - {"pyt", TZ, NEG(24)}, /* Paraguay Time */ - {"ret", DTZ, 24}, /* Reunion Island Time */ - {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */ - {"sadt", DTZ, 63}, /* S. Australian Dayl. Time */ -#if 0 -samst -samt -#endif - {"sast", TZ, 57}, /* South Australian Std Time */ - {"sat", DOW, 6}, - {"saturday", DOW, 6}, -#if 0 -sbt -#endif - {"sct", DTZ, 24}, /* Mahe Island Time */ - {"sep", MONTH, 9}, - {"sept", MONTH, 9}, - {"september", MONTH, 9}, - {"set", TZ, NEG(6)}, /* Seychelles Time ?? */ -#if 0 -sgt -#endif - {"sst", DTZ, 12}, /* Swedish Summer Time */ - {"sun", DOW, 0}, - {"sunday", DOW, 0}, - {"swt", TZ, 6}, /* Swedish Winter Time */ -#if 0 -syot -#endif - {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */ - {"that", TZ, NEG(60)}, /* Tahiti Time */ - {"tft", TZ, 30}, /* Kerguelen Time */ - {"thu", DOW, 4}, - {"thur", DOW, 4}, - {"thurs", DOW, 4}, - {"thursday", DOW, 4}, - {"tjt", TZ, 30}, /* Tajikistan Time */ - {"tkt", TZ, NEG(60)}, /* Tokelau Time */ - {"tmt", TZ, 30}, /* Turkmenistan Time */ - {TODAY, RESERV, DTK_TODAY}, /* midnight */ - {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */ -#if 0 -tost -#endif - {"tot", TZ, 78}, /* Tonga Time */ -#if 0 -tpt -#endif - {"truk", TZ, 60}, /* Truk Time */ - {"tue", DOW, 2}, - {"tues", DOW, 2}, - {"tuesday", DOW, 2}, - {"tvt", TZ, 72}, /* Tuvalu Time */ -#if 0 -uct -#endif - {"ulast", DTZ, 54}, /* Ulan Bator Summer Time */ - {"ulat", TZ, 48}, /* Ulan Bator Time */ - {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */ - {"ut", TZ, 0}, - {"utc", TZ, 0}, - {"uyst", DTZ, NEG(12)}, /* Uruguay Summer Time */ - {"uyt", TZ, NEG(18)}, /* Uruguay Time */ - {"uzst", DTZ, 36}, /* Uzbekistan Summer Time */ - {"uzt", TZ, 30}, /* Uzbekistan Time */ - {"vet", TZ, NEG(24)}, /* Venezuela Time */ - {"vlast", DTZ, 66}, /* Vladivostok Summer Time */ - {"vlat", TZ, 60}, /* Vladivostok Time */ -#if 0 -vust -#endif - {"vut", TZ, 66}, /* Vanuata Time */ - {"wadt", DTZ, 48}, /* West Australian DST */ - {"wakt", TZ, 72}, /* Wake Time */ -#if 0 -warst -#endif - {"wast", TZ, 42}, /* West Australian Std Time */ - {"wat", TZ, NEG(6)}, /* West Africa Time */ - {"wdt", DTZ, 54}, /* West Australian DST */ - {"wed", DOW, 3}, - {"wednesday", DOW, 3}, - {"weds", DOW, 3}, - {"west", DTZ, 6}, /* Western Europe Summer Time */ - {"wet", TZ, 0}, /* Western Europe */ - {"wetdst", DTZ, 6}, /* Western Europe Daylight Savings Time */ - {"wft", TZ, 72}, /* Wallis and Futuna Time */ - {"wgst", DTZ, NEG(12)}, /* West Greenland Summer Time */ - {"wgt", TZ, NEG(18)}, /* West Greenland Time */ - {"wst", TZ, 48}, /* West Australian Standard Time */ - {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */ - {"yakst", DTZ, 60}, /* Yakutsk Summer Time */ - {"yakt", TZ, 54}, /* Yakutsk Time */ - {"yapt", TZ, 60}, /* Yap Time (Micronesia) */ - {"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */ - {"yekst", DTZ, 36}, /* Yekaterinburg Summer Time */ - {"yekt", TZ, 30}, /* Yekaterinburg Time */ - {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */ - {"yst", TZ, NEG(54)}, /* Yukon Standard Time */ - {"z", TZ, 0}, /* time zone tag per ISO-8601 */ - {"zp4", TZ, NEG(24)}, /* UTC +4 hours. */ - {"zp5", TZ, NEG(30)}, /* UTC +5 hours. */ - {"zp6", TZ, NEG(36)}, /* UTC +6 hours. */ - {ZULU, TZ, 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, 57}, /* Cent. Australia */ - {"cst", TZ, 63}, /* Australia Central Std Time */ - {"east", TZ, 60}, /* East Australian Std Time */ - {"est", TZ, 60}, /* Australia Eastern Std Time */ - {"sat", TZ, 57}, -}; - -static unsigned int australian_szdatetktbl = sizeof australian_datetktbl / -sizeof australian_datetktbl[0]; - -static datetkn deltatktbl[] = { - /* text, token, lexval */ - {"@", IGNORE_DTF, 0}, /* postgres relative prefix */ - {DAGO, AGO, 0}, /* "ago" indicates negative time offset */ - {"c", UNITS, DTK_CENTURY}, /* "century" relative */ - {"cent", UNITS, DTK_CENTURY}, /* "century" relative */ - {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */ - {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */ - {"d", UNITS, DTK_DAY}, /* "day" relative */ - {DDAY, UNITS, DTK_DAY}, /* "day" relative */ - {"days", UNITS, DTK_DAY}, /* "days" relative */ - {"dec", UNITS, DTK_DECADE}, /* "decade" relative */ - {"decs", UNITS, DTK_DECADE}, /* "decades" relative */ - {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */ - {"decades", UNITS, DTK_DECADE}, /* "decades" relative */ - {"h", UNITS, DTK_HOUR}, /* "hour" relative */ - {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */ - {"hours", UNITS, DTK_HOUR}, /* "hours" relative */ - {"hr", UNITS, DTK_HOUR}, /* "hour" relative */ - {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */ - {INVALID, RESERV, DTK_INVALID}, /* reserved for invalid time */ - {"m", UNITS, DTK_MINUTE}, /* "minute" relative */ - {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */ - {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */ - {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */ - {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */ - {"millisecon", UNITS, DTK_MILLISEC}, /* relative */ - {"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */ - {"min", UNITS, DTK_MINUTE}, /* "minute" relative */ - {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */ - {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */ - {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */ - {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */ - {"mon", UNITS, DTK_MONTH}, /* "months" relative */ - {"mons", UNITS, DTK_MONTH}, /* "months" relative */ - {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */ - {"months", UNITS, DTK_MONTH}, - {"ms", UNITS, DTK_MILLISEC}, - {"msec", UNITS, DTK_MILLISEC}, - {DMILLISEC, UNITS, DTK_MILLISEC}, - {"mseconds", UNITS, DTK_MILLISEC}, - {"msecs", UNITS, DTK_MILLISEC}, - {"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */ - {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */ - {"reltime", IGNORE_DTF, 0}, /* pre-v6.1 "Undefined Reltime" */ - {"s", UNITS, DTK_SECOND}, - {"sec", UNITS, DTK_SECOND}, - {DSECOND, UNITS, DTK_SECOND}, - {"seconds", UNITS, DTK_SECOND}, - {"secs", UNITS, DTK_SECOND}, - {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */ - {"timezone", UNITS, DTK_TZ}, /* "timezone" time offset */ - {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */ - {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */ - {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */ - {"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */ - {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */ - {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */ - {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */ - {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */ - {"w", UNITS, DTK_WEEK}, /* "week" relative */ - {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */ - {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */ - {"y", UNITS, DTK_YEAR}, /* "year" relative */ - {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */ - {"years", UNITS, DTK_YEAR}, /* "years" relative */ - {"yr", UNITS, DTK_YEAR}, /* "year" relative */ - {"yrs", UNITS, DTK_YEAR}, /* "years" relative */ -}; - -static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0]; - -datetkn *datecache[MAXDATEFIELDS] = {NULL}; - -datetkn *deltacache[MAXDATEFIELDS] = {NULL}; - - -/* - * Calendar time to Julian date conversions. - * Julian date is commonly used in astronomical applications, - * since it is numerically accurate and computationally simple. - * The algorithms here will accurately convert between Julian day - * and calendar date for all non-negative Julian days - * (i.e. from Nov 23, -4713 on). - * - * Ref: Explanatory Supplement to the Astronomical Almanac, 1992. - * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941. - * - * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague - * now at Aerospace Corp. (hi, Henry!) - * - * These routines will be used by other date/time packages - * - thomas 97/02/25 - */ - -int -date2j(int y, int m, int d) -{ - int m12 = (m - 14) / 12; - - return ((1461 * (y + 4800 + m12)) / 4 - + (367 * (m - 2 - 12 * (m12))) / 12 - - (3 * ((y + 4900 + m12) / 100)) / 4 - + d - 32075); -} /* date2j() */ - -void -j2date(int jd, int *year, int *month, int *day) -{ - int j, - y, - m, - d; - - int i, - l, - n; - - l = jd + 68569; - n = (4 * l) / 146097; - l -= (146097 * n + 3) / 4; - i = (4000 * (l + 1)) / 1461001; - l += 31 - (1461 * i) / 4; - j = (80 * l) / 2447; - d = l - (2447 * j) / 80; - l = j / 11; - m = (j + 2) - (12 * l); - y = 100 * (n - 49) + i + l; - - *year = y; - *month = m; - *day = d; - return; -} /* j2date() */ - -int -j2day(int date) -{ - int day; - - day = (date + 1) % 7; - - return day; -} /* j2day() */ - - -/* TrimTrailingZeros() - * ... resulting from printing numbers with full precision. - */ -void -TrimTrailingZeros(char *str) -{ - int len = strlen(str); - -#if 0 - /* chop off trailing one to cope with interval rounding */ - if (strcmp((str + len - 4), "0001") == 0) - { - len -= 4; - *(str + len) = '\0'; - } -#endif - - /* chop off trailing zeros... */ - while ((*(str + len - 1) == '0') - && (*(str + len - 3) != '.')) - { - len--; - *(str + len) = '\0'; - } - return; -} - - -/* ParseDateTime() - * Break string into tokens based on a date/time context. - * Several field types are assigned: - * DTK_NUMBER - digits and (possibly) a decimal point - * DTK_DATE - digits and two delimiters, or digits and text - * DTK_TIME - digits, colon delimiters, and possibly a decimal point - * DTK_STRING - text (no digits) - * DTK_SPECIAL - leading "+" or "-" followed by text - * DTK_TZ - leading "+" or "-" followed by digits - * Note that some field types can hold unexpected items: - * DTK_NUMBER can hold date fields (yy.ddd) - * DTK_STRING can hold months (January) and time zones (PST) - * DTK_DATE can hold Posix time zones (GMT-8) - */ -int -ParseDateTime(char *timestr, char *lowstr, - char **field, int *ftype, int maxfields, int *numfields) -{ - int nf = 0; - char *cp = timestr; - char *lp = lowstr; - - /* outer loop through fields */ - while (*cp != '\0') - { - field[nf] = lp; - - /* leading digit? then date or time */ - if (isdigit((unsigned char) *cp)) - { - *lp++ = *cp++; - while (isdigit((unsigned char) *cp)) - *lp++ = *cp++; - - /* time field? */ - if (*cp == ':') - { - ftype[nf] = DTK_TIME; - *lp++ = *cp++; - while (isdigit((unsigned char) *cp) || - (*cp == ':') || (*cp == '.')) - *lp++ = *cp++; - } - /* date field? allow embedded text month */ - else if ((*cp == '-') || (*cp == '/') || (*cp == '.')) - { - /* save delimiting character to use later */ - char *dp = cp; - - *lp++ = *cp++; - /* second field is all digits? then no embedded text month */ - if (isdigit((unsigned char) *cp)) - { - ftype[nf] = ((*dp == '.')? DTK_NUMBER: DTK_DATE); - while (isdigit((unsigned char) *cp)) - *lp++ = *cp++; - - /* insist that the delimiters match to get a three-field date. */ - if (*cp == *dp) - { - ftype[nf] = DTK_DATE; - *lp++ = *cp++; - while (isdigit((unsigned char) *cp) || (*cp == *dp)) - *lp++ = *cp++; - } - } - else - { - ftype[nf] = DTK_DATE; - while (isalnum((unsigned char) *cp) || (*cp == *dp)) - *lp++ = tolower((unsigned char) *cp++); - } - } - /* otherwise, number only and will determine - * year, month, day, or concatenated fields later... - */ - else - { - ftype[nf] = DTK_NUMBER; - } - } - /* Leading decimal point? Then fractional seconds... */ - else if (*cp == '.') - { - *lp++ = *cp++; - while (isdigit((unsigned char) *cp)) - *lp++ = *cp++; - - ftype[nf] = DTK_NUMBER; - } - /* - * text? then date string, month, day of week, special, or - * timezone - */ - else if (isalpha((unsigned char) *cp)) - { - ftype[nf] = DTK_STRING; - *lp++ = tolower((unsigned char) *cp++); - while (isalpha((unsigned char) *cp)) - *lp++ = tolower((unsigned char) *cp++); - - /* - * Full date string with leading text month? Could also be a - * POSIX time zone... - */ - if ((*cp == '-') || (*cp == '/') || (*cp == '.')) - { - char *dp = cp; - - ftype[nf] = DTK_DATE; - *lp++ = *cp++; - while (isdigit((unsigned char) *cp) || (*cp == *dp)) - *lp++ = *cp++; - } - } - /* skip leading spaces */ - else if (isspace((unsigned char) *cp)) - { - cp++; - continue; - } - /* sign? then special or numeric timezone */ - else if ((*cp == '+') || (*cp == '-')) - { - *lp++ = *cp++; - /* soak up leading whitespace */ - while (isspace((unsigned char) *cp)) - cp++; - /* numeric timezone? */ - if (isdigit((unsigned char) *cp)) - { - ftype[nf] = DTK_TZ; - *lp++ = *cp++; - while (isdigit((unsigned char) *cp) || - (*cp == ':') || (*cp == '.')) - *lp++ = *cp++; - } - /* special? */ - else if (isalpha((unsigned char) *cp)) - { - ftype[nf] = DTK_SPECIAL; - *lp++ = tolower((unsigned char) *cp++); - while (isalpha((unsigned char) *cp)) - *lp++ = tolower((unsigned char) *cp++); - } - /* otherwise something wrong... */ - else - { - return -1; - } - } - /* ignore punctuation but use as delimiter */ - else if (ispunct((unsigned char) *cp)) - { - cp++; - continue; - - } - /* otherwise, something is not right... */ - else - { - return -1; - } - - /* force in a delimiter after each field */ - *lp++ = '\0'; - nf++; - if (nf > MAXDATEFIELDS) - return -1; - } - - *numfields = nf; - - return 0; -} /* ParseDateTime() */ - - -/* DecodeDateTime() - * Interpret previously parsed fields for general date and time. - * Return 0 if full date, 1 if only time, and -1 if problems. - * External format(s): - * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>" - * "Fri Feb-7-1997 15:23:27" - * "Feb-7-1997 15:23:27" - * "2-7-1997 15:23:27" - * "1997-2-7 15:23:27" - * "1997.038 15:23:27" (day of year 1-366) - * Also supports input in compact time: - * "970207 152327" - * "97038 152327" - * "20011225T040506.789-07" - * - * Use the system-provided functions to get the current time zone - * if not specified in the input string. - * If the date is outside the time_t system-supported time range, - * then assume UTC time zone. - thomas 1997-05-27 - */ -int -DecodeDateTime(char **field, int *ftype, int nf, - int *dtype, struct tm * tm, fsec_t *fsec, int *tzp) -{ - int fmask = 0, - tmask, - type; - int ptype = 0; /* "prefix type" for ISO y2001m02d04 format */ - int i; - int val; - int mer = HR24; - int haveTextMonth = FALSE; - int is2digits = FALSE; - int bc = FALSE; - - /*** - * We'll insist on at least all of the date fields, but initialize the - * remaining fields in case they are not set later... - ***/ - *dtype = DTK_DATE; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - *fsec = 0; - /* don't know daylight savings time status apriori */ - tm->tm_isdst = -1; - if (tzp != NULL) - *tzp = 0; - - for (i = 0; i < nf; i++) - { - switch (ftype[i]) - { - case DTK_DATE: - /*** - * Integral julian day with attached time zone? - * All other forms with JD will be separated into - * distinct fields, so we handle just this case here. - ***/ - if (ptype == DTK_JULIAN) - { - char *cp; - int val; - - if (tzp == NULL) - return -1; - - val = strtol(field[i], &cp, 10); - if (*cp != '-') - return -1; - - j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - /* Get the time zone from the end of the string */ - if (DecodeTimezone(cp, tzp) != 0) - return -1; - - tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ); - ptype = 0; - break; - } - /*** - * Already have a date? Then this might be a POSIX time - * zone with an embedded dash (e.g. "PST-3" == "EST") or - * a run-together time with trailing time zone (e.g. hhmmss-zz). - * - thomas 2001-12-25 - ***/ - else if (((fmask & DTK_DATE_M) == DTK_DATE_M) - || (ptype != 0)) - { - /* No time zone accepted? Then quit... */ - if (tzp == NULL) - return -1; - - if (isdigit((unsigned char) *field[i]) || ptype != 0) - { - char *cp; - - if (ptype != 0) - { - /* Sanity check; should not fail this test */ - if (ptype != DTK_TIME) - return -1; - ptype = 0; - } - - /* Starts with a digit but we already have a time field? - * Then we are in trouble with a date and time already... - */ - if ((fmask & DTK_TIME_M) == DTK_TIME_M) - return -1; - - if ((cp = strchr(field[i], '-')) == NULL) - return -1; - - /* Get the time zone from the end of the string */ - if (DecodeTimezone(cp, tzp) != 0) - return -1; - *cp = '\0'; - - /* Then read the rest of the field as a concatenated time */ - if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask, - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - - /* modify tmask after returning from DecodeNumberField() */ - tmask |= DTK_M(TZ); - } - else - { - if (DecodePosixTimezone(field[i], tzp) != 0) - return -1; - - ftype[i] = DTK_TZ; - tmask = DTK_M(TZ); - } - } - else if (DecodeDate(field[i], fmask, &tmask, tm) != 0) - { - return -1; - } - break; - - case DTK_TIME: - if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) - return -1; - - /* - * Check upper limit on hours; other limits checked in - * DecodeTime() - */ - if (tm->tm_hour > 23) - return -1; - break; - - case DTK_TZ: - { - int tz; - - if (tzp == NULL) - return -1; - - if (DecodeTimezone(field[i], &tz) != 0) - return -1; - - /* - * Already have a time zone? Then maybe this is the - * second field of a POSIX time: EST+3 (equivalent to - * PST) - */ - if ((i > 0) && ((fmask & DTK_M(TZ)) != 0) - && (ftype[i - 1] == DTK_TZ) - && (isalpha((unsigned char) *field[i - 1]))) - { - *tzp -= tz; - tmask = 0; - } - else - { - *tzp = tz; - tmask = DTK_M(TZ); - } - } - break; - - case DTK_NUMBER: - /* - * Was this an "ISO date" with embedded field labels? An - * example is "y2001m02d04" - thomas 2001-02-04 - */ - if (ptype != 0) - { - char *cp; - int val; - - val = strtol(field[i], &cp, 10); - /* only a few kinds are allowed to have an embedded decimal */ - if (*cp == '.') - switch (ptype) - { - case DTK_JULIAN: - case DTK_TIME: - case DTK_SECOND: - break; - default: - return 1; - break; - } - else if (*cp != '\0') - return -1; - - switch (ptype) - { - case DTK_YEAR: - tm->tm_year = val; - tmask = DTK_M(YEAR); - break; - - case DTK_MONTH: - /* already have a month and hour? then assume minutes */ - if (((fmask & DTK_M(MONTH)) != 0) - && ((fmask & DTK_M(HOUR)) != 0)) - { - tm->tm_min = val; - tmask = DTK_M(MINUTE); - } - else - { - tm->tm_mon = val; - tmask = DTK_M(MONTH); - } - break; - - case DTK_DAY: - tm->tm_mday = val; - tmask = DTK_M(DAY); - break; - - case DTK_HOUR: - tm->tm_hour = val; - tmask = DTK_M(HOUR); - break; - - case DTK_MINUTE: - tm->tm_min = val; - tmask = DTK_M(MINUTE); - break; - - case DTK_SECOND: - tm->tm_sec = val; - tmask = DTK_M(SECOND); - if (*cp == '.') - { - double frac; - frac = strtod(cp, &cp); - if (*cp != '\0') - return -1; -#ifdef HAVE_INT64_TIMESTAMP - *fsec = frac * 1000000; -#else - *fsec = frac; -#endif - } - break; - - case DTK_TZ: - tmask = DTK_M(TZ); - if (DecodeTimezone(field[i], tzp) != 0) - return -1; - break; - - case DTK_JULIAN: - /*** - * previous field was a label for "julian date"? - ***/ - tmask = DTK_DATE_M; - j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - /* fractional Julian Day? */ - if (*cp == '.') - { - double time; - - time = strtod(cp, &cp); - if (*cp != '\0') - return -1; - - tmask |= DTK_TIME_M; -#ifdef HAVE_INT64_TIMESTAMP - dt2time((time*86400000000), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec); -#else - dt2time((time*86400), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec); -#endif - } - break; - - case DTK_TIME: - /* previous field was "t" for ISO time */ - if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M), - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - - if (tmask != DTK_TIME_M) - return -1; - break; - - default: - return -1; - break; - } - - ptype = 0; - *dtype = DTK_DATE; - } - else - { - char *cp; - int flen; - - flen = strlen(field[i]); - cp = strchr(field[i], '.'); - - /* Embedded decimal and no date yet? */ - if ((cp != NULL) && !(fmask & DTK_DATE_M)) - { - if (DecodeDate(field[i], fmask, &tmask, tm) != 0) - return -1; - } - /* embedded decimal and several digits before? */ - else if ((cp != NULL) && ((flen - strlen(cp)) > 2)) - { - /* Interpret as a concatenated date or time - * Set the type field to allow decoding other fields later. - * Example: 20011223 or 040506 - */ - if ((ftype[i] = DecodeNumberField(flen, field[i], fmask, - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - } - else if (flen > 4) - { - if ((ftype[i] = DecodeNumberField(flen, field[i], fmask, - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - } - /* otherwise it is a single date/time field... */ - else if (DecodeNumber(flen, field[i], fmask, - &tmask, tm, fsec, &is2digits) != 0) - { - return -1; - } - } - break; - - case DTK_STRING: - case DTK_SPECIAL: - type = DecodeSpecial(i, field[i], &val); - if (type == IGNORE_DTF) - continue; - - tmask = DTK_M(type); - switch (type) - { - case RESERV: - switch (val) - { - case DTK_CURRENT: - elog(ERROR, "'CURRENT' is no longer supported"); - return -1; - break; - - case DTK_NOW: - tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ)); - *dtype = DTK_DATE; - GetCurrentTimeUsec(tm, fsec); - if (tzp != NULL) - *tzp = CTimeZone; - break; - - case DTK_YESTERDAY: - tmask = DTK_DATE_M; - *dtype = DTK_DATE; - GetCurrentDateTime(tm); - j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1), - &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - break; - - case DTK_TODAY: - tmask = DTK_DATE_M; - *dtype = DTK_DATE; - GetCurrentDateTime(tm); - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - break; - - case DTK_TOMORROW: - tmask = DTK_DATE_M; - *dtype = DTK_DATE; - GetCurrentDateTime(tm); - j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1), - &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - break; - - case DTK_ZULU: - tmask = (DTK_TIME_M | DTK_M(TZ)); - *dtype = DTK_DATE; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - if (tzp != NULL) - *tzp = 0; - break; - - default: - *dtype = val; - } - - break; - - case MONTH: - - /* - * already have a (numeric) month? then see if we - * can substitute... - */ - if ((fmask & DTK_M(MONTH)) && (!haveTextMonth) - && (!(fmask & DTK_M(DAY))) - && ((tm->tm_mon >= 1) && (tm->tm_mon <= 31))) - { - tm->tm_mday = tm->tm_mon; - tmask = DTK_M(DAY); - } - haveTextMonth = TRUE; - tm->tm_mon = val; - break; - - case DTZMOD: - - /* - * daylight savings time modifier (solves "MET - * DST" syntax) - */ - tmask |= DTK_M(DTZ); - tm->tm_isdst = 1; - if (tzp == NULL) - return -1; - *tzp += val * 60; - break; - - case DTZ: - - /* - * set mask for TZ here _or_ check for DTZ later - * when getting default timezone - */ - tmask |= DTK_M(TZ); - tm->tm_isdst = 1; - if (tzp == NULL) - return -1; - *tzp = val * 60; - ftype[i] = DTK_TZ; - break; - - case TZ: - tm->tm_isdst = 0; - if (tzp == NULL) - return -1; - *tzp = val * 60; - ftype[i] = DTK_TZ; - break; - - case IGNORE_DTF: - break; - - case AMPM: - mer = val; - break; - - case ADBC: - bc = (val == BC); - break; - - case DOW: - tm->tm_wday = val; - break; - - case UNITS: - tmask = 0; - ptype = val; - break; - - case ISOTIME: - /* This is a filler field "t" - * indicating that the next field is time. - * Try to verify that this is sensible. - */ - tmask = 0; - - /* No preceeding date? Then quit... */ - if ((fmask & DTK_DATE_M) != DTK_DATE_M) - return -1; - - /*** - * We will need one of the following fields: - * DTK_NUMBER should be hhmmss.fff - * DTK_TIME should be hh:mm:ss.fff - * DTK_DATE should be hhmmss-zz - ***/ - if ((i >= (nf - 1)) - || ((ftype[i + 1] != DTK_NUMBER) - && (ftype[i + 1] != DTK_TIME) - && (ftype[i + 1] != DTK_DATE))) - return -1; - - ptype = val; - break; - - default: - return -1; - } - break; - - default: - return -1; - } - - if (tmask & fmask) - return -1; - fmask |= tmask; - } - - /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */ - if (bc) - { - if (tm->tm_year > 0) - tm->tm_year = -(tm->tm_year - 1); - else - elog(ERROR, "Inconsistent use of year %04d and 'BC'", tm->tm_year); - } - else if (is2digits) - { - if (tm->tm_year < 70) - tm->tm_year += 2000; - else if (tm->tm_year < 100) - tm->tm_year += 1900; - } - - if ((mer != HR24) && (tm->tm_hour > 12)) - return -1; - if ((mer == AM) && (tm->tm_hour == 12)) - tm->tm_hour = 0; - else if ((mer == PM) && (tm->tm_hour != 12)) - tm->tm_hour += 12; - - /* do additional checking for full date specs... */ - if (*dtype == DTK_DATE) - { - if ((fmask & DTK_DATE_M) != DTK_DATE_M) - return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1; - - /* - * check for valid day of month, now that we know for sure the - * month and year... - */ - if ((tm->tm_mday < 1) - || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])) - return -1; - - /* timezone not specified? then find local timezone if possible */ - if (((fmask & DTK_DATE_M) == DTK_DATE_M) - && (tzp != NULL) && (!(fmask & DTK_M(TZ)))) - { - /* - * daylight savings time modifier but no standard timezone? - * then error - */ - if (fmask & DTK_M(DTZMOD)) - return -1; - - *tzp = DetermineLocalTimeZone(tm); - } - } - - return 0; -} /* DecodeDateTime() */ - - -/* DetermineLocalTimeZone() - * Given a struct tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and - * tm_sec fields are set, attempt to determine the applicable local zone - * (ie, regular or daylight-savings time) at that time. Set the struct tm's - * tm_isdst field accordingly, and return the actual timezone offset. - * - * This subroutine exists mainly to centralize uses of mktime() and defend - * against mktime() bugs on various platforms... - */ -int -DetermineLocalTimeZone(struct tm * tm) -{ - int tz; - - if (HasCTZSet) - tz = CTimeZone; - else if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { -#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE) - - /* - * Some buggy mktime() implementations may change the - * year/month/day when given a time right at a DST boundary. To - * prevent corruption of the caller's data, give mktime() a - * copy... - */ - struct tm tt, - *tmp = &tt; - - *tmp = *tm; - /* change to Unix conventions for year/month */ - tmp->tm_year -= 1900; - tmp->tm_mon -= 1; - - /* indicate timezone unknown */ - tmp->tm_isdst = -1; - - mktime(tmp); - - tm->tm_isdst = tmp->tm_isdst; - -#if defined(HAVE_TM_ZONE) - /* tm_gmtoff is Sun/DEC-ism */ - if (tmp->tm_isdst >= 0) - tz = -(tmp->tm_gmtoff); - else - tz = 0; /* assume UTC if mktime failed */ -#elif defined(HAVE_INT_TIMEZONE) - tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL); -#endif /* HAVE_INT_TIMEZONE */ - -#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */ - tm->tm_isdst = 0; - tz = CTimeZone; -#endif - } - else - { - /* Given date is out of range, so assume UTC */ - tm->tm_isdst = 0; - tz = 0; - } - - return tz; -} - - -/* DecodeTimeOnly() - * Interpret parsed string as time fields only. - * Note that support for time zone is here for - * SQL92 TIME WITH TIME ZONE, but it reveals - * bogosity with SQL92 date/time standards, since - * we must infer a time zone from current time. - * - thomas 2000-03-10 - * Allow specifying date to get a better time zone, - * if time zones are allowed. - thomas 2001-12-26 - */ -int -DecodeTimeOnly(char **field, int *ftype, int nf, - int *dtype, struct tm * tm, fsec_t *fsec, int *tzp) -{ - int fmask = 0, - tmask, - type; - int ptype = 0; /* "prefix type" for ISO h04mm05s06 format */ - int i; - int val; - int is2digits = FALSE; - int mer = HR24; - - *dtype = DTK_TIME; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - *fsec = 0; - /* don't know daylight savings time status apriori */ - tm->tm_isdst = -1; - - if (tzp != NULL) - *tzp = 0; - - for (i = 0; i < nf; i++) - { - switch (ftype[i]) - { - case DTK_DATE: - /* Time zone not allowed? - * Then should not accept dates or time zones - * no matter what else! - */ - if (tzp == NULL) - return -1; - - /* Under limited circumstances, we will accept a date... */ - if ((i == 0) && (nf >= 2) - && ((ftype[nf-1] == DTK_DATE) - || (ftype[1] == DTK_TIME))) - { - if (DecodeDate(field[i], fmask, &tmask, tm) != 0) - return -1; - } - /* otherwise, this is a time and/or time zone */ - else - { - if (isdigit((unsigned char) *field[i])) - { - char *cp; - - /* Starts with a digit but we already have a time field? - * Then we are in trouble with time already... - */ - if ((fmask & DTK_TIME_M) == DTK_TIME_M) - return -1; - - /* Should not get here and fail. Sanity check only... */ - if ((cp = strchr(field[i], '-')) == NULL) - return -1; - - /* Get the time zone from the end of the string */ - if (DecodeTimezone(cp, tzp) != 0) - return -1; - *cp = '\0'; - - /* Then read the rest of the field as a concatenated time */ - if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M), - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - - tmask |= DTK_M(TZ); - } - else - { - if (DecodePosixTimezone(field[i], tzp) != 0) - return -1; - - ftype[i] = DTK_TZ; - tmask = DTK_M(TZ); - } - } - break; - - case DTK_TIME: - if (DecodeTime(field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec) != 0) - return -1; - break; - - case DTK_TZ: - if (tzp == NULL) - return -1; - - { - int tz; - - if (DecodeTimezone(field[i], &tz) != 0) - return -1; - - /* - * Already have a time zone? Then maybe this is the - * second field of a POSIX time: EST+3 (equivalent to - * PST) - */ - if ((i > 0) && ((fmask & DTK_M(TZ)) != 0) - && (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1]))) - { - *tzp -= tz; - tmask = 0; - } - else - { - *tzp = tz; - tmask = DTK_M(TZ); - } - } - break; - - case DTK_NUMBER: - /* - * Was this an "ISO time" with embedded field labels? An - * example is "h04m05s06" - thomas 2001-02-04 - */ - if (ptype != 0) - { - char *cp; - int val; - - /* Only accept a date under limited circumstances */ - switch (ptype) - { - case DTK_JULIAN: - case DTK_YEAR: - case DTK_MONTH: - case DTK_DAY: - if (tzp == NULL) - return -1; - default: - break; - } - - val = strtol(field[i], &cp, 10); - /* only a few kinds are allowed to have an embedded decimal */ - if (*cp == '.') - switch (ptype) - { - case DTK_JULIAN: - case DTK_TIME: - case DTK_SECOND: - break; - default: - return 1; - break; - } - else if (*cp != '\0') - return -1; - - switch (ptype) - { - case DTK_YEAR: - tm->tm_year = val; - tmask = DTK_M(YEAR); - break; - - case DTK_MONTH: - /* already have a month and hour? then assume minutes */ - if (((fmask & DTK_M(MONTH)) != 0) - && ((fmask & DTK_M(HOUR)) != 0)) - { - tm->tm_min = val; - tmask = DTK_M(MINUTE); - } - else - { - tm->tm_mon = val; - tmask = DTK_M(MONTH); - } - break; - - case DTK_DAY: - tm->tm_mday = val; - tmask = DTK_M(DAY); - break; - - case DTK_HOUR: - tm->tm_hour = val; - tmask = DTK_M(HOUR); - break; - - case DTK_MINUTE: - tm->tm_min = val; - tmask = DTK_M(MINUTE); - break; - - case DTK_SECOND: - tm->tm_sec = val; - tmask = DTK_M(SECOND); - if (*cp == '.') - { - *fsec = strtod(cp, &cp); - if (*cp != '\0') - return -1; - } - break; - - case DTK_TZ: - tmask = DTK_M(TZ); - if (DecodeTimezone(field[i], tzp) != 0) - return -1; - break; - - case DTK_JULIAN: - /*** - * previous field was a label for "julian date"? - ***/ - tmask = DTK_DATE_M; - j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - if (*cp == '.') - { - double time; - - time = strtod(cp, &cp); - if (*cp != '\0') - return -1; - - tmask |= DTK_TIME_M; -#ifdef HAVE_INT64_TIMESTAMP - dt2time((time*86400000000), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec); -#else - dt2time((time*86400), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec); -#endif - } - break; - - case DTK_TIME: - /* previous field was "t" for ISO time */ - if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M), - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - - if (tmask != DTK_TIME_M) - return -1; - break; - - default: - return -1; - break; - } - - ptype = 0; - *dtype = DTK_DATE; - } - else - { - char *cp; - int flen; - - flen = strlen(field[i]); - cp = strchr(field[i], '.'); - - /* Embedded decimal? */ - if (cp != NULL) - { - /* Under limited circumstances, we will accept a date... */ - if ((i == 0) && ((nf >= 2) && (ftype[nf-1] == DTK_DATE))) - { - if (DecodeDate(field[i], fmask, &tmask, tm) != 0) - return -1; - } - /* embedded decimal and several digits before? */ - else if ((flen - strlen(cp)) > 2) - { - /* Interpret as a concatenated date or time - * Set the type field to allow decoding other fields later. - * Example: 20011223 or 040506 - */ - if ((ftype[i] = DecodeNumberField(flen, field[i], fmask, - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - } - else - { - return -1; - } - } - else if (flen > 4) - { - if ((ftype[i] = DecodeNumberField(flen, field[i], fmask, - &tmask, tm, fsec, &is2digits)) < 0) - return -1; - } - /* otherwise it is a single date/time field... */ - else if (DecodeNumber(flen, field[i], fmask, - &tmask, tm, fsec, &is2digits) != 0) - { - return -1; - } - } - break; - - case DTK_STRING: - case DTK_SPECIAL: - type = DecodeSpecial(i, field[i], &val); - if (type == IGNORE_DTF) - continue; - - tmask = DTK_M(type); - switch (type) - { - case RESERV: - switch (val) - { - case DTK_CURRENT: - elog(ERROR, "'CURRENT' is no longer supported"); - return -1; - break; - - case DTK_NOW: - tmask = DTK_TIME_M; - *dtype = DTK_TIME; - GetCurrentTimeUsec(tm, fsec); - break; - - case DTK_ZULU: - tmask = (DTK_TIME_M | DTK_M(TZ)); - *dtype = DTK_TIME; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - tm->tm_isdst = 0; - break; - - default: - return -1; - } - - break; - - case DTZMOD: - - /* - * daylight savings time modifier (solves "MET - * DST" syntax) - */ - tmask |= DTK_M(DTZ); - tm->tm_isdst = 1; - if (tzp == NULL) - return -1; - *tzp += val * 60; - break; - - case DTZ: - - /* - * set mask for TZ here _or_ check for DTZ later - * when getting default timezone - */ - tmask |= DTK_M(TZ); - tm->tm_isdst = 1; - if (tzp == NULL) - return -1; - *tzp = val * 60; - ftype[i] = DTK_TZ; - break; - - case TZ: - tm->tm_isdst = 0; - if (tzp == NULL) - return -1; - *tzp = val * 60; - ftype[i] = DTK_TZ; - break; - - case IGNORE_DTF: - break; - - case AMPM: - mer = val; - break; - - case UNITS: - tmask = 0; - ptype = val; - break; - - case ISOTIME: - tmask = 0; - - /*** - * We will need one of the following fields: - * DTK_NUMBER should be hhmmss.fff - * DTK_TIME should be hh:mm:ss.fff - * DTK_DATE should be hhmmss-zz - ***/ - if ((i >= (nf - 1)) - || ((ftype[i + 1] != DTK_NUMBER) - && (ftype[i + 1] != DTK_TIME) - && (ftype[i + 1] != DTK_DATE))) - return -1; - - ptype = val; - break; - - default: - return -1; - } - break; - - default: - return -1; - } - - if (tmask & fmask) - return -1; - fmask |= tmask; - } - - if ((mer != HR24) && (tm->tm_hour > 12)) - return -1; - if ((mer == AM) && (tm->tm_hour == 12)) - tm->tm_hour = 0; - else if ((mer == PM) && (tm->tm_hour != 12)) - tm->tm_hour += 12; - -#ifdef HAVE_INT64_TIMESTAMP - if (((tm->tm_hour < 0) || (tm->tm_hour > 23)) - || ((tm->tm_min < 0) || (tm->tm_min > 59)) - || ((tm->tm_sec < 0) || (tm->tm_sec > 60)) - || (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000))) - return -1; -#else - if (((tm->tm_hour < 0) || (tm->tm_hour > 23)) - || ((tm->tm_min < 0) || (tm->tm_min > 59)) - || ((tm->tm_sec < 0) || ((tm->tm_sec + *fsec) >= 60))) - return -1; -#endif - - if ((fmask & DTK_TIME_M) != DTK_TIME_M) - return -1; - - /* timezone not specified? then find local timezone if possible */ - if ((tzp != NULL) && (!(fmask & DTK_M(TZ)))) - { - struct tm tt, - *tmp = &tt; - - /* - * daylight savings time modifier but no standard timezone? then - * error - */ - if (fmask & DTK_M(DTZMOD)) - return -1; - - if ((fmask & DTK_DATE_M) == 0) - { - GetCurrentDateTime(tmp); - } - else - { - tmp->tm_year = tm->tm_year; - tmp->tm_mon = tm->tm_mon; - tmp->tm_mday = tm->tm_mday; - } - tmp->tm_hour = tm->tm_hour; - tmp->tm_min = tm->tm_min; - tmp->tm_sec = tm->tm_sec; - *tzp = DetermineLocalTimeZone(tmp); - tm->tm_isdst = tmp->tm_isdst; - } - - return 0; -} /* DecodeTimeOnly() */ - -/* DecodeDate() - * Decode date string which includes delimiters. - * Insist on a complete set of fields. - */ -static int -DecodeDate(char *str, int fmask, int *tmask, struct tm * tm) -{ - fsec_t fsec; - - int nf = 0; - int i, - len; - int bc = FALSE; - int is2digits = FALSE; - int type, - val, - dmask = 0; - char *field[MAXDATEFIELDS]; - - /* parse this string... */ - while ((*str != '\0') && (nf < MAXDATEFIELDS)) - { - /* skip field separators */ - while (!isalnum((unsigned char) *str)) - str++; - - field[nf] = str; - if (isdigit((unsigned char) *str)) - { - while (isdigit((unsigned char) *str)) - str++; - } - else if (isalpha((unsigned char) *str)) - { - while (isalpha((unsigned char) *str)) - str++; - } - - /* Just get rid of any non-digit, non-alpha characters... */ - if (*str != '\0') - *str++ = '\0'; - nf++; - } - -#if 0 - /* don't allow too many fields */ - if (nf > 3) - return -1; -#endif - - *tmask = 0; - - /* look first for text fields, since that will be unambiguous month */ - for (i = 0; i < nf; i++) - { - if (isalpha((unsigned char) *field[i])) - { - type = DecodeSpecial(i, field[i], &val); - if (type == IGNORE_DTF) - continue; - - dmask = DTK_M(type); - switch (type) - { - case MONTH: - tm->tm_mon = val; - break; - - case ADBC: - bc = (val == BC); - break; - - default: - return -1; - } - if (fmask & dmask) - return -1; - - fmask |= dmask; - *tmask |= dmask; - - /* mark this field as being completed */ - field[i] = NULL; - } - } - - /* now pick up remaining numeric fields */ - for (i = 0; i < nf; i++) - { - if (field[i] == NULL) - continue; - - if ((len = strlen(field[i])) <= 0) - return -1; - - if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0) - return -1; - - if (fmask & dmask) - return -1; - - fmask |= dmask; - *tmask |= dmask; - } - - if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M) - return -1; - - /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */ - if (bc) - { - if (tm->tm_year > 0) - tm->tm_year = -(tm->tm_year - 1); - else - elog(ERROR, "Inconsistent use of year %04d and 'BC'", tm->tm_year); - } - else if (is2digits) - { - if (tm->tm_year < 70) - tm->tm_year += 2000; - else if (tm->tm_year < 100) - tm->tm_year += 1900; - } - - return 0; -} /* DecodeDate() */ - - -/* DecodeTime() - * Decode time string which includes delimiters. - * Only check the lower limit on hours, since this same code - * can be used to represent time spans. - */ -static int -DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec) -{ - char *cp; - - *tmask = DTK_TIME_M; - - tm->tm_hour = strtol(str, &cp, 10); - if (*cp != ':') - return -1; - str = cp + 1; - tm->tm_min = strtol(str, &cp, 10); - if (*cp == '\0') - { - tm->tm_sec = 0; - *fsec = 0; - } - else if (*cp != ':') - { - return -1; - } - else - { - str = cp + 1; - tm->tm_sec = strtol(str, &cp, 10); - if (*cp == '\0') - *fsec = 0; - else if (*cp == '.') - { -#ifdef HAVE_INT64_TIMESTAMP - char fstr[MAXDATELEN + 1]; - - /* OK, we have at most six digits to work with. - * Let's construct a string and then do the conversion - * to an integer. - */ - strncpy(fstr, (cp+1), 7); - strcpy((fstr+strlen(fstr)), "000000"); - *(fstr+6) = '\0'; - *fsec = strtol(fstr, &cp, 10); -#else - str = cp; - *fsec = strtod(str, &cp); -#endif - if (*cp != '\0') - return -1; - } - else - return -1; - } - - /* do a sanity check */ -#ifdef HAVE_INT64_TIMESTAMP - if ((tm->tm_hour < 0) - || (tm->tm_min < 0) || (tm->tm_min > 59) - || (tm->tm_sec < 0) || (tm->tm_sec > 59) - || (*fsec >= INT64CONST(1000000))) - return -1; -#else - if ((tm->tm_hour < 0) - || (tm->tm_min < 0) || (tm->tm_min > 59) - || (tm->tm_sec < 0) || (tm->tm_sec > 59) - || (*fsec >= 1)) - return -1; -#endif - - return 0; -} /* DecodeTime() */ - - -/* DecodeNumber() - * Interpret plain numeric field as a date value in context. - */ -static int -DecodeNumber(int flen, char *str, int fmask, - int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits) -{ - int val; - char *cp; - - *tmask = 0; - - val = strtol(str, &cp, 10); - if (cp == str) - return -1; - - if (*cp == '.') - { - /* More than two digits? - * Then could be a date or a run-together time: - * 2001.360 - * 20011225 040506.789 - */ - if ((cp - str) > 2) - return DecodeNumberField(flen, str, (fmask | DTK_DATE_M), - tmask, tm, fsec, is2digits); - - *fsec = strtod(cp, &cp); - if (*cp != '\0') - return -1; - } - else if (*cp != '\0') - { - return -1; - } - - /* Special case day of year? */ - if ((flen == 3) && (fmask & DTK_M(YEAR)) - && ((val >= 1) && (val <= 366))) - { - *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY)); - tm->tm_yday = val; - j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1), - &tm->tm_year, &tm->tm_mon, &tm->tm_mday); - } - - /*** - * Enough digits to be unequivocal year? Used to test for 4 digits or - * more, but we now test first for a three-digit doy so anything - * bigger than two digits had better be an explicit year. - * - thomas 1999-01-09 - * Back to requiring a 4 digit year. We accept a two digit - * year farther down. - thomas 2000-03-28 - ***/ - else if (flen >= 4) - { - *tmask = DTK_M(YEAR); - - /* already have a year? then see if we can substitute... */ - if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY))) - && ((tm->tm_year >= 1) && (tm->tm_year <= 31))) - { - tm->tm_mday = tm->tm_year; - *tmask = DTK_M(DAY); - } - - tm->tm_year = val; - } - - /* already have year? then could be month */ - else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH))) - && ((val >= 1) && (val <= 12))) - { - *tmask = DTK_M(MONTH); - tm->tm_mon = val; - } - /* no year and EuroDates enabled? then could be day */ - else if ((EuroDates || (fmask & DTK_M(MONTH))) - && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY))) - && ((val >= 1) && (val <= 31))) - { - *tmask = DTK_M(DAY); - tm->tm_mday = val; - } - else if ((!(fmask & DTK_M(MONTH))) - && ((val >= 1) && (val <= 12))) - { - *tmask = DTK_M(MONTH); - tm->tm_mon = val; - } - else if ((!(fmask & DTK_M(DAY))) - && ((val >= 1) && (val <= 31))) - { - *tmask = DTK_M(DAY); - tm->tm_mday = val; - } - - /* - * Check for 2 or 4 or more digits, but currently we reach here only - * if two digits. - thomas 2000-03-28 - */ - else if (!(fmask & DTK_M(YEAR)) - && ((flen >= 4) || (flen == 2))) - { - *tmask = DTK_M(YEAR); - tm->tm_year = val; - - /* adjust ONLY if exactly two digits... */ - *is2digits = (flen == 2); - } - else - return -1; - - return 0; -} /* DecodeNumber() */ - - -/* DecodeNumberField() - * Interpret numeric string as a concatenated date or time field. - * Use the context of previously decoded fields to help with - * the interpretation. - */ -static int -DecodeNumberField(int len, char *str, int fmask, - int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits) -{ - char *cp; - - /* Have a decimal point? - * Then this is a date or something with a seconds field... - */ - if ((cp = strchr(str, '.')) != NULL) - { -#ifdef HAVE_INT64_TIMESTAMP - char fstr[MAXDATELEN + 1]; - - /* OK, we have at most six digits to care about. - * Let's construct a string and then do the conversion - * to an integer. - */ - strcpy(fstr, (cp+1)); - strcpy((fstr+strlen(fstr)), "000000"); - *(fstr+6) = '\0'; - *fsec = strtol(fstr, NULL, 10); -#else - *fsec = strtod(cp, NULL); -#endif - *cp = '\0'; - len = strlen(str); - } - /* No decimal point and no complete date yet? */ - else if ((fmask & DTK_DATE_M) != DTK_DATE_M) - { - /* yyyymmdd? */ - if (len == 8) - { - *tmask = DTK_DATE_M; - - tm->tm_mday = atoi(str + 6); - *(str + 6) = '\0'; - tm->tm_mon = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_year = atoi(str + 0); - - return DTK_DATE; - } - /* yymmdd? */ - else if (len == 6) - { - *tmask = DTK_DATE_M; - tm->tm_mday = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_mon = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_year = atoi(str + 0); - *is2digits = TRUE; - - return DTK_DATE; - } - /* yyddd? */ - else if (len == 5) - { - *tmask = DTK_DATE_M; - tm->tm_mday = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_mon = 1; - tm->tm_year = atoi(str + 0); - *is2digits = TRUE; - - return DTK_DATE; - } - } - - /* not all time fields are specified? */ - if ((fmask & DTK_TIME_M) != DTK_TIME_M) - { - /* hhmmss */ - if (len == 6) - { - *tmask = DTK_TIME_M; - tm->tm_sec = atoi(str + 4); - *(str + 4) = '\0'; - tm->tm_min = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_hour = atoi(str + 0); - - return DTK_TIME; - } - /* hhmm? */ - else if (len == 4) - { - *tmask = DTK_TIME_M; - tm->tm_sec = 0; - tm->tm_min = atoi(str + 2); - *(str + 2) = '\0'; - tm->tm_hour = atoi(str + 0); - - return DTK_TIME; - } - } - - return -1; -} /* DecodeNumberField() */ - - -/* DecodeTimezone() - * Interpret string as a numeric timezone. - */ -static int -DecodeTimezone(char *str, int *tzp) -{ - int tz; - int hr, - min; - char *cp; - int len; - - /* assume leading character is "+" or "-" */ - hr = strtol((str + 1), &cp, 10); - - /* explicit delimiter? */ - if (*cp == ':') - { - min = strtol((cp + 1), &cp, 10); - } - /* otherwise, might have run things together... */ - else if ((*cp == '\0') && ((len = strlen(str)) > 3)) - { - min = strtol((str + len - 2), &cp, 10); - if ((min < 0) || (min >= 60)) - return -1; - - *(str + len - 2) = '\0'; - hr = strtol((str + 1), &cp, 10); - if ((hr < 0) || (hr > 12)) - return -1; - } - else - { - min = 0; - } - - tz = (hr * 60 + min) * 60; - if (*str == '-') - tz = -tz; - - *tzp = -tz; - return *cp != '\0'; -} /* DecodeTimezone() */ - - -/* DecodePosixTimezone() - * Interpret string as a POSIX-compatible timezone: - * PST-hh:mm - * PST+h - * - thomas 2000-03-15 - */ -static int -DecodePosixTimezone(char *str, int *tzp) -{ - int val, - tz; - int type; - char *cp; - char delim; - - cp = str; - while ((*cp != '\0') && isalpha((unsigned char) *cp)) - cp++; - - if (DecodeTimezone(cp, &tz) != 0) - return -1; - - delim = *cp; - *cp = '\0'; - type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val); - *cp = delim; - - switch (type) - { - case DTZ: - case TZ: - *tzp = (val * 60) - tz; - break; - - default: - return -1; - } - - return 0; -} /* DecodePosixTimezone() */ - - -/* DecodeSpecial() - * Decode text string using lookup table. - * Implement a cache lookup since it is likely that dates - * will be related in format. - */ -int -DecodeSpecial(int field, char *lowtoken, int *val) -{ - int type; - datetkn *tp; - - if ((datecache[field] != NULL) - && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)) - tp = datecache[field]; - else - { - tp = NULL; - if (Australian_timezones) - tp = datebsearch(lowtoken, australian_datetktbl, - australian_szdatetktbl); - if (!tp) - tp = datebsearch(lowtoken, datetktbl, szdatetktbl); - } - datecache[field] = tp; - if (tp == NULL) - { - type = UNKNOWN_FIELD; - *val = 0; - } - else - { - type = tp->type; - switch (type) - { - case TZ: - case DTZ: - case DTZMOD: - *val = FROMVAL(tp); - break; - - default: - *val = tp->value; - break; - } - } - - return type; -} /* DecodeSpecial() */ - - -/* DecodeInterval() - * Interpret previously parsed fields for general time interval. - * Return 0 if decoded and -1 if problems. - * - * Allow "date" field DTK_DATE since this could be just - * an unsigned floating point number. - thomas 1997-11-16 - * - * Allow ISO-style time span, with implicit units on number of days - * preceding an hh:mm:ss field. - thomas 1998-04-30 - */ -int -DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec) -{ - int is_before = FALSE; - - char *cp; - int fmask = 0, - tmask, - type; - int i; - int val; - double fval; - - *dtype = DTK_DELTA; - - type = IGNORE_DTF; - tm->tm_year = 0; - tm->tm_mon = 0; - tm->tm_mday = 0; - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - *fsec = 0; - - /* read through list backwards to pick up units before values */ - for (i = nf - 1; i >= 0; i--) - { - switch (ftype[i]) - { - case DTK_TIME: - if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) - return -1; - type = DTK_DAY; - break; - - case DTK_TZ: - - /* - * Timezone is a token with a leading sign character and - * otherwise the same as a non-signed time field - */ - Assert((*field[i] == '-') || (*field[i] == '+')); - - /* - * A single signed number ends up here, but will be - * rejected by DecodeTime(). So, work this out to drop - * through to DTK_NUMBER, which *can* tolerate this. - */ - cp = field[i] + 1; - while ((*cp != '\0') && (*cp != ':') && (*cp != '.')) - cp++; - if ((*cp == ':') - && (DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0)) - { - if (*field[i] == '-') - { - /* flip the sign on all fields */ - tm->tm_hour = -tm->tm_hour; - tm->tm_min = -tm->tm_min; - tm->tm_sec = -tm->tm_sec; - *fsec = -(*fsec); - } - - /* - * Set the next type to be a day, if units are not - * specified. This handles the case of '1 +02:03' - * since we are reading right to left. - */ - type = DTK_DAY; - tmask = DTK_M(TZ); - break; - } - else if (type == IGNORE_DTF) - { - if (*cp == '.') - { - /* - * Got a decimal point? Then assume some sort of - * seconds specification - */ - type = DTK_SECOND; - } - else if (*cp == '\0') - { - /* - * Only a signed integer? Then must assume a - * timezone-like usage - */ - type = DTK_HOUR; - } - } - /* DROP THROUGH */ - - case DTK_DATE: - case DTK_NUMBER: - val = strtol(field[i], &cp, 10); - - if (type == IGNORE_DTF) - type = DTK_SECOND; - - if (*cp == '.') - { - fval = strtod(cp, &cp); - if (*cp != '\0') - return -1; - - if (val < 0) - fval = -(fval); - } - else if (*cp == '\0') - fval = 0; - else - return -1; - - tmask = 0; /* DTK_M(type); */ - - switch (type) - { - case DTK_MICROSEC: -#ifdef HAVE_INT64_TIMESTAMP - *fsec += (val + fval); -#else - *fsec += ((val + fval) * 1e-6); -#endif - break; - - case DTK_MILLISEC: -#ifdef HAVE_INT64_TIMESTAMP - *fsec += ((val + fval) * 1000); -#else - *fsec += ((val + fval) * 1e-3); -#endif - break; - - case DTK_SECOND: - tm->tm_sec += val; -#ifdef HAVE_INT64_TIMESTAMP - *fsec += (fval * 1000000); -#else - *fsec += fval; -#endif - tmask = DTK_M(SECOND); - break; - - case DTK_MINUTE: - tm->tm_min += val; - if (fval != 0) - { - int sec; - fval *= 60; - sec = fval; - tm->tm_sec += sec; -#ifdef HAVE_INT64_TIMESTAMP - *fsec += ((fval - sec) * 1000000); -#else - *fsec += (fval - sec); -#endif - } - tmask = DTK_M(MINUTE); - break; - - case DTK_HOUR: - tm->tm_hour += val; - if (fval != 0) - { - int sec; - fval *= 3600; - sec = fval; - tm->tm_sec += sec; -#ifdef HAVE_INT64_TIMESTAMP - *fsec += ((fval - sec) * 1000000); -#else - *fsec += (fval - sec); -#endif - } - tmask = DTK_M(HOUR); - break; - - case DTK_DAY: - tm->tm_mday += val; - if (fval != 0) - { - int sec; - fval *= 86400; - sec = fval; - tm->tm_sec += sec; -#ifdef HAVE_INT64_TIMESTAMP - *fsec += ((fval - sec) * 1000000); -#else - *fsec += (fval - sec); -#endif - } - tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY)); - break; - - case DTK_WEEK: - tm->tm_mday += val * 7; - if (fval != 0) - { - int sec; - fval *= (7*86400); - sec = fval; - tm->tm_sec += sec; -#ifdef HAVE_INT64_TIMESTAMP - *fsec += ((fval - sec) * 1000000); -#else - *fsec += (fval - sec); -#endif - } - tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY)); - break; - - case DTK_MONTH: - tm->tm_mon += val; - if (fval != 0) - { - int sec; - fval *= (30*86400); - sec = fval; - tm->tm_sec += sec; -#ifdef HAVE_INT64_TIMESTAMP - *fsec += ((fval - sec) * 1000000); -#else - *fsec += (fval - sec); -#endif - } - tmask = DTK_M(MONTH); - break; - - case DTK_YEAR: - tm->tm_year += val; - if (fval != 0) - tm->tm_mon += (fval * 12); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - case DTK_DECADE: - tm->tm_year += val * 10; - if (fval != 0) - tm->tm_mon += (fval * 120); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - case DTK_CENTURY: - tm->tm_year += val * 100; - if (fval != 0) - tm->tm_mon += (fval * 1200); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - case DTK_MILLENNIUM: - tm->tm_year += val * 1000; - if (fval != 0) - tm->tm_mon += (fval * 12000); - tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); - break; - - default: - return -1; - } - break; - - case DTK_STRING: - case DTK_SPECIAL: - type = DecodeUnits(i, field[i], &val); - if (type == IGNORE_DTF) - continue; - - tmask = 0; /* DTK_M(type); */ - switch (type) - { - case UNITS: - type = val; - break; - - case AGO: - is_before = TRUE; - type = val; - break; - - case RESERV: - tmask = (DTK_DATE_M || DTK_TIME_M); - *dtype = val; - break; - - default: - return -1; - } - break; - - default: - return -1; - } - - if (tmask & fmask) - return -1; - fmask |= tmask; - } - - if (*fsec != 0) - { - int sec; - -#ifdef HAVE_INT64_TIMESTAMP - sec = (*fsec / INT64CONST(1000000)); - *fsec -= (sec * INT64CONST(1000000)); -#else - TMODULO(*fsec, sec, 1e0); -#endif - tm->tm_sec += sec; - } - - if (is_before) - { - *fsec = -(*fsec); - tm->tm_sec = -(tm->tm_sec); - tm->tm_min = -(tm->tm_min); - tm->tm_hour = -(tm->tm_hour); - tm->tm_mday = -(tm->tm_mday); - tm->tm_mon = -(tm->tm_mon); - tm->tm_year = -(tm->tm_year); - } - - /* ensure that at least one time field has been found */ - return (fmask != 0) ? 0 : -1; -} /* DecodeInterval() */ - - -/* DecodeUnits() - * Decode text string using lookup table. - * This routine supports time interval decoding. - */ -int -DecodeUnits(int field, char *lowtoken, int *val) -{ - int type; - datetkn *tp; - - if ((deltacache[field] != NULL) - && (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)) - tp = deltacache[field]; - else - tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl); - deltacache[field] = tp; - if (tp == NULL) - { - type = UNKNOWN_FIELD; - *val = 0; - } - else - { - type = tp->type; - if ((type == TZ) || (type == DTZ)) - *val = FROMVAL(tp); - else - *val = tp->value; - } - - return type; -} /* DecodeUnits() */ - - -/* datebsearch() - * 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) -{ - datetkn *last = base + nel - 1, - *position; - int result; - - while (last >= base) - { - position = base + ((last - base) >> 1); - result = key[0] - position->token[0]; - if (result == 0) - { - result = strncmp(key, position->token, TOKMAXLEN); - if (result == 0) - return position; - } - if (result < 0) - last = position - 1; - else - base = position + 1; - } - return NULL; -} - - -/* EncodeDateOnly() - * Encode date as local time. - */ -int -EncodeDateOnly(struct tm * tm, int style, char *str) -{ - if ((tm->tm_mon < 1) || (tm->tm_mon > 12)) - return -1; - - switch (style) - { - case USE_ISO_DATES: - /* compatible with ISO date formats */ - if (tm->tm_year > 0) - sprintf(str, "%04d-%02d-%02d", - tm->tm_year, tm->tm_mon, tm->tm_mday); - else - sprintf(str, "%04d-%02d-%02d %s", - -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC"); - break; - - case USE_SQL_DATES: - /* compatible with Oracle/Ingres date formats */ - if (EuroDates) - sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon); - else - sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday); - if (tm->tm_year > 0) - sprintf((str + 5), "/%04d", tm->tm_year); - else - sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC"); - break; - - case USE_GERMAN_DATES: - /* German-style date format */ - sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon); - if (tm->tm_year > 0) - sprintf((str + 5), ".%04d", tm->tm_year); - else - sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC"); - break; - - case USE_POSTGRES_DATES: - default: - /* traditional date-only style for Postgres */ - if (EuroDates) - sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon); - else - sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday); - if (tm->tm_year > 0) - sprintf((str + 5), "-%04d", tm->tm_year); - else - sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC"); - break; - } - - return TRUE; -} /* EncodeDateOnly() */ - - -/* EncodeTimeOnly() - * Encode time fields only. - */ -int -EncodeTimeOnly(struct tm * tm, fsec_t fsec, int *tzp, int style, char *str) -{ -#ifndef HAVE_INT64_TIMESTAMP - fsec_t sec; -#endif - - if ((tm->tm_hour < 0) || (tm->tm_hour > 24)) - return -1; - -#ifndef HAVE_INT64_TIMESTAMP - sec = (tm->tm_sec + fsec); -#endif - - sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min); - - /* - * If we have fractional seconds, then include a decimal point We will - * do up to 6 fractional digits, and we have rounded any inputs to - * eliminate anything to the right of 6 digits anyway. If there are no - * fractional seconds, then do not bother printing a decimal point at - * all. - thomas 2001-09-29 - */ - if (fsec != 0) - { -#ifdef HAVE_INT64_TIMESTAMP - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - sprintf((str + strlen(str)), ".%06d", fsec); -#else - sprintf((str + strlen(str)), ":%013.10f", sec); -#endif - /* chop off trailing pairs of zeros... */ - while ((strcmp((str + strlen(str) - 2), "00") == 0) - && (*(str + strlen(str) - 3) != '.')) - *(str + strlen(str) - 2) = '\0'; - } - else -#ifdef HAVE_INT64_TIMESTAMP - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); -#else - sprintf((str + strlen(str)), ":%02.0f", sec); -#endif - - if (tzp != NULL) - { - int hour, - min; - - hour = -(*tzp / 3600); - min = ((abs(*tzp) / 60) % 60); - sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min); - } - - return TRUE; -} /* EncodeTimeOnly() */ - - -/* EncodeDateTime() - * Encode date and time interpreted as local time. - * Support several date styles: - * Postgres - day mon hh:mm:ss yyyy tz - * SQL - mm/dd/yyyy hh:mm:ss.ss tz - * ISO - yyyy-mm-dd hh:mm:ss+/-tz - * German - dd.mm.yyyy hh:mm:ss tz - * Variants (affects order of month and day for Postgres and SQL styles): - * US - mm/dd/yyyy - * European - dd/mm/yyyy - */ -int -EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, char *str) -{ - int day, - hour, - min; -#ifndef HAVE_INT64_TIMESTAMP - fsec_t sec; -#endif - - /* Why are we checking only the month field? Change this to an assert... - * if ((tm->tm_mon < 1) || (tm->tm_mon > 12)) - * return -1; - */ - Assert((tm->tm_mon >= 1) && (tm->tm_mon <= 12)); - -#ifndef HAVE_INT64_TIMESTAMP - sec = (tm->tm_sec + fsec); -#endif - - switch (style) - { - case USE_ISO_DATES: - /* Compatible with ISO-8601 date formats */ - - sprintf(str, "%04d-%02d-%02d %02d:%02d", - ((tm->tm_year > 0)? tm->tm_year: -(tm->tm_year - 1)), - tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min); - - /* - * If we have fractional seconds, then include a decimal - * point We will do up to 6 fractional digits, and we have - * rounded any inputs to eliminate anything to the right - * of 6 digits anyway. If there are no fractional seconds, - * then do not bother printing a decimal point at all. - - * thomas 2001-09-29 - */ -#ifdef HAVE_INT64_TIMESTAMP - if (fsec != 0) - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - sprintf((str + strlen(str)), ".%06d", fsec); -#else - if ((fsec != 0) && (tm->tm_year > 0)) - { - sprintf((str + strlen(str)), ":%013.10f", sec); -#endif - TrimTrailingZeros(str); - } - else - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - } - - if (tm->tm_year <= 0) - { - sprintf((str + strlen(str)), " BC"); - } - - /* - * tzp == NULL indicates that we don't want *any* time - * zone info in the output string. - * *tzn != NULL indicates that we have alpha time zone - * info available. - * tm_isdst != -1 indicates that we have a valid time zone - * translation. - */ - if ((tzp != NULL) && (tm->tm_isdst >= 0)) - { - hour = -(*tzp / 3600); - min = ((abs(*tzp) / 60) % 60); - sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min); - } - break; - - case USE_SQL_DATES: - /* Compatible with Oracle/Ingres date formats */ - - if (EuroDates) - sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon); - else - sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday); - - sprintf((str + 5), "/%04d %02d:%02d", - ((tm->tm_year > 0)? tm->tm_year: -(tm->tm_year - 1)), - tm->tm_hour, tm->tm_min); - - /* - * If we have fractional seconds, then include a decimal - * point We will do up to 6 fractional digits, and we have - * rounded any inputs to eliminate anything to the right - * of 6 digits anyway. If there are no fractional seconds, - * then do not bother printing a decimal point at all. - - * thomas 2001-09-29 - */ -#ifdef HAVE_INT64_TIMESTAMP - if (fsec != 0) - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - sprintf((str + strlen(str)), ".%06d", fsec); -#else - if ((fsec != 0) && (tm->tm_year > 0)) - { - sprintf((str + strlen(str)), ":%013.10f", sec); -#endif - TrimTrailingZeros(str); - } - else - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - } - - if (tm->tm_year <= 0) - { - sprintf((str + strlen(str)), " BC"); - } - - if ((tzp != NULL) && (tm->tm_isdst >= 0)) - { - if (*tzn != NULL) - sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn); - else - { - hour = -(*tzp / 3600); - min = ((abs(*tzp) / 60) % 60); - sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min); - } - } - break; - - case USE_GERMAN_DATES: - /* German variant on European style */ - - sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon); - - sprintf((str + 5), ".%04d %02d:%02d", - ((tm->tm_year > 0)? tm->tm_year: -(tm->tm_year - 1)), - tm->tm_hour, tm->tm_min); - - /* - * If we have fractional seconds, then include a decimal - * point We will do up to 6 fractional digits, and we have - * rounded any inputs to eliminate anything to the right - * of 6 digits anyway. If there are no fractional seconds, - * then do not bother printing a decimal point at all. - - * thomas 2001-09-29 - */ -#ifdef HAVE_INT64_TIMESTAMP - if (fsec != 0) - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - sprintf((str + strlen(str)), ".%06d", fsec); -#else - if ((fsec != 0) && (tm->tm_year > 0)) - { - sprintf((str + strlen(str)), ":%013.10f", sec); -#endif - TrimTrailingZeros(str); - } - else - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - } - - if (tm->tm_year <= 0) - { - sprintf((str + strlen(str)), " BC"); - } - - if ((tzp != NULL) && (tm->tm_isdst >= 0)) - { - if (*tzn != NULL) - sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn); - else - { - hour = -(*tzp / 3600); - min = ((abs(*tzp) / 60) % 60); - sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min); - } - } - break; - - case USE_POSTGRES_DATES: - default: - /* Backward-compatible with traditional Postgres abstime dates */ - - day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); - tm->tm_wday = j2day(day); - - strncpy(str, days[tm->tm_wday], 3); - strcpy((str + 3), " "); - - if (EuroDates) - sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]); - else - sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday); - - sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min); - - /* - * If we have fractional seconds, then include a decimal - * point We will do up to 6 fractional digits, and we have - * rounded any inputs to eliminate anything to the right - * of 6 digits anyway. If there are no fractional seconds, - * then do not bother printing a decimal point at all. - - * thomas 2001-09-29 - */ -#ifdef HAVE_INT64_TIMESTAMP - if (fsec != 0) - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - sprintf((str + strlen(str)), ".%06d", fsec); -#else - if ((fsec != 0) && (tm->tm_year > 0)) - { - sprintf((str + strlen(str)), ":%013.10f", sec); -#endif - TrimTrailingZeros(str); - } - else - { - sprintf((str + strlen(str)), ":%02d", tm->tm_sec); - } - - sprintf((str + strlen(str)), " %04d", - ((tm->tm_year > 0)? tm->tm_year: -(tm->tm_year - 1))); - if (tm->tm_year <= 0) - { - sprintf((str + strlen(str)), " BC"); - } - - if ((tzp != NULL) && (tm->tm_isdst >= 0)) - { - if (*tzn != NULL) - sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn); - else - { - /* - * We have a time zone, but no string version. Use - * the numeric form, but be sure to include a - * leading space to avoid formatting something - * which would be rejected by the date/time parser - * later. - thomas 2001-10-19 - */ - hour = -(*tzp / 3600); - min = ((abs(*tzp) / 60) % 60); - sprintf((str + strlen(str)), ((min != 0) ? " %+03d:%02d" : " %+03d"), hour, min); - } - } - break; - } - - return TRUE; -} /* EncodeDateTime() */ - - -/* EncodeInterval() - * Interpret time structure as a delta time and convert to string. - * - * Support "traditional Postgres" and ISO-8601 styles. - * Actually, afaik ISO does not address time interval formatting, - * but this looks similar to the spec for absolute date/time. - * - thomas 1998-04-30 - */ -int -EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str) -{ - int is_before = FALSE; - int is_nonzero = FALSE; - char *cp = str; - - /* - * The sign of year and month are guaranteed to match, since they are - * stored internally as "month". But we'll need to check for is_before - * and is_nonzero when determining the signs of hour/minute/seconds - * fields. - */ - switch (style) - { - /* compatible with ISO date formats */ - case USE_ISO_DATES: - if (tm->tm_year != 0) - { - sprintf(cp, "%d year%s", - tm->tm_year, ((tm->tm_year != 1) ? "s" : "")); - cp += strlen(cp); - is_before = (tm->tm_year < 0); - is_nonzero = TRUE; - } - - if (tm->tm_mon != 0) - { - sprintf(cp, "%s%s%d mon%s", (is_nonzero ? " " : ""), - ((is_before && (tm->tm_mon > 0)) ? "+" : ""), - tm->tm_mon, ((tm->tm_mon != 1) ? "s" : "")); - cp += strlen(cp); - is_before = (tm->tm_mon < 0); - is_nonzero = TRUE; - } - - if (tm->tm_mday != 0) - { - sprintf(cp, "%s%s%d day%s", (is_nonzero ? " " : ""), - ((is_before && (tm->tm_mday > 0)) ? "+" : ""), - tm->tm_mday, ((tm->tm_mday != 1) ? "s" : "")); - cp += strlen(cp); - is_before = (tm->tm_mday < 0); - is_nonzero = TRUE; - } - if ((!is_nonzero) || (tm->tm_hour != 0) || (tm->tm_min != 0) - || (tm->tm_sec != 0) || (fsec != 0)) - { - int minus = ((tm->tm_hour < 0) || (tm->tm_min < 0) - || (tm->tm_sec < 0) || (fsec < 0)); - - sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""), - (minus ? "-" : (is_before ? "+" : "")), - abs(tm->tm_hour), abs(tm->tm_min)); - cp += strlen(cp); - /* Mark as "non-zero" since the fields are now filled in */ - is_nonzero = TRUE; - - /* fractional seconds? */ - if (fsec != 0) - { -#ifdef HAVE_INT64_TIMESTAMP - sprintf(cp, ":%02d", abs(tm->tm_sec)); - cp += strlen(cp); - sprintf(cp, ".%06d", ((fsec >= 0)? fsec: -(fsec))); -#else - fsec += tm->tm_sec; - sprintf(cp, ":%013.10f", fabs(fsec)); -#endif - TrimTrailingZeros(cp); - cp += strlen(cp); - is_nonzero = TRUE; - } - /* otherwise, integer seconds only? */ - else if (tm->tm_sec != 0) - { - sprintf(cp, ":%02d", abs(tm->tm_sec)); - cp += strlen(cp); - is_nonzero = TRUE; - } - } - break; - - case USE_POSTGRES_DATES: - default: - strcpy(cp, "@ "); - cp += strlen(cp); - - if (tm->tm_year != 0) - { - int year = tm->tm_year; - - if (tm->tm_year < 0) - year = -year; - - sprintf(cp, "%d year%s", year, - ((year != 1) ? "s" : "")); - cp += strlen(cp); - is_before = (tm->tm_year < 0); - is_nonzero = TRUE; - } - - if (tm->tm_mon != 0) - { - int mon = tm->tm_mon; - - if (is_before || ((!is_nonzero) && (tm->tm_mon < 0))) - mon = -mon; - - sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""), mon, - ((mon != 1) ? "s" : "")); - cp += strlen(cp); - if (!is_nonzero) - is_before = (tm->tm_mon < 0); - is_nonzero = TRUE; - } - - if (tm->tm_mday != 0) - { - int day = tm->tm_mday; - - if (is_before || ((!is_nonzero) && (tm->tm_mday < 0))) - day = -day; - - sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""), day, - ((day != 1) ? "s" : "")); - cp += strlen(cp); - if (!is_nonzero) - is_before = (tm->tm_mday < 0); - is_nonzero = TRUE; - } - if (tm->tm_hour != 0) - { - int hour = tm->tm_hour; - - if (is_before || ((!is_nonzero) && (tm->tm_hour < 0))) - hour = -hour; - - sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""), hour, - ((hour != 1) ? "s" : "")); - cp += strlen(cp); - if (!is_nonzero) - is_before = (tm->tm_hour < 0); - is_nonzero = TRUE; - } - - if (tm->tm_min != 0) - { - int min = tm->tm_min; - - if (is_before || ((!is_nonzero) && (tm->tm_min < 0))) - min = -min; - - sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""), min, - ((min != 1) ? "s" : "")); - cp += strlen(cp); - if (!is_nonzero) - is_before = (tm->tm_min < 0); - is_nonzero = TRUE; - } - - /* fractional seconds? */ - if (fsec != 0) - { -#ifdef HAVE_INT64_TIMESTAMP - if (is_before || ((!is_nonzero) && (tm->tm_sec < 0))) - tm->tm_sec = -tm->tm_sec; - sprintf(cp, "%s%d.%02d secs", (is_nonzero ? " " : ""), - tm->tm_sec, (((int) fsec) / 10000)); - cp += strlen(cp); - if (!is_nonzero) - is_before = (fsec < 0); -#else - fsec_t sec; - - fsec += tm->tm_sec; - sec = fsec; - if (is_before || ((!is_nonzero) && (fsec < 0))) - sec = -sec; - - sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), sec); - cp += strlen(cp); - if (!is_nonzero) - is_before = (fsec < 0); -#endif - is_nonzero = TRUE; - - /* otherwise, integer seconds only? */ - } - else if (tm->tm_sec != 0) - { - int sec = tm->tm_sec; - - if (is_before || ((!is_nonzero) && (tm->tm_sec < 0))) - sec = -sec; - - sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""), sec, - ((sec != 1) ? "s" : "")); - cp += strlen(cp); - if (!is_nonzero) - is_before = (tm->tm_sec < 0); - is_nonzero = TRUE; - } - break; - } - - /* identically zero? then put in a unitless zero... */ - if (!is_nonzero) - { - strcat(cp, "0"); - cp += strlen(cp); - } - - if (is_before && (style == USE_POSTGRES_DATES)) - { - strcat(cp, " ago"); - cp += strlen(cp); - } - - return 0; -} /* EncodeInterval() */ - - -/* GUC assign_hook for australian_timezones */ -bool -ClearDateCache(bool newval, bool doit, bool interactive) -{ - int i; - - if (doit) - { - for (i = 0; i < MAXDATEFIELDS; i++) - datecache[i] = NULL; - } - - return true; -} |