LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - dt_common.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 57.1 % 1251 714
Test Date: 2026-05-30 00:16:21 Functions: 91.3 % 23 21
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* src/interfaces/ecpg/pgtypeslib/dt_common.c */
       2              : 
       3              : #include "postgres_fe.h"
       4              : 
       5              : #include <time.h>
       6              : #include <ctype.h>
       7              : #include <math.h>
       8              : 
       9              : #include "common/string.h"
      10              : #include "dt.h"
      11              : #include "pgtypes_timestamp.h"
      12              : #include "pgtypeslib_extern.h"
      13              : 
      14              : const int   day_tab[2][13] = {
      15              :     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
      16              : {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
      17              : 
      18              : typedef long AbsoluteTime;
      19              : 
      20              : static const datetkn datetktbl[] = {
      21              : /*  text, token, lexval */
      22              :     {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
      23              :     {"acsst", DTZ, 37800},        /* Cent. Australia */
      24              :     {"acst", DTZ, -14400},        /* Atlantic/Porto Acre */
      25              :     {"act", TZ, -18000},      /* Atlantic/Porto Acre */
      26              :     {DA_D, ADBC, AD},           /* "ad" for years >= 0 */
      27              :     {"adt", DTZ, -10800},     /* Atlantic Daylight Time */
      28              :     {"aesst", DTZ, 39600},        /* E. Australia */
      29              :     {"aest", TZ, 36000},      /* Australia Eastern Std Time */
      30              :     {"aft", TZ, 16200},           /* Kabul */
      31              :     {"ahst", TZ, -36000},     /* Alaska-Hawaii Std Time */
      32              :     {"akdt", DTZ, -28800},        /* Alaska Daylight Time */
      33              :     {"akst", DTZ, -32400},        /* Alaska Standard Time */
      34              :     {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
      35              :     {"almst", TZ, 25200},     /* Almaty Savings Time */
      36              :     {"almt", TZ, 21600},      /* Almaty Time */
      37              :     {"am", AMPM, AM},
      38              :     {"amst", DTZ, 18000},     /* Armenia Summer Time (Yerevan) */
      39              : #if 0
      40              :     {"amst", DTZ, -10800},        /* Porto Velho */
      41              : #endif
      42              :     {"amt", TZ, 14400},           /* Armenia Time (Yerevan) */
      43              :     {"anast", DTZ, 46800},        /* Anadyr Summer Time (Russia) */
      44              :     {"anat", TZ, 43200},      /* Anadyr Time (Russia) */
      45              :     {"apr", MONTH, 4},
      46              :     {"april", MONTH, 4},
      47              : #if 0
      48              :     aqtst
      49              :     aqtt
      50              :     arst
      51              : #endif
      52              :     {"art", TZ, -10800},      /* Argentina Time */
      53              : #if 0
      54              :     ashst
      55              :     ast                         /* Atlantic Standard Time, Arabia Standard
      56              :                                  * Time, Acre Standard Time */
      57              : #endif
      58              :     {"ast", TZ, -14400},      /* Atlantic Std Time (Canada) */
      59              :     {"at", IGNORE_DTF, 0},        /* "at" (throwaway) */
      60              :     {"aug", MONTH, 8},
      61              :     {"august", MONTH, 8},
      62              :     {"awsst", DTZ, 32400},        /* W. Australia */
      63              :     {"awst", TZ, 28800},      /* W. Australia */
      64              :     {"awt", DTZ, -10800},
      65              :     {"azost", DTZ, 0},            /* Azores Summer Time */
      66              :     {"azot", TZ, -3600},      /* Azores Time */
      67              :     {"azst", DTZ, 18000},     /* Azerbaijan Summer Time */
      68              :     {"azt", TZ, 14400},           /* Azerbaijan Time */
      69              :     {DB_C, ADBC, BC},           /* "bc" for years < 0 */
      70              :     {"bdst", TZ, 7200},           /* British Double Summer Time */
      71              :     {"bdt", TZ, 21600},           /* Dacca */
      72              :     {"bnt", TZ, 28800},           /* Brunei Darussalam Time */
      73              :     {"bort", TZ, 28800},      /* Borneo Time (Indonesia) */
      74              : #if 0
      75              :     bortst
      76              :     bost
      77              : #endif
      78              :     {"bot", TZ, -14400},      /* Bolivia Time */
      79              :     {"bra", TZ, -10800},      /* Brazil Time */
      80              : #if 0
      81              :     brst
      82              :     brt
      83              : #endif
      84              :     {"bst", DTZ, 3600},           /* British Summer Time */
      85              : #if 0
      86              :     {"bst", TZ, -10800},      /* Brazil Standard Time */
      87              :     {"bst", DTZ, -39600},     /* Bering Summer Time */
      88              : #endif
      89              :     {"bt", TZ, 10800},            /* Baghdad Time */
      90              :     {"btt", TZ, 21600},           /* Bhutan Time */
      91              :     {"cadt", DTZ, 37800},     /* Central Australian DST */
      92              :     {"cast", TZ, 34200},      /* Central Australian ST */
      93              :     {"cat", TZ, -36000},      /* Central Alaska Time */
      94              :     {"cct", TZ, 28800},           /* China Coast Time */
      95              : #if 0
      96              :     {"cct", TZ, 23400},           /* Indian Cocos (Island) Time */
      97              : #endif
      98              :     {"cdt", DTZ, -18000},     /* Central Daylight Time */
      99              :     {"cest", DTZ, 7200},      /* Central European Dayl.Time */
     100              :     {"cet", TZ, 3600},            /* Central European Time */
     101              :     {"cetdst", DTZ, 7200},        /* Central European Dayl.Time */
     102              :     {"chadt", DTZ, 49500},        /* Chatham Island Daylight Time (13:45) */
     103              :     {"chast", TZ, 45900},     /* Chatham Island Time (12:45) */
     104              : #if 0
     105              :     ckhst
     106              : #endif
     107              :     {"ckt", TZ, 43200},           /* Cook Islands Time */
     108              :     {"clst", DTZ, -10800},        /* Chile Summer Time */
     109              :     {"clt", TZ, -14400},      /* Chile Time */
     110              : #if 0
     111              :     cost
     112              : #endif
     113              :     {"cot", TZ, -18000},      /* Columbia Time */
     114              :     {"cst", TZ, -21600},      /* Central Standard Time */
     115              : #if 0
     116              :     cvst
     117              : #endif
     118              :     {"cvt", TZ, 25200},           /* Christmas Island Time (Indian Ocean) */
     119              :     {"cxt", TZ, 25200},           /* Christmas Island Time (Indian Ocean) */
     120              :     {"d", UNITS, DTK_DAY},        /* "day of month" for ISO input */
     121              :     {"davt", TZ, 25200},      /* Davis Time (Antarctica) */
     122              :     {"ddut", TZ, 36000},      /* Dumont-d'Urville Time (Antarctica) */
     123              :     {"dec", MONTH, 12},
     124              :     {"december", MONTH, 12},
     125              :     {"dnt", TZ, 3600},            /* Dansk Normal Tid */
     126              :     {"dow", UNITS, DTK_DOW},  /* day of week */
     127              :     {"doy", UNITS, DTK_DOY},  /* day of year */
     128              :     {"dst", DTZMOD, SECS_PER_HOUR},
     129              : #if 0
     130              :     {"dusst", DTZ, 21600},        /* Dushanbe Summer Time */
     131              : #endif
     132              :     {"easst", DTZ, -18000},       /* Easter Island Summer Time */
     133              :     {"east", TZ, -21600},     /* Easter Island Time */
     134              :     {"eat", TZ, 10800},           /* East Africa Time */
     135              : #if 0
     136              :     {"east", DTZ, 14400},     /* Indian Antananarivo Savings Time */
     137              :     {"eat", TZ, 10800},           /* Indian Antananarivo Time */
     138              :     {"ect", TZ, -14400},      /* Eastern Caribbean Time */
     139              :     {"ect", TZ, -18000},      /* Ecuador Time */
     140              : #endif
     141              :     {"edt", DTZ, -14400},     /* Eastern Daylight Time */
     142              :     {"eest", DTZ, 10800},     /* Eastern Europe Summer Time */
     143              :     {"eet", TZ, 7200},            /* East. Europe, USSR Zone 1 */
     144              :     {"eetdst", DTZ, 10800},       /* Eastern Europe Daylight Time */
     145              :     {"egst", DTZ, 0},         /* East Greenland Summer Time */
     146              :     {"egt", TZ, -3600},           /* East Greenland Time */
     147              : #if 0
     148              :     ehdt
     149              : #endif
     150              :     {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
     151              :     {"est", TZ, -18000},      /* Eastern Standard Time */
     152              :     {"feb", MONTH, 2},
     153              :     {"february", MONTH, 2},
     154              :     {"fjst", DTZ, -46800},        /* Fiji Summer Time (13 hour offset!) */
     155              :     {"fjt", TZ, -43200},      /* Fiji Time */
     156              :     {"fkst", DTZ, -10800},        /* Falkland Islands Summer Time */
     157              :     {"fkt", TZ, -7200},           /* Falkland Islands Time */
     158              : #if 0
     159              :     fnst
     160              :     fnt
     161              : #endif
     162              :     {"fri", DOW, 5},
     163              :     {"friday", DOW, 5},
     164              :     {"fst", TZ, 3600},            /* French Summer Time */
     165              :     {"fwt", DTZ, 7200},           /* French Winter Time  */
     166              :     {"galt", TZ, -21600},     /* Galapagos Time */
     167              :     {"gamt", TZ, -32400},     /* Gambier Time */
     168              :     {"gest", DTZ, 18000},     /* Georgia Summer Time */
     169              :     {"get", TZ, 14400},           /* Georgia Time */
     170              :     {"gft", TZ, -10800},      /* French Guiana Time */
     171              : #if 0
     172              :     ghst
     173              : #endif
     174              :     {"gilt", TZ, 43200},      /* Gilbert Islands Time */
     175              :     {"gmt", TZ, 0},               /* Greenwich Mean Time */
     176              :     {"gst", TZ, 36000},           /* Guam Std Time, USSR Zone 9 */
     177              :     {"gyt", TZ, -14400},      /* Guyana Time */
     178              :     {"h", UNITS, DTK_HOUR},       /* "hour" */
     179              : #if 0
     180              :     hadt
     181              :     hast
     182              : #endif
     183              :     {"hdt", DTZ, -32400},     /* Hawaii/Alaska Daylight Time */
     184              : #if 0
     185              :     hkst
     186              : #endif
     187              :     {"hkt", TZ, 28800},           /* Hong Kong Time */
     188              : #if 0
     189              :     {"hmt", TZ, 10800},           /* Hellas ? ? */
     190              :     hovst
     191              :     hovt
     192              : #endif
     193              :     {"hst", TZ, -36000},      /* Hawaii Std Time */
     194              : #if 0
     195              :     hwt
     196              : #endif
     197              :     {"ict", TZ, 25200},           /* Indochina Time */
     198              :     {"idle", TZ, 43200},      /* Intl. Date Line, East */
     199              :     {"idlw", TZ, -43200},     /* Intl. Date Line, West */
     200              : #if 0
     201              :     idt                         /* Israeli, Iran, Indian Daylight Time */
     202              : #endif
     203              :     {LATE, RESERV, DTK_LATE},   /* "infinity" reserved for "late time" */
     204              :     {"iot", TZ, 18000},           /* Indian Chagos Time */
     205              :     {"irkst", DTZ, 32400},        /* Irkutsk Summer Time */
     206              :     {"irkt", TZ, 28800},      /* Irkutsk Time */
     207              :     {"irt", TZ, 12600},           /* Iran Time */
     208              :     {"isodow", UNITS, DTK_ISODOW},    /* ISO day of week, Sunday == 7 */
     209              : #if 0
     210              :     isst
     211              : #endif
     212              :     {"ist", TZ, 7200},            /* Israel */
     213              :     {"it", TZ, 12600},            /* Iran Time */
     214              :     {"j", UNITS, DTK_JULIAN},
     215              :     {"jan", MONTH, 1},
     216              :     {"january", MONTH, 1},
     217              :     {"javt", TZ, 25200},      /* Java Time (07:00? see JT) */
     218              :     {"jayt", TZ, 32400},      /* Jayapura Time (Indonesia) */
     219              :     {"jd", UNITS, DTK_JULIAN},
     220              :     {"jst", TZ, 32400},           /* Japan Std Time,USSR Zone 8 */
     221              :     {"jt", TZ, 27000},            /* Java Time (07:30? see JAVT) */
     222              :     {"jul", MONTH, 7},
     223              :     {"julian", UNITS, DTK_JULIAN},
     224              :     {"july", MONTH, 7},
     225              :     {"jun", MONTH, 6},
     226              :     {"june", MONTH, 6},
     227              :     {"kdt", DTZ, 36000},      /* Korea Daylight Time */
     228              :     {"kgst", DTZ, 21600},     /* Kyrgyzstan Summer Time */
     229              :     {"kgt", TZ, 18000},           /* Kyrgyzstan Time */
     230              :     {"kost", TZ, 43200},      /* Kosrae Time */
     231              :     {"krast", DTZ, 25200},        /* Krasnoyarsk Summer Time */
     232              :     {"krat", TZ, 28800},      /* Krasnoyarsk Standard Time */
     233              :     {"kst", TZ, 32400},           /* Korea Standard Time */
     234              :     {"lhdt", DTZ, 39600},     /* Lord Howe Daylight Time, Australia */
     235              :     {"lhst", TZ, 37800},      /* Lord Howe Standard Time, Australia */
     236              :     {"ligt", TZ, 36000},      /* From Melbourne, Australia */
     237              :     {"lint", TZ, 50400},      /* Line Islands Time (Kiribati; +14 hours!) */
     238              :     {"lkt", TZ, 21600},           /* Lanka Time */
     239              :     {"m", UNITS, DTK_MONTH},  /* "month" for ISO input */
     240              :     {"magst", DTZ, 43200},        /* Magadan Summer Time */
     241              :     {"magt", TZ, 39600},      /* Magadan Time */
     242              :     {"mar", MONTH, 3},
     243              :     {"march", MONTH, 3},
     244              :     {"mart", TZ, -34200},     /* Marquesas Time */
     245              :     {"mawt", TZ, 21600},      /* Mawson, Antarctica */
     246              :     {"may", MONTH, 5},
     247              :     {"mdt", DTZ, -21600},     /* Mountain Daylight Time */
     248              :     {"mest", DTZ, 7200},      /* Middle Europe Summer Time */
     249              :     {"met", TZ, 3600},            /* Middle Europe Time */
     250              :     {"metdst", DTZ, 7200},        /* Middle Europe Daylight Time */
     251              :     {"mewt", TZ, 3600},           /* Middle Europe Winter Time */
     252              :     {"mez", TZ, 3600},            /* Middle Europe Zone */
     253              :     {"mht", TZ, 43200},           /* Kwajalein */
     254              :     {"mm", UNITS, DTK_MINUTE},    /* "minute" for ISO input */
     255              :     {"mmt", TZ, 23400},           /* Myannar Time */
     256              :     {"mon", DOW, 1},
     257              :     {"monday", DOW, 1},
     258              : #if 0
     259              :     most
     260              : #endif
     261              :     {"mpt", TZ, 36000},           /* North Mariana Islands Time */
     262              :     {"msd", DTZ, 14400},      /* Moscow Summer Time */
     263              :     {"msk", TZ, 10800},           /* Moscow Time */
     264              :     {"mst", TZ, -25200},      /* Mountain Standard Time */
     265              :     {"mt", TZ, 30600},            /* Moluccas Time */
     266              :     {"mut", TZ, 14400},           /* Mauritius Island Time */
     267              :     {"mvt", TZ, 18000},           /* Maldives Island Time */
     268              :     {"myt", TZ, 28800},           /* Malaysia Time */
     269              : #if 0
     270              :     ncst
     271              : #endif
     272              :     {"nct", TZ, 39600},           /* New Caledonia Time */
     273              :     {"ndt", DTZ, -9000},      /* Nfld. Daylight Time */
     274              :     {"nft", TZ, -12600},      /* Newfoundland Standard Time */
     275              :     {"nor", TZ, 3600},            /* Norway Standard Time */
     276              :     {"nov", MONTH, 11},
     277              :     {"november", MONTH, 11},
     278              :     {"novst", DTZ, 25200},        /* Novosibirsk Summer Time */
     279              :     {"novt", TZ, 21600},      /* Novosibirsk Standard Time */
     280              :     {NOW, RESERV, DTK_NOW},     /* current transaction time */
     281              :     {"npt", TZ, 20700},           /* Nepal Standard Time (GMT-5:45) */
     282              :     {"nst", TZ, -12600},      /* Nfld. Standard Time */
     283              :     {"nt", TZ, -39600},           /* Nome Time */
     284              :     {"nut", TZ, -39600},      /* Niue Time */
     285              :     {"nzdt", DTZ, 46800},     /* New Zealand Daylight Time */
     286              :     {"nzst", TZ, 43200},      /* New Zealand Standard Time */
     287              :     {"nzt", TZ, 43200},           /* New Zealand Time */
     288              :     {"oct", MONTH, 10},
     289              :     {"october", MONTH, 10},
     290              :     {"omsst", DTZ, 25200},        /* Omsk Summer Time */
     291              :     {"omst", TZ, 21600},      /* Omsk Time */
     292              :     {"on", IGNORE_DTF, 0},        /* "on" (throwaway) */
     293              :     {"pdt", DTZ, -25200},     /* Pacific Daylight Time */
     294              : #if 0
     295              :     pest
     296              : #endif
     297              :     {"pet", TZ, -18000},      /* Peru Time */
     298              :     {"petst", DTZ, 46800},        /* Petropavlovsk-Kamchatski Summer Time */
     299              :     {"pett", TZ, 43200},      /* Petropavlovsk-Kamchatski Time */
     300              :     {"pgt", TZ, 36000},           /* Papua New Guinea Time */
     301              :     {"phot", TZ, 46800},      /* Phoenix Islands (Kiribati) Time */
     302              : #if 0
     303              :     phst
     304              : #endif
     305              :     {"pht", TZ, 28800},           /* Philippine Time */
     306              :     {"pkt", TZ, 18000},           /* Pakistan Time */
     307              :     {"pm", AMPM, PM},
     308              :     {"pmdt", DTZ, -7200},     /* Pierre & Miquelon Daylight Time */
     309              : #if 0
     310              :     pmst
     311              : #endif
     312              :     {"pont", TZ, 39600},      /* Ponape Time (Micronesia) */
     313              :     {"pst", TZ, -28800},      /* Pacific Standard Time */
     314              :     {"pwt", TZ, 32400},           /* Palau Time */
     315              :     {"pyst", DTZ, -10800},        /* Paraguay Summer Time */
     316              :     {"pyt", TZ, -14400},      /* Paraguay Time */
     317              :     {"ret", DTZ, 14400},      /* Reunion Island Time */
     318              :     {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
     319              :     {"sadt", DTZ, 37800},     /* S. Australian Dayl. Time */
     320              : #if 0
     321              :     samst
     322              :     samt
     323              : #endif
     324              :     {"sast", TZ, 34200},      /* South Australian Std Time */
     325              :     {"sat", DOW, 6},
     326              :     {"saturday", DOW, 6},
     327              : #if 0
     328              :     sbt
     329              : #endif
     330              :     {"sct", DTZ, 14400},      /* Mahe Island Time */
     331              :     {"sep", MONTH, 9},
     332              :     {"sept", MONTH, 9},
     333              :     {"september", MONTH, 9},
     334              :     {"set", TZ, -3600},           /* Seychelles Time ?? */
     335              : #if 0
     336              :     sgt
     337              : #endif
     338              :     {"sst", DTZ, 7200},           /* Swedish Summer Time */
     339              :     {"sun", DOW, 0},
     340              :     {"sunday", DOW, 0},
     341              :     {"swt", TZ, 3600},            /* Swedish Winter Time */
     342              : #if 0
     343              :     syot
     344              : #endif
     345              :     {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
     346              :     {"tft", TZ, 18000},           /* Kerguelen Time */
     347              :     {"that", TZ, -36000},     /* Tahiti Time */
     348              :     {"thu", DOW, 4},
     349              :     {"thur", DOW, 4},
     350              :     {"thurs", DOW, 4},
     351              :     {"thursday", DOW, 4},
     352              :     {"tjt", TZ, 18000},           /* Tajikistan Time */
     353              :     {"tkt", TZ, -36000},      /* Tokelau Time */
     354              :     {"tmt", TZ, 18000},           /* Turkmenistan Time */
     355              :     {TODAY, RESERV, DTK_TODAY}, /* midnight */
     356              :     {TOMORROW, RESERV, DTK_TOMORROW},   /* tomorrow midnight */
     357              : #if 0
     358              :     tost
     359              : #endif
     360              :     {"tot", TZ, 46800},           /* Tonga Time */
     361              : #if 0
     362              :     tpt
     363              : #endif
     364              :     {"truk", TZ, 36000},      /* Truk Time */
     365              :     {"tue", DOW, 2},
     366              :     {"tues", DOW, 2},
     367              :     {"tuesday", DOW, 2},
     368              :     {"tvt", TZ, 43200},           /* Tuvalu Time */
     369              : #if 0
     370              :     uct
     371              : #endif
     372              :     {"ulast", DTZ, 32400},        /* Ulan Bator Summer Time */
     373              :     {"ulat", TZ, 28800},      /* Ulan Bator Time */
     374              :     {"ut", TZ, 0},
     375              :     {"utc", TZ, 0},
     376              :     {"uyst", DTZ, -7200},     /* Uruguay Summer Time */
     377              :     {"uyt", TZ, -10800},      /* Uruguay Time */
     378              :     {"uzst", DTZ, 21600},     /* Uzbekistan Summer Time */
     379              :     {"uzt", TZ, 18000},           /* Uzbekistan Time */
     380              :     {"vet", TZ, -14400},      /* Venezuela Time */
     381              :     {"vlast", DTZ, 39600},        /* Vladivostok Summer Time */
     382              :     {"vlat", TZ, 36000},      /* Vladivostok Time */
     383              : #if 0
     384              :     vust
     385              : #endif
     386              :     {"vut", TZ, 39600},           /* Vanuata Time */
     387              :     {"wadt", DTZ, 28800},     /* West Australian DST */
     388              :     {"wakt", TZ, 43200},      /* Wake Time */
     389              : #if 0
     390              :     warst
     391              : #endif
     392              :     {"wast", TZ, 25200},      /* West Australian Std Time */
     393              :     {"wat", TZ, -3600},           /* West Africa Time */
     394              :     {"wdt", DTZ, 32400},      /* West Australian DST */
     395              :     {"wed", DOW, 3},
     396              :     {"wednesday", DOW, 3},
     397              :     {"weds", DOW, 3},
     398              :     {"west", DTZ, 3600},      /* Western Europe Summer Time */
     399              :     {"wet", TZ, 0},               /* Western Europe */
     400              :     {"wetdst", DTZ, 3600},        /* Western Europe Daylight Savings Time */
     401              :     {"wft", TZ, 43200},           /* Wallis and Futuna Time */
     402              :     {"wgst", DTZ, -7200},     /* West Greenland Summer Time */
     403              :     {"wgt", TZ, -10800},      /* West Greenland Time */
     404              :     {"wst", TZ, 28800},           /* West Australian Standard Time */
     405              :     {"y", UNITS, DTK_YEAR},       /* "year" for ISO input */
     406              :     {"yakst", DTZ, 36000},        /* Yakutsk Summer Time */
     407              :     {"yakt", TZ, 32400},      /* Yakutsk Time */
     408              :     {"yapt", TZ, 36000},      /* Yap Time (Micronesia) */
     409              :     {"ydt", DTZ, -28800},     /* Yukon Daylight Time */
     410              :     {"yekst", DTZ, 21600},        /* Yekaterinburg Summer Time */
     411              :     {"yekt", TZ, 18000},      /* Yekaterinburg Time */
     412              :     {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
     413              :     {"yst", TZ, -32400},      /* Yukon Standard Time */
     414              :     {"z", TZ, 0},             /* time zone tag per ISO-8601 */
     415              :     {"zp4", TZ, -14400},      /* UTC +4  hours. */
     416              :     {"zp5", TZ, -18000},      /* UTC +5  hours. */
     417              :     {"zp6", TZ, -21600},      /* UTC +6  hours. */
     418              :     {ZULU, TZ, 0},              /* UTC */
     419              : };
     420              : 
     421              : static const datetkn deltatktbl[] = {
     422              :     /* text, token, lexval */
     423              :     {"@", IGNORE_DTF, 0},     /* postgres relative prefix */
     424              :     {DAGO, AGO, 0},             /* "ago" indicates negative time offset */
     425              :     {"c", UNITS, DTK_CENTURY},    /* "century" relative */
     426              :     {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
     427              :     {"centuries", UNITS, DTK_CENTURY},    /* "centuries" relative */
     428              :     {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
     429              :     {"d", UNITS, DTK_DAY},        /* "day" relative */
     430              :     {DDAY, UNITS, DTK_DAY},     /* "day" relative */
     431              :     {"days", UNITS, DTK_DAY}, /* "days" relative */
     432              :     {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
     433              :     {DDECADE, UNITS, DTK_DECADE},   /* "decade" relative */
     434              :     {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
     435              :     {"decs", UNITS, DTK_DECADE},  /* "decades" relative */
     436              :     {"h", UNITS, DTK_HOUR},       /* "hour" relative */
     437              :     {DHOUR, UNITS, DTK_HOUR},   /* "hour" relative */
     438              :     {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
     439              :     {"hr", UNITS, DTK_HOUR},  /* "hour" relative */
     440              :     {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
     441              :     {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
     442              :     {"microsecon", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
     443              :     {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
     444              :     {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
     445              :     {DMILLENNIUM, UNITS, DTK_MILLENNIUM},   /* "millennium" relative */
     446              :     {"millisecon", UNITS, DTK_MILLISEC},  /* relative */
     447              :     {"mils", UNITS, DTK_MILLENNIUM},  /* "millennia" relative */
     448              :     {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
     449              :     {"mins", UNITS, DTK_MINUTE},  /* "minutes" relative */
     450              :     {DMINUTE, UNITS, DTK_MINUTE},   /* "minute" relative */
     451              :     {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
     452              :     {"mon", UNITS, DTK_MONTH},    /* "months" relative */
     453              :     {"mons", UNITS, DTK_MONTH}, /* "months" relative */
     454              :     {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
     455              :     {"months", UNITS, DTK_MONTH},
     456              :     {"ms", UNITS, DTK_MILLISEC},
     457              :     {"msec", UNITS, DTK_MILLISEC},
     458              :     {DMILLISEC, UNITS, DTK_MILLISEC},
     459              :     {"mseconds", UNITS, DTK_MILLISEC},
     460              :     {"msecs", UNITS, DTK_MILLISEC},
     461              :     {"qtr", UNITS, DTK_QUARTER},  /* "quarter" relative */
     462              :     {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
     463              :     {"s", UNITS, DTK_SECOND},
     464              :     {"sec", UNITS, DTK_SECOND},
     465              :     {DSECOND, UNITS, DTK_SECOND},
     466              :     {"seconds", UNITS, DTK_SECOND},
     467              :     {"secs", UNITS, DTK_SECOND},
     468              :     {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
     469              :     {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
     470              :     {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
     471              :     {"us", UNITS, DTK_MICROSEC},  /* "microsecond" relative */
     472              :     {"usec", UNITS, DTK_MICROSEC},    /* "microsecond" relative */
     473              :     {DMICROSEC, UNITS, DTK_MICROSEC},   /* "microsecond" relative */
     474              :     {"useconds", UNITS, DTK_MICROSEC},    /* "microseconds" relative */
     475              :     {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
     476              :     {"w", UNITS, DTK_WEEK},       /* "week" relative */
     477              :     {DWEEK, UNITS, DTK_WEEK},   /* "week" relative */
     478              :     {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
     479              :     {"y", UNITS, DTK_YEAR},       /* "year" relative */
     480              :     {DYEAR, UNITS, DTK_YEAR},   /* "year" relative */
     481              :     {"years", UNITS, DTK_YEAR}, /* "years" relative */
     482              :     {"yr", UNITS, DTK_YEAR},  /* "year" relative */
     483              :     {"yrs", UNITS, DTK_YEAR}, /* "years" relative */
     484              : };
     485              : 
     486              : static const unsigned int szdatetktbl = lengthof(datetktbl);
     487              : static const unsigned int szdeltatktbl = lengthof(deltatktbl);
     488              : 
     489              : static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
     490              : 
     491              : static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
     492              : 
     493              : char       *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
     494              : 
     495              : char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL};
     496              : 
     497              : char       *pgtypes_date_weekdays_short[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
     498              : 
     499              : char       *pgtypes_date_months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL};
     500              : 
     501              : static const datetkn *
     502           90 : datebsearch(const char *key, const datetkn *base, unsigned int nel)
     503              : {
     504           90 :     if (nel > 0)
     505              :     {
     506           90 :         const datetkn *last = base + nel - 1,
     507              :                    *position;
     508              :         int         result;
     509              : 
     510          586 :         while (last >= base)
     511              :         {
     512          576 :             position = base + ((last - base) >> 1);
     513              :             /* precheck the first character for a bit of extra speed */
     514          576 :             result = (int) key[0] - (int) position->token[0];
     515          576 :             if (result == 0)
     516              :             {
     517              :                 /* use strncmp so that we match truncated tokens */
     518          282 :                 result = strncmp(key, position->token, TOKMAXLEN);
     519          282 :                 if (result == 0)
     520           80 :                     return position;
     521              :             }
     522          496 :             if (result < 0)
     523          232 :                 last = position - 1;
     524              :             else
     525          264 :                 base = position + 1;
     526              :         }
     527              :     }
     528           10 :     return NULL;
     529              : }
     530              : 
     531              : /*
     532              :  * DecodeUnits()
     533              :  * Decode text string using lookup table.
     534              :  * This routine supports time interval decoding.
     535              :  */
     536              : int
     537           88 : DecodeUnits(int field, char *lowtoken, int *val)
     538              : {
     539              :     int         type;
     540              :     const datetkn *tp;
     541              : 
     542              :     /* use strncmp so that we match truncated tokens */
     543           88 :     if (deltacache[field] != NULL &&
     544           60 :         strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)
     545           40 :         tp = deltacache[field];
     546              :     else
     547           48 :         tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
     548           88 :     deltacache[field] = tp;
     549           88 :     if (tp == NULL)
     550              :     {
     551            2 :         type = UNKNOWN_FIELD;
     552            2 :         *val = 0;
     553              :     }
     554              :     else
     555              :     {
     556           86 :         type = tp->type;
     557           86 :         *val = tp->value;
     558              :     }
     559              : 
     560           88 :     return type;
     561              : }                               /* DecodeUnits() */
     562              : 
     563              : /*
     564              :  * Calendar time to Julian date conversions.
     565              :  * Julian date is commonly used in astronomical applications,
     566              :  *  since it is numerically accurate and computationally simple.
     567              :  * The algorithms here will accurately convert between Julian day
     568              :  *  and calendar date for all non-negative Julian days
     569              :  *  (i.e. from Nov 24, -4713 on).
     570              :  *
     571              :  * These routines will be used by other date/time packages
     572              :  * - thomas 97/02/25
     573              :  *
     574              :  * Rewritten to eliminate overflow problems. This now allows the
     575              :  * routines to work correctly for all Julian day counts from
     576              :  * 0 to 2147483647  (Nov 24, -4713 to Jun 3, 5874898) assuming
     577              :  * a 32-bit integer. Longer types should also work to the limits
     578              :  * of their precision.
     579              :  */
     580              : 
     581              : int
     582         1826 : date2j(int y, int m, int d)
     583              : {
     584              :     int         julian;
     585              :     int         century;
     586              : 
     587         1826 :     if (m > 2)
     588              :     {
     589          104 :         m += 1;
     590          104 :         y += 4800;
     591              :     }
     592              :     else
     593              :     {
     594         1722 :         m += 13;
     595         1722 :         y += 4799;
     596              :     }
     597              : 
     598         1826 :     century = y / 100;
     599         1826 :     julian = y * 365 - 32167;
     600         1826 :     julian += y / 4 - century + century / 4;
     601         1826 :     julian += 7834 * m / 256 + d;
     602              : 
     603         1826 :     return julian;
     604              : }                               /* date2j() */
     605              : 
     606              : void
     607          570 : j2date(int jd, int *year, int *month, int *day)
     608              : {
     609              :     unsigned int julian;
     610              :     unsigned int quad;
     611              :     unsigned int extra;
     612              :     int         y;
     613              : 
     614          570 :     julian = jd;
     615          570 :     julian += 32044;
     616          570 :     quad = julian / 146097;
     617          570 :     extra = (julian - quad * 146097) * 4 + 3;
     618          570 :     julian += 60 + quad * 3 + extra / 146097;
     619          570 :     quad = julian / 1461;
     620          570 :     julian -= quad * 1461;
     621          570 :     y = julian * 4 / 1461;
     622          570 :     julian = ((y != 0) ? (julian + 305) % 365 : (julian + 306) % 366) + 123;
     623          570 :     y += quad * 4;
     624          570 :     *year = y - 4800;
     625          570 :     quad = julian * 2141 / 65536;
     626          570 :     *day = julian - 7834 * quad / 256;
     627          570 :     *month = (quad + 10) % 12 + 1;
     628          570 : }                               /* j2date() */
     629              : 
     630              : /*
     631              :  * DecodeSpecial()
     632              :  * Decode text string using lookup table.
     633              :  * Implement a cache lookup since it is likely that dates
     634              :  *  will be related in format.
     635              :  */
     636              : static int
     637          228 : DecodeSpecial(int field, char *lowtoken, int *val)
     638              : {
     639              :     int         type;
     640              :     const datetkn *tp;
     641              : 
     642              :     /* use strncmp so that we match truncated tokens */
     643          228 :     if (datecache[field] != NULL &&
     644          198 :         strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)
     645          186 :         tp = datecache[field];
     646              :     else
     647              :     {
     648           42 :         tp = NULL;
     649           42 :         if (!tp)
     650           42 :             tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
     651              :     }
     652          228 :     datecache[field] = tp;
     653          228 :     if (tp == NULL)
     654              :     {
     655            8 :         type = UNKNOWN_FIELD;
     656            8 :         *val = 0;
     657              :     }
     658              :     else
     659              :     {
     660          220 :         type = tp->type;
     661          220 :         *val = tp->value;
     662              :     }
     663              : 
     664          228 :     return type;
     665              : }                               /* DecodeSpecial() */
     666              : 
     667              : /*
     668              :  * EncodeDateOnly()
     669              :  * Encode date as local time.
     670              :  */
     671              : void
     672          190 : EncodeDateOnly(struct tm *tm, int style, char *str, bool EuroDates)
     673              : {
     674              :     Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
     675              : 
     676          190 :     switch (style)
     677              :     {
     678          190 :         case USE_ISO_DATES:
     679              :             /* compatible with ISO date formats */
     680          190 :             if (tm->tm_year > 0)
     681          188 :                 sprintf(str, "%04d-%02d-%02d",
     682              :                         tm->tm_year, tm->tm_mon, tm->tm_mday);
     683              :             else
     684            2 :                 sprintf(str, "%04d-%02d-%02d %s",
     685            2 :                         -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
     686          190 :             break;
     687              : 
     688            0 :         case USE_SQL_DATES:
     689              :             /* compatible with Oracle/Ingres date formats */
     690            0 :             if (EuroDates)
     691            0 :                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
     692              :             else
     693            0 :                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
     694            0 :             if (tm->tm_year > 0)
     695            0 :                 sprintf(str + 5, "/%04d", tm->tm_year);
     696              :             else
     697            0 :                 sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
     698            0 :             break;
     699              : 
     700            0 :         case USE_GERMAN_DATES:
     701              :             /* German-style date format */
     702            0 :             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
     703            0 :             if (tm->tm_year > 0)
     704            0 :                 sprintf(str + 5, ".%04d", tm->tm_year);
     705              :             else
     706            0 :                 sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
     707            0 :             break;
     708              : 
     709            0 :         case USE_POSTGRES_DATES:
     710              :         default:
     711              :             /* traditional date-only style for Postgres */
     712            0 :             if (EuroDates)
     713            0 :                 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
     714              :             else
     715            0 :                 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
     716            0 :             if (tm->tm_year > 0)
     717            0 :                 sprintf(str + 5, "-%04d", tm->tm_year);
     718              :             else
     719            0 :                 sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
     720            0 :             break;
     721              :     }
     722          190 : }
     723              : 
     724              : void
     725           68 : TrimTrailingZeros(char *str)
     726              : {
     727           68 :     int         len = strlen(str);
     728              : 
     729              :     /* chop off trailing zeros... but leave at least 2 fractional digits */
     730          170 :     while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
     731              :     {
     732          102 :         len--;
     733          102 :         *(str + len) = '\0';
     734              :     }
     735           68 : }
     736              : 
     737              : /*
     738              :  * EncodeDateTime()
     739              :  * Encode date and time interpreted as local time.
     740              :  *
     741              :  * tm and fsec are the value to encode, print_tz determines whether to include
     742              :  * a time zone (the difference between timestamp and timestamptz types), tz is
     743              :  * the numeric time zone offset, tzn is the textual time zone, which if
     744              :  * specified will be used instead of tz by some styles, style is the date
     745              :  * style, str is where to write the output.
     746              :  *
     747              :  * Supported date styles:
     748              :  *  Postgres - day mon hh:mm:ss yyyy tz
     749              :  *  SQL - mm/dd/yyyy hh:mm:ss.ss tz
     750              :  *  ISO - yyyy-mm-dd hh:mm:ss+/-tz
     751              :  *  German - dd.mm.yyyy hh:mm:ss tz
     752              :  * Variants (affects order of month and day for Postgres and SQL styles):
     753              :  *  US - mm/dd/yyyy
     754              :  *  European - dd/mm/yyyy
     755              :  */
     756              : void
     757          318 : EncodeDateTime(struct tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates)
     758              : {
     759              :     int         day,
     760              :                 hour,
     761              :                 min;
     762              : 
     763              :     /*
     764              :      * Negative tm_isdst means we have no valid time zone translation.
     765              :      */
     766          318 :     if (tm->tm_isdst < 0)
     767          318 :         print_tz = false;
     768              : 
     769          318 :     switch (style)
     770              :     {
     771          318 :         case USE_ISO_DATES:
     772              :             /* Compatible with ISO-8601 date formats */
     773              : 
     774          318 :             sprintf(str, "%04d-%02d-%02d %02d:%02d",
     775          318 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
     776              :                     tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
     777              : 
     778              :             /*
     779              :              * Print fractional seconds if any.  The field widths here should
     780              :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     781              :              */
     782          318 :             if (fsec != 0)
     783              :             {
     784           68 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     785           68 :                 TrimTrailingZeros(str);
     786              :             }
     787              :             else
     788          250 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     789              : 
     790          318 :             if (tm->tm_year <= 0)
     791           10 :                 sprintf(str + strlen(str), " BC");
     792              : 
     793          318 :             if (print_tz)
     794              :             {
     795            0 :                 hour = -(tz / SECS_PER_HOUR);
     796            0 :                 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     797            0 :                 if (min != 0)
     798            0 :                     sprintf(str + strlen(str), "%+03d:%02d", hour, min);
     799              :                 else
     800            0 :                     sprintf(str + strlen(str), "%+03d", hour);
     801              :             }
     802          318 :             break;
     803              : 
     804            0 :         case USE_SQL_DATES:
     805              :             /* Compatible with Oracle/Ingres date formats */
     806              : 
     807            0 :             if (EuroDates)
     808            0 :                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
     809              :             else
     810            0 :                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
     811              : 
     812            0 :             sprintf(str + 5, "/%04d %02d:%02d",
     813            0 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
     814              :                     tm->tm_hour, tm->tm_min);
     815              : 
     816              :             /*
     817              :              * Print fractional seconds if any.  The field widths here should
     818              :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     819              :              */
     820            0 :             if (fsec != 0)
     821              :             {
     822            0 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     823            0 :                 TrimTrailingZeros(str);
     824              :             }
     825              :             else
     826            0 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     827              : 
     828            0 :             if (tm->tm_year <= 0)
     829            0 :                 sprintf(str + strlen(str), " BC");
     830              : 
     831              :             /*
     832              :              * Note: the uses of %.*s in this function would be risky if the
     833              :              * timezone names ever contain non-ASCII characters, since we are
     834              :              * not being careful to do encoding-aware clipping.  However, all
     835              :              * TZ abbreviations in the IANA database are plain ASCII.
     836              :              */
     837              : 
     838            0 :             if (print_tz)
     839              :             {
     840            0 :                 if (tzn)
     841            0 :                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
     842              :                 else
     843              :                 {
     844            0 :                     hour = -(tz / SECS_PER_HOUR);
     845            0 :                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     846            0 :                     if (min != 0)
     847            0 :                         sprintf(str + strlen(str), "%+03d:%02d", hour, min);
     848              :                     else
     849            0 :                         sprintf(str + strlen(str), "%+03d", hour);
     850              :                 }
     851              :             }
     852            0 :             break;
     853              : 
     854            0 :         case USE_GERMAN_DATES:
     855              :             /* German variant on European style */
     856              : 
     857            0 :             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
     858              : 
     859            0 :             sprintf(str + 5, ".%04d %02d:%02d",
     860            0 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
     861              :                     tm->tm_hour, tm->tm_min);
     862              : 
     863              :             /*
     864              :              * Print fractional seconds if any.  The field widths here should
     865              :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     866              :              */
     867            0 :             if (fsec != 0)
     868              :             {
     869            0 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     870            0 :                 TrimTrailingZeros(str);
     871              :             }
     872              :             else
     873            0 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     874              : 
     875            0 :             if (tm->tm_year <= 0)
     876            0 :                 sprintf(str + strlen(str), " BC");
     877              : 
     878            0 :             if (print_tz)
     879              :             {
     880            0 :                 if (tzn)
     881            0 :                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
     882              :                 else
     883              :                 {
     884            0 :                     hour = -(tz / SECS_PER_HOUR);
     885            0 :                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     886            0 :                     if (min != 0)
     887            0 :                         sprintf(str + strlen(str), "%+03d:%02d", hour, min);
     888              :                     else
     889            0 :                         sprintf(str + strlen(str), "%+03d", hour);
     890              :                 }
     891              :             }
     892            0 :             break;
     893              : 
     894            0 :         case USE_POSTGRES_DATES:
     895              :         default:
     896              :             /* Backward-compatible with traditional Postgres abstime dates */
     897              : 
     898            0 :             day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
     899            0 :             tm->tm_wday = (int) ((day + date2j(2000, 1, 1) + 1) % 7);
     900              : 
     901            0 :             memcpy(str, days[tm->tm_wday], 3);
     902            0 :             strcpy(str + 3, " ");
     903              : 
     904            0 :             if (EuroDates)
     905            0 :                 sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
     906              :             else
     907            0 :                 sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
     908              : 
     909            0 :             sprintf(str + 10, " %02d:%02d", tm->tm_hour, tm->tm_min);
     910              : 
     911              :             /*
     912              :              * Print fractional seconds if any.  The field widths here should
     913              :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     914              :              */
     915            0 :             if (fsec != 0)
     916              :             {
     917            0 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     918            0 :                 TrimTrailingZeros(str);
     919              :             }
     920              :             else
     921            0 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     922              : 
     923            0 :             sprintf(str + strlen(str), " %04d",
     924            0 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
     925            0 :             if (tm->tm_year <= 0)
     926            0 :                 sprintf(str + strlen(str), " BC");
     927              : 
     928            0 :             if (print_tz)
     929              :             {
     930            0 :                 if (tzn)
     931            0 :                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
     932              :                 else
     933              :                 {
     934              :                     /*
     935              :                      * We have a time zone, but no string version. Use the
     936              :                      * numeric form, but be sure to include a leading space to
     937              :                      * avoid formatting something which would be rejected by
     938              :                      * the date/time parser later. - thomas 2001-10-19
     939              :                      */
     940            0 :                     hour = -(tz / SECS_PER_HOUR);
     941            0 :                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     942            0 :                     if (min != 0)
     943            0 :                         sprintf(str + strlen(str), " %+03d:%02d", hour, min);
     944              :                     else
     945            0 :                         sprintf(str + strlen(str), " %+03d", hour);
     946              :                 }
     947              :             }
     948            0 :             break;
     949              :     }
     950          318 : }
     951              : 
     952              : int
     953            0 : GetEpochTime(struct tm *tm)
     954              : {
     955              :     struct tm  *t0;
     956              :     struct tm   tmbuf;
     957            0 :     time_t      epoch = 0;
     958              : 
     959            0 :     t0 = gmtime_r(&epoch, &tmbuf);
     960              : 
     961            0 :     if (t0)
     962              :     {
     963            0 :         tm->tm_year = t0->tm_year + 1900;
     964            0 :         tm->tm_mon = t0->tm_mon + 1;
     965            0 :         tm->tm_mday = t0->tm_mday;
     966            0 :         tm->tm_hour = t0->tm_hour;
     967            0 :         tm->tm_min = t0->tm_min;
     968            0 :         tm->tm_sec = t0->tm_sec;
     969              : 
     970            0 :         return 0;
     971              :     }
     972              : 
     973            0 :     return -1;
     974              : }                               /* GetEpochTime() */
     975              : 
     976              : static void
     977            2 : abstime2tm(AbsoluteTime _time, int *tzp, struct tm *tm, char **tzn)
     978              : {
     979            2 :     time_t      time = (time_t) _time;
     980              :     struct tm  *tx;
     981              :     struct tm   tmbuf;
     982              : 
     983            2 :     errno = 0;
     984            2 :     if (tzp != NULL)
     985            2 :         tx = localtime_r(&time, &tmbuf);
     986              :     else
     987            0 :         tx = gmtime_r(&time, &tmbuf);
     988              : 
     989            2 :     if (!tx)
     990              :     {
     991            0 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     992            0 :         return;
     993              :     }
     994              : 
     995            2 :     tm->tm_year = tx->tm_year + 1900;
     996            2 :     tm->tm_mon = tx->tm_mon + 1;
     997            2 :     tm->tm_mday = tx->tm_mday;
     998            2 :     tm->tm_hour = tx->tm_hour;
     999            2 :     tm->tm_min = tx->tm_min;
    1000            2 :     tm->tm_sec = tx->tm_sec;
    1001            2 :     tm->tm_isdst = tx->tm_isdst;
    1002              : 
    1003              : #if defined(HAVE_STRUCT_TM_TM_ZONE)
    1004            2 :     tm->tm_gmtoff = tx->tm_gmtoff;
    1005            2 :     tm->tm_zone = tx->tm_zone;
    1006              : 
    1007            2 :     if (tzp != NULL)
    1008              :     {
    1009              :         /*
    1010              :          * We have a brute force time zone per SQL99? Then use it without
    1011              :          * change since we have already rotated to the time zone.
    1012              :          */
    1013            2 :         *tzp = -tm->tm_gmtoff;   /* tm_gmtoff is Sun/DEC-ism */
    1014              : 
    1015              :         /*
    1016              :          * FreeBSD man pages indicate that this should work - tgl 97/04/23
    1017              :          */
    1018            2 :         if (tzn != NULL)
    1019              :         {
    1020              :             /*
    1021              :              * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
    1022              :              * contains an error message, which doesn't fit in the buffer
    1023              :              */
    1024            0 :             strlcpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
    1025            0 :             if (strlen(tm->tm_zone) > MAXTZLEN)
    1026            0 :                 tm->tm_isdst = -1;
    1027              :         }
    1028              :     }
    1029              :     else
    1030            0 :         tm->tm_isdst = -1;
    1031              : #elif defined(HAVE_INT_TIMEZONE)
    1032              :     if (tzp != NULL)
    1033              :     {
    1034              :         *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
    1035              : 
    1036              :         if (tzn != NULL)
    1037              :         {
    1038              :             /*
    1039              :              * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
    1040              :              * contains an error message, which doesn't fit in the buffer
    1041              :              */
    1042              :             strlcpy(*tzn, TZNAME_GLOBAL[tm->tm_isdst], MAXTZLEN + 1);
    1043              :             if (strlen(TZNAME_GLOBAL[tm->tm_isdst]) > MAXTZLEN)
    1044              :                 tm->tm_isdst = -1;
    1045              :         }
    1046              :     }
    1047              :     else
    1048              :         tm->tm_isdst = -1;
    1049              : #else                           /* not (HAVE_STRUCT_TM_TM_ZONE ||
    1050              :                                  * HAVE_INT_TIMEZONE) */
    1051              :     if (tzp != NULL)
    1052              :     {
    1053              :         /* default to UTC */
    1054              :         *tzp = 0;
    1055              :         if (tzn != NULL)
    1056              :             *tzn = NULL;
    1057              :     }
    1058              :     else
    1059              :         tm->tm_isdst = -1;
    1060              : #endif
    1061              : }
    1062              : 
    1063              : void
    1064            2 : GetCurrentDateTime(struct tm *tm)
    1065              : {
    1066              :     int         tz;
    1067              : 
    1068            2 :     abstime2tm(time(NULL), &tz, tm, NULL);
    1069            2 : }
    1070              : 
    1071              : void
    1072          328 : dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
    1073              : {
    1074              :     int64       time;
    1075              : 
    1076          328 :     time = jd;
    1077          328 :     *hour = time / USECS_PER_HOUR;
    1078          328 :     time -= (*hour) * USECS_PER_HOUR;
    1079          328 :     *min = time / USECS_PER_MINUTE;
    1080          328 :     time -= (*min) * USECS_PER_MINUTE;
    1081          328 :     *sec = time / USECS_PER_SEC;
    1082          328 :     *fsec = time - (*sec * USECS_PER_SEC);
    1083          328 : }                               /* dt2time() */
    1084              : 
    1085              : 
    1086              : 
    1087              : /*
    1088              :  * DecodeNumberField()
    1089              :  * Interpret numeric string as a concatenated date or time field.
    1090              :  * Use the context of previously decoded fields to help with
    1091              :  * the interpretation.
    1092              :  */
    1093              : static int
    1094           30 : DecodeNumberField(int len, char *str, int fmask,
    1095              :                   int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits)
    1096              : {
    1097              :     char       *cp;
    1098              : 
    1099              :     /*
    1100              :      * Have a decimal point? Then this is a date or something with a seconds
    1101              :      * field...
    1102              :      */
    1103           30 :     if ((cp = strchr(str, '.')) != NULL)
    1104              :     {
    1105              :         char        fstr[7];
    1106              :         int         i;
    1107              : 
    1108            0 :         cp++;
    1109              : 
    1110              :         /*
    1111              :          * OK, we have at most six digits to care about. Let's construct a
    1112              :          * string with those digits, zero-padded on the right, and then do the
    1113              :          * conversion to an integer.
    1114              :          *
    1115              :          * XXX This truncates the seventh digit, unlike rounding it as the
    1116              :          * backend does.
    1117              :          */
    1118            0 :         for (i = 0; i < 6; i++)
    1119            0 :             fstr[i] = *cp != '\0' ? *cp++ : '0';
    1120            0 :         fstr[i] = '\0';
    1121            0 :         *fsec = strtoint(fstr, NULL, 10);
    1122            0 :         *cp = '\0';
    1123            0 :         len = strlen(str);
    1124              :     }
    1125              :     /* No decimal point and no complete date yet? */
    1126           30 :     else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
    1127              :     {
    1128              :         /* yyyymmdd? */
    1129           30 :         if (len == 8)
    1130              :         {
    1131           18 :             *tmask = DTK_DATE_M;
    1132              : 
    1133           18 :             tm->tm_mday = atoi(str + 6);
    1134           18 :             *(str + 6) = '\0';
    1135           18 :             tm->tm_mon = atoi(str + 4);
    1136           18 :             *(str + 4) = '\0';
    1137           18 :             tm->tm_year = atoi(str + 0);
    1138              : 
    1139           18 :             return DTK_DATE;
    1140              :         }
    1141              :         /* yymmdd? */
    1142           12 :         else if (len == 6)
    1143              :         {
    1144           12 :             *tmask = DTK_DATE_M;
    1145           12 :             tm->tm_mday = atoi(str + 4);
    1146           12 :             *(str + 4) = '\0';
    1147           12 :             tm->tm_mon = atoi(str + 2);
    1148           12 :             *(str + 2) = '\0';
    1149           12 :             tm->tm_year = atoi(str + 0);
    1150           12 :             *is2digits = true;
    1151              : 
    1152           12 :             return DTK_DATE;
    1153              :         }
    1154              :         /* yyddd? */
    1155            0 :         else if (len == 5)
    1156              :         {
    1157            0 :             *tmask = DTK_DATE_M;
    1158            0 :             tm->tm_mday = atoi(str + 2);
    1159            0 :             *(str + 2) = '\0';
    1160            0 :             tm->tm_mon = 1;
    1161            0 :             tm->tm_year = atoi(str + 0);
    1162            0 :             *is2digits = true;
    1163              : 
    1164            0 :             return DTK_DATE;
    1165              :         }
    1166              :     }
    1167              : 
    1168              :     /* not all time fields are specified? */
    1169            0 :     if ((fmask & DTK_TIME_M) != DTK_TIME_M)
    1170              :     {
    1171              :         /* hhmmss */
    1172            0 :         if (len == 6)
    1173              :         {
    1174            0 :             *tmask = DTK_TIME_M;
    1175            0 :             tm->tm_sec = atoi(str + 4);
    1176            0 :             *(str + 4) = '\0';
    1177            0 :             tm->tm_min = atoi(str + 2);
    1178            0 :             *(str + 2) = '\0';
    1179            0 :             tm->tm_hour = atoi(str + 0);
    1180              : 
    1181            0 :             return DTK_TIME;
    1182              :         }
    1183              :         /* hhmm? */
    1184            0 :         else if (len == 4)
    1185              :         {
    1186            0 :             *tmask = DTK_TIME_M;
    1187            0 :             tm->tm_sec = 0;
    1188            0 :             tm->tm_min = atoi(str + 2);
    1189            0 :             *(str + 2) = '\0';
    1190            0 :             tm->tm_hour = atoi(str + 0);
    1191              : 
    1192            0 :             return DTK_TIME;
    1193              :         }
    1194              :     }
    1195              : 
    1196            0 :     return -1;
    1197              : }                               /* DecodeNumberField() */
    1198              : 
    1199              : 
    1200              : /*
    1201              :  * DecodeNumber()
    1202              :  * Interpret plain numeric field as a date value in context.
    1203              :  */
    1204              : static int
    1205          796 : DecodeNumber(int flen, char *str, int fmask,
    1206              :              int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits, bool EuroDates)
    1207              : {
    1208              :     int         val;
    1209              :     char       *cp;
    1210              : 
    1211          796 :     *tmask = 0;
    1212              : 
    1213          796 :     val = strtoint(str, &cp, 10);
    1214          796 :     if (cp == str)
    1215            0 :         return -1;
    1216              : 
    1217          796 :     if (*cp == '.')
    1218              :     {
    1219              :         /*
    1220              :          * More than two digits? Then could be a date or a run-together time:
    1221              :          * 2001.360 20011225 040506.789
    1222              :          */
    1223            0 :         if (cp - str > 2)
    1224            0 :             return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
    1225              :                                      tmask, tm, fsec, is2digits);
    1226              : 
    1227            0 :         *fsec = strtod(cp, &cp);
    1228            0 :         if (*cp != '\0')
    1229            0 :             return -1;
    1230              :     }
    1231          796 :     else if (*cp != '\0')
    1232            0 :         return -1;
    1233              : 
    1234              :     /* Special case day of year? */
    1235          796 :     if (flen == 3 && (fmask & DTK_M(YEAR)) && val >= 1 && val <= 366)
    1236              :     {
    1237           12 :         *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
    1238           12 :         tm->tm_yday = val;
    1239           12 :         j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
    1240              :                &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    1241              :     }
    1242              : 
    1243              :     /***
    1244              :      * Enough digits to be unequivocal year? Used to test for 4 digits or
    1245              :      * more, but we now test first for a three-digit doy so anything
    1246              :      * bigger than two digits had better be an explicit year.
    1247              :      * - thomas 1999-01-09
    1248              :      * Back to requiring a 4 digit year. We accept a two digit
    1249              :      * year farther down. - thomas 2000-03-28
    1250              :      ***/
    1251          784 :     else if (flen >= 4)
    1252              :     {
    1253          240 :         *tmask = DTK_M(YEAR);
    1254              : 
    1255              :         /* already have a year? then see if we can substitute... */
    1256          240 :         if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
    1257            0 :             tm->tm_year >= 1 && tm->tm_year <= 31)
    1258              :         {
    1259            0 :             tm->tm_mday = tm->tm_year;
    1260            0 :             *tmask = DTK_M(DAY);
    1261              :         }
    1262              : 
    1263          240 :         tm->tm_year = val;
    1264              :     }
    1265              : 
    1266              :     /* already have year? then could be month */
    1267          544 :     else if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
    1268              :     {
    1269          112 :         *tmask = DTK_M(MONTH);
    1270          112 :         tm->tm_mon = val;
    1271              :     }
    1272              :     /* no year and EuroDates enabled? then could be day */
    1273          432 :     else if ((EuroDates || (fmask & DTK_M(MONTH))) &&
    1274          370 :              !(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
    1275          174 :              val >= 1 && val <= 31)
    1276              :     {
    1277          162 :         *tmask = DTK_M(DAY);
    1278          162 :         tm->tm_mday = val;
    1279              :     }
    1280          270 :     else if (!(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
    1281              :     {
    1282           60 :         *tmask = DTK_M(MONTH);
    1283           60 :         tm->tm_mon = val;
    1284              :     }
    1285          210 :     else if (!(fmask & DTK_M(DAY)) && val >= 1 && val <= 31)
    1286              :     {
    1287          136 :         *tmask = DTK_M(DAY);
    1288          136 :         tm->tm_mday = val;
    1289              :     }
    1290              : 
    1291              :     /*
    1292              :      * Check for 2 or 4 or more digits, but currently we reach here only if
    1293              :      * two digits. - thomas 2000-03-28
    1294              :      */
    1295           74 :     else if (!(fmask & DTK_M(YEAR)) && (flen >= 4 || flen == 2))
    1296              :     {
    1297           74 :         *tmask = DTK_M(YEAR);
    1298           74 :         tm->tm_year = val;
    1299              : 
    1300              :         /* adjust ONLY if exactly two digits... */
    1301           74 :         *is2digits = (flen == 2);
    1302              :     }
    1303              :     else
    1304            0 :         return -1;
    1305              : 
    1306          796 :     return 0;
    1307              : }                               /* DecodeNumber() */
    1308              : 
    1309              : /*
    1310              :  * DecodeDate()
    1311              :  * Decode date string which includes delimiters.
    1312              :  * Insist on a complete set of fields.
    1313              :  */
    1314              : static int
    1315          266 : DecodeDate(char *str, int fmask, int *tmask, struct tm *tm, bool EuroDates)
    1316              : {
    1317              :     fsec_t      fsec;
    1318              : 
    1319          266 :     int         nf = 0;
    1320              :     int         i,
    1321              :                 len;
    1322          266 :     bool        bc = false;
    1323          266 :     bool        is2digits = false;
    1324              :     int         type,
    1325              :                 val,
    1326          266 :                 dmask = 0;
    1327              :     char       *field[MAXDATEFIELDS];
    1328              : 
    1329              :     /* parse this string... */
    1330         1048 :     while (*str != '\0' && nf < MAXDATEFIELDS)
    1331              :     {
    1332              :         /* skip field separators */
    1333          878 :         while (!isalnum((unsigned char) *str))
    1334           96 :             str++;
    1335              : 
    1336          782 :         field[nf] = str;
    1337          782 :         if (isdigit((unsigned char) *str))
    1338              :         {
    1339         2406 :             while (isdigit((unsigned char) *str))
    1340         1710 :                 str++;
    1341              :         }
    1342           86 :         else if (isalpha((unsigned char) *str))
    1343              :         {
    1344          546 :             while (isalpha((unsigned char) *str))
    1345          460 :                 str++;
    1346              :         }
    1347              : 
    1348              :         /* Just get rid of any non-digit, non-alpha characters... */
    1349          782 :         if (*str != '\0')
    1350          516 :             *str++ = '\0';
    1351          782 :         nf++;
    1352              :     }
    1353              : 
    1354              : #if 0
    1355              :     /* don't allow too many fields */
    1356              :     if (nf > 3)
    1357              :         return -1;
    1358              : #endif
    1359              : 
    1360          266 :     *tmask = 0;
    1361              : 
    1362              :     /* look first for text fields, since that will be unambiguous month */
    1363         1046 :     for (i = 0; i < nf; i++)
    1364              :     {
    1365          782 :         if (isalpha((unsigned char) *field[i]))
    1366              :         {
    1367           86 :             type = DecodeSpecial(i, field[i], &val);
    1368           86 :             if (type == IGNORE_DTF)
    1369            0 :                 continue;
    1370              : 
    1371           86 :             dmask = DTK_M(type);
    1372           86 :             switch (type)
    1373              :             {
    1374           84 :                 case MONTH:
    1375           84 :                     tm->tm_mon = val;
    1376           84 :                     break;
    1377              : 
    1378            0 :                 case ADBC:
    1379            0 :                     bc = (val == BC);
    1380            0 :                     break;
    1381              : 
    1382            2 :                 default:
    1383            2 :                     return -1;
    1384              :             }
    1385           84 :             if (fmask & dmask)
    1386            0 :                 return -1;
    1387              : 
    1388           84 :             fmask |= dmask;
    1389           84 :             *tmask |= dmask;
    1390              : 
    1391              :             /* mark this field as being completed */
    1392           84 :             field[i] = NULL;
    1393              :         }
    1394              :     }
    1395              : 
    1396              :     /* now pick up remaining numeric fields */
    1397         1044 :     for (i = 0; i < nf; i++)
    1398              :     {
    1399          780 :         if (field[i] == NULL)
    1400           84 :             continue;
    1401              : 
    1402          696 :         if ((len = strlen(field[i])) <= 0)
    1403            0 :             return -1;
    1404              : 
    1405          696 :         if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
    1406            0 :             return -1;
    1407              : 
    1408          696 :         if (fmask & dmask)
    1409            0 :             return -1;
    1410              : 
    1411          696 :         fmask |= dmask;
    1412          696 :         *tmask |= dmask;
    1413              :     }
    1414              : 
    1415          264 :     if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
    1416            0 :         return -1;
    1417              : 
    1418              :     /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
    1419          264 :     if (bc)
    1420              :     {
    1421            0 :         if (tm->tm_year > 0)
    1422            0 :             tm->tm_year = -(tm->tm_year - 1);
    1423              :         else
    1424            0 :             return -1;
    1425              :     }
    1426          264 :     else if (is2digits)
    1427              :     {
    1428           62 :         if (tm->tm_year < 70)
    1429           24 :             tm->tm_year += 2000;
    1430           38 :         else if (tm->tm_year < 100)
    1431           38 :             tm->tm_year += 1900;
    1432              :     }
    1433              : 
    1434          264 :     return 0;
    1435              : }                               /* DecodeDate() */
    1436              : 
    1437              : 
    1438              : /*
    1439              :  * DecodeTime()
    1440              :  * Decode time string which includes delimiters.
    1441              :  * Only check the lower limit on hours, since this same code
    1442              :  *  can be used to represent time spans.
    1443              :  */
    1444              : int
    1445          250 : DecodeTime(char *str, int *tmask, struct tm *tm, fsec_t *fsec)
    1446              : {
    1447              :     char       *cp;
    1448              : 
    1449          250 :     *tmask = DTK_TIME_M;
    1450              : 
    1451          250 :     tm->tm_hour = strtoint(str, &cp, 10);
    1452          250 :     if (*cp != ':')
    1453            0 :         return -1;
    1454          250 :     str = cp + 1;
    1455          250 :     tm->tm_min = strtoint(str, &cp, 10);
    1456          250 :     if (*cp == '\0')
    1457              :     {
    1458           68 :         tm->tm_sec = 0;
    1459           68 :         *fsec = 0;
    1460              :     }
    1461          182 :     else if (*cp != ':')
    1462            0 :         return -1;
    1463              :     else
    1464              :     {
    1465          182 :         str = cp + 1;
    1466          182 :         tm->tm_sec = strtoint(str, &cp, 10);
    1467          182 :         if (*cp == '\0')
    1468          114 :             *fsec = 0;
    1469           68 :         else if (*cp == '.')
    1470              :         {
    1471              :             char        fstr[7];
    1472              :             int         i;
    1473              : 
    1474           68 :             cp++;
    1475              : 
    1476              :             /*
    1477              :              * OK, we have at most six digits to care about. Let's construct a
    1478              :              * string with those digits, zero-padded on the right, and then do
    1479              :              * the conversion to an integer.
    1480              :              *
    1481              :              * XXX This truncates the seventh digit, unlike rounding it as the
    1482              :              * backend does.
    1483              :              */
    1484          476 :             for (i = 0; i < 6; i++)
    1485          408 :                 fstr[i] = *cp != '\0' ? *cp++ : '0';
    1486           68 :             fstr[i] = '\0';
    1487           68 :             *fsec = strtoint(fstr, &cp, 10);
    1488           68 :             if (*cp != '\0')
    1489            0 :                 return -1;
    1490              :         }
    1491              :         else
    1492            0 :             return -1;
    1493              :     }
    1494              : 
    1495              :     /* do a sanity check */
    1496          250 :     if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
    1497          250 :         tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
    1498            0 :         return -1;
    1499              : 
    1500          250 :     return 0;
    1501              : }                               /* DecodeTime() */
    1502              : 
    1503              : /*
    1504              :  * DecodeTimezone()
    1505              :  * Interpret string as a numeric timezone.
    1506              :  *
    1507              :  * Note: we allow timezone offsets up to 13:59.  There are places that
    1508              :  * use +1300 summer time.
    1509              :  */
    1510              : static int
    1511          118 : DecodeTimezone(char *str, int *tzp)
    1512              : {
    1513              :     int         tz;
    1514              :     int         hr,
    1515              :                 min;
    1516              :     char       *cp;
    1517              :     int         len;
    1518              : 
    1519              :     /* assume leading character is "+" or "-" */
    1520          118 :     hr = strtoint(str + 1, &cp, 10);
    1521              : 
    1522              :     /* explicit delimiter? */
    1523          118 :     if (*cp == ':')
    1524           34 :         min = strtoint(cp + 1, &cp, 10);
    1525              :     /* otherwise, might have run things together... */
    1526           84 :     else if (*cp == '\0' && (len = strlen(str)) > 3)
    1527              :     {
    1528           16 :         min = strtoint(str + len - 2, &cp, 10);
    1529           16 :         if (min < 0 || min >= 60)
    1530            0 :             return -1;
    1531              : 
    1532           16 :         *(str + len - 2) = '\0';
    1533           16 :         hr = strtoint(str + 1, &cp, 10);
    1534           16 :         if (hr < 0 || hr > 13)
    1535            0 :             return -1;
    1536              :     }
    1537              :     else
    1538           68 :         min = 0;
    1539              : 
    1540          118 :     tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE;
    1541          118 :     if (*str == '-')
    1542           34 :         tz = -tz;
    1543              : 
    1544          118 :     *tzp = -tz;
    1545          118 :     return *cp != '\0';
    1546              : }                               /* DecodeTimezone() */
    1547              : 
    1548              : 
    1549              : /*
    1550              :  * DecodePosixTimezone()
    1551              :  * Interpret string as a POSIX-compatible timezone:
    1552              :  *  PST-hh:mm
    1553              :  *  PST+h
    1554              :  * - thomas 2000-03-15
    1555              :  */
    1556              : static int
    1557            0 : DecodePosixTimezone(char *str, int *tzp)
    1558              : {
    1559              :     int         val,
    1560              :                 tz;
    1561              :     int         type;
    1562              :     char       *cp;
    1563              :     char        delim;
    1564              : 
    1565            0 :     cp = str;
    1566            0 :     while (*cp != '\0' && isalpha((unsigned char) *cp))
    1567            0 :         cp++;
    1568              : 
    1569            0 :     if (DecodeTimezone(cp, &tz) != 0)
    1570            0 :         return -1;
    1571              : 
    1572            0 :     delim = *cp;
    1573            0 :     *cp = '\0';
    1574            0 :     type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
    1575            0 :     *cp = delim;
    1576              : 
    1577            0 :     switch (type)
    1578              :     {
    1579            0 :         case DTZ:
    1580              :         case TZ:
    1581            0 :             *tzp = -(val + tz);
    1582            0 :             break;
    1583              : 
    1584            0 :         default:
    1585            0 :             return -1;
    1586              :     }
    1587              : 
    1588            0 :     return 0;
    1589              : }                               /* DecodePosixTimezone() */
    1590              : 
    1591              : /*
    1592              :  * ParseDateTime()
    1593              :  * Break string into tokens based on a date/time context.
    1594              :  * Several field types are assigned:
    1595              :  *  DTK_NUMBER - digits and (possibly) a decimal point
    1596              :  *  DTK_DATE - digits and two delimiters, or digits and text
    1597              :  *  DTK_TIME - digits, colon delimiters, and possibly a decimal point
    1598              :  *  DTK_STRING - text (no digits)
    1599              :  *  DTK_SPECIAL - leading "+" or "-" followed by text
    1600              :  *  DTK_TZ - leading "+" or "-" followed by digits
    1601              :  * Note that some field types can hold unexpected items:
    1602              :  *  DTK_NUMBER can hold date fields (yy.ddd)
    1603              :  *  DTK_STRING can hold months (January) and time zones (PST)
    1604              :  *  DTK_DATE can hold Posix time zones (GMT-8)
    1605              :  *
    1606              :  * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS
    1607              :  * bytes of space.  On output, field[] entries will point into it.
    1608              :  * The field[] and ftype[] arrays must have at least MAXDATEFIELDS entries.
    1609              :  */
    1610              : int
    1611          418 : ParseDateTime(char *timestr, char *lowstr,
    1612              :               char **field, int *ftype, int *numfields, char **endstr)
    1613              : {
    1614          418 :     int         nf = 0;
    1615          418 :     char       *lp = lowstr;
    1616              : 
    1617          418 :     *endstr = timestr;
    1618              :     /* outer loop through fields */
    1619         2274 :     while (*(*endstr) != '\0')
    1620              :     {
    1621              :         /* Record start of current field */
    1622         1858 :         if (nf >= MAXDATEFIELDS)
    1623            2 :             return -1;
    1624         1856 :         field[nf] = lp;
    1625              : 
    1626              :         /* leading digit? then date or time */
    1627         1856 :         if (isdigit((unsigned char) *(*endstr)))
    1628              :         {
    1629          724 :             *lp++ = *(*endstr)++;
    1630         1872 :             while (isdigit((unsigned char) *(*endstr)))
    1631         1148 :                 *lp++ = *(*endstr)++;
    1632              : 
    1633              :             /* time field? */
    1634          724 :             if (*(*endstr) == ':')
    1635              :             {
    1636          250 :                 ftype[nf] = DTK_TIME;
    1637          250 :                 *lp++ = *(*endstr)++;
    1638          250 :                 while (isdigit((unsigned char) *(*endstr)) ||
    1639         1772 :                        (*(*endstr) == ':') || (*(*endstr) == '.'))
    1640         1522 :                     *lp++ = *(*endstr)++;
    1641              :             }
    1642              :             /* date field? allow embedded text month */
    1643          474 :             else if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
    1644          240 :             {
    1645              :                 /* save delimiting character to use later */
    1646          240 :                 char       *dp = (*endstr);
    1647              : 
    1648          240 :                 *lp++ = *(*endstr)++;
    1649              :                 /* second field is all digits? then no embedded text month */
    1650          240 :                 if (isdigit((unsigned char) *(*endstr)))
    1651              :                 {
    1652          180 :                     ftype[nf] = (*dp == '.') ? DTK_NUMBER : DTK_DATE;
    1653          498 :                     while (isdigit((unsigned char) *(*endstr)))
    1654          318 :                         *lp++ = *(*endstr)++;
    1655              : 
    1656              :                     /*
    1657              :                      * insist that the delimiters match to get a three-field
    1658              :                      * date.
    1659              :                      */
    1660          180 :                     if (*(*endstr) == *dp)
    1661              :                     {
    1662          168 :                         ftype[nf] = DTK_DATE;
    1663          168 :                         *lp++ = *(*endstr)++;
    1664          572 :                         while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
    1665          404 :                             *lp++ = *(*endstr)++;
    1666              :                     }
    1667              :                 }
    1668              :                 else
    1669              :                 {
    1670           60 :                     ftype[nf] = DTK_DATE;
    1671          444 :                     while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
    1672          384 :                         *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1673              :                 }
    1674              :             }
    1675              : 
    1676              :             /*
    1677              :              * otherwise, number only and will determine year, month, day, or
    1678              :              * concatenated fields later...
    1679              :              */
    1680              :             else
    1681          234 :                 ftype[nf] = DTK_NUMBER;
    1682              :         }
    1683              :         /* Leading decimal point? Then fractional seconds... */
    1684         1132 :         else if (*(*endstr) == '.')
    1685              :         {
    1686           98 :             *lp++ = *(*endstr)++;
    1687           98 :             while (isdigit((unsigned char) *(*endstr)))
    1688            0 :                 *lp++ = *(*endstr)++;
    1689              : 
    1690           98 :             ftype[nf] = DTK_NUMBER;
    1691              :         }
    1692              : 
    1693              :         /*
    1694              :          * text? then date string, month, day of week, special, or timezone
    1695              :          */
    1696         1034 :         else if (isalpha((unsigned char) *(*endstr)))
    1697              :         {
    1698          260 :             ftype[nf] = DTK_STRING;
    1699          260 :             *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1700         1242 :             while (isalpha((unsigned char) *(*endstr)))
    1701          982 :                 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1702              : 
    1703              :             /*
    1704              :              * Full date string with leading text month? Could also be a POSIX
    1705              :              * time zone...
    1706              :              */
    1707          260 :             if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
    1708              :             {
    1709           24 :                 char       *dp = (*endstr);
    1710              : 
    1711           24 :                 ftype[nf] = DTK_DATE;
    1712           24 :                 *lp++ = *(*endstr)++;
    1713          168 :                 while (isdigit((unsigned char) *(*endstr)) || *(*endstr) == *dp)
    1714          144 :                     *lp++ = *(*endstr)++;
    1715              :             }
    1716              :         }
    1717              :         /* skip leading spaces */
    1718          774 :         else if (isspace((unsigned char) *(*endstr)))
    1719              :         {
    1720          628 :             (*endstr)++;
    1721          628 :             continue;
    1722              :         }
    1723              :         /* sign? then special or numeric timezone */
    1724          146 :         else if (*(*endstr) == '+' || *(*endstr) == '-')
    1725              :         {
    1726          102 :             *lp++ = *(*endstr)++;
    1727              :             /* soak up leading whitespace */
    1728          102 :             while (isspace((unsigned char) *(*endstr)))
    1729            0 :                 (*endstr)++;
    1730              :             /* numeric timezone? */
    1731          204 :             if (isdigit((unsigned char) *(*endstr)))
    1732              :             {
    1733          102 :                 ftype[nf] = DTK_TZ;
    1734          102 :                 *lp++ = *(*endstr)++;
    1735          102 :                 while (isdigit((unsigned char) *(*endstr)) ||
    1736          204 :                        (*(*endstr) == ':') || (*(*endstr) == '.'))
    1737          102 :                     *lp++ = *(*endstr)++;
    1738              :             }
    1739              :             /* special? */
    1740            0 :             else if (isalpha((unsigned char) *(*endstr)))
    1741              :             {
    1742            0 :                 ftype[nf] = DTK_SPECIAL;
    1743            0 :                 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1744            0 :                 while (isalpha((unsigned char) *(*endstr)))
    1745            0 :                     *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1746              :             }
    1747              :             /* otherwise something wrong... */
    1748              :             else
    1749            0 :                 return -1;
    1750              :         }
    1751              :         /* ignore punctuation but use as delimiter */
    1752           44 :         else if (ispunct((unsigned char) *(*endstr)))
    1753              :         {
    1754           44 :             (*endstr)++;
    1755           44 :             continue;
    1756              :         }
    1757              :         /* otherwise, something is not right... */
    1758              :         else
    1759            0 :             return -1;
    1760              : 
    1761              :         /* force in a delimiter after each field */
    1762         1184 :         *lp++ = '\0';
    1763         1184 :         nf++;
    1764              :     }
    1765              : 
    1766          416 :     *numfields = nf;
    1767              : 
    1768          416 :     return 0;
    1769              : }                               /* ParseDateTime() */
    1770              : 
    1771              : 
    1772              : /*
    1773              :  * DecodeDateTime()
    1774              :  * Interpret previously parsed fields for general date and time.
    1775              :  * Return 0 if full date, 1 if only time, and -1 if problems.
    1776              :  *      External format(s):
    1777              :  *              "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
    1778              :  *              "Fri Feb-7-1997 15:23:27"
    1779              :  *              "Feb-7-1997 15:23:27"
    1780              :  *              "2-7-1997 15:23:27"
    1781              :  *              "1997-2-7 15:23:27"
    1782              :  *              "1997.038 15:23:27"       (day of year 1-366)
    1783              :  *      Also supports input in compact time:
    1784              :  *              "970207 152327"
    1785              :  *              "97038 152327"
    1786              :  *              "20011225T040506.789-07"
    1787              :  *
    1788              :  * Use the system-provided functions to get the current time zone
    1789              :  *  if not specified in the input string.
    1790              :  * If the date is outside the time_t system-supported time range,
    1791              :  *  then assume UTC time zone. - thomas 1997-05-27
    1792              :  */
    1793              : int
    1794          358 : DecodeDateTime(char **field, int *ftype, int nf,
    1795              :                int *dtype, struct tm *tm, fsec_t *fsec, bool EuroDates)
    1796              : {
    1797          358 :     int         fmask = 0,
    1798              :                 tmask,
    1799              :                 type;
    1800          358 :     int         ptype = 0;      /* "prefix type" for ISO y2001m02d04 format */
    1801              :     int         i;
    1802              :     int         val;
    1803          358 :     int         mer = HR24;
    1804          358 :     bool        haveTextMonth = false;
    1805          358 :     bool        is2digits = false;
    1806          358 :     bool        bc = false;
    1807          358 :     int         t = 0;
    1808          358 :     int        *tzp = &t;
    1809              : 
    1810              :     /***
    1811              :      * We'll insist on at least all of the date fields, but initialize the
    1812              :      * remaining fields in case they are not set later...
    1813              :      ***/
    1814          358 :     *dtype = DTK_DATE;
    1815          358 :     tm->tm_hour = 0;
    1816          358 :     tm->tm_min = 0;
    1817          358 :     tm->tm_sec = 0;
    1818          358 :     *fsec = 0;
    1819              :     /* don't know daylight savings time status apriori */
    1820          358 :     tm->tm_isdst = -1;
    1821          358 :     if (tzp != NULL)
    1822          358 :         *tzp = 0;
    1823              : 
    1824         1248 :     for (i = 0; i < nf; i++)
    1825              :     {
    1826          900 :         switch (ftype[i])
    1827              :         {
    1828          252 :             case DTK_DATE:
    1829              :                 /***
    1830              :                  * Integral julian day with attached time zone?
    1831              :                  * All other forms with JD will be separated into
    1832              :                  * distinct fields, so we handle just this case here.
    1833              :                  ***/
    1834          252 :                 if (ptype == DTK_JULIAN)
    1835              :                 {
    1836              :                     char       *cp;
    1837              :                     int         jday;
    1838              : 
    1839            0 :                     if (tzp == NULL)
    1840            0 :                         return -1;
    1841              : 
    1842            0 :                     jday = strtoint(field[i], &cp, 10);
    1843            0 :                     if (*cp != '-')
    1844            0 :                         return -1;
    1845              : 
    1846            0 :                     j2date(jday, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    1847              :                     /* Get the time zone from the end of the string */
    1848            0 :                     if (DecodeTimezone(cp, tzp) != 0)
    1849            0 :                         return -1;
    1850              : 
    1851            0 :                     tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
    1852            0 :                     ptype = 0;
    1853            0 :                     break;
    1854              :                 }
    1855              :                 /***
    1856              :                  * Already have a date? Then this might be a POSIX time
    1857              :                  * zone with an embedded dash (e.g. "PST-3" == "EST") or
    1858              :                  * a run-together time with trailing time zone (e.g. hhmmss-zz).
    1859              :                  * - thomas 2001-12-25
    1860              :                  ***/
    1861          252 :                 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
    1862          252 :                          || (ptype != 0))
    1863              :                 {
    1864              :                     /* No time zone accepted? Then quit... */
    1865            0 :                     if (tzp == NULL)
    1866            0 :                         return -1;
    1867              : 
    1868            0 :                     if (isdigit((unsigned char) *field[i]) || ptype != 0)
    1869            0 :                     {
    1870              :                         char       *cp;
    1871              : 
    1872            0 :                         if (ptype != 0)
    1873              :                         {
    1874              :                             /* Sanity check; should not fail this test */
    1875            0 :                             if (ptype != DTK_TIME)
    1876            0 :                                 return -1;
    1877            0 :                             ptype = 0;
    1878              :                         }
    1879              : 
    1880              :                         /*
    1881              :                          * Starts with a digit but we already have a time
    1882              :                          * field? Then we are in trouble with a date and time
    1883              :                          * already...
    1884              :                          */
    1885            0 :                         if ((fmask & DTK_TIME_M) == DTK_TIME_M)
    1886            0 :                             return -1;
    1887              : 
    1888            0 :                         if ((cp = strchr(field[i], '-')) == NULL)
    1889            0 :                             return -1;
    1890              : 
    1891              :                         /* Get the time zone from the end of the string */
    1892            0 :                         if (DecodeTimezone(cp, tzp) != 0)
    1893            0 :                             return -1;
    1894            0 :                         *cp = '\0';
    1895              : 
    1896              :                         /*
    1897              :                          * Then read the rest of the field as a concatenated
    1898              :                          * time
    1899              :                          */
    1900            0 :                         if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
    1901              :                                                           &tmask, tm, fsec, &is2digits)) < 0)
    1902            0 :                             return -1;
    1903              : 
    1904              :                         /*
    1905              :                          * modify tmask after returning from
    1906              :                          * DecodeNumberField()
    1907              :                          */
    1908            0 :                         tmask |= DTK_M(TZ);
    1909              :                     }
    1910              :                     else
    1911              :                     {
    1912            0 :                         if (DecodePosixTimezone(field[i], tzp) != 0)
    1913            0 :                             return -1;
    1914              : 
    1915            0 :                         ftype[i] = DTK_TZ;
    1916            0 :                         tmask = DTK_M(TZ);
    1917              :                     }
    1918              :                 }
    1919          252 :                 else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
    1920            0 :                     return -1;
    1921          252 :                 break;
    1922              : 
    1923          248 :             case DTK_TIME:
    1924          248 :                 if (DecodeTime(field[i], &tmask, tm, fsec) != 0)
    1925            0 :                     return -1;
    1926              : 
    1927              :                 /*
    1928              :                  * Check upper limit on hours; other limits checked in
    1929              :                  * DecodeTime()
    1930              :                  */
    1931              :                 /* test for > 24:00:00 */
    1932          248 :                 if (tm->tm_hour > 24 ||
    1933          246 :                     (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)))
    1934            2 :                     return -1;
    1935          246 :                 break;
    1936              : 
    1937          102 :             case DTK_TZ:
    1938              :                 {
    1939              :                     int         tz;
    1940              : 
    1941          102 :                     if (tzp == NULL)
    1942            0 :                         return -1;
    1943              : 
    1944          102 :                     if (DecodeTimezone(field[i], &tz) != 0)
    1945            0 :                         return -1;
    1946              : 
    1947              :                     /*
    1948              :                      * Already have a time zone? Then maybe this is the second
    1949              :                      * field of a POSIX time: EST+3 (equivalent to PST)
    1950              :                      */
    1951          102 :                     if (i > 0 && (fmask & DTK_M(TZ)) != 0 &&
    1952            0 :                         ftype[i - 1] == DTK_TZ &&
    1953            0 :                         isalpha((unsigned char) *field[i - 1]))
    1954              :                     {
    1955            0 :                         *tzp -= tz;
    1956            0 :                         tmask = 0;
    1957              :                     }
    1958              :                     else
    1959              :                     {
    1960          102 :                         *tzp = tz;
    1961          102 :                         tmask = DTK_M(TZ);
    1962              :                     }
    1963              :                 }
    1964          102 :                 break;
    1965              : 
    1966          156 :             case DTK_NUMBER:
    1967              : 
    1968              :                 /*
    1969              :                  * Was this an "ISO date" with embedded field labels? An
    1970              :                  * example is "y2001m02d04" - thomas 2001-02-04
    1971              :                  */
    1972          156 :                 if (ptype != 0)
    1973              :                 {
    1974              :                     char       *cp;
    1975              :                     int         value;
    1976              : 
    1977           12 :                     value = strtoint(field[i], &cp, 10);
    1978              : 
    1979              :                     /*
    1980              :                      * only a few kinds are allowed to have an embedded
    1981              :                      * decimal
    1982              :                      */
    1983           12 :                     if (*cp == '.')
    1984            0 :                         switch (ptype)
    1985              :                         {
    1986            0 :                             case DTK_JULIAN:
    1987              :                             case DTK_TIME:
    1988              :                             case DTK_SECOND:
    1989            0 :                                 break;
    1990            0 :                             default:
    1991            0 :                                 return 1;
    1992              :                                 break;
    1993              :                         }
    1994           12 :                     else if (*cp != '\0')
    1995            0 :                         return -1;
    1996              : 
    1997           12 :                     switch (ptype)
    1998              :                     {
    1999            0 :                         case DTK_YEAR:
    2000            0 :                             tm->tm_year = value;
    2001            0 :                             tmask = DTK_M(YEAR);
    2002            0 :                             break;
    2003              : 
    2004            0 :                         case DTK_MONTH:
    2005              : 
    2006              :                             /*
    2007              :                              * already have a month and hour? then assume
    2008              :                              * minutes
    2009              :                              */
    2010            0 :                             if ((fmask & DTK_M(MONTH)) != 0 &&
    2011            0 :                                 (fmask & DTK_M(HOUR)) != 0)
    2012              :                             {
    2013            0 :                                 tm->tm_min = value;
    2014            0 :                                 tmask = DTK_M(MINUTE);
    2015              :                             }
    2016              :                             else
    2017              :                             {
    2018            0 :                                 tm->tm_mon = value;
    2019            0 :                                 tmask = DTK_M(MONTH);
    2020              :                             }
    2021            0 :                             break;
    2022              : 
    2023            0 :                         case DTK_DAY:
    2024            0 :                             tm->tm_mday = value;
    2025            0 :                             tmask = DTK_M(DAY);
    2026            0 :                             break;
    2027              : 
    2028            0 :                         case DTK_HOUR:
    2029            0 :                             tm->tm_hour = value;
    2030            0 :                             tmask = DTK_M(HOUR);
    2031            0 :                             break;
    2032              : 
    2033            0 :                         case DTK_MINUTE:
    2034            0 :                             tm->tm_min = value;
    2035            0 :                             tmask = DTK_M(MINUTE);
    2036            0 :                             break;
    2037              : 
    2038            0 :                         case DTK_SECOND:
    2039            0 :                             tm->tm_sec = value;
    2040            0 :                             tmask = DTK_M(SECOND);
    2041            0 :                             if (*cp == '.')
    2042              :                             {
    2043              :                                 double      frac;
    2044              : 
    2045            0 :                                 frac = strtod(cp, &cp);
    2046            0 :                                 if (*cp != '\0')
    2047            0 :                                     return -1;
    2048            0 :                                 *fsec = frac * 1000000;
    2049              :                             }
    2050            0 :                             break;
    2051              : 
    2052            0 :                         case DTK_TZ:
    2053            0 :                             tmask = DTK_M(TZ);
    2054            0 :                             if (DecodeTimezone(field[i], tzp) != 0)
    2055            0 :                                 return -1;
    2056            0 :                             break;
    2057              : 
    2058           12 :                         case DTK_JULIAN:
    2059              :                             /***
    2060              :                              * previous field was a label for "julian date"?
    2061              :                              ***/
    2062           12 :                             tmask = DTK_DATE_M;
    2063           12 :                             j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    2064              :                             /* fractional Julian Day? */
    2065           12 :                             if (*cp == '.')
    2066              :                             {
    2067              :                                 double      time;
    2068              : 
    2069            0 :                                 time = strtod(cp, &cp);
    2070            0 :                                 if (*cp != '\0')
    2071            0 :                                     return -1;
    2072              : 
    2073            0 :                                 tmask |= DTK_TIME_M;
    2074            0 :                                 dt2time((time * USECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
    2075              :                             }
    2076           12 :                             break;
    2077              : 
    2078            0 :                         case DTK_TIME:
    2079              :                             /* previous field was "t" for ISO time */
    2080            0 :                             if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
    2081              :                                                               &tmask, tm, fsec, &is2digits)) < 0)
    2082            0 :                                 return -1;
    2083              : 
    2084            0 :                             if (tmask != DTK_TIME_M)
    2085            0 :                                 return -1;
    2086            0 :                             break;
    2087              : 
    2088            0 :                         default:
    2089            0 :                             return -1;
    2090              :                             break;
    2091              :                     }
    2092              : 
    2093           12 :                     ptype = 0;
    2094           12 :                     *dtype = DTK_DATE;
    2095              :                 }
    2096              :                 else
    2097              :                 {
    2098              :                     char       *cp;
    2099              :                     int         flen;
    2100              : 
    2101          144 :                     flen = strlen(field[i]);
    2102          144 :                     cp = strchr(field[i], '.');
    2103              : 
    2104              :                     /* Embedded decimal and no date yet? */
    2105          144 :                     if (cp != NULL && !(fmask & DTK_DATE_M))
    2106              :                     {
    2107           14 :                         if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
    2108            2 :                             return -1;
    2109              :                     }
    2110              :                     /* embedded decimal and several digits before? */
    2111          130 :                     else if (cp != NULL && flen - strlen(cp) > 2)
    2112              :                     {
    2113              :                         /*
    2114              :                          * Interpret as a concatenated date or time Set the
    2115              :                          * type field to allow decoding other fields later.
    2116              :                          * Example: 20011223 or 040506
    2117              :                          */
    2118            0 :                         if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
    2119              :                                                           &tmask, tm, fsec, &is2digits)) < 0)
    2120            0 :                             return -1;
    2121              :                     }
    2122          130 :                     else if (flen > 4)
    2123              :                     {
    2124           30 :                         if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
    2125              :                                                           &tmask, tm, fsec, &is2digits)) < 0)
    2126            0 :                             return -1;
    2127              :                     }
    2128              :                     /* otherwise it is a single date/time field... */
    2129          100 :                     else if (DecodeNumber(flen, field[i], fmask,
    2130              :                                           &tmask, tm, fsec, &is2digits, EuroDates) != 0)
    2131            0 :                         return -1;
    2132              :                 }
    2133          154 :                 break;
    2134              : 
    2135          142 :             case DTK_STRING:
    2136              :             case DTK_SPECIAL:
    2137          142 :                 type = DecodeSpecial(i, field[i], &val);
    2138          142 :                 if (type == IGNORE_DTF)
    2139            0 :                     continue;
    2140              : 
    2141          142 :                 tmask = DTK_M(type);
    2142          142 :                 switch (type)
    2143              :                 {
    2144            0 :                     case RESERV:
    2145            0 :                         switch (val)
    2146              :                         {
    2147            0 :                             case DTK_NOW:
    2148            0 :                                 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
    2149            0 :                                 *dtype = DTK_DATE;
    2150            0 :                                 GetCurrentDateTime(tm);
    2151            0 :                                 break;
    2152              : 
    2153            0 :                             case DTK_YESTERDAY:
    2154            0 :                                 tmask = DTK_DATE_M;
    2155            0 :                                 *dtype = DTK_DATE;
    2156            0 :                                 GetCurrentDateTime(tm);
    2157            0 :                                 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1,
    2158              :                                        &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    2159            0 :                                 tm->tm_hour = 0;
    2160            0 :                                 tm->tm_min = 0;
    2161            0 :                                 tm->tm_sec = 0;
    2162            0 :                                 break;
    2163              : 
    2164            0 :                             case DTK_TODAY:
    2165            0 :                                 tmask = DTK_DATE_M;
    2166            0 :                                 *dtype = DTK_DATE;
    2167            0 :                                 GetCurrentDateTime(tm);
    2168            0 :                                 tm->tm_hour = 0;
    2169            0 :                                 tm->tm_min = 0;
    2170            0 :                                 tm->tm_sec = 0;
    2171            0 :                                 break;
    2172              : 
    2173            0 :                             case DTK_TOMORROW:
    2174            0 :                                 tmask = DTK_DATE_M;
    2175            0 :                                 *dtype = DTK_DATE;
    2176            0 :                                 GetCurrentDateTime(tm);
    2177            0 :                                 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1,
    2178              :                                        &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    2179            0 :                                 tm->tm_hour = 0;
    2180            0 :                                 tm->tm_min = 0;
    2181            0 :                                 tm->tm_sec = 0;
    2182            0 :                                 break;
    2183              : 
    2184            0 :                             case DTK_ZULU:
    2185            0 :                                 tmask = (DTK_TIME_M | DTK_M(TZ));
    2186            0 :                                 *dtype = DTK_DATE;
    2187            0 :                                 tm->tm_hour = 0;
    2188            0 :                                 tm->tm_min = 0;
    2189            0 :                                 tm->tm_sec = 0;
    2190            0 :                                 if (tzp != NULL)
    2191            0 :                                     *tzp = 0;
    2192            0 :                                 break;
    2193              : 
    2194            0 :                             default:
    2195            0 :                                 *dtype = val;
    2196              :                         }
    2197              : 
    2198            0 :                         break;
    2199              : 
    2200           50 :                     case MONTH:
    2201              : 
    2202              :                         /*
    2203              :                          * already have a (numeric) month? then see if we can
    2204              :                          * substitute...
    2205              :                          */
    2206           50 :                         if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
    2207            4 :                             !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && tm->tm_mon <= 31)
    2208              :                         {
    2209            4 :                             tm->tm_mday = tm->tm_mon;
    2210            4 :                             tmask = DTK_M(DAY);
    2211              :                         }
    2212           50 :                         haveTextMonth = true;
    2213           50 :                         tm->tm_mon = val;
    2214           50 :                         break;
    2215              : 
    2216            0 :                     case DTZMOD:
    2217              : 
    2218              :                         /*
    2219              :                          * daylight savings time modifier (solves "MET DST"
    2220              :                          * syntax)
    2221              :                          */
    2222            0 :                         tmask |= DTK_M(DTZ);
    2223            0 :                         tm->tm_isdst = 1;
    2224            0 :                         if (tzp == NULL)
    2225            0 :                             return -1;
    2226            0 :                         *tzp -= val;
    2227            0 :                         break;
    2228              : 
    2229           34 :                     case DTZ:
    2230              : 
    2231              :                         /*
    2232              :                          * set mask for TZ here _or_ check for DTZ later when
    2233              :                          * getting default timezone
    2234              :                          */
    2235           34 :                         tmask |= DTK_M(TZ);
    2236           34 :                         tm->tm_isdst = 1;
    2237           34 :                         if (tzp == NULL)
    2238            0 :                             return -1;
    2239           34 :                         *tzp = -val;
    2240           34 :                         ftype[i] = DTK_TZ;
    2241           34 :                         break;
    2242              : 
    2243            0 :                     case TZ:
    2244            0 :                         tm->tm_isdst = 0;
    2245            0 :                         if (tzp == NULL)
    2246            0 :                             return -1;
    2247            0 :                         *tzp = -val;
    2248            0 :                         ftype[i] = DTK_TZ;
    2249            0 :                         break;
    2250              : 
    2251            0 :                     case IGNORE_DTF:
    2252            0 :                         break;
    2253              : 
    2254            2 :                     case AMPM:
    2255            2 :                         mer = val;
    2256            2 :                         break;
    2257              : 
    2258           12 :                     case ADBC:
    2259           12 :                         bc = (val == BC);
    2260           12 :                         break;
    2261              : 
    2262           26 :                     case DOW:
    2263           26 :                         tm->tm_wday = val;
    2264           26 :                         break;
    2265              : 
    2266           12 :                     case UNITS:
    2267           12 :                         tmask = 0;
    2268           12 :                         ptype = val;
    2269           12 :                         break;
    2270              : 
    2271            0 :                     case ISOTIME:
    2272              : 
    2273              :                         /*
    2274              :                          * This is a filler field "t" indicating that the next
    2275              :                          * field is time. Try to verify that this is sensible.
    2276              :                          */
    2277            0 :                         tmask = 0;
    2278              : 
    2279              :                         /* No preceding date? Then quit... */
    2280            0 :                         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
    2281            0 :                             return -1;
    2282              : 
    2283              :                         /***
    2284              :                          * We will need one of the following fields:
    2285              :                          *  DTK_NUMBER should be hhmmss.fff
    2286              :                          *  DTK_TIME should be hh:mm:ss.fff
    2287              :                          *  DTK_DATE should be hhmmss-zz
    2288              :                          ***/
    2289            0 :                         if (i >= nf - 1 ||
    2290            0 :                             (ftype[i + 1] != DTK_NUMBER &&
    2291            0 :                              ftype[i + 1] != DTK_TIME &&
    2292            0 :                              ftype[i + 1] != DTK_DATE))
    2293            0 :                             return -1;
    2294              : 
    2295            0 :                         ptype = val;
    2296            0 :                         break;
    2297              : 
    2298            6 :                     default:
    2299            6 :                         return -1;
    2300              :                 }
    2301          136 :                 break;
    2302              : 
    2303            0 :             default:
    2304            0 :                 return -1;
    2305              :         }
    2306              : 
    2307          890 :         if (tmask & fmask)
    2308            0 :             return -1;
    2309          890 :         fmask |= tmask;
    2310              :     }
    2311              : 
    2312              :     /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
    2313          348 :     if (bc)
    2314              :     {
    2315           12 :         if (tm->tm_year > 0)
    2316           12 :             tm->tm_year = -(tm->tm_year - 1);
    2317              :         else
    2318            0 :             return -1;
    2319              :     }
    2320          336 :     else if (is2digits)
    2321              :     {
    2322           12 :         if (tm->tm_year < 70)
    2323            0 :             tm->tm_year += 2000;
    2324           12 :         else if (tm->tm_year < 100)
    2325           12 :             tm->tm_year += 1900;
    2326              :     }
    2327              : 
    2328          348 :     if (mer != HR24 && tm->tm_hour > 12)
    2329            0 :         return -1;
    2330          348 :     if (mer == AM && tm->tm_hour == 12)
    2331            0 :         tm->tm_hour = 0;
    2332          348 :     else if (mer == PM && tm->tm_hour != 12)
    2333            0 :         tm->tm_hour += 12;
    2334              : 
    2335              :     /* do additional checking for full date specs... */
    2336          348 :     if (*dtype == DTK_DATE)
    2337              :     {
    2338          348 :         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
    2339            0 :             return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
    2340              : 
    2341              :         /*
    2342              :          * check for valid day of month and month, now that we know for sure
    2343              :          * the month and year...
    2344              :          */
    2345          348 :         if (tm->tm_mon < 1 || tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
    2346            2 :             return -1;
    2347              : 
    2348              :         /*
    2349              :          * backend tried to find local timezone here but we don't use the
    2350              :          * result afterwards anyway so we only check for this error: daylight
    2351              :          * savings time modifier but no standard timezone?
    2352              :          */
    2353          346 :         if ((fmask & DTK_DATE_M) == DTK_DATE_M && tzp != NULL && !(fmask & DTK_M(TZ)) && (fmask & DTK_M(DTZMOD)))
    2354            0 :             return -1;
    2355              :     }
    2356              : 
    2357          346 :     return 0;
    2358              : }                               /* DecodeDateTime() */
    2359              : 
    2360              : /*
    2361              :  * Function works as follows:
    2362              :  *
    2363              :  *
    2364              :  *
    2365              :  */
    2366              : 
    2367              : static char *
    2368          228 : find_end_token(char *str, char *fmt)
    2369              : {
    2370              :     /*
    2371              :      * str: here is28the day12the hour fmt: here is%dthe day%hthe hour
    2372              :      *
    2373              :      * we extract the 28, we read the percent sign and the type "d" then this
    2374              :      * functions gets called as find_end_token("28the day12the hour", "the
    2375              :      * day%hthehour")
    2376              :      *
    2377              :      * fmt points to "the day%hthehour", next_percent points to %hthehour and
    2378              :      * we have to find a match for everything between these positions ("the
    2379              :      * day"). We look for "the day" in str and know that the pattern we are
    2380              :      * about to scan ends where this string starts (right after the "28")
    2381              :      *
    2382              :      * At the end, *fmt is '\0' and *str isn't. end_position then is
    2383              :      * unchanged.
    2384              :      */
    2385          228 :     char       *end_position = NULL;
    2386              :     char       *next_percent,
    2387          228 :                *subst_location = NULL;
    2388          228 :     int         scan_offset = 0;
    2389              :     char        last_char;
    2390              : 
    2391              :     /* are we at the end? */
    2392          228 :     if (!*fmt)
    2393              :     {
    2394           34 :         end_position = fmt;
    2395           34 :         return end_position;
    2396              :     }
    2397              : 
    2398              :     /* not at the end */
    2399          198 :     while (fmt[scan_offset] == '%' && fmt[scan_offset + 1])
    2400              :     {
    2401              :         /*
    2402              :          * there is no delimiter, skip to the next delimiter if we're reading
    2403              :          * a number and then something that is not a number "9:15pm", we might
    2404              :          * be able to recover with the strtol end pointer. Go for the next
    2405              :          * percent sign
    2406              :          */
    2407            4 :         scan_offset += 2;
    2408              :     }
    2409          194 :     next_percent = strchr(fmt + scan_offset, '%');
    2410          194 :     if (next_percent)
    2411              :     {
    2412              :         /*
    2413              :          * we don't want to allocate extra memory, so we temporarily set the
    2414              :          * '%' sign to '\0' and call strstr However since we allow whitespace
    2415              :          * to float around everything, we have to shorten the pattern until we
    2416              :          * reach a non-whitespace character
    2417              :          */
    2418              : 
    2419          188 :         subst_location = next_percent;
    2420          214 :         while (*(subst_location - 1) == ' ' && subst_location - 1 > fmt + scan_offset)
    2421           26 :             subst_location--;
    2422          188 :         last_char = *subst_location;
    2423          188 :         *subst_location = '\0';
    2424              : 
    2425              :         /*
    2426              :          * the haystack is the str and the needle is the original fmt but it
    2427              :          * ends at the position where the next percent sign would be
    2428              :          */
    2429              : 
    2430              :         /*
    2431              :          * There is one special case. Imagine: str = " 2", fmt = "%d %...",
    2432              :          * since we want to allow blanks as "dynamic" padding we have to
    2433              :          * accept this. Now, we are called with a fmt of " %..." and look for
    2434              :          * " " in str. We find it at the first position and never read the
    2435              :          * 2...
    2436              :          */
    2437          188 :         while (*str == ' ')
    2438            0 :             str++;
    2439          188 :         end_position = strstr(str, fmt + scan_offset);
    2440          188 :         *subst_location = last_char;
    2441              :     }
    2442              :     else
    2443              :     {
    2444              :         /*
    2445              :          * there is no other percent sign. So everything up to the end has to
    2446              :          * match.
    2447              :          */
    2448            6 :         end_position = str + strlen(str);
    2449              :     }
    2450          194 :     if (!end_position)
    2451              :     {
    2452              :         /*
    2453              :          * maybe we have the following case:
    2454              :          *
    2455              :          * str = "4:15am" fmt = "%M:%S %p"
    2456              :          *
    2457              :          * at this place we could have
    2458              :          *
    2459              :          * str = "15am" fmt = " %p"
    2460              :          *
    2461              :          * and have set fmt to " " because overwrote the % sign with a NULL
    2462              :          *
    2463              :          * In this case where we would have to match a space but can't find
    2464              :          * it, set end_position to the end of the string
    2465              :          */
    2466            2 :         if ((fmt + scan_offset)[0] == ' ' && fmt + scan_offset + 1 == subst_location)
    2467            2 :             end_position = str + strlen(str);
    2468              :     }
    2469          194 :     return end_position;
    2470              : }
    2471              : 
    2472              : static int
    2473          228 : pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char *pfmt)
    2474              : {
    2475              :     /*
    2476              :      * scan everything between pstr and pstr_end. This is not including the
    2477              :      * last character so we might set it to '\0' for the parsing
    2478              :      */
    2479              : 
    2480              :     char        last_char;
    2481          228 :     int         err = 0;
    2482              :     char       *pstr_end;
    2483          228 :     char       *strtol_end = NULL;
    2484              : 
    2485          228 :     while (**pstr == ' ')
    2486            0 :         pstr++;
    2487          228 :     pstr_end = find_end_token(*pstr, pfmt);
    2488          228 :     if (!pstr_end)
    2489              :     {
    2490              :         /* there was an error, no match */
    2491            0 :         return 1;
    2492              :     }
    2493          228 :     last_char = *pstr_end;
    2494          228 :     *pstr_end = '\0';
    2495              : 
    2496          228 :     switch (scan_type)
    2497              :     {
    2498          202 :         case PGTYPES_TYPE_UINT:
    2499              : 
    2500              :             /*
    2501              :              * numbers may be blank-padded, this is the only deviation from
    2502              :              * the fmt-string we accept
    2503              :              */
    2504          202 :             while (**pstr == ' ')
    2505            0 :                 (*pstr)++;
    2506          202 :             errno = 0;
    2507          202 :             scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
    2508          202 :             if (errno)
    2509            0 :                 err = 1;
    2510          202 :             break;
    2511            2 :         case PGTYPES_TYPE_UINT_LONG:
    2512            2 :             while (**pstr == ' ')
    2513            0 :                 (*pstr)++;
    2514            2 :             errno = 0;
    2515            2 :             scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
    2516            2 :             if (errno)
    2517            0 :                 err = 1;
    2518            2 :             break;
    2519           24 :         case PGTYPES_TYPE_STRING_MALLOCED:
    2520           24 :             scan_val->str_val = pgtypes_strdup(*pstr);
    2521           24 :             if (scan_val->str_val == NULL)
    2522            0 :                 err = 1;
    2523           24 :             break;
    2524              :     }
    2525          228 :     if (strtol_end && *strtol_end)
    2526           18 :         *pstr = strtol_end;
    2527              :     else
    2528          210 :         *pstr = pstr_end;
    2529          228 :     *pstr_end = last_char;
    2530          228 :     return err;
    2531              : }
    2532              : 
    2533              : /* XXX range checking */
    2534              : int
    2535           48 : PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
    2536              :                             int *year, int *month, int *day,
    2537              :                             int *hour, int *minute, int *second,
    2538              :                             int *tz)
    2539              : {
    2540              :     union un_fmt_comb scan_val;
    2541              :     int         scan_type;
    2542              : 
    2543              :     char       *pstr,
    2544              :                *pfmt,
    2545              :                *tmp;
    2546           48 :     int         err = 1;
    2547              :     unsigned int j;
    2548              :     struct tm   tm;
    2549              : 
    2550           48 :     pfmt = fmt;
    2551           48 :     pstr = *str;
    2552              : 
    2553          548 :     while (*pfmt)
    2554              :     {
    2555          504 :         err = 0;
    2556          708 :         while (*pfmt == ' ')
    2557          204 :             pfmt++;
    2558          750 :         while (*pstr == ' ')
    2559          246 :             pstr++;
    2560          504 :         if (*pfmt != '%')
    2561              :         {
    2562          196 :             if (*pfmt == *pstr)
    2563              :             {
    2564          192 :                 pfmt++;
    2565          192 :                 pstr++;
    2566              :             }
    2567              :             else
    2568              :             {
    2569              :                 /* Error: no match */
    2570            4 :                 err = 1;
    2571            4 :                 return err;
    2572              :             }
    2573          192 :             continue;
    2574              :         }
    2575              :         /* here *pfmt equals '%' */
    2576          308 :         pfmt++;
    2577          308 :         switch (*pfmt)
    2578              :         {
    2579           18 :             case 'a':
    2580           18 :                 pfmt++;
    2581              : 
    2582              :                 /*
    2583              :                  * we parse the day and see if it is a week day but we do not
    2584              :                  * check if the week day really matches the date
    2585              :                  */
    2586           18 :                 err = 1;
    2587           18 :                 j = 0;
    2588           50 :                 while (pgtypes_date_weekdays_short[j])
    2589              :                 {
    2590           50 :                     if (strncmp(pgtypes_date_weekdays_short[j], pstr,
    2591           50 :                                 strlen(pgtypes_date_weekdays_short[j])) == 0)
    2592              :                     {
    2593              :                         /* found it */
    2594           18 :                         err = 0;
    2595           18 :                         pstr += strlen(pgtypes_date_weekdays_short[j]);
    2596           18 :                         break;
    2597              :                     }
    2598           32 :                     j++;
    2599              :                 }
    2600           18 :                 break;
    2601            0 :             case 'A':
    2602              :                 /* see note above */
    2603            0 :                 pfmt++;
    2604            0 :                 err = 1;
    2605            0 :                 j = 0;
    2606            0 :                 while (days[j])
    2607              :                 {
    2608            0 :                     if (strncmp(days[j], pstr, strlen(days[j])) == 0)
    2609              :                     {
    2610              :                         /* found it */
    2611            0 :                         err = 0;
    2612            0 :                         pstr += strlen(days[j]);
    2613            0 :                         break;
    2614              :                     }
    2615            0 :                     j++;
    2616              :                 }
    2617            0 :                 break;
    2618           24 :             case 'b':
    2619              :             case 'h':
    2620           24 :                 pfmt++;
    2621           24 :                 err = 1;
    2622           24 :                 j = 0;
    2623          148 :                 while (months[j])
    2624              :                 {
    2625          148 :                     if (strncmp(months[j], pstr, strlen(months[j])) == 0)
    2626              :                     {
    2627              :                         /* found it */
    2628           24 :                         err = 0;
    2629           24 :                         pstr += strlen(months[j]);
    2630           24 :                         *month = j + 1;
    2631           24 :                         break;
    2632              :                     }
    2633          124 :                     j++;
    2634              :                 }
    2635           24 :                 break;
    2636           14 :             case 'B':
    2637              :                 /* see note above */
    2638           14 :                 pfmt++;
    2639           14 :                 err = 1;
    2640           14 :                 j = 0;
    2641          114 :                 while (pgtypes_date_months[j])
    2642              :                 {
    2643          114 :                     if (strncmp(pgtypes_date_months[j], pstr, strlen(pgtypes_date_months[j])) == 0)
    2644              :                     {
    2645              :                         /* found it */
    2646           14 :                         err = 0;
    2647           14 :                         pstr += strlen(pgtypes_date_months[j]);
    2648           14 :                         *month = j + 1;
    2649           14 :                         break;
    2650              :                     }
    2651          100 :                     j++;
    2652              :                 }
    2653           14 :                 break;
    2654            0 :             case 'c':
    2655              :                 /* XXX */
    2656            0 :                 break;
    2657            4 :             case 'C':
    2658            4 :                 pfmt++;
    2659            4 :                 scan_type = PGTYPES_TYPE_UINT;
    2660            4 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2661            4 :                 *year = scan_val.uint_val * 100;
    2662            4 :                 break;
    2663           42 :             case 'd':
    2664              :             case 'e':
    2665           42 :                 pfmt++;
    2666           42 :                 scan_type = PGTYPES_TYPE_UINT;
    2667           42 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2668           42 :                 *day = scan_val.uint_val;
    2669           42 :                 break;
    2670            0 :             case 'D':
    2671              : 
    2672              :                 /*
    2673              :                  * we have to concatenate the strings in order to be able to
    2674              :                  * find the end of the substitution
    2675              :                  */
    2676            0 :                 pfmt++;
    2677            0 :                 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
    2678            0 :                 if (!tmp)
    2679            0 :                     return 1;
    2680            0 :                 strcpy(tmp, "%m/%d/%y");
    2681            0 :                 strcat(tmp, pfmt);
    2682            0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2683            0 :                 free(tmp);
    2684            0 :                 return err;
    2685            4 :             case 'm':
    2686            4 :                 pfmt++;
    2687            4 :                 scan_type = PGTYPES_TYPE_UINT;
    2688            4 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2689            4 :                 *month = scan_val.uint_val;
    2690            4 :                 break;
    2691            4 :             case 'y':
    2692              :             case 'g':           /* XXX difference to y (ISO) */
    2693            4 :                 pfmt++;
    2694            4 :                 scan_type = PGTYPES_TYPE_UINT;
    2695            4 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2696            4 :                 if (*year < 0)
    2697              :                 {
    2698              :                     /* not yet set */
    2699            2 :                     *year = scan_val.uint_val;
    2700              :                 }
    2701              :                 else
    2702            2 :                     *year += scan_val.uint_val;
    2703            4 :                 if (*year < 100)
    2704            2 :                     *year += 1900;
    2705            4 :                 break;
    2706            0 :             case 'G':
    2707              :                 /* XXX difference to %V (ISO) */
    2708            0 :                 pfmt++;
    2709            0 :                 scan_type = PGTYPES_TYPE_UINT;
    2710            0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2711            0 :                 *year = scan_val.uint_val;
    2712            0 :                 break;
    2713           42 :             case 'H':
    2714              :             case 'I':
    2715              :             case 'k':
    2716              :             case 'l':
    2717           42 :                 pfmt++;
    2718           42 :                 scan_type = PGTYPES_TYPE_UINT;
    2719           42 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2720           42 :                 *hour += scan_val.uint_val;
    2721           42 :                 break;
    2722            0 :             case 'j':
    2723            0 :                 pfmt++;
    2724            0 :                 scan_type = PGTYPES_TYPE_UINT;
    2725            0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2726              : 
    2727              :                 /*
    2728              :                  * XXX what should we do with that? We could say that it's
    2729              :                  * sufficient if we have the year and the day within the year
    2730              :                  * to get at least a specific day.
    2731              :                  */
    2732            0 :                 break;
    2733           40 :             case 'M':
    2734           40 :                 pfmt++;
    2735           40 :                 scan_type = PGTYPES_TYPE_UINT;
    2736           40 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2737           40 :                 *minute = scan_val.uint_val;
    2738           40 :                 break;
    2739            4 :             case 'n':
    2740            4 :                 pfmt++;
    2741            4 :                 if (*pstr == '\n')
    2742            4 :                     pstr++;
    2743              :                 else
    2744            0 :                     err = 1;
    2745            4 :                 break;
    2746            8 :             case 'p':
    2747            8 :                 err = 1;
    2748            8 :                 pfmt++;
    2749            8 :                 if (strncmp(pstr, "am", 2) == 0)
    2750              :                 {
    2751            6 :                     *hour += 0;
    2752            6 :                     err = 0;
    2753            6 :                     pstr += 2;
    2754              :                 }
    2755            8 :                 if (strncmp(pstr, "a.m.", 4) == 0)
    2756              :                 {
    2757            0 :                     *hour += 0;
    2758            0 :                     err = 0;
    2759            0 :                     pstr += 4;
    2760              :                 }
    2761            8 :                 if (strncmp(pstr, "pm", 2) == 0)
    2762              :                 {
    2763            2 :                     *hour += 12;
    2764            2 :                     err = 0;
    2765            2 :                     pstr += 2;
    2766              :                 }
    2767            8 :                 if (strncmp(pstr, "p.m.", 4) == 0)
    2768              :                 {
    2769            0 :                     *hour += 12;
    2770            0 :                     err = 0;
    2771            0 :                     pstr += 4;
    2772              :                 }
    2773            8 :                 break;
    2774            2 :             case 'P':
    2775            2 :                 err = 1;
    2776            2 :                 pfmt++;
    2777            2 :                 if (strncmp(pstr, "AM", 2) == 0)
    2778              :                 {
    2779            0 :                     *hour += 0;
    2780            0 :                     err = 0;
    2781            0 :                     pstr += 2;
    2782              :                 }
    2783            2 :                 if (strncmp(pstr, "A.M.", 4) == 0)
    2784              :                 {
    2785            0 :                     *hour += 0;
    2786            0 :                     err = 0;
    2787            0 :                     pstr += 4;
    2788              :                 }
    2789            2 :                 if (strncmp(pstr, "PM", 2) == 0)
    2790              :                 {
    2791            0 :                     *hour += 12;
    2792            0 :                     err = 0;
    2793            0 :                     pstr += 2;
    2794              :                 }
    2795            2 :                 if (strncmp(pstr, "P.M.", 4) == 0)
    2796              :                 {
    2797            2 :                     *hour += 12;
    2798            2 :                     err = 0;
    2799            2 :                     pstr += 4;
    2800              :                 }
    2801            2 :                 break;
    2802            0 :             case 'r':
    2803            0 :                 pfmt++;
    2804            0 :                 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
    2805            0 :                 if (!tmp)
    2806            0 :                     return 1;
    2807            0 :                 strcpy(tmp, "%I:%M:%S %p");
    2808            0 :                 strcat(tmp, pfmt);
    2809            0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2810            0 :                 free(tmp);
    2811            0 :                 return err;
    2812            0 :             case 'R':
    2813            0 :                 pfmt++;
    2814            0 :                 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
    2815            0 :                 if (!tmp)
    2816            0 :                     return 1;
    2817            0 :                 strcpy(tmp, "%H:%M");
    2818            0 :                 strcat(tmp, pfmt);
    2819            0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2820            0 :                 free(tmp);
    2821            0 :                 return err;
    2822            2 :             case 's':
    2823            2 :                 pfmt++;
    2824            2 :                 scan_type = PGTYPES_TYPE_UINT_LONG;
    2825            2 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2826              :                 /* number of seconds in scan_val.luint_val */
    2827              :                 {
    2828              :                     struct tm  *tms;
    2829              :                     struct tm   tmbuf;
    2830            2 :                     time_t      et = (time_t) scan_val.luint_val;
    2831              : 
    2832            2 :                     tms = gmtime_r(&et, &tmbuf);
    2833              : 
    2834            2 :                     if (tms)
    2835              :                     {
    2836            2 :                         *year = tms->tm_year + 1900;
    2837            2 :                         *month = tms->tm_mon + 1;
    2838            2 :                         *day = tms->tm_mday;
    2839            2 :                         *hour = tms->tm_hour;
    2840            2 :                         *minute = tms->tm_min;
    2841            2 :                         *second = tms->tm_sec;
    2842              :                     }
    2843              :                     else
    2844            0 :                         err = 1;
    2845              :                 }
    2846            2 :                 break;
    2847           30 :             case 'S':
    2848           30 :                 pfmt++;
    2849           30 :                 scan_type = PGTYPES_TYPE_UINT;
    2850           30 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2851           30 :                 *second = scan_val.uint_val;
    2852           30 :                 break;
    2853            0 :             case 't':
    2854            0 :                 pfmt++;
    2855            0 :                 if (*pstr == '\t')
    2856            0 :                     pstr++;
    2857              :                 else
    2858            0 :                     err = 1;
    2859            0 :                 break;
    2860            0 :             case 'T':
    2861            0 :                 pfmt++;
    2862            0 :                 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
    2863            0 :                 if (!tmp)
    2864            0 :                     return 1;
    2865            0 :                 strcpy(tmp, "%H:%M:%S");
    2866            0 :                 strcat(tmp, pfmt);
    2867            0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2868            0 :                 free(tmp);
    2869            0 :                 return err;
    2870            0 :             case 'u':
    2871            0 :                 pfmt++;
    2872            0 :                 scan_type = PGTYPES_TYPE_UINT;
    2873            0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2874            0 :                 if (scan_val.uint_val < 1 || scan_val.uint_val > 7)
    2875            0 :                     err = 1;
    2876            0 :                 break;
    2877            0 :             case 'U':
    2878            0 :                 pfmt++;
    2879            0 :                 scan_type = PGTYPES_TYPE_UINT;
    2880            0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2881            0 :                 if (scan_val.uint_val > 53)
    2882            0 :                     err = 1;
    2883            0 :                 break;
    2884            0 :             case 'V':
    2885            0 :                 pfmt++;
    2886            0 :                 scan_type = PGTYPES_TYPE_UINT;
    2887            0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2888            0 :                 if (scan_val.uint_val < 1 || scan_val.uint_val > 53)
    2889            0 :                     err = 1;
    2890            0 :                 break;
    2891            0 :             case 'w':
    2892            0 :                 pfmt++;
    2893            0 :                 scan_type = PGTYPES_TYPE_UINT;
    2894            0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2895            0 :                 if (scan_val.uint_val > 6)
    2896            0 :                     err = 1;
    2897            0 :                 break;
    2898            0 :             case 'W':
    2899            0 :                 pfmt++;
    2900            0 :                 scan_type = PGTYPES_TYPE_UINT;
    2901            0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2902            0 :                 if (scan_val.uint_val > 53)
    2903            0 :                     err = 1;
    2904            0 :                 break;
    2905            0 :             case 'x':
    2906              :             case 'X':
    2907              :                 /* XXX */
    2908            0 :                 break;
    2909           36 :             case 'Y':
    2910           36 :                 pfmt++;
    2911           36 :                 scan_type = PGTYPES_TYPE_UINT;
    2912           36 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2913           36 :                 *year = scan_val.uint_val;
    2914           36 :                 break;
    2915           16 :             case 'z':
    2916           16 :                 pfmt++;
    2917           16 :                 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
    2918           16 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2919           16 :                 if (!err)
    2920              :                 {
    2921           16 :                     err = DecodeTimezone(scan_val.str_val, tz);
    2922           16 :                     free(scan_val.str_val);
    2923              :                 }
    2924           16 :                 break;
    2925            8 :             case 'Z':
    2926            8 :                 pfmt++;
    2927            8 :                 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
    2928            8 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2929            8 :                 if (!err)
    2930              :                 {
    2931              :                     /*
    2932              :                      * XXX use DecodeSpecial instead?  Do we need strcasecmp
    2933              :                      * here?
    2934              :                      */
    2935            8 :                     err = 1;
    2936          400 :                     for (j = 0; j < szdatetktbl; j++)
    2937              :                     {
    2938          720 :                         if ((datetktbl[j].type == TZ || datetktbl[j].type == DTZ) &&
    2939          320 :                             pg_strcasecmp(datetktbl[j].token,
    2940          320 :                                           scan_val.str_val) == 0)
    2941              :                         {
    2942            8 :                             *tz = -datetktbl[j].value;
    2943            8 :                             err = 0;
    2944            8 :                             break;
    2945              :                         }
    2946              :                     }
    2947            8 :                     free(scan_val.str_val);
    2948              :                 }
    2949            8 :                 break;
    2950            0 :             case '+':
    2951              :                 /* XXX */
    2952            0 :                 break;
    2953            8 :             case '%':
    2954            8 :                 pfmt++;
    2955            8 :                 if (*pstr == '%')
    2956            8 :                     pstr++;
    2957              :                 else
    2958            0 :                     err = 1;
    2959            8 :                 break;
    2960            2 :             default:
    2961            2 :                 err = 1;
    2962              :         }
    2963              :     }
    2964           44 :     if (!err)
    2965              :     {
    2966           42 :         if (*second < 0)
    2967           10 :             *second = 0;
    2968           42 :         if (*minute < 0)
    2969            0 :             *minute = 0;
    2970           42 :         if (*hour < 0)
    2971            0 :             *hour = 0;
    2972           42 :         if (*day < 0)
    2973              :         {
    2974            0 :             err = 1;
    2975            0 :             *day = 1;
    2976              :         }
    2977           42 :         if (*month < 0)
    2978              :         {
    2979            0 :             err = 1;
    2980            0 :             *month = 1;
    2981              :         }
    2982           42 :         if (*year < 0)
    2983              :         {
    2984            0 :             err = 1;
    2985            0 :             *year = 1970;
    2986              :         }
    2987              : 
    2988           42 :         if (*second > 59)
    2989              :         {
    2990            0 :             err = 1;
    2991            0 :             *second = 0;
    2992              :         }
    2993           42 :         if (*minute > 59)
    2994              :         {
    2995            0 :             err = 1;
    2996            0 :             *minute = 0;
    2997              :         }
    2998           42 :         if (*hour > 24 ||        /* test for > 24:00:00 */
    2999           42 :             (*hour == 24 && (*minute > 0 || *second > 0)))
    3000              :         {
    3001            0 :             err = 1;
    3002            0 :             *hour = 0;
    3003              :         }
    3004           42 :         if (*month > MONTHS_PER_YEAR)
    3005              :         {
    3006            0 :             err = 1;
    3007            0 :             *month = 1;
    3008              :         }
    3009           42 :         if (*day > day_tab[isleap(*year)][*month - 1])
    3010              :         {
    3011            8 :             *day = day_tab[isleap(*year)][*month - 1];
    3012            8 :             err = 1;
    3013              :         }
    3014              : 
    3015           42 :         tm.tm_sec = *second;
    3016           42 :         tm.tm_min = *minute;
    3017           42 :         tm.tm_hour = *hour;
    3018           42 :         tm.tm_mday = *day;
    3019           42 :         tm.tm_mon = *month;
    3020           42 :         tm.tm_year = *year;
    3021              : 
    3022           42 :         tm2timestamp(&tm, 0, tz, d);
    3023              :     }
    3024           44 :     return err;
    3025              : }
    3026              : 
    3027              : /* XXX: 1900 is compiled in as the base for years */
        

Generated by: LCOV version 2.0-1