Header And Logo

PostgreSQL
| The world's most advanced open source database.

dt_common.c

Go to the documentation of this file.
00001 /* src/interfaces/ecpg/pgtypeslib/dt_common.c */
00002 
00003 #include "postgres_fe.h"
00004 
00005 #include <time.h>
00006 #include <ctype.h>
00007 #include <math.h>
00008 
00009 #include "extern.h"
00010 #include "dt.h"
00011 #include "pgtypes_timestamp.h"
00012 
00013 int         day_tab[2][13] = {
00014     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
00015 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
00016 
00017 typedef long AbsoluteTime;
00018 
00019 #define ABS_SIGNBIT             ((char) 0200)
00020 #define POS(n)                  (n)
00021 #define NEG(n)                  ((n)|ABS_SIGNBIT)
00022 #define FROMVAL(tp)             (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
00023 #define VALMASK                 ((char) 0177)
00024 #define SIGNEDCHAR(c)   ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
00025 
00026 static datetkn datetktbl[] = {
00027 /*  text, token, lexval */
00028     {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
00029     {"acsst", DTZ, POS(42)},    /* Cent. Australia */
00030     {"acst", DTZ, NEG(16)},     /* Atlantic/Porto Acre */
00031     {"act", TZ, NEG(20)},       /* Atlantic/Porto Acre */
00032     {DA_D, ADBC, AD},           /* "ad" for years >= 0 */
00033     {"adt", DTZ, NEG(12)},      /* Atlantic Daylight Time */
00034     {"aesst", DTZ, POS(44)},    /* E. Australia */
00035     {"aest", TZ, POS(40)},      /* Australia Eastern Std Time */
00036     {"aft", TZ, POS(18)},       /* Kabul */
00037     {"ahst", TZ, NEG(40)},      /* Alaska-Hawaii Std Time */
00038     {"akdt", DTZ, NEG(32)},     /* Alaska Daylight Time */
00039     {"akst", DTZ, NEG(36)},     /* Alaska Standard Time */
00040     {"allballs", RESERV, DTK_ZULU},     /* 00:00:00 */
00041     {"almst", TZ, POS(28)},     /* Almaty Savings Time */
00042     {"almt", TZ, POS(24)},      /* Almaty Time */
00043     {"am", AMPM, AM},
00044     {"amst", DTZ, POS(20)},     /* Armenia Summer Time (Yerevan) */
00045 #if 0
00046     {"amst", DTZ, NEG(12)},     /* Porto Velho */
00047 #endif
00048     {"amt", TZ, POS(16)},       /* Armenia Time (Yerevan) */
00049     {"anast", DTZ, POS(52)},    /* Anadyr Summer Time (Russia) */
00050     {"anat", TZ, POS(48)},      /* Anadyr Time (Russia) */
00051     {"apr", MONTH, 4},
00052     {"april", MONTH, 4},
00053 #if 0
00054     aqtst
00055     aqtt
00056     arst
00057 #endif
00058     {"art", TZ, NEG(12)},       /* Argentina Time */
00059 #if 0
00060     ashst
00061     ast                         /* Atlantic Standard Time, Arabia Standard
00062                                  * Time, Acre Standard Time */
00063 #endif
00064     {"ast", TZ, NEG(16)},       /* Atlantic Std Time (Canada) */
00065     {"at", IGNORE_DTF, 0},      /* "at" (throwaway) */
00066     {"aug", MONTH, 8},
00067     {"august", MONTH, 8},
00068     {"awsst", DTZ, POS(36)},    /* W. Australia */
00069     {"awst", TZ, POS(32)},      /* W. Australia */
00070     {"awt", DTZ, NEG(12)},
00071     {"azost", DTZ, POS(0)},     /* Azores Summer Time */
00072     {"azot", TZ, NEG(4)},       /* Azores Time */
00073     {"azst", DTZ, POS(20)},     /* Azerbaijan Summer Time */
00074     {"azt", TZ, POS(16)},       /* Azerbaijan Time */
00075     {DB_C, ADBC, BC},           /* "bc" for years < 0 */
00076     {"bdst", TZ, POS(8)},       /* British Double Summer Time */
00077     {"bdt", TZ, POS(24)},       /* Dacca */
00078     {"bnt", TZ, POS(32)},       /* Brunei Darussalam Time */
00079     {"bort", TZ, POS(32)},      /* Borneo Time (Indonesia) */
00080 #if 0
00081     bortst
00082     bost
00083 #endif
00084     {"bot", TZ, NEG(16)},       /* Bolivia Time */
00085     {"bra", TZ, NEG(12)},       /* Brazil Time */
00086 #if 0
00087     brst
00088     brt
00089 #endif
00090     {"bst", DTZ, POS(4)},       /* British Summer Time */
00091 #if 0
00092     {"bst", TZ, NEG(12)},       /* Brazil Standard Time */
00093     {"bst", DTZ, NEG(44)},      /* Bering Summer Time */
00094 #endif
00095     {"bt", TZ, POS(12)},        /* Baghdad Time */
00096     {"btt", TZ, POS(24)},       /* Bhutan Time */
00097     {"cadt", DTZ, POS(42)},     /* Central Australian DST */
00098     {"cast", TZ, POS(38)},      /* Central Australian ST */
00099     {"cat", TZ, NEG(40)},       /* Central Alaska Time */
00100     {"cct", TZ, POS(32)},       /* China Coast Time */
00101 #if 0
00102     {"cct", TZ, POS(26)},       /* Indian Cocos (Island) Time */
00103 #endif
00104     {"cdt", DTZ, NEG(20)},      /* Central Daylight Time */
00105     {"cest", DTZ, POS(8)},      /* Central European Dayl.Time */
00106     {"cet", TZ, POS(4)},        /* Central European Time */
00107     {"cetdst", DTZ, POS(8)},    /* Central European Dayl.Time */
00108     {"chadt", DTZ, POS(55)},    /* Chatham Island Daylight Time (13:45) */
00109     {"chast", TZ, POS(51)},     /* Chatham Island Time (12:45) */
00110 #if 0
00111     ckhst
00112 #endif
00113     {"ckt", TZ, POS(48)},       /* Cook Islands Time */
00114     {"clst", DTZ, NEG(12)},     /* Chile Summer Time */
00115     {"clt", TZ, NEG(16)},       /* Chile Time */
00116 #if 0
00117     cost
00118 #endif
00119     {"cot", TZ, NEG(20)},       /* Columbia Time */
00120     {"cst", TZ, NEG(24)},       /* Central Standard Time */
00121     {DCURRENT, RESERV, DTK_CURRENT},    /* "current" is always now */
00122 #if 0
00123     cvst
00124 #endif
00125     {"cvt", TZ, POS(28)},       /* Christmas Island Time (Indian Ocean) */
00126     {"cxt", TZ, POS(28)},       /* Christmas Island Time (Indian Ocean) */
00127     {"d", UNITS, DTK_DAY},      /* "day of month" for ISO input */
00128     {"davt", TZ, POS(28)},      /* Davis Time (Antarctica) */
00129     {"ddut", TZ, POS(40)},      /* Dumont-d'Urville Time (Antarctica) */
00130     {"dec", MONTH, 12},
00131     {"december", MONTH, 12},
00132     {"dnt", TZ, POS(4)},        /* Dansk Normal Tid */
00133     {"dow", RESERV, DTK_DOW},   /* day of week */
00134     {"doy", RESERV, DTK_DOY},   /* day of year */
00135     {"dst", DTZMOD, 6},
00136 #if 0
00137     {"dusst", DTZ, POS(24)},    /* Dushanbe Summer Time */
00138 #endif
00139     {"easst", DTZ, NEG(20)},    /* Easter Island Summer Time */
00140     {"east", TZ, NEG(24)},      /* Easter Island Time */
00141     {"eat", TZ, POS(12)},       /* East Africa Time */
00142 #if 0
00143     {"east", DTZ, POS(16)},     /* Indian Antananarivo Savings Time */
00144     {"eat", TZ, POS(12)},       /* Indian Antananarivo Time */
00145     {"ect", TZ, NEG(16)},       /* Eastern Caribbean Time */
00146     {"ect", TZ, NEG(20)},       /* Ecuador Time */
00147 #endif
00148     {"edt", DTZ, NEG(16)},      /* Eastern Daylight Time */
00149     {"eest", DTZ, POS(12)},     /* Eastern Europe Summer Time */
00150     {"eet", TZ, POS(8)},        /* East. Europe, USSR Zone 1 */
00151     {"eetdst", DTZ, POS(12)},   /* Eastern Europe Daylight Time */
00152     {"egst", DTZ, POS(0)},      /* East Greenland Summer Time */
00153     {"egt", TZ, NEG(4)},        /* East Greenland Time */
00154 #if 0
00155     ehdt
00156 #endif
00157     {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
00158     {"est", TZ, NEG(20)},       /* Eastern Standard Time */
00159     {"feb", MONTH, 2},
00160     {"february", MONTH, 2},
00161     {"fjst", DTZ, NEG(52)},     /* Fiji Summer Time (13 hour offset!) */
00162     {"fjt", TZ, NEG(48)},       /* Fiji Time */
00163     {"fkst", DTZ, NEG(12)},     /* Falkland Islands Summer Time */
00164     {"fkt", TZ, NEG(8)},        /* Falkland Islands Time */
00165 #if 0
00166     fnst
00167     fnt
00168 #endif
00169     {"fri", DOW, 5},
00170     {"friday", DOW, 5},
00171     {"fst", TZ, POS(4)},        /* French Summer Time */
00172     {"fwt", DTZ, POS(8)},       /* French Winter Time  */
00173     {"galt", TZ, NEG(24)},      /* Galapagos Time */
00174     {"gamt", TZ, NEG(36)},      /* Gambier Time */
00175     {"gest", DTZ, POS(20)},     /* Georgia Summer Time */
00176     {"get", TZ, POS(16)},       /* Georgia Time */
00177     {"gft", TZ, NEG(12)},       /* French Guiana Time */
00178 #if 0
00179     ghst
00180 #endif
00181     {"gilt", TZ, POS(48)},      /* Gilbert Islands Time */
00182     {"gmt", TZ, POS(0)},        /* Greenwish Mean Time */
00183     {"gst", TZ, POS(40)},       /* Guam Std Time, USSR Zone 9 */
00184     {"gyt", TZ, NEG(16)},       /* Guyana Time */
00185     {"h", UNITS, DTK_HOUR},     /* "hour" */
00186 #if 0
00187     hadt
00188     hast
00189 #endif
00190     {"hdt", DTZ, NEG(36)},      /* Hawaii/Alaska Daylight Time */
00191 #if 0
00192     hkst
00193 #endif
00194     {"hkt", TZ, POS(32)},       /* Hong Kong Time */
00195 #if 0
00196     {"hmt", TZ, POS(12)},       /* Hellas ? ? */
00197     hovst
00198     hovt
00199 #endif
00200     {"hst", TZ, NEG(40)},       /* Hawaii Std Time */
00201 #if 0
00202     hwt
00203 #endif
00204     {"ict", TZ, POS(28)},       /* Indochina Time */
00205     {"idle", TZ, POS(48)},      /* Intl. Date Line, East */
00206     {"idlw", TZ, NEG(48)},      /* Intl. Date Line, West */
00207 #if 0
00208     idt                         /* Israeli, Iran, Indian Daylight Time */
00209 #endif
00210     {LATE, RESERV, DTK_LATE},   /* "infinity" reserved for "late time" */
00211     {INVALID, RESERV, DTK_INVALID},     /* "invalid" reserved for bad time */
00212     {"iot", TZ, POS(20)},       /* Indian Chagos Time */
00213     {"irkst", DTZ, POS(36)},    /* Irkutsk Summer Time */
00214     {"irkt", TZ, POS(32)},      /* Irkutsk Time */
00215     {"irt", TZ, POS(14)},       /* Iran Time */
00216     {"isodow", RESERV, DTK_ISODOW},     /* ISO day of week, Sunday == 7 */
00217 #if 0
00218     isst
00219 #endif
00220     {"ist", TZ, POS(8)},        /* Israel */
00221     {"it", TZ, POS(14)},        /* Iran Time */
00222     {"j", UNITS, DTK_JULIAN},
00223     {"jan", MONTH, 1},
00224     {"january", MONTH, 1},
00225     {"javt", TZ, POS(28)},      /* Java Time (07:00? see JT) */
00226     {"jayt", TZ, POS(36)},      /* Jayapura Time (Indonesia) */
00227     {"jd", UNITS, DTK_JULIAN},
00228     {"jst", TZ, POS(36)},       /* Japan Std Time,USSR Zone 8 */
00229     {"jt", TZ, POS(30)},        /* Java Time (07:30? see JAVT) */
00230     {"jul", MONTH, 7},
00231     {"julian", UNITS, DTK_JULIAN},
00232     {"july", MONTH, 7},
00233     {"jun", MONTH, 6},
00234     {"june", MONTH, 6},
00235     {"kdt", DTZ, POS(40)},      /* Korea Daylight Time */
00236     {"kgst", DTZ, POS(24)},     /* Kyrgyzstan Summer Time */
00237     {"kgt", TZ, POS(20)},       /* Kyrgyzstan Time */
00238     {"kost", TZ, POS(48)},      /* Kosrae Time */
00239     {"krast", DTZ, POS(28)},    /* Krasnoyarsk Summer Time */
00240     {"krat", TZ, POS(32)},      /* Krasnoyarsk Standard Time */
00241     {"kst", TZ, POS(36)},       /* Korea Standard Time */
00242     {"lhdt", DTZ, POS(44)},     /* Lord Howe Daylight Time, Australia */
00243     {"lhst", TZ, POS(42)},      /* Lord Howe Standard Time, Australia */
00244     {"ligt", TZ, POS(40)},      /* From Melbourne, Australia */
00245     {"lint", TZ, POS(56)},      /* Line Islands Time (Kiribati; +14 hours!) */
00246     {"lkt", TZ, POS(24)},       /* Lanka Time */
00247     {"m", UNITS, DTK_MONTH},    /* "month" for ISO input */
00248     {"magst", DTZ, POS(48)},    /* Magadan Summer Time */
00249     {"magt", TZ, POS(44)},      /* Magadan Time */
00250     {"mar", MONTH, 3},
00251     {"march", MONTH, 3},
00252     {"mart", TZ, NEG(38)},      /* Marquesas Time */
00253     {"mawt", TZ, POS(24)},      /* Mawson, Antarctica */
00254     {"may", MONTH, 5},
00255     {"mdt", DTZ, NEG(24)},      /* Mountain Daylight Time */
00256     {"mest", DTZ, POS(8)},      /* Middle Europe Summer Time */
00257     {"met", TZ, POS(4)},        /* Middle Europe Time */
00258     {"metdst", DTZ, POS(8)},    /* Middle Europe Daylight Time */
00259     {"mewt", TZ, POS(4)},       /* Middle Europe Winter Time */
00260     {"mez", TZ, POS(4)},        /* Middle Europe Zone */
00261     {"mht", TZ, POS(48)},       /* Kwajalein */
00262     {"mm", UNITS, DTK_MINUTE},  /* "minute" for ISO input */
00263     {"mmt", TZ, POS(26)},       /* Myannar Time */
00264     {"mon", DOW, 1},
00265     {"monday", DOW, 1},
00266 #if 0
00267     most
00268 #endif
00269     {"mpt", TZ, POS(40)},       /* North Mariana Islands Time */
00270     {"msd", DTZ, POS(16)},      /* Moscow Summer Time */
00271     {"msk", TZ, POS(12)},       /* Moscow Time */
00272     {"mst", TZ, NEG(28)},       /* Mountain Standard Time */
00273     {"mt", TZ, POS(34)},        /* Moluccas Time */
00274     {"mut", TZ, POS(16)},       /* Mauritius Island Time */
00275     {"mvt", TZ, POS(20)},       /* Maldives Island Time */
00276     {"myt", TZ, POS(32)},       /* Malaysia Time */
00277 #if 0
00278     ncst
00279 #endif
00280     {"nct", TZ, POS(44)},       /* New Caledonia Time */
00281     {"ndt", DTZ, NEG(10)},      /* Nfld. Daylight Time */
00282     {"nft", TZ, NEG(14)},       /* Newfoundland Standard Time */
00283     {"nor", TZ, POS(4)},        /* Norway Standard Time */
00284     {"nov", MONTH, 11},
00285     {"november", MONTH, 11},
00286     {"novst", DTZ, POS(28)},    /* Novosibirsk Summer Time */
00287     {"novt", TZ, POS(24)},      /* Novosibirsk Standard Time */
00288     {NOW, RESERV, DTK_NOW},     /* current transaction time */
00289     {"npt", TZ, POS(23)},       /* Nepal Standard Time (GMT-5:45) */
00290     {"nst", TZ, NEG(14)},       /* Nfld. Standard Time */
00291     {"nt", TZ, NEG(44)},        /* Nome Time */
00292     {"nut", TZ, NEG(44)},       /* Niue Time */
00293     {"nzdt", DTZ, POS(52)},     /* New Zealand Daylight Time */
00294     {"nzst", TZ, POS(48)},      /* New Zealand Standard Time */
00295     {"nzt", TZ, POS(48)},       /* New Zealand Time */
00296     {"oct", MONTH, 10},
00297     {"october", MONTH, 10},
00298     {"omsst", DTZ, POS(28)},    /* Omsk Summer Time */
00299     {"omst", TZ, POS(24)},      /* Omsk Time */
00300     {"on", IGNORE_DTF, 0},      /* "on" (throwaway) */
00301     {"pdt", DTZ, NEG(28)},      /* Pacific Daylight Time */
00302 #if 0
00303     pest
00304 #endif
00305     {"pet", TZ, NEG(20)},       /* Peru Time */
00306     {"petst", DTZ, POS(52)},    /* Petropavlovsk-Kamchatski Summer Time */
00307     {"pett", TZ, POS(48)},      /* Petropavlovsk-Kamchatski Time */
00308     {"pgt", TZ, POS(40)},       /* Papua New Guinea Time */
00309     {"phot", TZ, POS(52)},      /* Phoenix Islands (Kiribati) Time */
00310 #if 0
00311     phst
00312 #endif
00313     {"pht", TZ, POS(32)},       /* Philippine Time */
00314     {"pkt", TZ, POS(20)},       /* Pakistan Time */
00315     {"pm", AMPM, PM},
00316     {"pmdt", DTZ, NEG(8)},      /* Pierre & Miquelon Daylight Time */
00317 #if 0
00318     pmst
00319 #endif
00320     {"pont", TZ, POS(44)},      /* Ponape Time (Micronesia) */
00321     {"pst", TZ, NEG(32)},       /* Pacific Standard Time */
00322     {"pwt", TZ, POS(36)},       /* Palau Time */
00323     {"pyst", DTZ, NEG(12)},     /* Paraguay Summer Time */
00324     {"pyt", TZ, NEG(16)},       /* Paraguay Time */
00325     {"ret", DTZ, POS(16)},      /* Reunion Island Time */
00326     {"s", UNITS, DTK_SECOND},   /* "seconds" for ISO input */
00327     {"sadt", DTZ, POS(42)},     /* S. Australian Dayl. Time */
00328 #if 0
00329     samst
00330     samt
00331 #endif
00332     {"sast", TZ, POS(38)},      /* South Australian Std Time */
00333     {"sat", DOW, 6},
00334     {"saturday", DOW, 6},
00335 #if 0
00336     sbt
00337 #endif
00338     {"sct", DTZ, POS(16)},      /* Mahe Island Time */
00339     {"sep", MONTH, 9},
00340     {"sept", MONTH, 9},
00341     {"september", MONTH, 9},
00342     {"set", TZ, NEG(4)},        /* Seychelles Time ?? */
00343 #if 0
00344     sgt
00345 #endif
00346     {"sst", DTZ, POS(8)},       /* Swedish Summer Time */
00347     {"sun", DOW, 0},
00348     {"sunday", DOW, 0},
00349     {"swt", TZ, POS(4)},        /* Swedish Winter Time */
00350 #if 0
00351     syot
00352 #endif
00353     {"t", ISOTIME, DTK_TIME},   /* Filler for ISO time fields */
00354     {"tft", TZ, POS(20)},       /* Kerguelen Time */
00355     {"that", TZ, NEG(40)},      /* Tahiti Time */
00356     {"thu", DOW, 4},
00357     {"thur", DOW, 4},
00358     {"thurs", DOW, 4},
00359     {"thursday", DOW, 4},
00360     {"tjt", TZ, POS(20)},       /* Tajikistan Time */
00361     {"tkt", TZ, NEG(40)},       /* Tokelau Time */
00362     {"tmt", TZ, POS(20)},       /* Turkmenistan Time */
00363     {TODAY, RESERV, DTK_TODAY}, /* midnight */
00364     {TOMORROW, RESERV, DTK_TOMORROW},   /* tomorrow midnight */
00365 #if 0
00366     tost
00367 #endif
00368     {"tot", TZ, POS(52)},       /* Tonga Time */
00369 #if 0
00370     tpt
00371 #endif
00372     {"truk", TZ, POS(40)},      /* Truk Time */
00373     {"tue", DOW, 2},
00374     {"tues", DOW, 2},
00375     {"tuesday", DOW, 2},
00376     {"tvt", TZ, POS(48)},       /* Tuvalu Time */
00377 #if 0
00378     uct
00379 #endif
00380     {"ulast", DTZ, POS(36)},    /* Ulan Bator Summer Time */
00381     {"ulat", TZ, POS(32)},      /* Ulan Bator Time */
00382     {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
00383     {"ut", TZ, POS(0)},
00384     {"utc", TZ, POS(0)},
00385     {"uyst", DTZ, NEG(8)},      /* Uruguay Summer Time */
00386     {"uyt", TZ, NEG(12)},       /* Uruguay Time */
00387     {"uzst", DTZ, POS(24)},     /* Uzbekistan Summer Time */
00388     {"uzt", TZ, POS(20)},       /* Uzbekistan Time */
00389     {"vet", TZ, NEG(16)},       /* Venezuela Time */
00390     {"vlast", DTZ, POS(44)},    /* Vladivostok Summer Time */
00391     {"vlat", TZ, POS(40)},      /* Vladivostok Time */
00392 #if 0
00393     vust
00394 #endif
00395     {"vut", TZ, POS(44)},       /* Vanuata Time */
00396     {"wadt", DTZ, POS(32)},     /* West Australian DST */
00397     {"wakt", TZ, POS(48)},      /* Wake Time */
00398 #if 0
00399     warst
00400 #endif
00401     {"wast", TZ, POS(28)},      /* West Australian Std Time */
00402     {"wat", TZ, NEG(4)},        /* West Africa Time */
00403     {"wdt", DTZ, POS(36)},      /* West Australian DST */
00404     {"wed", DOW, 3},
00405     {"wednesday", DOW, 3},
00406     {"weds", DOW, 3},
00407     {"west", DTZ, POS(4)},      /* Western Europe Summer Time */
00408     {"wet", TZ, POS(0)},        /* Western Europe */
00409     {"wetdst", DTZ, POS(4)},    /* Western Europe Daylight Savings Time */
00410     {"wft", TZ, POS(48)},       /* Wallis and Futuna Time */
00411     {"wgst", DTZ, NEG(8)},      /* West Greenland Summer Time */
00412     {"wgt", TZ, NEG(12)},       /* West Greenland Time */
00413     {"wst", TZ, POS(32)},       /* West Australian Standard Time */
00414     {"y", UNITS, DTK_YEAR},     /* "year" for ISO input */
00415     {"yakst", DTZ, POS(40)},    /* Yakutsk Summer Time */
00416     {"yakt", TZ, POS(36)},      /* Yakutsk Time */
00417     {"yapt", TZ, POS(40)},      /* Yap Time (Micronesia) */
00418     {"ydt", DTZ, NEG(32)},      /* Yukon Daylight Time */
00419     {"yekst", DTZ, POS(24)},    /* Yekaterinburg Summer Time */
00420     {"yekt", TZ, POS(20)},      /* Yekaterinburg Time */
00421     {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
00422     {"yst", TZ, NEG(36)},       /* Yukon Standard Time */
00423     {"z", TZ, POS(0)},          /* time zone tag per ISO-8601 */
00424     {"zp4", TZ, NEG(16)},       /* UTC +4  hours. */
00425     {"zp5", TZ, NEG(20)},       /* UTC +5  hours. */
00426     {"zp6", TZ, NEG(24)},       /* UTC +6  hours. */
00427     {ZULU, TZ, POS(0)},         /* UTC */
00428 };
00429 
00430 static datetkn deltatktbl[] = {
00431     /* text, token, lexval */
00432     {"@", IGNORE_DTF, 0},       /* postgres relative prefix */
00433     {DAGO, AGO, 0},             /* "ago" indicates negative time offset */
00434     {"c", UNITS, DTK_CENTURY},  /* "century" relative */
00435     {"cent", UNITS, DTK_CENTURY},       /* "century" relative */
00436     {"centuries", UNITS, DTK_CENTURY},  /* "centuries" relative */
00437     {DCENTURY, UNITS, DTK_CENTURY},     /* "century" relative */
00438     {"d", UNITS, DTK_DAY},      /* "day" relative */
00439     {DDAY, UNITS, DTK_DAY},     /* "day" relative */
00440     {"days", UNITS, DTK_DAY},   /* "days" relative */
00441     {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
00442     {DDECADE, UNITS, DTK_DECADE},       /* "decade" relative */
00443     {"decades", UNITS, DTK_DECADE},     /* "decades" relative */
00444     {"decs", UNITS, DTK_DECADE},    /* "decades" relative */
00445     {"h", UNITS, DTK_HOUR},     /* "hour" relative */
00446     {DHOUR, UNITS, DTK_HOUR},   /* "hour" relative */
00447     {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
00448     {"hr", UNITS, DTK_HOUR},    /* "hour" relative */
00449     {"hrs", UNITS, DTK_HOUR},   /* "hours" relative */
00450     {INVALID, RESERV, DTK_INVALID},     /* reserved for invalid time */
00451     {"m", UNITS, DTK_MINUTE},   /* "minute" relative */
00452     {"microsecon", UNITS, DTK_MICROSEC},        /* "microsecond" relative */
00453     {"mil", UNITS, DTK_MILLENNIUM},     /* "millennium" relative */
00454     {"millennia", UNITS, DTK_MILLENNIUM},       /* "millennia" relative */
00455     {DMILLENNIUM, UNITS, DTK_MILLENNIUM},       /* "millennium" relative */
00456     {"millisecon", UNITS, DTK_MILLISEC},        /* relative */
00457     {"mils", UNITS, DTK_MILLENNIUM},    /* "millennia" relative */
00458     {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
00459     {"mins", UNITS, DTK_MINUTE},    /* "minutes" relative */
00460     {DMINUTE, UNITS, DTK_MINUTE},       /* "minute" relative */
00461     {"minutes", UNITS, DTK_MINUTE},     /* "minutes" relative */
00462     {"mon", UNITS, DTK_MONTH},  /* "months" relative */
00463     {"mons", UNITS, DTK_MONTH}, /* "months" relative */
00464     {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
00465     {"months", UNITS, DTK_MONTH},
00466     {"ms", UNITS, DTK_MILLISEC},
00467     {"msec", UNITS, DTK_MILLISEC},
00468     {DMILLISEC, UNITS, DTK_MILLISEC},
00469     {"mseconds", UNITS, DTK_MILLISEC},
00470     {"msecs", UNITS, DTK_MILLISEC},
00471     {"qtr", UNITS, DTK_QUARTER},    /* "quarter" relative */
00472     {DQUARTER, UNITS, DTK_QUARTER},     /* "quarter" relative */
00473     {"s", UNITS, DTK_SECOND},
00474     {"sec", UNITS, DTK_SECOND},
00475     {DSECOND, UNITS, DTK_SECOND},
00476     {"seconds", UNITS, DTK_SECOND},
00477     {"secs", UNITS, DTK_SECOND},
00478     {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
00479     {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
00480     {"timezone_m", UNITS, DTK_TZ_MINUTE},       /* timezone minutes units */
00481     {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
00482     {"us", UNITS, DTK_MICROSEC},    /* "microsecond" relative */
00483     {"usec", UNITS, DTK_MICROSEC},      /* "microsecond" relative */
00484     {DMICROSEC, UNITS, DTK_MICROSEC},   /* "microsecond" relative */
00485     {"useconds", UNITS, DTK_MICROSEC},  /* "microseconds" relative */
00486     {"usecs", UNITS, DTK_MICROSEC},     /* "microseconds" relative */
00487     {"w", UNITS, DTK_WEEK},     /* "week" relative */
00488     {DWEEK, UNITS, DTK_WEEK},   /* "week" relative */
00489     {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
00490     {"y", UNITS, DTK_YEAR},     /* "year" relative */
00491     {DYEAR, UNITS, DTK_YEAR},   /* "year" relative */
00492     {"years", UNITS, DTK_YEAR}, /* "years" relative */
00493     {"yr", UNITS, DTK_YEAR},    /* "year" relative */
00494     {"yrs", UNITS, DTK_YEAR},   /* "years" relative */
00495 };
00496 
00497 static const unsigned int szdatetktbl = lengthof(datetktbl);
00498 static const unsigned int szdeltatktbl = lengthof(deltatktbl);
00499 
00500 static datetkn *datecache[MAXDATEFIELDS] = {NULL};
00501 
00502 static datetkn *deltacache[MAXDATEFIELDS] = {NULL};
00503 
00504 char       *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
00505 
00506 char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL};
00507 
00508 char       *pgtypes_date_weekdays_short[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
00509 
00510 char       *pgtypes_date_months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL};
00511 
00512 static datetkn *
00513 datebsearch(char *key, datetkn *base, unsigned int nel)
00514 {
00515     if (nel > 0)
00516     {
00517         datetkn    *last = base + nel - 1,
00518                    *position;
00519         int         result;
00520 
00521         while (last >= base)
00522         {
00523             position = base + ((last - base) >> 1);
00524             result = key[0] - position->token[0];
00525             if (result == 0)
00526             {
00527                 result = strncmp(key, position->token, TOKMAXLEN);
00528                 if (result == 0)
00529                     return position;
00530             }
00531             if (result < 0)
00532                 last = position - 1;
00533             else
00534                 base = position + 1;
00535         }
00536     }
00537     return NULL;
00538 }
00539 
00540 /* DecodeUnits()
00541  * Decode text string using lookup table.
00542  * This routine supports time interval decoding.
00543  */
00544 int
00545 DecodeUnits(int field, char *lowtoken, int *val)
00546 {
00547     int         type;
00548     datetkn    *tp;
00549 
00550     if (deltacache[field] != NULL &&
00551         strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)
00552         tp = deltacache[field];
00553     else
00554         tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
00555     deltacache[field] = tp;
00556     if (tp == NULL)
00557     {
00558         type = UNKNOWN_FIELD;
00559         *val = 0;
00560     }
00561     else
00562     {
00563         type = tp->type;
00564         if (type == TZ || type == DTZ)
00565             *val = FROMVAL(tp);
00566         else
00567             *val = tp->value;
00568     }
00569 
00570     return type;
00571 }   /* DecodeUnits() */
00572 
00573 /*
00574  * Calendar time to Julian date conversions.
00575  * Julian date is commonly used in astronomical applications,
00576  *  since it is numerically accurate and computationally simple.
00577  * The algorithms here will accurately convert between Julian day
00578  *  and calendar date for all non-negative Julian days
00579  *  (i.e. from Nov 24, -4713 on).
00580  *
00581  * These routines will be used by other date/time packages
00582  * - thomas 97/02/25
00583  *
00584  * Rewritten to eliminate overflow problems. This now allows the
00585  * routines to work correctly for all Julian day counts from
00586  * 0 to 2147483647  (Nov 24, -4713 to Jun 3, 5874898) assuming
00587  * a 32-bit integer. Longer types should also work to the limits
00588  * of their precision.
00589  */
00590 
00591 int
00592 date2j(int y, int m, int d)
00593 {
00594     int         julian;
00595     int         century;
00596 
00597     if (m > 2)
00598     {
00599         m += 1;
00600         y += 4800;
00601     }
00602     else
00603     {
00604         m += 13;
00605         y += 4799;
00606     }
00607 
00608     century = y / 100;
00609     julian = y * 365 - 32167;
00610     julian += y / 4 - century + century / 4;
00611     julian += 7834 * m / 256 + d;
00612 
00613     return julian;
00614 }   /* date2j() */
00615 
00616 void
00617 j2date(int jd, int *year, int *month, int *day)
00618 {
00619     unsigned int julian;
00620     unsigned int quad;
00621     unsigned int extra;
00622     int         y;
00623 
00624     julian = jd;
00625     julian += 32044;
00626     quad = julian / 146097;
00627     extra = (julian - quad * 146097) * 4 + 3;
00628     julian += 60 + quad * 3 + extra / 146097;
00629     quad = julian / 1461;
00630     julian -= quad * 1461;
00631     y = julian * 4 / 1461;
00632     julian = ((y != 0) ? (julian + 305) % 365 : (julian + 306) % 366) + 123;
00633     y += quad * 4;
00634     *year = y - 4800;
00635     quad = julian * 2141 / 65536;
00636     *day = julian - 7834 * quad / 256;
00637     *month = (quad + 10) % 12 + 1;
00638 
00639     return;
00640 }   /* j2date() */
00641 
00642 /* DecodeSpecial()
00643  * Decode text string using lookup table.
00644  * Implement a cache lookup since it is likely that dates
00645  *  will be related in format.
00646  */
00647 static int
00648 DecodeSpecial(int field, char *lowtoken, int *val)
00649 {
00650     int         type;
00651     datetkn    *tp;
00652 
00653     if (datecache[field] != NULL &&
00654         strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)
00655         tp = datecache[field];
00656     else
00657     {
00658         tp = NULL;
00659         if (!tp)
00660             tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
00661     }
00662     datecache[field] = tp;
00663     if (tp == NULL)
00664     {
00665         type = UNKNOWN_FIELD;
00666         *val = 0;
00667     }
00668     else
00669     {
00670         type = tp->type;
00671         switch (type)
00672         {
00673             case TZ:
00674             case DTZ:
00675             case DTZMOD:
00676                 *val = FROMVAL(tp);
00677                 break;
00678 
00679             default:
00680                 *val = tp->value;
00681                 break;
00682         }
00683     }
00684 
00685     return type;
00686 }   /* DecodeSpecial() */
00687 
00688 /* EncodeDateOnly()
00689  * Encode date as local time.
00690  */
00691 int
00692 EncodeDateOnly(struct tm * tm, int style, char *str, bool EuroDates)
00693 {
00694     if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
00695         return -1;
00696 
00697     switch (style)
00698     {
00699         case USE_ISO_DATES:
00700             /* compatible with ISO date formats */
00701             if (tm->tm_year > 0)
00702                 sprintf(str, "%04d-%02d-%02d",
00703                         tm->tm_year, tm->tm_mon, tm->tm_mday);
00704             else
00705                 sprintf(str, "%04d-%02d-%02d %s",
00706                         -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
00707             break;
00708 
00709         case USE_SQL_DATES:
00710             /* compatible with Oracle/Ingres date formats */
00711             if (EuroDates)
00712                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
00713             else
00714                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
00715             if (tm->tm_year > 0)
00716                 sprintf(str + 5, "/%04d", tm->tm_year);
00717             else
00718                 sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
00719             break;
00720 
00721         case USE_GERMAN_DATES:
00722             /* German-style date format */
00723             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
00724             if (tm->tm_year > 0)
00725                 sprintf(str + 5, ".%04d", tm->tm_year);
00726             else
00727                 sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
00728             break;
00729 
00730         case USE_POSTGRES_DATES:
00731         default:
00732             /* traditional date-only style for Postgres */
00733             if (EuroDates)
00734                 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
00735             else
00736                 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
00737             if (tm->tm_year > 0)
00738                 sprintf(str + 5, "-%04d", tm->tm_year);
00739             else
00740                 sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
00741             break;
00742     }
00743 
00744     return TRUE;
00745 }   /* EncodeDateOnly() */
00746 
00747 void
00748 TrimTrailingZeros(char *str)
00749 {
00750     int         len = strlen(str);
00751 
00752     /* chop off trailing zeros... but leave at least 2 fractional digits */
00753     while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
00754     {
00755         len--;
00756         *(str + len) = '\0';
00757     }
00758 }
00759 
00760 /* EncodeDateTime()
00761  * Encode date and time interpreted as local time.
00762  *
00763  * tm and fsec are the value to encode, print_tz determines whether to include
00764  * a time zone (the difference between timestamp and timestamptz types), tz is
00765  * the numeric time zone offset, tzn is the textual time zone, which if
00766  * specified will be used instead of tz by some styles, style is the date
00767  * style, str is where to write the output.
00768  *
00769  * Supported date styles:
00770  *  Postgres - day mon hh:mm:ss yyyy tz
00771  *  SQL - mm/dd/yyyy hh:mm:ss.ss tz
00772  *  ISO - yyyy-mm-dd hh:mm:ss+/-tz
00773  *  German - dd.mm.yyyy hh:mm:ss tz
00774  * Variants (affects order of month and day for Postgres and SQL styles):
00775  *  US - mm/dd/yyyy
00776  *  European - dd/mm/yyyy
00777  */
00778 int
00779 EncodeDateTime(struct tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates)
00780 {
00781     int         day,
00782                 hour,
00783                 min;
00784 
00785     /*
00786      * Negative tm_isdst means we have no valid time zone translation.
00787      */
00788     if (tm->tm_isdst < 0)
00789         print_tz = false;
00790 
00791     switch (style)
00792     {
00793         case USE_ISO_DATES:
00794             /* Compatible with ISO-8601 date formats */
00795 
00796             sprintf(str, "%04d-%02d-%02d %02d:%02d",
00797                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
00798                     tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
00799 
00800             /*
00801              * Print fractional seconds if any.  The field widths here should
00802              * be at least equal to MAX_TIMESTAMP_PRECISION.
00803              *
00804              * In float mode, don't print fractional seconds before 1 AD,
00805              * since it's unlikely there's any precision left ...
00806              */
00807 #ifdef HAVE_INT64_TIMESTAMP
00808             if (fsec != 0)
00809             {
00810                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
00811 #else
00812             if ((fsec != 0) && (tm->tm_year > 0))
00813             {
00814                 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
00815 #endif
00816                 TrimTrailingZeros(str);
00817             }
00818             else
00819                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
00820 
00821             if (tm->tm_year <= 0)
00822                 sprintf(str + strlen(str), " BC");
00823 
00824             if (print_tz)
00825             {
00826                 hour = -(tz / SECS_PER_HOUR);
00827                 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
00828                 if (min != 0)
00829                     sprintf(str + strlen(str), "%+03d:%02d", hour, min);
00830                 else
00831                     sprintf(str + strlen(str), "%+03d", hour);
00832             }
00833             break;
00834 
00835         case USE_SQL_DATES:
00836             /* Compatible with Oracle/Ingres date formats */
00837 
00838             if (EuroDates)
00839                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
00840             else
00841                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
00842 
00843             sprintf(str + 5, "/%04d %02d:%02d",
00844                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
00845                     tm->tm_hour, tm->tm_min);
00846 
00847             /*
00848              * Print fractional seconds if any.  The field widths here should
00849              * be at least equal to MAX_TIMESTAMP_PRECISION.
00850              *
00851              * In float mode, don't print fractional seconds before 1 AD,
00852              * since it's unlikely there's any precision left ...
00853              */
00854 #ifdef HAVE_INT64_TIMESTAMP
00855             if (fsec != 0)
00856             {
00857                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
00858 #else
00859             if (fsec != 0 && tm->tm_year > 0)
00860             {
00861                 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
00862 #endif
00863                 TrimTrailingZeros(str);
00864             }
00865             else
00866                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
00867 
00868             if (tm->tm_year <= 0)
00869                 sprintf(str + strlen(str), " BC");
00870 
00871             /*
00872              * Note: the uses of %.*s in this function would be risky if the
00873              * timezone names ever contain non-ASCII characters.  However, all
00874              * TZ abbreviations in the Olson database are plain ASCII.
00875              */
00876 
00877             if (print_tz)
00878             {
00879                 if (tzn)
00880                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
00881                 else
00882                 {
00883                     hour = -(tz / SECS_PER_HOUR);
00884                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
00885                     if (min != 0)
00886                         sprintf(str + strlen(str), "%+03d:%02d", hour, min);
00887                     else
00888                         sprintf(str + strlen(str), "%+03d", hour);
00889                 }
00890             }
00891             break;
00892 
00893         case USE_GERMAN_DATES:
00894             /* German variant on European style */
00895 
00896             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
00897 
00898             sprintf(str + 5, ".%04d %02d:%02d",
00899                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
00900                     tm->tm_hour, tm->tm_min);
00901 
00902             /*
00903              * Print fractional seconds if any.  The field widths here should
00904              * be at least equal to MAX_TIMESTAMP_PRECISION.
00905              *
00906              * In float mode, don't print fractional seconds before 1 AD,
00907              * since it's unlikely there's any precision left ...
00908              */
00909 #ifdef HAVE_INT64_TIMESTAMP
00910             if (fsec != 0)
00911             {
00912                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
00913 #else
00914             if (fsec != 0 && tm->tm_year > 0)
00915             {
00916                 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
00917 #endif
00918                 TrimTrailingZeros(str);
00919             }
00920             else
00921                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
00922 
00923             if (tm->tm_year <= 0)
00924                 sprintf(str + strlen(str), " BC");
00925 
00926             if (print_tz)
00927             {
00928                 if (tzn)
00929                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
00930                 else
00931                 {
00932                     hour = -(tz / SECS_PER_HOUR);
00933                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
00934                     if (min != 0)
00935                         sprintf(str + strlen(str), "%+03d:%02d", hour, min);
00936                     else
00937                         sprintf(str + strlen(str), "%+03d", hour);
00938                 }
00939             }
00940             break;
00941 
00942         case USE_POSTGRES_DATES:
00943         default:
00944             /* Backward-compatible with traditional Postgres abstime dates */
00945 
00946             day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
00947             tm->tm_wday = (int) ((day + date2j(2000, 1, 1) + 1) % 7);
00948 
00949             strncpy(str, days[tm->tm_wday], 3);
00950             strcpy(str + 3, " ");
00951 
00952             if (EuroDates)
00953                 sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
00954             else
00955                 sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
00956 
00957             sprintf(str + 10, " %02d:%02d", tm->tm_hour, tm->tm_min);
00958 
00959             /*
00960              * Print fractional seconds if any.  The field widths here should
00961              * be at least equal to MAX_TIMESTAMP_PRECISION.
00962              *
00963              * In float mode, don't print fractional seconds before 1 AD,
00964              * since it's unlikely there's any precision left ...
00965              */
00966 #ifdef HAVE_INT64_TIMESTAMP
00967             if (fsec != 0)
00968             {
00969                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
00970 #else
00971             if (fsec != 0 && tm->tm_year > 0)
00972             {
00973                 sprintf(str + strlen(str), ":%09.6f", tm->tm_sec + fsec);
00974 #endif
00975                 TrimTrailingZeros(str);
00976             }
00977             else
00978                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
00979 
00980             sprintf(str + strlen(str), " %04d",
00981                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
00982             if (tm->tm_year <= 0)
00983                 sprintf(str + strlen(str), " BC");
00984 
00985             if (print_tz)
00986             {
00987                 if (tzn)
00988                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
00989                 else
00990                 {
00991                     /*
00992                      * We have a time zone, but no string version. Use the
00993                      * numeric form, but be sure to include a leading space to
00994                      * avoid formatting something which would be rejected by
00995                      * the date/time parser later. - thomas 2001-10-19
00996                      */
00997                     hour = -(tz / SECS_PER_HOUR);
00998                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
00999                     if (min != 0)
01000                         sprintf(str + strlen(str), " %+03d:%02d", hour, min);
01001                     else
01002                         sprintf(str + strlen(str), " %+03d", hour);
01003                 }
01004             }
01005             break;
01006     }
01007 
01008     return TRUE;
01009 }   /* EncodeDateTime() */
01010 
01011 int
01012 GetEpochTime(struct tm * tm)
01013 {
01014     struct tm  *t0;
01015     time_t      epoch = 0;
01016 
01017     t0 = gmtime(&epoch);
01018 
01019     if (t0)
01020     {
01021         tm->tm_year = t0->tm_year + 1900;
01022         tm->tm_mon = t0->tm_mon + 1;
01023         tm->tm_mday = t0->tm_mday;
01024         tm->tm_hour = t0->tm_hour;
01025         tm->tm_min = t0->tm_min;
01026         tm->tm_sec = t0->tm_sec;
01027 
01028         return 0;
01029     }
01030 
01031     return -1;
01032 }   /* GetEpochTime() */
01033 
01034 static void
01035 abstime2tm(AbsoluteTime _time, int *tzp, struct tm * tm, char **tzn)
01036 {
01037     time_t      time = (time_t) _time;
01038     struct tm  *tx;
01039 
01040     errno = 0;
01041     if (tzp != NULL)
01042         tx = localtime((time_t *) &time);
01043     else
01044         tx = gmtime((time_t *) &time);
01045 
01046     if (!tx)
01047     {
01048         errno = PGTYPES_TS_BAD_TIMESTAMP;
01049         return;
01050     }
01051 
01052     tm->tm_year = tx->tm_year + 1900;
01053     tm->tm_mon = tx->tm_mon + 1;
01054     tm->tm_mday = tx->tm_mday;
01055     tm->tm_hour = tx->tm_hour;
01056     tm->tm_min = tx->tm_min;
01057     tm->tm_sec = tx->tm_sec;
01058     tm->tm_isdst = tx->tm_isdst;
01059 
01060 #if defined(HAVE_TM_ZONE)
01061     tm->tm_gmtoff = tx->tm_gmtoff;
01062     tm->tm_zone = tx->tm_zone;
01063 
01064     if (tzp != NULL)
01065     {
01066         /*
01067          * We have a brute force time zone per SQL99? Then use it without
01068          * change since we have already rotated to the time zone.
01069          */
01070         *tzp = -tm->tm_gmtoff;  /* tm_gmtoff is Sun/DEC-ism */
01071 
01072         /*
01073          * FreeBSD man pages indicate that this should work - tgl 97/04/23
01074          */
01075         if (tzn != NULL)
01076         {
01077             /*
01078              * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
01079              * contains an error message, which doesn't fit in the buffer
01080              */
01081             StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
01082             if (strlen(tm->tm_zone) > MAXTZLEN)
01083                 tm->tm_isdst = -1;
01084         }
01085     }
01086     else
01087         tm->tm_isdst = -1;
01088 #elif defined(HAVE_INT_TIMEZONE)
01089     if (tzp != NULL)
01090     {
01091         *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
01092 
01093         if (tzn != NULL)
01094         {
01095             /*
01096              * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
01097              * contains an error message, which doesn't fit in the buffer
01098              */
01099             StrNCpy(*tzn, TZNAME_GLOBAL[tm->tm_isdst], MAXTZLEN + 1);
01100             if (strlen(TZNAME_GLOBAL[tm->tm_isdst]) > MAXTZLEN)
01101                 tm->tm_isdst = -1;
01102         }
01103     }
01104     else
01105         tm->tm_isdst = -1;
01106 #else                           /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
01107     if (tzp != NULL)
01108     {
01109         /* default to UTC */
01110         *tzp = 0;
01111         if (tzn != NULL)
01112             *tzn = NULL;
01113     }
01114     else
01115         tm->tm_isdst = -1;
01116 #endif
01117 }
01118 
01119 void
01120 GetCurrentDateTime(struct tm * tm)
01121 {
01122     int         tz;
01123 
01124     abstime2tm(time(NULL), &tz, tm, NULL);
01125 }
01126 
01127 void
01128 dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
01129 {
01130 #ifdef HAVE_INT64_TIMESTAMP
01131     int64       time;
01132 #else
01133     double      time;
01134 #endif
01135 
01136     time = jd;
01137 #ifdef HAVE_INT64_TIMESTAMP
01138     *hour = time / USECS_PER_HOUR;
01139     time -= (*hour) * USECS_PER_HOUR;
01140     *min = time / USECS_PER_MINUTE;
01141     time -= (*min) * USECS_PER_MINUTE;
01142     *sec = time / USECS_PER_SEC;
01143     *fsec = time - (*sec * USECS_PER_SEC);
01144 #else
01145     *hour = time / SECS_PER_HOUR;
01146     time -= (*hour) * SECS_PER_HOUR;
01147     *min = time / SECS_PER_MINUTE;
01148     time -= (*min) * SECS_PER_MINUTE;
01149     *sec = time;
01150     *fsec = time - *sec;
01151 #endif
01152 }   /* dt2time() */
01153 
01154 
01155 
01156 /* DecodeNumberField()
01157  * Interpret numeric string as a concatenated date or time field.
01158  * Use the context of previously decoded fields to help with
01159  * the interpretation.
01160  */
01161 static int
01162 DecodeNumberField(int len, char *str, int fmask,
01163                   int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits)
01164 {
01165     char       *cp;
01166 
01167     /*
01168      * Have a decimal point? Then this is a date or something with a seconds
01169      * field...
01170      */
01171     if ((cp = strchr(str, '.')) != NULL)
01172     {
01173 #ifdef HAVE_INT64_TIMESTAMP
01174         char        fstr[MAXDATELEN + 1];
01175 
01176         /*
01177          * OK, we have at most six digits to care about. Let's construct a
01178          * string and then do the conversion to an integer.
01179          */
01180         strcpy(fstr, (cp + 1));
01181         strcpy(fstr + strlen(fstr), "000000");
01182         *(fstr + 6) = '\0';
01183         *fsec = strtol(fstr, NULL, 10);
01184 #else
01185         *fsec = strtod(cp, NULL);
01186 #endif
01187         *cp = '\0';
01188         len = strlen(str);
01189     }
01190     /* No decimal point and no complete date yet? */
01191     else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
01192     {
01193         /* yyyymmdd? */
01194         if (len == 8)
01195         {
01196             *tmask = DTK_DATE_M;
01197 
01198             tm->tm_mday = atoi(str + 6);
01199             *(str + 6) = '\0';
01200             tm->tm_mon = atoi(str + 4);
01201             *(str + 4) = '\0';
01202             tm->tm_year = atoi(str + 0);
01203 
01204             return DTK_DATE;
01205         }
01206         /* yymmdd? */
01207         else if (len == 6)
01208         {
01209             *tmask = DTK_DATE_M;
01210             tm->tm_mday = atoi(str + 4);
01211             *(str + 4) = '\0';
01212             tm->tm_mon = atoi(str + 2);
01213             *(str + 2) = '\0';
01214             tm->tm_year = atoi(str + 0);
01215             *is2digits = TRUE;
01216 
01217             return DTK_DATE;
01218         }
01219         /* yyddd? */
01220         else if (len == 5)
01221         {
01222             *tmask = DTK_DATE_M;
01223             tm->tm_mday = atoi(str + 2);
01224             *(str + 2) = '\0';
01225             tm->tm_mon = 1;
01226             tm->tm_year = atoi(str + 0);
01227             *is2digits = TRUE;
01228 
01229             return DTK_DATE;
01230         }
01231     }
01232 
01233     /* not all time fields are specified? */
01234     if ((fmask & DTK_TIME_M) != DTK_TIME_M)
01235     {
01236         /* hhmmss */
01237         if (len == 6)
01238         {
01239             *tmask = DTK_TIME_M;
01240             tm->tm_sec = atoi(str + 4);
01241             *(str + 4) = '\0';
01242             tm->tm_min = atoi(str + 2);
01243             *(str + 2) = '\0';
01244             tm->tm_hour = atoi(str + 0);
01245 
01246             return DTK_TIME;
01247         }
01248         /* hhmm? */
01249         else if (len == 4)
01250         {
01251             *tmask = DTK_TIME_M;
01252             tm->tm_sec = 0;
01253             tm->tm_min = atoi(str + 2);
01254             *(str + 2) = '\0';
01255             tm->tm_hour = atoi(str + 0);
01256 
01257             return DTK_TIME;
01258         }
01259     }
01260 
01261     return -1;
01262 }   /* DecodeNumberField() */
01263 
01264 
01265 /* DecodeNumber()
01266  * Interpret plain numeric field as a date value in context.
01267  */
01268 static int
01269 DecodeNumber(int flen, char *str, int fmask,
01270     int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits, bool EuroDates)
01271 {
01272     int         val;
01273     char       *cp;
01274 
01275     *tmask = 0;
01276 
01277     val = strtol(str, &cp, 10);
01278     if (cp == str)
01279         return -1;
01280 
01281     if (*cp == '.')
01282     {
01283         /*
01284          * More than two digits? Then could be a date or a run-together time:
01285          * 2001.360 20011225 040506.789
01286          */
01287         if (cp - str > 2)
01288             return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
01289                                      tmask, tm, fsec, is2digits);
01290 
01291         *fsec = strtod(cp, &cp);
01292         if (*cp != '\0')
01293             return -1;
01294     }
01295     else if (*cp != '\0')
01296         return -1;
01297 
01298     /* Special case day of year? */
01299     if (flen == 3 && (fmask & DTK_M(YEAR)) && val >= 1 && val <= 366)
01300     {
01301         *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
01302         tm->tm_yday = val;
01303         j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
01304                &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
01305     }
01306 
01307     /***
01308      * Enough digits to be unequivocal year? Used to test for 4 digits or
01309      * more, but we now test first for a three-digit doy so anything
01310      * bigger than two digits had better be an explicit year.
01311      * - thomas 1999-01-09
01312      * Back to requiring a 4 digit year. We accept a two digit
01313      * year farther down. - thomas 2000-03-28
01314      ***/
01315     else if (flen >= 4)
01316     {
01317         *tmask = DTK_M(YEAR);
01318 
01319         /* already have a year? then see if we can substitute... */
01320         if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
01321             tm->tm_year >= 1 && tm->tm_year <= 31)
01322         {
01323             tm->tm_mday = tm->tm_year;
01324             *tmask = DTK_M(DAY);
01325         }
01326 
01327         tm->tm_year = val;
01328     }
01329 
01330     /* already have year? then could be month */
01331     else if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
01332     {
01333         *tmask = DTK_M(MONTH);
01334         tm->tm_mon = val;
01335     }
01336     /* no year and EuroDates enabled? then could be day */
01337     else if ((EuroDates || (fmask & DTK_M(MONTH))) &&
01338              !(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
01339              val >= 1 && val <= 31)
01340     {
01341         *tmask = DTK_M(DAY);
01342         tm->tm_mday = val;
01343     }
01344     else if (!(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
01345     {
01346         *tmask = DTK_M(MONTH);
01347         tm->tm_mon = val;
01348     }
01349     else if (!(fmask & DTK_M(DAY)) && val >= 1 && val <= 31)
01350     {
01351         *tmask = DTK_M(DAY);
01352         tm->tm_mday = val;
01353     }
01354 
01355     /*
01356      * Check for 2 or 4 or more digits, but currently we reach here only if
01357      * two digits. - thomas 2000-03-28
01358      */
01359     else if (!(fmask & DTK_M(YEAR)) && (flen >= 4 || flen == 2))
01360     {
01361         *tmask = DTK_M(YEAR);
01362         tm->tm_year = val;
01363 
01364         /* adjust ONLY if exactly two digits... */
01365         *is2digits = (flen == 2);
01366     }
01367     else
01368         return -1;
01369 
01370     return 0;
01371 }   /* DecodeNumber() */
01372 
01373 /* DecodeDate()
01374  * Decode date string which includes delimiters.
01375  * Insist on a complete set of fields.
01376  */
01377 static int
01378 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm, bool EuroDates)
01379 {
01380     fsec_t      fsec;
01381 
01382     int         nf = 0;
01383     int         i,
01384                 len;
01385     int         bc = FALSE;
01386     int         is2digits = FALSE;
01387     int         type,
01388                 val,
01389                 dmask = 0;
01390     char       *field[MAXDATEFIELDS];
01391 
01392     /* parse this string... */
01393     while (*str != '\0' && nf < MAXDATEFIELDS)
01394     {
01395         /* skip field separators */
01396         while (!isalnum((unsigned char) *str))
01397             str++;
01398 
01399         field[nf] = str;
01400         if (isdigit((unsigned char) *str))
01401         {
01402             while (isdigit((unsigned char) *str))
01403                 str++;
01404         }
01405         else if (isalpha((unsigned char) *str))
01406         {
01407             while (isalpha((unsigned char) *str))
01408                 str++;
01409         }
01410 
01411         /* Just get rid of any non-digit, non-alpha characters... */
01412         if (*str != '\0')
01413             *str++ = '\0';
01414         nf++;
01415     }
01416 
01417 #if 0
01418     /* don't allow too many fields */
01419     if (nf > 3)
01420         return -1;
01421 #endif
01422 
01423     *tmask = 0;
01424 
01425     /* look first for text fields, since that will be unambiguous month */
01426     for (i = 0; i < nf; i++)
01427     {
01428         if (isalpha((unsigned char) *field[i]))
01429         {
01430             type = DecodeSpecial(i, field[i], &val);
01431             if (type == IGNORE_DTF)
01432                 continue;
01433 
01434             dmask = DTK_M(type);
01435             switch (type)
01436             {
01437                 case MONTH:
01438                     tm->tm_mon = val;
01439                     break;
01440 
01441                 case ADBC:
01442                     bc = (val == BC);
01443                     break;
01444 
01445                 default:
01446                     return -1;
01447             }
01448             if (fmask & dmask)
01449                 return -1;
01450 
01451             fmask |= dmask;
01452             *tmask |= dmask;
01453 
01454             /* mark this field as being completed */
01455             field[i] = NULL;
01456         }
01457     }
01458 
01459     /* now pick up remaining numeric fields */
01460     for (i = 0; i < nf; i++)
01461     {
01462         if (field[i] == NULL)
01463             continue;
01464 
01465         if ((len = strlen(field[i])) <= 0)
01466             return -1;
01467 
01468         if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
01469             return -1;
01470 
01471         if (fmask & dmask)
01472             return -1;
01473 
01474         fmask |= dmask;
01475         *tmask |= dmask;
01476     }
01477 
01478     if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
01479         return -1;
01480 
01481     /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
01482     if (bc)
01483     {
01484         if (tm->tm_year > 0)
01485             tm->tm_year = -(tm->tm_year - 1);
01486         else
01487             return -1;
01488     }
01489     else if (is2digits)
01490     {
01491         if (tm->tm_year < 70)
01492             tm->tm_year += 2000;
01493         else if (tm->tm_year < 100)
01494             tm->tm_year += 1900;
01495     }
01496 
01497     return 0;
01498 }   /* DecodeDate() */
01499 
01500 
01501 /* DecodeTime()
01502  * Decode time string which includes delimiters.
01503  * Only check the lower limit on hours, since this same code
01504  *  can be used to represent time spans.
01505  */
01506 int
01507 DecodeTime(char *str, int *tmask, struct tm * tm, fsec_t *fsec)
01508 {
01509     char       *cp;
01510 
01511     *tmask = DTK_TIME_M;
01512 
01513     tm->tm_hour = strtol(str, &cp, 10);
01514     if (*cp != ':')
01515         return -1;
01516     str = cp + 1;
01517     tm->tm_min = strtol(str, &cp, 10);
01518     if (*cp == '\0')
01519     {
01520         tm->tm_sec = 0;
01521         *fsec = 0;
01522     }
01523     else if (*cp != ':')
01524         return -1;
01525     else
01526     {
01527         str = cp + 1;
01528         tm->tm_sec = strtol(str, &cp, 10);
01529         if (*cp == '\0')
01530             *fsec = 0;
01531         else if (*cp == '.')
01532         {
01533 #ifdef HAVE_INT64_TIMESTAMP
01534             char        fstr[MAXDATELEN + 1];
01535 
01536             /*
01537              * OK, we have at most six digits to work with. Let's construct a
01538              * string and then do the conversion to an integer.
01539              */
01540             strncpy(fstr, (cp + 1), 7);
01541             strcpy(fstr + strlen(fstr), "000000");
01542             *(fstr + 6) = '\0';
01543             *fsec = strtol(fstr, &cp, 10);
01544 #else
01545             str = cp;
01546             *fsec = strtod(str, &cp);
01547 #endif
01548             if (*cp != '\0')
01549                 return -1;
01550         }
01551         else
01552             return -1;
01553     }
01554 
01555     /* do a sanity check */
01556 #ifdef HAVE_INT64_TIMESTAMP
01557     if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
01558         tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
01559         return -1;
01560 #else
01561     if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
01562         tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= 1)
01563         return -1;
01564 #endif
01565 
01566     return 0;
01567 }   /* DecodeTime() */
01568 
01569 /* DecodeTimezone()
01570  * Interpret string as a numeric timezone.
01571  *
01572  * Note: we allow timezone offsets up to 13:59.  There are places that
01573  * use +1300 summer time.
01574  */
01575 static int
01576 DecodeTimezone(char *str, int *tzp)
01577 {
01578     int         tz;
01579     int         hr,
01580                 min;
01581     char       *cp;
01582     int         len;
01583 
01584     /* assume leading character is "+" or "-" */
01585     hr = strtol(str + 1, &cp, 10);
01586 
01587     /* explicit delimiter? */
01588     if (*cp == ':')
01589         min = strtol(cp + 1, &cp, 10);
01590     /* otherwise, might have run things together... */
01591     else if (*cp == '\0' && (len = strlen(str)) > 3)
01592     {
01593         min = strtol(str + len - 2, &cp, 10);
01594         if (min < 0 || min >= 60)
01595             return -1;
01596 
01597         *(str + len - 2) = '\0';
01598         hr = strtol(str + 1, &cp, 10);
01599         if (hr < 0 || hr > 13)
01600             return -1;
01601     }
01602     else
01603         min = 0;
01604 
01605     tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE;
01606     if (*str == '-')
01607         tz = -tz;
01608 
01609     *tzp = -tz;
01610     return *cp != '\0';
01611 }   /* DecodeTimezone() */
01612 
01613 
01614 /* DecodePosixTimezone()
01615  * Interpret string as a POSIX-compatible timezone:
01616  *  PST-hh:mm
01617  *  PST+h
01618  * - thomas 2000-03-15
01619  */
01620 static int
01621 DecodePosixTimezone(char *str, int *tzp)
01622 {
01623     int         val,
01624                 tz;
01625     int         type;
01626     char       *cp;
01627     char        delim;
01628 
01629     cp = str;
01630     while (*cp != '\0' && isalpha((unsigned char) *cp))
01631         cp++;
01632 
01633     if (DecodeTimezone(cp, &tz) != 0)
01634         return -1;
01635 
01636     delim = *cp;
01637     *cp = '\0';
01638     type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
01639     *cp = delim;
01640 
01641     switch (type)
01642     {
01643         case DTZ:
01644         case TZ:
01645             *tzp = (val * MINS_PER_HOUR) - tz;
01646             break;
01647 
01648         default:
01649             return -1;
01650     }
01651 
01652     return 0;
01653 }   /* DecodePosixTimezone() */
01654 
01655 /* ParseDateTime()
01656  * Break string into tokens based on a date/time context.
01657  * Several field types are assigned:
01658  *  DTK_NUMBER - digits and (possibly) a decimal point
01659  *  DTK_DATE - digits and two delimiters, or digits and text
01660  *  DTK_TIME - digits, colon delimiters, and possibly a decimal point
01661  *  DTK_STRING - text (no digits)
01662  *  DTK_SPECIAL - leading "+" or "-" followed by text
01663  *  DTK_TZ - leading "+" or "-" followed by digits
01664  * Note that some field types can hold unexpected items:
01665  *  DTK_NUMBER can hold date fields (yy.ddd)
01666  *  DTK_STRING can hold months (January) and time zones (PST)
01667  *  DTK_DATE can hold Posix time zones (GMT-8)
01668  */
01669 int
01670 ParseDateTime(char *timestr, char *lowstr,
01671               char **field, int *ftype, int *numfields, char **endstr)
01672 {
01673     int         nf = 0;
01674     char       *lp = lowstr;
01675 
01676     *endstr = timestr;
01677     /* outer loop through fields */
01678     while (*(*endstr) != '\0')
01679     {
01680         field[nf] = lp;
01681 
01682         /* leading digit? then date or time */
01683         if (isdigit((unsigned char) *(*endstr)))
01684         {
01685             *lp++ = *(*endstr)++;
01686             while (isdigit((unsigned char) *(*endstr)))
01687                 *lp++ = *(*endstr)++;
01688 
01689             /* time field? */
01690             if (*(*endstr) == ':')
01691             {
01692                 ftype[nf] = DTK_TIME;
01693                 *lp++ = *(*endstr)++;
01694                 while (isdigit((unsigned char) *(*endstr)) ||
01695                        (*(*endstr) == ':') || (*(*endstr) == '.'))
01696                     *lp++ = *(*endstr)++;
01697             }
01698             /* date field? allow embedded text month */
01699             else if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
01700             {
01701                 /* save delimiting character to use later */
01702                 char       *dp = (*endstr);
01703 
01704                 *lp++ = *(*endstr)++;
01705                 /* second field is all digits? then no embedded text month */
01706                 if (isdigit((unsigned char) *(*endstr)))
01707                 {
01708                     ftype[nf] = (*dp == '.') ? DTK_NUMBER : DTK_DATE;
01709                     while (isdigit((unsigned char) *(*endstr)))
01710                         *lp++ = *(*endstr)++;
01711 
01712                     /*
01713                      * insist that the delimiters match to get a three-field
01714                      * date.
01715                      */
01716                     if (*(*endstr) == *dp)
01717                     {
01718                         ftype[nf] = DTK_DATE;
01719                         *lp++ = *(*endstr)++;
01720                         while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
01721                             *lp++ = *(*endstr)++;
01722                     }
01723                 }
01724                 else
01725                 {
01726                     ftype[nf] = DTK_DATE;
01727                     while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
01728                         *lp++ = pg_tolower((unsigned char) *(*endstr)++);
01729                 }
01730             }
01731 
01732             /*
01733              * otherwise, number only and will determine year, month, day, or
01734              * concatenated fields later...
01735              */
01736             else
01737                 ftype[nf] = DTK_NUMBER;
01738         }
01739         /* Leading decimal point? Then fractional seconds... */
01740         else if (*(*endstr) == '.')
01741         {
01742             *lp++ = *(*endstr)++;
01743             while (isdigit((unsigned char) *(*endstr)))
01744                 *lp++ = *(*endstr)++;
01745 
01746             ftype[nf] = DTK_NUMBER;
01747         }
01748 
01749         /*
01750          * text? then date string, month, day of week, special, or timezone
01751          */
01752         else if (isalpha((unsigned char) *(*endstr)))
01753         {
01754             ftype[nf] = DTK_STRING;
01755             *lp++ = pg_tolower((unsigned char) *(*endstr)++);
01756             while (isalpha((unsigned char) *(*endstr)))
01757                 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
01758 
01759             /*
01760              * Full date string with leading text month? Could also be a POSIX
01761              * time zone...
01762              */
01763             if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
01764             {
01765                 char       *dp = (*endstr);
01766 
01767                 ftype[nf] = DTK_DATE;
01768                 *lp++ = *(*endstr)++;
01769                 while (isdigit((unsigned char) *(*endstr)) || *(*endstr) == *dp)
01770                     *lp++ = *(*endstr)++;
01771             }
01772         }
01773         /* skip leading spaces */
01774         else if (isspace((unsigned char) *(*endstr)))
01775         {
01776             (*endstr)++;
01777             continue;
01778         }
01779         /* sign? then special or numeric timezone */
01780         else if (*(*endstr) == '+' || *(*endstr) == '-')
01781         {
01782             *lp++ = *(*endstr)++;
01783             /* soak up leading whitespace */
01784             while (isspace((unsigned char) *(*endstr)))
01785                 (*endstr)++;
01786             /* numeric timezone? */
01787             if (isdigit((unsigned char) *(*endstr)))
01788             {
01789                 ftype[nf] = DTK_TZ;
01790                 *lp++ = *(*endstr)++;
01791                 while (isdigit((unsigned char) *(*endstr)) ||
01792                        (*(*endstr) == ':') || (*(*endstr) == '.'))
01793                     *lp++ = *(*endstr)++;
01794             }
01795             /* special? */
01796             else if (isalpha((unsigned char) *(*endstr)))
01797             {
01798                 ftype[nf] = DTK_SPECIAL;
01799                 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
01800                 while (isalpha((unsigned char) *(*endstr)))
01801                     *lp++ = pg_tolower((unsigned char) *(*endstr)++);
01802             }
01803             /* otherwise something wrong... */
01804             else
01805                 return -1;
01806         }
01807         /* ignore punctuation but use as delimiter */
01808         else if (ispunct((unsigned char) *(*endstr)))
01809         {
01810             (*endstr)++;
01811             continue;
01812 
01813         }
01814         /* otherwise, something is not right... */
01815         else
01816             return -1;
01817 
01818         /* force in a delimiter after each field */
01819         *lp++ = '\0';
01820         nf++;
01821         if (nf > MAXDATEFIELDS)
01822             return -1;
01823     }
01824 
01825     *numfields = nf;
01826 
01827     return 0;
01828 }   /* ParseDateTime() */
01829 
01830 
01831 /* DecodeDateTime()
01832  * Interpret previously parsed fields for general date and time.
01833  * Return 0 if full date, 1 if only time, and -1 if problems.
01834  *      External format(s):
01835  *              "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
01836  *              "Fri Feb-7-1997 15:23:27"
01837  *              "Feb-7-1997 15:23:27"
01838  *              "2-7-1997 15:23:27"
01839  *              "1997-2-7 15:23:27"
01840  *              "1997.038 15:23:27"     (day of year 1-366)
01841  *      Also supports input in compact time:
01842  *              "970207 152327"
01843  *              "97038 152327"
01844  *              "20011225T040506.789-07"
01845  *
01846  * Use the system-provided functions to get the current time zone
01847  *  if not specified in the input string.
01848  * If the date is outside the time_t system-supported time range,
01849  *  then assume UTC time zone. - thomas 1997-05-27
01850  */
01851 int
01852 DecodeDateTime(char **field, int *ftype, int nf,
01853                int *dtype, struct tm * tm, fsec_t *fsec, bool EuroDates)
01854 {
01855     int         fmask = 0,
01856                 tmask,
01857                 type;
01858     int         ptype = 0;      /* "prefix type" for ISO y2001m02d04 format */
01859     int         i;
01860     int         val;
01861     int         mer = HR24;
01862     int         haveTextMonth = FALSE;
01863     int         is2digits = FALSE;
01864     int         bc = FALSE;
01865     int         t = 0;
01866     int        *tzp = &t;
01867 
01868     /***
01869      * We'll insist on at least all of the date fields, but initialize the
01870      * remaining fields in case they are not set later...
01871      ***/
01872     *dtype = DTK_DATE;
01873     tm->tm_hour = 0;
01874     tm->tm_min = 0;
01875     tm->tm_sec = 0;
01876     *fsec = 0;
01877     /* don't know daylight savings time status apriori */
01878     tm->tm_isdst = -1;
01879     if (tzp != NULL)
01880         *tzp = 0;
01881 
01882     for (i = 0; i < nf; i++)
01883     {
01884         switch (ftype[i])
01885         {
01886             case DTK_DATE:
01887                 /***
01888                  * Integral julian day with attached time zone?
01889                  * All other forms with JD will be separated into
01890                  * distinct fields, so we handle just this case here.
01891                  ***/
01892                 if (ptype == DTK_JULIAN)
01893                 {
01894                     char       *cp;
01895                     int         val;
01896 
01897                     if (tzp == NULL)
01898                         return -1;
01899 
01900                     val = strtol(field[i], &cp, 10);
01901                     if (*cp != '-')
01902                         return -1;
01903 
01904                     j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
01905                     /* Get the time zone from the end of the string */
01906                     if (DecodeTimezone(cp, tzp) != 0)
01907                         return -1;
01908 
01909                     tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
01910                     ptype = 0;
01911                     break;
01912                 }
01913                 /***
01914                  * Already have a date? Then this might be a POSIX time
01915                  * zone with an embedded dash (e.g. "PST-3" == "EST") or
01916                  * a run-together time with trailing time zone (e.g. hhmmss-zz).
01917                  * - thomas 2001-12-25
01918                  ***/
01919                 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
01920                          || (ptype != 0))
01921                 {
01922                     /* No time zone accepted? Then quit... */
01923                     if (tzp == NULL)
01924                         return -1;
01925 
01926                     if (isdigit((unsigned char) *field[i]) || ptype != 0)
01927                     {
01928                         char       *cp;
01929 
01930                         if (ptype != 0)
01931                         {
01932                             /* Sanity check; should not fail this test */
01933                             if (ptype != DTK_TIME)
01934                                 return -1;
01935                             ptype = 0;
01936                         }
01937 
01938                         /*
01939                          * Starts with a digit but we already have a time
01940                          * field? Then we are in trouble with a date and time
01941                          * already...
01942                          */
01943                         if ((fmask & DTK_TIME_M) == DTK_TIME_M)
01944                             return -1;
01945 
01946                         if ((cp = strchr(field[i], '-')) == NULL)
01947                             return -1;
01948 
01949                         /* Get the time zone from the end of the string */
01950                         if (DecodeTimezone(cp, tzp) != 0)
01951                             return -1;
01952                         *cp = '\0';
01953 
01954                         /*
01955                          * Then read the rest of the field as a concatenated
01956                          * time
01957                          */
01958                         if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
01959                                           &tmask, tm, fsec, &is2digits)) < 0)
01960                             return -1;
01961 
01962                         /*
01963                          * modify tmask after returning from
01964                          * DecodeNumberField()
01965                          */
01966                         tmask |= DTK_M(TZ);
01967                     }
01968                     else
01969                     {
01970                         if (DecodePosixTimezone(field[i], tzp) != 0)
01971                             return -1;
01972 
01973                         ftype[i] = DTK_TZ;
01974                         tmask = DTK_M(TZ);
01975                     }
01976                 }
01977                 else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
01978                     return -1;
01979                 break;
01980 
01981             case DTK_TIME:
01982                 if (DecodeTime(field[i], &tmask, tm, fsec) != 0)
01983                     return -1;
01984 
01985                 /*
01986                  * Check upper limit on hours; other limits checked in
01987                  * DecodeTime()
01988                  */
01989                 /* test for > 24:00:00 */
01990                 if (tm->tm_hour > 24 ||
01991                     (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)))
01992                     return -1;
01993                 break;
01994 
01995             case DTK_TZ:
01996                 {
01997                     int         tz;
01998 
01999                     if (tzp == NULL)
02000                         return -1;
02001 
02002                     if (DecodeTimezone(field[i], &tz) != 0)
02003                         return -1;
02004 
02005                     /*
02006                      * Already have a time zone? Then maybe this is the second
02007                      * field of a POSIX time: EST+3 (equivalent to PST)
02008                      */
02009                     if (i > 0 && (fmask & DTK_M(TZ)) != 0 &&
02010                         ftype[i - 1] == DTK_TZ &&
02011                         isalpha((unsigned char) *field[i - 1]))
02012                     {
02013                         *tzp -= tz;
02014                         tmask = 0;
02015                     }
02016                     else
02017                     {
02018                         *tzp = tz;
02019                         tmask = DTK_M(TZ);
02020                     }
02021                 }
02022                 break;
02023 
02024             case DTK_NUMBER:
02025 
02026                 /*
02027                  * Was this an "ISO date" with embedded field labels? An
02028                  * example is "y2001m02d04" - thomas 2001-02-04
02029                  */
02030                 if (ptype != 0)
02031                 {
02032                     char       *cp;
02033                     int         val;
02034 
02035                     val = strtol(field[i], &cp, 10);
02036 
02037                     /*
02038                      * only a few kinds are allowed to have an embedded
02039                      * decimal
02040                      */
02041                     if (*cp == '.')
02042                         switch (ptype)
02043                         {
02044                             case DTK_JULIAN:
02045                             case DTK_TIME:
02046                             case DTK_SECOND:
02047                                 break;
02048                             default:
02049                                 return 1;
02050                                 break;
02051                         }
02052                     else if (*cp != '\0')
02053                         return -1;
02054 
02055                     switch (ptype)
02056                     {
02057                         case DTK_YEAR:
02058                             tm->tm_year = val;
02059                             tmask = DTK_M(YEAR);
02060                             break;
02061 
02062                         case DTK_MONTH:
02063 
02064                             /*
02065                              * already have a month and hour? then assume
02066                              * minutes
02067                              */
02068                             if ((fmask & DTK_M(MONTH)) != 0 &&
02069                                 (fmask & DTK_M(HOUR)) != 0)
02070                             {
02071                                 tm->tm_min = val;
02072                                 tmask = DTK_M(MINUTE);
02073                             }
02074                             else
02075                             {
02076                                 tm->tm_mon = val;
02077                                 tmask = DTK_M(MONTH);
02078                             }
02079                             break;
02080 
02081                         case DTK_DAY:
02082                             tm->tm_mday = val;
02083                             tmask = DTK_M(DAY);
02084                             break;
02085 
02086                         case DTK_HOUR:
02087                             tm->tm_hour = val;
02088                             tmask = DTK_M(HOUR);
02089                             break;
02090 
02091                         case DTK_MINUTE:
02092                             tm->tm_min = val;
02093                             tmask = DTK_M(MINUTE);
02094                             break;
02095 
02096                         case DTK_SECOND:
02097                             tm->tm_sec = val;
02098                             tmask = DTK_M(SECOND);
02099                             if (*cp == '.')
02100                             {
02101                                 double      frac;
02102 
02103                                 frac = strtod(cp, &cp);
02104                                 if (*cp != '\0')
02105                                     return -1;
02106 #ifdef HAVE_INT64_TIMESTAMP
02107                                 *fsec = frac * 1000000;
02108 #else
02109                                 *fsec = frac;
02110 #endif
02111                             }
02112                             break;
02113 
02114                         case DTK_TZ:
02115                             tmask = DTK_M(TZ);
02116                             if (DecodeTimezone(field[i], tzp) != 0)
02117                                 return -1;
02118                             break;
02119 
02120                         case DTK_JULIAN:
02121                             /***
02122                              * previous field was a label for "julian date"?
02123                              ***/
02124                             tmask = DTK_DATE_M;
02125                             j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
02126                             /* fractional Julian Day? */
02127                             if (*cp == '.')
02128                             {
02129                                 double      time;
02130 
02131                                 time = strtod(cp, &cp);
02132                                 if (*cp != '\0')
02133                                     return -1;
02134 
02135                                 tmask |= DTK_TIME_M;
02136 #ifdef HAVE_INT64_TIMESTAMP
02137                                 dt2time((time * USECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
02138 #else
02139                                 dt2time((time * SECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
02140 #endif
02141                             }
02142                             break;
02143 
02144                         case DTK_TIME:
02145                             /* previous field was "t" for ISO time */
02146                             if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
02147                                           &tmask, tm, fsec, &is2digits)) < 0)
02148                                 return -1;
02149 
02150                             if (tmask != DTK_TIME_M)
02151                                 return -1;
02152                             break;
02153 
02154                         default:
02155                             return -1;
02156                             break;
02157                     }
02158 
02159                     ptype = 0;
02160                     *dtype = DTK_DATE;
02161                 }
02162                 else
02163                 {
02164                     char       *cp;
02165                     int         flen;
02166 
02167                     flen = strlen(field[i]);
02168                     cp = strchr(field[i], '.');
02169 
02170                     /* Embedded decimal and no date yet? */
02171                     if (cp != NULL && !(fmask & DTK_DATE_M))
02172                     {
02173                         if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
02174                             return -1;
02175                     }
02176                     /* embedded decimal and several digits before? */
02177                     else if (cp != NULL && flen - strlen(cp) > 2)
02178                     {
02179                         /*
02180                          * Interpret as a concatenated date or time Set the
02181                          * type field to allow decoding other fields later.
02182                          * Example: 20011223 or 040506
02183                          */
02184                         if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
02185                                           &tmask, tm, fsec, &is2digits)) < 0)
02186                             return -1;
02187                     }
02188                     else if (flen > 4)
02189                     {
02190                         if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
02191                                           &tmask, tm, fsec, &is2digits)) < 0)
02192                             return -1;
02193                     }
02194                     /* otherwise it is a single date/time field... */
02195                     else if (DecodeNumber(flen, field[i], fmask,
02196                                &tmask, tm, fsec, &is2digits, EuroDates) != 0)
02197                         return -1;
02198                 }
02199                 break;
02200 
02201             case DTK_STRING:
02202             case DTK_SPECIAL:
02203                 type = DecodeSpecial(i, field[i], &val);
02204                 if (type == IGNORE_DTF)
02205                     continue;
02206 
02207                 tmask = DTK_M(type);
02208                 switch (type)
02209                 {
02210                     case RESERV:
02211                         switch (val)
02212                         {
02213                             case DTK_NOW:
02214                                 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
02215                                 *dtype = DTK_DATE;
02216                                 GetCurrentDateTime(tm);
02217                                 break;
02218 
02219                             case DTK_YESTERDAY:
02220                                 tmask = DTK_DATE_M;
02221                                 *dtype = DTK_DATE;
02222                                 GetCurrentDateTime(tm);
02223                                 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1,
02224                                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
02225                                 tm->tm_hour = 0;
02226                                 tm->tm_min = 0;
02227                                 tm->tm_sec = 0;
02228                                 break;
02229 
02230                             case DTK_TODAY:
02231                                 tmask = DTK_DATE_M;
02232                                 *dtype = DTK_DATE;
02233                                 GetCurrentDateTime(tm);
02234                                 tm->tm_hour = 0;
02235                                 tm->tm_min = 0;
02236                                 tm->tm_sec = 0;
02237                                 break;
02238 
02239                             case DTK_TOMORROW:
02240                                 tmask = DTK_DATE_M;
02241                                 *dtype = DTK_DATE;
02242                                 GetCurrentDateTime(tm);
02243                                 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1,
02244                                     &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
02245                                 tm->tm_hour = 0;
02246                                 tm->tm_min = 0;
02247                                 tm->tm_sec = 0;
02248                                 break;
02249 
02250                             case DTK_ZULU:
02251                                 tmask = (DTK_TIME_M | DTK_M(TZ));
02252                                 *dtype = DTK_DATE;
02253                                 tm->tm_hour = 0;
02254                                 tm->tm_min = 0;
02255                                 tm->tm_sec = 0;
02256                                 if (tzp != NULL)
02257                                     *tzp = 0;
02258                                 break;
02259 
02260                             default:
02261                                 *dtype = val;
02262                         }
02263 
02264                         break;
02265 
02266                     case MONTH:
02267 
02268                         /*
02269                          * already have a (numeric) month? then see if we can
02270                          * substitute...
02271                          */
02272                         if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
02273                             !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && tm->tm_mon <= 31)
02274                         {
02275                             tm->tm_mday = tm->tm_mon;
02276                             tmask = DTK_M(DAY);
02277                         }
02278                         haveTextMonth = TRUE;
02279                         tm->tm_mon = val;
02280                         break;
02281 
02282                     case DTZMOD:
02283 
02284                         /*
02285                          * daylight savings time modifier (solves "MET DST"
02286                          * syntax)
02287                          */
02288                         tmask |= DTK_M(DTZ);
02289                         tm->tm_isdst = 1;
02290                         if (tzp == NULL)
02291                             return -1;
02292                         *tzp += val * MINS_PER_HOUR;
02293                         break;
02294 
02295                     case DTZ:
02296 
02297                         /*
02298                          * set mask for TZ here _or_ check for DTZ later when
02299                          * getting default timezone
02300                          */
02301                         tmask |= DTK_M(TZ);
02302                         tm->tm_isdst = 1;
02303                         if (tzp == NULL)
02304                             return -1;
02305                         *tzp = val * MINS_PER_HOUR;
02306                         ftype[i] = DTK_TZ;
02307                         break;
02308 
02309                     case TZ:
02310                         tm->tm_isdst = 0;
02311                         if (tzp == NULL)
02312                             return -1;
02313                         *tzp = val * MINS_PER_HOUR;
02314                         ftype[i] = DTK_TZ;
02315                         break;
02316 
02317                     case IGNORE_DTF:
02318                         break;
02319 
02320                     case AMPM:
02321                         mer = val;
02322                         break;
02323 
02324                     case ADBC:
02325                         bc = (val == BC);
02326                         break;
02327 
02328                     case DOW:
02329                         tm->tm_wday = val;
02330                         break;
02331 
02332                     case UNITS:
02333                         tmask = 0;
02334                         ptype = val;
02335                         break;
02336 
02337                     case ISOTIME:
02338 
02339                         /*
02340                          * This is a filler field "t" indicating that the next
02341                          * field is time. Try to verify that this is sensible.
02342                          */
02343                         tmask = 0;
02344 
02345                         /* No preceding date? Then quit... */
02346                         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
02347                             return -1;
02348 
02349                         /***
02350                          * We will need one of the following fields:
02351                          *  DTK_NUMBER should be hhmmss.fff
02352                          *  DTK_TIME should be hh:mm:ss.fff
02353                          *  DTK_DATE should be hhmmss-zz
02354                          ***/
02355                         if (i >= nf - 1 ||
02356                             (ftype[i + 1] != DTK_NUMBER &&
02357                              ftype[i + 1] != DTK_TIME &&
02358                              ftype[i + 1] != DTK_DATE))
02359                             return -1;
02360 
02361                         ptype = val;
02362                         break;
02363 
02364                     default:
02365                         return -1;
02366                 }
02367                 break;
02368 
02369             default:
02370                 return -1;
02371         }
02372 
02373         if (tmask & fmask)
02374             return -1;
02375         fmask |= tmask;
02376     }
02377 
02378     /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
02379     if (bc)
02380     {
02381         if (tm->tm_year > 0)
02382             tm->tm_year = -(tm->tm_year - 1);
02383         else
02384             return -1;
02385     }
02386     else if (is2digits)
02387     {
02388         if (tm->tm_year < 70)
02389             tm->tm_year += 2000;
02390         else if (tm->tm_year < 100)
02391             tm->tm_year += 1900;
02392     }
02393 
02394     if (mer != HR24 && tm->tm_hour > 12)
02395         return -1;
02396     if (mer == AM && tm->tm_hour == 12)
02397         tm->tm_hour = 0;
02398     else if (mer == PM && tm->tm_hour != 12)
02399         tm->tm_hour += 12;
02400 
02401     /* do additional checking for full date specs... */
02402     if (*dtype == DTK_DATE)
02403     {
02404         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
02405             return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
02406 
02407         /*
02408          * check for valid day of month, now that we know for sure the month
02409          * and year...
02410          */
02411         if (tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
02412             return -1;
02413 
02414         /*
02415          * backend tried to find local timezone here but we don't use the
02416          * result afterwards anyway so we only check for this error: daylight
02417          * savings time modifier but no standard timezone?
02418          */
02419         if ((fmask & DTK_DATE_M) == DTK_DATE_M && tzp != NULL && !(fmask & DTK_M(TZ)) && (fmask & DTK_M(DTZMOD)))
02420             return -1;
02421     }
02422 
02423     return 0;
02424 }   /* DecodeDateTime() */
02425 
02426 /* Function works as follows:
02427  *
02428  *
02429  * */
02430 
02431 static char *
02432 find_end_token(char *str, char *fmt)
02433 {
02434     /*
02435      * str: here is28the day12the hour fmt: here is%dthe day%hthe hour
02436      *
02437      * we extract the 28, we read the percent sign and the type "d" then this
02438      * functions gets called as find_end_token("28the day12the hour", "the
02439      * day%hthehour")
02440      *
02441      * fmt points to "the day%hthehour", next_percent points to %hthehour and
02442      * we have to find a match for everything between these positions ("the
02443      * day"). We look for "the day" in str and know that the pattern we are
02444      * about to scan ends where this string starts (right after the "28")
02445      *
02446      * At the end, *fmt is '\0' and *str isn't. end_position then is
02447      * unchanged.
02448      */
02449     char       *end_position = NULL;
02450     char       *next_percent,
02451                *subst_location = NULL;
02452     int         scan_offset = 0;
02453     char        last_char;
02454 
02455     /* are we at the end? */
02456     if (!*fmt)
02457     {
02458         end_position = fmt;
02459         return end_position;
02460     }
02461 
02462     /* not at the end */
02463     while (fmt[scan_offset] == '%' && fmt[scan_offset + 1])
02464     {
02465         /*
02466          * there is no delimiter, skip to the next delimiter if we're reading
02467          * a number and then something that is not a number "9:15pm", we might
02468          * be able to recover with the strtol end pointer. Go for the next
02469          * percent sign
02470          */
02471         scan_offset += 2;
02472     }
02473     next_percent = strchr(fmt + scan_offset, '%');
02474     if (next_percent)
02475     {
02476         /*
02477          * we don't want to allocate extra memory, so we temporarily set the
02478          * '%' sign to '\0' and call strstr However since we allow whitespace
02479          * to float around everything, we have to shorten the pattern until we
02480          * reach a non-whitespace character
02481          */
02482 
02483         subst_location = next_percent;
02484         while (*(subst_location - 1) == ' ' && subst_location - 1 > fmt + scan_offset)
02485             subst_location--;
02486         last_char = *subst_location;
02487         *subst_location = '\0';
02488 
02489         /*
02490          * the haystack is the str and the needle is the original fmt but it
02491          * ends at the position where the next percent sign would be
02492          */
02493 
02494         /*
02495          * There is one special case. Imagine: str = " 2", fmt = "%d %...",
02496          * since we want to allow blanks as "dynamic" padding we have to
02497          * accept this. Now, we are called with a fmt of " %..." and look for
02498          * " " in str. We find it at the first position and never read the
02499          * 2...
02500          */
02501         while (*str == ' ')
02502             str++;
02503         end_position = strstr(str, fmt + scan_offset);
02504         *subst_location = last_char;
02505     }
02506     else
02507     {
02508         /*
02509          * there is no other percent sign. So everything up to the end has to
02510          * match.
02511          */
02512         end_position = str + strlen(str);
02513     }
02514     if (!end_position)
02515     {
02516         /*
02517          * maybe we have the following case:
02518          *
02519          * str = "4:15am" fmt = "%M:%S %p"
02520          *
02521          * at this place we could have
02522          *
02523          * str = "15am" fmt = " %p"
02524          *
02525          * and have set fmt to " " because overwrote the % sign with a NULL
02526          *
02527          * In this case where we would have to match a space but can't find
02528          * it, set end_position to the end of the string
02529          */
02530         if ((fmt + scan_offset)[0] == ' ' && fmt + scan_offset + 1 == subst_location)
02531             end_position = str + strlen(str);
02532     }
02533     return end_position;
02534 }
02535 
02536 static int
02537 pgtypes_defmt_scan(union un_fmt_comb * scan_val, int scan_type, char **pstr, char *pfmt)
02538 {
02539     /*
02540      * scan everything between pstr and pstr_end. This is not including the
02541      * last character so we might set it to '\0' for the parsing
02542      */
02543 
02544     char        last_char;
02545     int         err = 0;
02546     char       *pstr_end;
02547     char       *strtol_end = NULL;
02548 
02549     while (**pstr == ' ')
02550         pstr++;
02551     pstr_end = find_end_token(*pstr, pfmt);
02552     if (!pstr_end)
02553     {
02554         /* there was an error, no match */
02555         return 1;
02556     }
02557     last_char = *pstr_end;
02558     *pstr_end = '\0';
02559 
02560     switch (scan_type)
02561     {
02562         case PGTYPES_TYPE_UINT:
02563 
02564             /*
02565              * numbers may be blank-padded, this is the only deviation from
02566              * the fmt-string we accept
02567              */
02568             while (**pstr == ' ')
02569                 (*pstr)++;
02570             errno = 0;
02571             scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
02572             if (errno)
02573                 err = 1;
02574             break;
02575         case PGTYPES_TYPE_UINT_LONG:
02576             while (**pstr == ' ')
02577                 (*pstr)++;
02578             errno = 0;
02579             scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
02580             if (errno)
02581                 err = 1;
02582             break;
02583         case PGTYPES_TYPE_STRING_MALLOCED:
02584             scan_val->str_val = pgtypes_strdup(*pstr);
02585             if (scan_val->str_val == NULL)
02586                 err = 1;
02587             break;
02588     }
02589     if (strtol_end && *strtol_end)
02590         *pstr = strtol_end;
02591     else
02592         *pstr = pstr_end;
02593     *pstr_end = last_char;
02594     return err;
02595 }
02596 
02597 /* XXX range checking */
02598 int PGTYPEStimestamp_defmt_scan(char **, char *, timestamp *, int *, int *, int *,
02599                             int *, int *, int *, int *);
02600 
02601 int
02602 PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
02603                             int *year, int *month, int *day,
02604                             int *hour, int *minute, int *second,
02605                             int *tz)
02606 {
02607     union un_fmt_comb scan_val;
02608     int         scan_type;
02609 
02610     char       *pstr,
02611                *pfmt,
02612                *tmp;
02613     int         err = 1;
02614     unsigned int j;
02615     struct tm   tm;
02616 
02617     pfmt = fmt;
02618     pstr = *str;
02619 
02620     while (*pfmt)
02621     {
02622         err = 0;
02623         while (*pfmt == ' ')
02624             pfmt++;
02625         while (*pstr == ' ')
02626             pstr++;
02627         if (*pfmt != '%')
02628         {
02629             if (*pfmt == *pstr)
02630             {
02631                 pfmt++;
02632                 pstr++;
02633             }
02634             else
02635             {
02636                 /* Error: no match */
02637                 err = 1;
02638                 return err;
02639             }
02640             continue;
02641         }
02642         /* here *pfmt equals '%' */
02643         pfmt++;
02644         switch (*pfmt)
02645         {
02646             case 'a':
02647                 pfmt++;
02648 
02649                 /*
02650                  * we parse the day and see if it is a week day but we do not
02651                  * check if the week day really matches the date
02652                  */
02653                 err = 1;
02654                 j = 0;
02655                 while (pgtypes_date_weekdays_short[j])
02656                 {
02657                     if (strncmp(pgtypes_date_weekdays_short[j], pstr,
02658                                 strlen(pgtypes_date_weekdays_short[j])) == 0)
02659                     {
02660                         /* found it */
02661                         err = 0;
02662                         pstr += strlen(pgtypes_date_weekdays_short[j]);
02663                         break;
02664                     }
02665                     j++;
02666                 }
02667                 break;
02668             case 'A':
02669                 /* see note above */
02670                 pfmt++;
02671                 err = 1;
02672                 j = 0;
02673                 while (days[j])
02674                 {
02675                     if (strncmp(days[j], pstr, strlen(days[j])) == 0)
02676                     {
02677                         /* found it */
02678                         err = 0;
02679                         pstr += strlen(days[j]);
02680                         break;
02681                     }
02682                     j++;
02683                 }
02684                 break;
02685             case 'b':
02686             case 'h':
02687                 pfmt++;
02688                 err = 1;
02689                 j = 0;
02690                 while (months[j])
02691                 {
02692                     if (strncmp(months[j], pstr, strlen(months[j])) == 0)
02693                     {
02694                         /* found it */
02695                         err = 0;
02696                         pstr += strlen(months[j]);
02697                         *month = j + 1;
02698                         break;
02699                     }
02700                     j++;
02701                 }
02702                 break;
02703             case 'B':
02704                 /* see note above */
02705                 pfmt++;
02706                 err = 1;
02707                 j = 0;
02708                 while (pgtypes_date_months[j])
02709                 {
02710                     if (strncmp(pgtypes_date_months[j], pstr, strlen(pgtypes_date_months[j])) == 0)
02711                     {
02712                         /* found it */
02713                         err = 0;
02714                         pstr += strlen(pgtypes_date_months[j]);
02715                         *month = j + 1;
02716                         break;
02717                     }
02718                     j++;
02719                 }
02720                 break;
02721             case 'c':
02722                 /* XXX */
02723                 break;
02724             case 'C':
02725                 pfmt++;
02726                 scan_type = PGTYPES_TYPE_UINT;
02727                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02728                 *year = scan_val.uint_val * 100;
02729                 break;
02730             case 'd':
02731             case 'e':
02732                 pfmt++;
02733                 scan_type = PGTYPES_TYPE_UINT;
02734                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02735                 *day = scan_val.uint_val;
02736                 break;
02737             case 'D':
02738 
02739                 /*
02740                  * we have to concatenate the strings in order to be able to
02741                  * find the end of the substitution
02742                  */
02743                 pfmt++;
02744                 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
02745                 strcpy(tmp, "%m/%d/%y");
02746                 strcat(tmp, pfmt);
02747                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
02748                 free(tmp);
02749                 return err;
02750             case 'm':
02751                 pfmt++;
02752                 scan_type = PGTYPES_TYPE_UINT;
02753                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02754                 *month = scan_val.uint_val;
02755                 break;
02756             case 'y':
02757             case 'g':           /* XXX difference to y (ISO) */
02758                 pfmt++;
02759                 scan_type = PGTYPES_TYPE_UINT;
02760                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02761                 if (*year < 0)
02762                 {
02763                     /* not yet set */
02764                     *year = scan_val.uint_val;
02765                 }
02766                 else
02767                     *year += scan_val.uint_val;
02768                 if (*year < 100)
02769                     *year += 1900;
02770                 break;
02771             case 'G':
02772                 /* XXX difference to %V (ISO) */
02773                 pfmt++;
02774                 scan_type = PGTYPES_TYPE_UINT;
02775                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02776                 *year = scan_val.uint_val;
02777                 break;
02778             case 'H':
02779             case 'I':
02780             case 'k':
02781             case 'l':
02782                 pfmt++;
02783                 scan_type = PGTYPES_TYPE_UINT;
02784                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02785                 *hour += scan_val.uint_val;
02786                 break;
02787             case 'j':
02788                 pfmt++;
02789                 scan_type = PGTYPES_TYPE_UINT;
02790                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02791 
02792                 /*
02793                  * XXX what should we do with that? We could say that it's
02794                  * sufficient if we have the year and the day within the year
02795                  * to get at least a specific day.
02796                  */
02797                 break;
02798             case 'M':
02799                 pfmt++;
02800                 scan_type = PGTYPES_TYPE_UINT;
02801                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02802                 *minute = scan_val.uint_val;
02803                 break;
02804             case 'n':
02805                 pfmt++;
02806                 if (*pstr == '\n')
02807                     pstr++;
02808                 else
02809                     err = 1;
02810                 break;
02811             case 'p':
02812                 err = 1;
02813                 pfmt++;
02814                 if (strncmp(pstr, "am", 2) == 0)
02815                 {
02816                     *hour += 0;
02817                     err = 0;
02818                     pstr += 2;
02819                 }
02820                 if (strncmp(pstr, "a.m.", 4) == 0)
02821                 {
02822                     *hour += 0;
02823                     err = 0;
02824                     pstr += 4;
02825                 }
02826                 if (strncmp(pstr, "pm", 2) == 0)
02827                 {
02828                     *hour += 12;
02829                     err = 0;
02830                     pstr += 2;
02831                 }
02832                 if (strncmp(pstr, "p.m.", 4) == 0)
02833                 {
02834                     *hour += 12;
02835                     err = 0;
02836                     pstr += 4;
02837                 }
02838                 break;
02839             case 'P':
02840                 err = 1;
02841                 pfmt++;
02842                 if (strncmp(pstr, "AM", 2) == 0)
02843                 {
02844                     *hour += 0;
02845                     err = 0;
02846                     pstr += 2;
02847                 }
02848                 if (strncmp(pstr, "A.M.", 4) == 0)
02849                 {
02850                     *hour += 0;
02851                     err = 0;
02852                     pstr += 4;
02853                 }
02854                 if (strncmp(pstr, "PM", 2) == 0)
02855                 {
02856                     *hour += 12;
02857                     err = 0;
02858                     pstr += 2;
02859                 }
02860                 if (strncmp(pstr, "P.M.", 4) == 0)
02861                 {
02862                     *hour += 12;
02863                     err = 0;
02864                     pstr += 4;
02865                 }
02866                 break;
02867             case 'r':
02868                 pfmt++;
02869                 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
02870                 strcpy(tmp, "%I:%M:%S %p");
02871                 strcat(tmp, pfmt);
02872                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
02873                 free(tmp);
02874                 return err;
02875             case 'R':
02876                 pfmt++;
02877                 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
02878                 strcpy(tmp, "%H:%M");
02879                 strcat(tmp, pfmt);
02880                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
02881                 free(tmp);
02882                 return err;
02883             case 's':
02884                 pfmt++;
02885                 scan_type = PGTYPES_TYPE_UINT_LONG;
02886                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02887                 /* number of seconds in scan_val.luint_val */
02888                 {
02889                     struct tm  *tms;
02890                     time_t      et = (time_t) scan_val.luint_val;
02891 
02892                     tms = gmtime(&et);
02893 
02894                     if (tms)
02895                     {
02896                         *year = tms->tm_year + 1900;
02897                         *month = tms->tm_mon + 1;
02898                         *day = tms->tm_mday;
02899                         *hour = tms->tm_hour;
02900                         *minute = tms->tm_min;
02901                         *second = tms->tm_sec;
02902                     }
02903                     else
02904                         err = 1;
02905                 }
02906                 break;
02907             case 'S':
02908                 pfmt++;
02909                 scan_type = PGTYPES_TYPE_UINT;
02910                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02911                 *second = scan_val.uint_val;
02912                 break;
02913             case 't':
02914                 pfmt++;
02915                 if (*pstr == '\t')
02916                     pstr++;
02917                 else
02918                     err = 1;
02919                 break;
02920             case 'T':
02921                 pfmt++;
02922                 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
02923                 strcpy(tmp, "%H:%M:%S");
02924                 strcat(tmp, pfmt);
02925                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
02926                 free(tmp);
02927                 return err;
02928             case 'u':
02929                 pfmt++;
02930                 scan_type = PGTYPES_TYPE_UINT;
02931                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02932                 if (scan_val.uint_val < 1 || scan_val.uint_val > 7)
02933                     err = 1;
02934                 break;
02935             case 'U':
02936                 pfmt++;
02937                 scan_type = PGTYPES_TYPE_UINT;
02938                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02939                 if (scan_val.uint_val > 53)
02940                     err = 1;
02941                 break;
02942             case 'V':
02943                 pfmt++;
02944                 scan_type = PGTYPES_TYPE_UINT;
02945                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02946                 if (scan_val.uint_val < 1 || scan_val.uint_val > 53)
02947                     err = 1;
02948                 break;
02949             case 'w':
02950                 pfmt++;
02951                 scan_type = PGTYPES_TYPE_UINT;
02952                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02953                 if (scan_val.uint_val > 6)
02954                     err = 1;
02955                 break;
02956             case 'W':
02957                 pfmt++;
02958                 scan_type = PGTYPES_TYPE_UINT;
02959                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02960                 if (scan_val.uint_val > 53)
02961                     err = 1;
02962                 break;
02963             case 'x':
02964             case 'X':
02965                 /* XXX */
02966                 break;
02967             case 'Y':
02968                 pfmt++;
02969                 scan_type = PGTYPES_TYPE_UINT;
02970                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02971                 *year = scan_val.uint_val;
02972                 break;
02973             case 'z':
02974                 pfmt++;
02975                 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
02976                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02977                 if (!err)
02978                 {
02979                     err = DecodeTimezone(scan_val.str_val, tz);
02980                     free(scan_val.str_val);
02981                 }
02982                 break;
02983             case 'Z':
02984                 pfmt++;
02985                 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
02986                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
02987 
02988                 /*
02989                  * XXX use DecodeSpecial instead ? - it's declared static but
02990                  * the arrays as well. :-(
02991                  */
02992                 for (j = 0; !err && j < szdatetktbl; j++)
02993                 {
02994                     if (pg_strcasecmp(datetktbl[j].token, scan_val.str_val) == 0)
02995                     {
02996                         /*
02997                          * tz calculates the offset for the seconds, the
02998                          * timezone value of the datetktbl table is in quarter
02999                          * hours
03000                          */
03001                         *tz = -15 * MINS_PER_HOUR * datetktbl[j].value;
03002                         break;
03003                     }
03004                 }
03005                 free(scan_val.str_val);
03006                 break;
03007             case '+':
03008                 /* XXX */
03009                 break;
03010             case '%':
03011                 pfmt++;
03012                 if (*pstr == '%')
03013                     pstr++;
03014                 else
03015                     err = 1;
03016                 break;
03017             default:
03018                 err = 1;
03019         }
03020     }
03021     if (!err)
03022     {
03023         if (*second < 0)
03024             *second = 0;
03025         if (*minute < 0)
03026             *minute = 0;
03027         if (*hour < 0)
03028             *hour = 0;
03029         if (*day < 0)
03030         {
03031             err = 1;
03032             *day = 1;
03033         }
03034         if (*month < 0)
03035         {
03036             err = 1;
03037             *month = 1;
03038         }
03039         if (*year < 0)
03040         {
03041             err = 1;
03042             *year = 1970;
03043         }
03044 
03045         if (*second > 59)
03046         {
03047             err = 1;
03048             *second = 0;
03049         }
03050         if (*minute > 59)
03051         {
03052             err = 1;
03053             *minute = 0;
03054         }
03055         if (*hour > 24 ||       /* test for > 24:00:00 */
03056             (*hour == 24 && (*minute > 0 || *second > 0)))
03057         {
03058             err = 1;
03059             *hour = 0;
03060         }
03061         if (*month > MONTHS_PER_YEAR)
03062         {
03063             err = 1;
03064             *month = 1;
03065         }
03066         if (*day > day_tab[isleap(*year)][*month - 1])
03067         {
03068             *day = day_tab[isleap(*year)][*month - 1];
03069             err = 1;
03070         }
03071 
03072         tm.tm_sec = *second;
03073         tm.tm_min = *minute;
03074         tm.tm_hour = *hour;
03075         tm.tm_mday = *day;
03076         tm.tm_mon = *month;
03077         tm.tm_year = *year;
03078 
03079         tm2timestamp(&tm, 0, tz, d);
03080     }
03081     return err;
03082 }
03083 
03084 /* XXX: 1900 is compiled in as the base for years */