LCOV - code coverage report
Current view: top level - src/interfaces/ecpg/pgtypeslib - dt_common.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 712 1248 57.1 %
Date: 2024-11-21 09:14:53 Functions: 21 23 91.3 %
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         180 : datebsearch(const char *key, const datetkn *base, unsigned int nel)
     503             : {
     504         180 :     if (nel > 0)
     505             :     {
     506         180 :         const datetkn *last = base + nel - 1,
     507             :                    *position;
     508             :         int         result;
     509             : 
     510        1172 :         while (last >= base)
     511             :         {
     512        1152 :             position = base + ((last - base) >> 1);
     513             :             /* precheck the first character for a bit of extra speed */
     514        1152 :             result = (int) key[0] - (int) position->token[0];
     515        1152 :             if (result == 0)
     516             :             {
     517             :                 /* use strncmp so that we match truncated tokens */
     518         564 :                 result = strncmp(key, position->token, TOKMAXLEN);
     519         564 :                 if (result == 0)
     520         160 :                     return position;
     521             :             }
     522         992 :             if (result < 0)
     523         464 :                 last = position - 1;
     524             :             else
     525         528 :                 base = position + 1;
     526             :         }
     527             :     }
     528          20 :     return NULL;
     529             : }
     530             : 
     531             : /* DecodeUnits()
     532             :  * Decode text string using lookup table.
     533             :  * This routine supports time interval decoding.
     534             :  */
     535             : int
     536         176 : DecodeUnits(int field, char *lowtoken, int *val)
     537             : {
     538             :     int         type;
     539             :     const datetkn *tp;
     540             : 
     541             :     /* use strncmp so that we match truncated tokens */
     542         176 :     if (deltacache[field] != NULL &&
     543         120 :         strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0)
     544          80 :         tp = deltacache[field];
     545             :     else
     546          96 :         tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
     547         176 :     deltacache[field] = tp;
     548         176 :     if (tp == NULL)
     549             :     {
     550           4 :         type = UNKNOWN_FIELD;
     551           4 :         *val = 0;
     552             :     }
     553             :     else
     554             :     {
     555         172 :         type = tp->type;
     556         172 :         *val = tp->value;
     557             :     }
     558             : 
     559         176 :     return type;
     560             : }                               /* DecodeUnits() */
     561             : 
     562             : /*
     563             :  * Calendar time to Julian date conversions.
     564             :  * Julian date is commonly used in astronomical applications,
     565             :  *  since it is numerically accurate and computationally simple.
     566             :  * The algorithms here will accurately convert between Julian day
     567             :  *  and calendar date for all non-negative Julian days
     568             :  *  (i.e. from Nov 24, -4713 on).
     569             :  *
     570             :  * These routines will be used by other date/time packages
     571             :  * - thomas 97/02/25
     572             :  *
     573             :  * Rewritten to eliminate overflow problems. This now allows the
     574             :  * routines to work correctly for all Julian day counts from
     575             :  * 0 to 2147483647  (Nov 24, -4713 to Jun 3, 5874898) assuming
     576             :  * a 32-bit integer. Longer types should also work to the limits
     577             :  * of their precision.
     578             :  */
     579             : 
     580             : int
     581        3652 : date2j(int y, int m, int d)
     582             : {
     583             :     int         julian;
     584             :     int         century;
     585             : 
     586        3652 :     if (m > 2)
     587             :     {
     588         208 :         m += 1;
     589         208 :         y += 4800;
     590             :     }
     591             :     else
     592             :     {
     593        3444 :         m += 13;
     594        3444 :         y += 4799;
     595             :     }
     596             : 
     597        3652 :     century = y / 100;
     598        3652 :     julian = y * 365 - 32167;
     599        3652 :     julian += y / 4 - century + century / 4;
     600        3652 :     julian += 7834 * m / 256 + d;
     601             : 
     602        3652 :     return julian;
     603             : }                               /* date2j() */
     604             : 
     605             : void
     606        1140 : j2date(int jd, int *year, int *month, int *day)
     607             : {
     608             :     unsigned int julian;
     609             :     unsigned int quad;
     610             :     unsigned int extra;
     611             :     int         y;
     612             : 
     613        1140 :     julian = jd;
     614        1140 :     julian += 32044;
     615        1140 :     quad = julian / 146097;
     616        1140 :     extra = (julian - quad * 146097) * 4 + 3;
     617        1140 :     julian += 60 + quad * 3 + extra / 146097;
     618        1140 :     quad = julian / 1461;
     619        1140 :     julian -= quad * 1461;
     620        1140 :     y = julian * 4 / 1461;
     621        1140 :     julian = ((y != 0) ? (julian + 305) % 365 : (julian + 306) % 366) + 123;
     622        1140 :     y += quad * 4;
     623        1140 :     *year = y - 4800;
     624        1140 :     quad = julian * 2141 / 65536;
     625        1140 :     *day = julian - 7834 * quad / 256;
     626        1140 :     *month = (quad + 10) % 12 + 1;
     627        1140 : }                               /* j2date() */
     628             : 
     629             : /* DecodeSpecial()
     630             :  * Decode text string using lookup table.
     631             :  * Implement a cache lookup since it is likely that dates
     632             :  *  will be related in format.
     633             :  */
     634             : static int
     635         456 : DecodeSpecial(int field, char *lowtoken, int *val)
     636             : {
     637             :     int         type;
     638             :     const datetkn *tp;
     639             : 
     640             :     /* use strncmp so that we match truncated tokens */
     641         456 :     if (datecache[field] != NULL &&
     642         396 :         strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0)
     643         372 :         tp = datecache[field];
     644             :     else
     645             :     {
     646          84 :         tp = NULL;
     647          84 :         if (!tp)
     648          84 :             tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
     649             :     }
     650         456 :     datecache[field] = tp;
     651         456 :     if (tp == NULL)
     652             :     {
     653          16 :         type = UNKNOWN_FIELD;
     654          16 :         *val = 0;
     655             :     }
     656             :     else
     657             :     {
     658         440 :         type = tp->type;
     659         440 :         *val = tp->value;
     660             :     }
     661             : 
     662         456 :     return type;
     663             : }                               /* DecodeSpecial() */
     664             : 
     665             : /* EncodeDateOnly()
     666             :  * Encode date as local time.
     667             :  */
     668             : void
     669         380 : EncodeDateOnly(struct tm *tm, int style, char *str, bool EuroDates)
     670             : {
     671             :     Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
     672             : 
     673         380 :     switch (style)
     674             :     {
     675         380 :         case USE_ISO_DATES:
     676             :             /* compatible with ISO date formats */
     677         380 :             if (tm->tm_year > 0)
     678         376 :                 sprintf(str, "%04d-%02d-%02d",
     679             :                         tm->tm_year, tm->tm_mon, tm->tm_mday);
     680             :             else
     681           4 :                 sprintf(str, "%04d-%02d-%02d %s",
     682           4 :                         -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
     683         380 :             break;
     684             : 
     685           0 :         case USE_SQL_DATES:
     686             :             /* compatible with Oracle/Ingres date formats */
     687           0 :             if (EuroDates)
     688           0 :                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
     689             :             else
     690           0 :                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
     691           0 :             if (tm->tm_year > 0)
     692           0 :                 sprintf(str + 5, "/%04d", tm->tm_year);
     693             :             else
     694           0 :                 sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
     695           0 :             break;
     696             : 
     697           0 :         case USE_GERMAN_DATES:
     698             :             /* German-style date format */
     699           0 :             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
     700           0 :             if (tm->tm_year > 0)
     701           0 :                 sprintf(str + 5, ".%04d", tm->tm_year);
     702             :             else
     703           0 :                 sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
     704           0 :             break;
     705             : 
     706           0 :         case USE_POSTGRES_DATES:
     707             :         default:
     708             :             /* traditional date-only style for Postgres */
     709           0 :             if (EuroDates)
     710           0 :                 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
     711             :             else
     712           0 :                 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
     713           0 :             if (tm->tm_year > 0)
     714           0 :                 sprintf(str + 5, "-%04d", tm->tm_year);
     715             :             else
     716           0 :                 sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
     717           0 :             break;
     718             :     }
     719         380 : }
     720             : 
     721             : void
     722         136 : TrimTrailingZeros(char *str)
     723             : {
     724         136 :     int         len = strlen(str);
     725             : 
     726             :     /* chop off trailing zeros... but leave at least 2 fractional digits */
     727         340 :     while (*(str + len - 1) == '0' && *(str + len - 3) != '.')
     728             :     {
     729         204 :         len--;
     730         204 :         *(str + len) = '\0';
     731             :     }
     732         136 : }
     733             : 
     734             : /* EncodeDateTime()
     735             :  * Encode date and time interpreted as local time.
     736             :  *
     737             :  * tm and fsec are the value to encode, print_tz determines whether to include
     738             :  * a time zone (the difference between timestamp and timestamptz types), tz is
     739             :  * the numeric time zone offset, tzn is the textual time zone, which if
     740             :  * specified will be used instead of tz by some styles, style is the date
     741             :  * style, str is where to write the output.
     742             :  *
     743             :  * Supported date styles:
     744             :  *  Postgres - day mon hh:mm:ss yyyy tz
     745             :  *  SQL - mm/dd/yyyy hh:mm:ss.ss tz
     746             :  *  ISO - yyyy-mm-dd hh:mm:ss+/-tz
     747             :  *  German - dd.mm.yyyy hh:mm:ss tz
     748             :  * Variants (affects order of month and day for Postgres and SQL styles):
     749             :  *  US - mm/dd/yyyy
     750             :  *  European - dd/mm/yyyy
     751             :  */
     752             : void
     753         636 : EncodeDateTime(struct tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str, bool EuroDates)
     754             : {
     755             :     int         day,
     756             :                 hour,
     757             :                 min;
     758             : 
     759             :     /*
     760             :      * Negative tm_isdst means we have no valid time zone translation.
     761             :      */
     762         636 :     if (tm->tm_isdst < 0)
     763         636 :         print_tz = false;
     764             : 
     765         636 :     switch (style)
     766             :     {
     767         636 :         case USE_ISO_DATES:
     768             :             /* Compatible with ISO-8601 date formats */
     769             : 
     770         636 :             sprintf(str, "%04d-%02d-%02d %02d:%02d",
     771         636 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
     772             :                     tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
     773             : 
     774             :             /*
     775             :              * Print fractional seconds if any.  The field widths here should
     776             :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     777             :              */
     778         636 :             if (fsec != 0)
     779             :             {
     780         136 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     781         136 :                 TrimTrailingZeros(str);
     782             :             }
     783             :             else
     784         500 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     785             : 
     786         636 :             if (tm->tm_year <= 0)
     787          20 :                 sprintf(str + strlen(str), " BC");
     788             : 
     789         636 :             if (print_tz)
     790             :             {
     791           0 :                 hour = -(tz / SECS_PER_HOUR);
     792           0 :                 min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     793           0 :                 if (min != 0)
     794           0 :                     sprintf(str + strlen(str), "%+03d:%02d", hour, min);
     795             :                 else
     796           0 :                     sprintf(str + strlen(str), "%+03d", hour);
     797             :             }
     798         636 :             break;
     799             : 
     800           0 :         case USE_SQL_DATES:
     801             :             /* Compatible with Oracle/Ingres date formats */
     802             : 
     803           0 :             if (EuroDates)
     804           0 :                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
     805             :             else
     806           0 :                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
     807             : 
     808           0 :             sprintf(str + 5, "/%04d %02d:%02d",
     809           0 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
     810             :                     tm->tm_hour, tm->tm_min);
     811             : 
     812             :             /*
     813             :              * Print fractional seconds if any.  The field widths here should
     814             :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     815             :              */
     816           0 :             if (fsec != 0)
     817             :             {
     818           0 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     819           0 :                 TrimTrailingZeros(str);
     820             :             }
     821             :             else
     822           0 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     823             : 
     824           0 :             if (tm->tm_year <= 0)
     825           0 :                 sprintf(str + strlen(str), " BC");
     826             : 
     827             :             /*
     828             :              * Note: the uses of %.*s in this function would be risky if the
     829             :              * timezone names ever contain non-ASCII characters, since we are
     830             :              * not being careful to do encoding-aware clipping.  However, all
     831             :              * TZ abbreviations in the IANA database are plain ASCII.
     832             :              */
     833             : 
     834           0 :             if (print_tz)
     835             :             {
     836           0 :                 if (tzn)
     837           0 :                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
     838             :                 else
     839             :                 {
     840           0 :                     hour = -(tz / SECS_PER_HOUR);
     841           0 :                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     842           0 :                     if (min != 0)
     843           0 :                         sprintf(str + strlen(str), "%+03d:%02d", hour, min);
     844             :                     else
     845           0 :                         sprintf(str + strlen(str), "%+03d", hour);
     846             :                 }
     847             :             }
     848           0 :             break;
     849             : 
     850           0 :         case USE_GERMAN_DATES:
     851             :             /* German variant on European style */
     852             : 
     853           0 :             sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
     854             : 
     855           0 :             sprintf(str + 5, ".%04d %02d:%02d",
     856           0 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
     857             :                     tm->tm_hour, tm->tm_min);
     858             : 
     859             :             /*
     860             :              * Print fractional seconds if any.  The field widths here should
     861             :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     862             :              */
     863           0 :             if (fsec != 0)
     864             :             {
     865           0 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     866           0 :                 TrimTrailingZeros(str);
     867             :             }
     868             :             else
     869           0 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     870             : 
     871           0 :             if (tm->tm_year <= 0)
     872           0 :                 sprintf(str + strlen(str), " BC");
     873             : 
     874           0 :             if (print_tz)
     875             :             {
     876           0 :                 if (tzn)
     877           0 :                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
     878             :                 else
     879             :                 {
     880           0 :                     hour = -(tz / SECS_PER_HOUR);
     881           0 :                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     882           0 :                     if (min != 0)
     883           0 :                         sprintf(str + strlen(str), "%+03d:%02d", hour, min);
     884             :                     else
     885           0 :                         sprintf(str + strlen(str), "%+03d", hour);
     886             :                 }
     887             :             }
     888           0 :             break;
     889             : 
     890           0 :         case USE_POSTGRES_DATES:
     891             :         default:
     892             :             /* Backward-compatible with traditional Postgres abstime dates */
     893             : 
     894           0 :             day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
     895           0 :             tm->tm_wday = (int) ((day + date2j(2000, 1, 1) + 1) % 7);
     896             : 
     897           0 :             memcpy(str, days[tm->tm_wday], 3);
     898           0 :             strcpy(str + 3, " ");
     899             : 
     900           0 :             if (EuroDates)
     901           0 :                 sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
     902             :             else
     903           0 :                 sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
     904             : 
     905           0 :             sprintf(str + 10, " %02d:%02d", tm->tm_hour, tm->tm_min);
     906             : 
     907             :             /*
     908             :              * Print fractional seconds if any.  The field widths here should
     909             :              * be at least equal to MAX_TIMESTAMP_PRECISION.
     910             :              */
     911           0 :             if (fsec != 0)
     912             :             {
     913           0 :                 sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
     914           0 :                 TrimTrailingZeros(str);
     915             :             }
     916             :             else
     917           0 :                 sprintf(str + strlen(str), ":%02d", tm->tm_sec);
     918             : 
     919           0 :             sprintf(str + strlen(str), " %04d",
     920           0 :                     (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
     921           0 :             if (tm->tm_year <= 0)
     922           0 :                 sprintf(str + strlen(str), " BC");
     923             : 
     924           0 :             if (print_tz)
     925             :             {
     926           0 :                 if (tzn)
     927           0 :                     sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
     928             :                 else
     929             :                 {
     930             :                     /*
     931             :                      * We have a time zone, but no string version. Use the
     932             :                      * numeric form, but be sure to include a leading space to
     933             :                      * avoid formatting something which would be rejected by
     934             :                      * the date/time parser later. - thomas 2001-10-19
     935             :                      */
     936           0 :                     hour = -(tz / SECS_PER_HOUR);
     937           0 :                     min = (abs(tz) / MINS_PER_HOUR) % MINS_PER_HOUR;
     938           0 :                     if (min != 0)
     939           0 :                         sprintf(str + strlen(str), " %+03d:%02d", hour, min);
     940             :                     else
     941           0 :                         sprintf(str + strlen(str), " %+03d", hour);
     942             :                 }
     943             :             }
     944           0 :             break;
     945             :     }
     946         636 : }
     947             : 
     948             : int
     949           0 : GetEpochTime(struct tm *tm)
     950             : {
     951             :     struct tm  *t0;
     952             :     struct tm   tmbuf;
     953           0 :     time_t      epoch = 0;
     954             : 
     955           0 :     t0 = gmtime_r(&epoch, &tmbuf);
     956             : 
     957           0 :     if (t0)
     958             :     {
     959           0 :         tm->tm_year = t0->tm_year + 1900;
     960           0 :         tm->tm_mon = t0->tm_mon + 1;
     961           0 :         tm->tm_mday = t0->tm_mday;
     962           0 :         tm->tm_hour = t0->tm_hour;
     963           0 :         tm->tm_min = t0->tm_min;
     964           0 :         tm->tm_sec = t0->tm_sec;
     965             : 
     966           0 :         return 0;
     967             :     }
     968             : 
     969           0 :     return -1;
     970             : }                               /* GetEpochTime() */
     971             : 
     972             : static void
     973           4 : abstime2tm(AbsoluteTime _time, int *tzp, struct tm *tm, char **tzn)
     974             : {
     975           4 :     time_t      time = (time_t) _time;
     976             :     struct tm  *tx;
     977             :     struct tm   tmbuf;
     978             : 
     979           4 :     errno = 0;
     980           4 :     if (tzp != NULL)
     981           4 :         tx = localtime_r(&time, &tmbuf);
     982             :     else
     983           0 :         tx = gmtime_r(&time, &tmbuf);
     984             : 
     985           4 :     if (!tx)
     986             :     {
     987           0 :         errno = PGTYPES_TS_BAD_TIMESTAMP;
     988           0 :         return;
     989             :     }
     990             : 
     991           4 :     tm->tm_year = tx->tm_year + 1900;
     992           4 :     tm->tm_mon = tx->tm_mon + 1;
     993           4 :     tm->tm_mday = tx->tm_mday;
     994           4 :     tm->tm_hour = tx->tm_hour;
     995           4 :     tm->tm_min = tx->tm_min;
     996           4 :     tm->tm_sec = tx->tm_sec;
     997           4 :     tm->tm_isdst = tx->tm_isdst;
     998             : 
     999             : #if defined(HAVE_STRUCT_TM_TM_ZONE)
    1000           4 :     tm->tm_gmtoff = tx->tm_gmtoff;
    1001           4 :     tm->tm_zone = tx->tm_zone;
    1002             : 
    1003           4 :     if (tzp != NULL)
    1004             :     {
    1005             :         /*
    1006             :          * We have a brute force time zone per SQL99? Then use it without
    1007             :          * change since we have already rotated to the time zone.
    1008             :          */
    1009           4 :         *tzp = -tm->tm_gmtoff;   /* tm_gmtoff is Sun/DEC-ism */
    1010             : 
    1011             :         /*
    1012             :          * FreeBSD man pages indicate that this should work - tgl 97/04/23
    1013             :          */
    1014           4 :         if (tzn != NULL)
    1015             :         {
    1016             :             /*
    1017             :              * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
    1018             :              * contains an error message, which doesn't fit in the buffer
    1019             :              */
    1020           0 :             strlcpy(*tzn, tm->tm_zone, MAXTZLEN + 1);
    1021           0 :             if (strlen(tm->tm_zone) > MAXTZLEN)
    1022           0 :                 tm->tm_isdst = -1;
    1023             :         }
    1024             :     }
    1025             :     else
    1026           0 :         tm->tm_isdst = -1;
    1027             : #elif defined(HAVE_INT_TIMEZONE)
    1028             :     if (tzp != NULL)
    1029             :     {
    1030             :         *tzp = (tm->tm_isdst > 0) ? TIMEZONE_GLOBAL - SECS_PER_HOUR : TIMEZONE_GLOBAL;
    1031             : 
    1032             :         if (tzn != NULL)
    1033             :         {
    1034             :             /*
    1035             :              * Copy no more than MAXTZLEN bytes of timezone to tzn, in case it
    1036             :              * contains an error message, which doesn't fit in the buffer
    1037             :              */
    1038             :             strlcpy(*tzn, TZNAME_GLOBAL[tm->tm_isdst], MAXTZLEN + 1);
    1039             :             if (strlen(TZNAME_GLOBAL[tm->tm_isdst]) > MAXTZLEN)
    1040             :                 tm->tm_isdst = -1;
    1041             :         }
    1042             :     }
    1043             :     else
    1044             :         tm->tm_isdst = -1;
    1045             : #else                           /* not (HAVE_STRUCT_TM_TM_ZONE ||
    1046             :                                  * HAVE_INT_TIMEZONE) */
    1047             :     if (tzp != NULL)
    1048             :     {
    1049             :         /* default to UTC */
    1050             :         *tzp = 0;
    1051             :         if (tzn != NULL)
    1052             :             *tzn = NULL;
    1053             :     }
    1054             :     else
    1055             :         tm->tm_isdst = -1;
    1056             : #endif
    1057             : }
    1058             : 
    1059             : void
    1060           4 : GetCurrentDateTime(struct tm *tm)
    1061             : {
    1062             :     int         tz;
    1063             : 
    1064           4 :     abstime2tm(time(NULL), &tz, tm, NULL);
    1065           4 : }
    1066             : 
    1067             : void
    1068         656 : dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
    1069             : {
    1070             :     int64       time;
    1071             : 
    1072         656 :     time = jd;
    1073         656 :     *hour = time / USECS_PER_HOUR;
    1074         656 :     time -= (*hour) * USECS_PER_HOUR;
    1075         656 :     *min = time / USECS_PER_MINUTE;
    1076         656 :     time -= (*min) * USECS_PER_MINUTE;
    1077         656 :     *sec = time / USECS_PER_SEC;
    1078         656 :     *fsec = time - (*sec * USECS_PER_SEC);
    1079         656 : }                               /* dt2time() */
    1080             : 
    1081             : 
    1082             : 
    1083             : /* DecodeNumberField()
    1084             :  * Interpret numeric string as a concatenated date or time field.
    1085             :  * Use the context of previously decoded fields to help with
    1086             :  * the interpretation.
    1087             :  */
    1088             : static int
    1089          60 : DecodeNumberField(int len, char *str, int fmask,
    1090             :                   int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits)
    1091             : {
    1092             :     char       *cp;
    1093             : 
    1094             :     /*
    1095             :      * Have a decimal point? Then this is a date or something with a seconds
    1096             :      * field...
    1097             :      */
    1098          60 :     if ((cp = strchr(str, '.')) != NULL)
    1099             :     {
    1100             :         char        fstr[7];
    1101             :         int         i;
    1102             : 
    1103           0 :         cp++;
    1104             : 
    1105             :         /*
    1106             :          * OK, we have at most six digits to care about. Let's construct a
    1107             :          * string with those digits, zero-padded on the right, and then do the
    1108             :          * conversion to an integer.
    1109             :          *
    1110             :          * XXX This truncates the seventh digit, unlike rounding it as the
    1111             :          * backend does.
    1112             :          */
    1113           0 :         for (i = 0; i < 6; i++)
    1114           0 :             fstr[i] = *cp != '\0' ? *cp++ : '0';
    1115           0 :         fstr[i] = '\0';
    1116           0 :         *fsec = strtoint(fstr, NULL, 10);
    1117           0 :         *cp = '\0';
    1118           0 :         len = strlen(str);
    1119             :     }
    1120             :     /* No decimal point and no complete date yet? */
    1121          60 :     else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
    1122             :     {
    1123             :         /* yyyymmdd? */
    1124          60 :         if (len == 8)
    1125             :         {
    1126          36 :             *tmask = DTK_DATE_M;
    1127             : 
    1128          36 :             tm->tm_mday = atoi(str + 6);
    1129          36 :             *(str + 6) = '\0';
    1130          36 :             tm->tm_mon = atoi(str + 4);
    1131          36 :             *(str + 4) = '\0';
    1132          36 :             tm->tm_year = atoi(str + 0);
    1133             : 
    1134          36 :             return DTK_DATE;
    1135             :         }
    1136             :         /* yymmdd? */
    1137          24 :         else if (len == 6)
    1138             :         {
    1139          24 :             *tmask = DTK_DATE_M;
    1140          24 :             tm->tm_mday = atoi(str + 4);
    1141          24 :             *(str + 4) = '\0';
    1142          24 :             tm->tm_mon = atoi(str + 2);
    1143          24 :             *(str + 2) = '\0';
    1144          24 :             tm->tm_year = atoi(str + 0);
    1145          24 :             *is2digits = true;
    1146             : 
    1147          24 :             return DTK_DATE;
    1148             :         }
    1149             :         /* yyddd? */
    1150           0 :         else if (len == 5)
    1151             :         {
    1152           0 :             *tmask = DTK_DATE_M;
    1153           0 :             tm->tm_mday = atoi(str + 2);
    1154           0 :             *(str + 2) = '\0';
    1155           0 :             tm->tm_mon = 1;
    1156           0 :             tm->tm_year = atoi(str + 0);
    1157           0 :             *is2digits = true;
    1158             : 
    1159           0 :             return DTK_DATE;
    1160             :         }
    1161             :     }
    1162             : 
    1163             :     /* not all time fields are specified? */
    1164           0 :     if ((fmask & DTK_TIME_M) != DTK_TIME_M)
    1165             :     {
    1166             :         /* hhmmss */
    1167           0 :         if (len == 6)
    1168             :         {
    1169           0 :             *tmask = DTK_TIME_M;
    1170           0 :             tm->tm_sec = atoi(str + 4);
    1171           0 :             *(str + 4) = '\0';
    1172           0 :             tm->tm_min = atoi(str + 2);
    1173           0 :             *(str + 2) = '\0';
    1174           0 :             tm->tm_hour = atoi(str + 0);
    1175             : 
    1176           0 :             return DTK_TIME;
    1177             :         }
    1178             :         /* hhmm? */
    1179           0 :         else if (len == 4)
    1180             :         {
    1181           0 :             *tmask = DTK_TIME_M;
    1182           0 :             tm->tm_sec = 0;
    1183           0 :             tm->tm_min = atoi(str + 2);
    1184           0 :             *(str + 2) = '\0';
    1185           0 :             tm->tm_hour = atoi(str + 0);
    1186             : 
    1187           0 :             return DTK_TIME;
    1188             :         }
    1189             :     }
    1190             : 
    1191           0 :     return -1;
    1192             : }                               /* DecodeNumberField() */
    1193             : 
    1194             : 
    1195             : /* DecodeNumber()
    1196             :  * Interpret plain numeric field as a date value in context.
    1197             :  */
    1198             : static int
    1199        1592 : DecodeNumber(int flen, char *str, int fmask,
    1200             :              int *tmask, struct tm *tm, fsec_t *fsec, bool *is2digits, bool EuroDates)
    1201             : {
    1202             :     int         val;
    1203             :     char       *cp;
    1204             : 
    1205        1592 :     *tmask = 0;
    1206             : 
    1207        1592 :     val = strtoint(str, &cp, 10);
    1208        1592 :     if (cp == str)
    1209           0 :         return -1;
    1210             : 
    1211        1592 :     if (*cp == '.')
    1212             :     {
    1213             :         /*
    1214             :          * More than two digits? Then could be a date or a run-together time:
    1215             :          * 2001.360 20011225 040506.789
    1216             :          */
    1217           0 :         if (cp - str > 2)
    1218           0 :             return DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
    1219             :                                      tmask, tm, fsec, is2digits);
    1220             : 
    1221           0 :         *fsec = strtod(cp, &cp);
    1222           0 :         if (*cp != '\0')
    1223           0 :             return -1;
    1224             :     }
    1225        1592 :     else if (*cp != '\0')
    1226           0 :         return -1;
    1227             : 
    1228             :     /* Special case day of year? */
    1229        1592 :     if (flen == 3 && (fmask & DTK_M(YEAR)) && val >= 1 && val <= 366)
    1230             :     {
    1231          24 :         *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
    1232          24 :         tm->tm_yday = val;
    1233          24 :         j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
    1234             :                &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    1235             :     }
    1236             : 
    1237             :     /***
    1238             :      * Enough digits to be unequivocal year? Used to test for 4 digits or
    1239             :      * more, but we now test first for a three-digit doy so anything
    1240             :      * bigger than two digits had better be an explicit year.
    1241             :      * - thomas 1999-01-09
    1242             :      * Back to requiring a 4 digit year. We accept a two digit
    1243             :      * year farther down. - thomas 2000-03-28
    1244             :      ***/
    1245        1568 :     else if (flen >= 4)
    1246             :     {
    1247         480 :         *tmask = DTK_M(YEAR);
    1248             : 
    1249             :         /* already have a year? then see if we can substitute... */
    1250         480 :         if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
    1251           0 :             tm->tm_year >= 1 && tm->tm_year <= 31)
    1252             :         {
    1253           0 :             tm->tm_mday = tm->tm_year;
    1254           0 :             *tmask = DTK_M(DAY);
    1255             :         }
    1256             : 
    1257         480 :         tm->tm_year = val;
    1258             :     }
    1259             : 
    1260             :     /* already have year? then could be month */
    1261        1088 :     else if ((fmask & DTK_M(YEAR)) && !(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
    1262             :     {
    1263         224 :         *tmask = DTK_M(MONTH);
    1264         224 :         tm->tm_mon = val;
    1265             :     }
    1266             :     /* no year and EuroDates enabled? then could be day */
    1267         864 :     else if ((EuroDates || (fmask & DTK_M(MONTH))) &&
    1268         740 :              !(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)) &&
    1269         348 :              val >= 1 && val <= 31)
    1270             :     {
    1271         324 :         *tmask = DTK_M(DAY);
    1272         324 :         tm->tm_mday = val;
    1273             :     }
    1274         540 :     else if (!(fmask & DTK_M(MONTH)) && val >= 1 && val <= MONTHS_PER_YEAR)
    1275             :     {
    1276         120 :         *tmask = DTK_M(MONTH);
    1277         120 :         tm->tm_mon = val;
    1278             :     }
    1279         420 :     else if (!(fmask & DTK_M(DAY)) && val >= 1 && val <= 31)
    1280             :     {
    1281         272 :         *tmask = DTK_M(DAY);
    1282         272 :         tm->tm_mday = val;
    1283             :     }
    1284             : 
    1285             :     /*
    1286             :      * Check for 2 or 4 or more digits, but currently we reach here only if
    1287             :      * two digits. - thomas 2000-03-28
    1288             :      */
    1289         148 :     else if (!(fmask & DTK_M(YEAR)) && (flen >= 4 || flen == 2))
    1290             :     {
    1291         148 :         *tmask = DTK_M(YEAR);
    1292         148 :         tm->tm_year = val;
    1293             : 
    1294             :         /* adjust ONLY if exactly two digits... */
    1295         148 :         *is2digits = (flen == 2);
    1296             :     }
    1297             :     else
    1298           0 :         return -1;
    1299             : 
    1300        1592 :     return 0;
    1301             : }                               /* DecodeNumber() */
    1302             : 
    1303             : /* DecodeDate()
    1304             :  * Decode date string which includes delimiters.
    1305             :  * Insist on a complete set of fields.
    1306             :  */
    1307             : static int
    1308         532 : DecodeDate(char *str, int fmask, int *tmask, struct tm *tm, bool EuroDates)
    1309             : {
    1310             :     fsec_t      fsec;
    1311             : 
    1312         532 :     int         nf = 0;
    1313             :     int         i,
    1314             :                 len;
    1315         532 :     bool        bc = false;
    1316         532 :     bool        is2digits = false;
    1317             :     int         type,
    1318             :                 val,
    1319         532 :                 dmask = 0;
    1320             :     char       *field[MAXDATEFIELDS];
    1321             : 
    1322             :     /* parse this string... */
    1323        2096 :     while (*str != '\0' && nf < MAXDATEFIELDS)
    1324             :     {
    1325             :         /* skip field separators */
    1326        1756 :         while (!isalnum((unsigned char) *str))
    1327         192 :             str++;
    1328             : 
    1329        1564 :         field[nf] = str;
    1330        1564 :         if (isdigit((unsigned char) *str))
    1331             :         {
    1332        4812 :             while (isdigit((unsigned char) *str))
    1333        3420 :                 str++;
    1334             :         }
    1335         172 :         else if (isalpha((unsigned char) *str))
    1336             :         {
    1337        1092 :             while (isalpha((unsigned char) *str))
    1338         920 :                 str++;
    1339             :         }
    1340             : 
    1341             :         /* Just get rid of any non-digit, non-alpha characters... */
    1342        1564 :         if (*str != '\0')
    1343        1032 :             *str++ = '\0';
    1344        1564 :         nf++;
    1345             :     }
    1346             : 
    1347             : #if 0
    1348             :     /* don't allow too many fields */
    1349             :     if (nf > 3)
    1350             :         return -1;
    1351             : #endif
    1352             : 
    1353         532 :     *tmask = 0;
    1354             : 
    1355             :     /* look first for text fields, since that will be unambiguous month */
    1356        2092 :     for (i = 0; i < nf; i++)
    1357             :     {
    1358        1564 :         if (isalpha((unsigned char) *field[i]))
    1359             :         {
    1360         172 :             type = DecodeSpecial(i, field[i], &val);
    1361         172 :             if (type == IGNORE_DTF)
    1362           0 :                 continue;
    1363             : 
    1364         172 :             dmask = DTK_M(type);
    1365         172 :             switch (type)
    1366             :             {
    1367         168 :                 case MONTH:
    1368         168 :                     tm->tm_mon = val;
    1369         168 :                     break;
    1370             : 
    1371           0 :                 case ADBC:
    1372           0 :                     bc = (val == BC);
    1373           0 :                     break;
    1374             : 
    1375           4 :                 default:
    1376           4 :                     return -1;
    1377             :             }
    1378         168 :             if (fmask & dmask)
    1379           0 :                 return -1;
    1380             : 
    1381         168 :             fmask |= dmask;
    1382         168 :             *tmask |= dmask;
    1383             : 
    1384             :             /* mark this field as being completed */
    1385         168 :             field[i] = NULL;
    1386             :         }
    1387             :     }
    1388             : 
    1389             :     /* now pick up remaining numeric fields */
    1390        2088 :     for (i = 0; i < nf; i++)
    1391             :     {
    1392        1560 :         if (field[i] == NULL)
    1393         168 :             continue;
    1394             : 
    1395        1392 :         if ((len = strlen(field[i])) <= 0)
    1396           0 :             return -1;
    1397             : 
    1398        1392 :         if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits, EuroDates) != 0)
    1399           0 :             return -1;
    1400             : 
    1401        1392 :         if (fmask & dmask)
    1402           0 :             return -1;
    1403             : 
    1404        1392 :         fmask |= dmask;
    1405        1392 :         *tmask |= dmask;
    1406             :     }
    1407             : 
    1408         528 :     if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
    1409           0 :         return -1;
    1410             : 
    1411             :     /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
    1412         528 :     if (bc)
    1413             :     {
    1414           0 :         if (tm->tm_year > 0)
    1415           0 :             tm->tm_year = -(tm->tm_year - 1);
    1416             :         else
    1417           0 :             return -1;
    1418             :     }
    1419         528 :     else if (is2digits)
    1420             :     {
    1421         124 :         if (tm->tm_year < 70)
    1422          48 :             tm->tm_year += 2000;
    1423          76 :         else if (tm->tm_year < 100)
    1424          76 :             tm->tm_year += 1900;
    1425             :     }
    1426             : 
    1427         528 :     return 0;
    1428             : }                               /* DecodeDate() */
    1429             : 
    1430             : 
    1431             : /* DecodeTime()
    1432             :  * Decode time string which includes delimiters.
    1433             :  * Only check the lower limit on hours, since this same code
    1434             :  *  can be used to represent time spans.
    1435             :  */
    1436             : int
    1437         500 : DecodeTime(char *str, int *tmask, struct tm *tm, fsec_t *fsec)
    1438             : {
    1439             :     char       *cp;
    1440             : 
    1441         500 :     *tmask = DTK_TIME_M;
    1442             : 
    1443         500 :     tm->tm_hour = strtoint(str, &cp, 10);
    1444         500 :     if (*cp != ':')
    1445           0 :         return -1;
    1446         500 :     str = cp + 1;
    1447         500 :     tm->tm_min = strtoint(str, &cp, 10);
    1448         500 :     if (*cp == '\0')
    1449             :     {
    1450         136 :         tm->tm_sec = 0;
    1451         136 :         *fsec = 0;
    1452             :     }
    1453         364 :     else if (*cp != ':')
    1454           0 :         return -1;
    1455             :     else
    1456             :     {
    1457         364 :         str = cp + 1;
    1458         364 :         tm->tm_sec = strtoint(str, &cp, 10);
    1459         364 :         if (*cp == '\0')
    1460         228 :             *fsec = 0;
    1461         136 :         else if (*cp == '.')
    1462             :         {
    1463             :             char        fstr[7];
    1464             :             int         i;
    1465             : 
    1466         136 :             cp++;
    1467             : 
    1468             :             /*
    1469             :              * OK, we have at most six digits to care about. Let's construct a
    1470             :              * string with those digits, zero-padded on the right, and then do
    1471             :              * the conversion to an integer.
    1472             :              *
    1473             :              * XXX This truncates the seventh digit, unlike rounding it as the
    1474             :              * backend does.
    1475             :              */
    1476         952 :             for (i = 0; i < 6; i++)
    1477         816 :                 fstr[i] = *cp != '\0' ? *cp++ : '0';
    1478         136 :             fstr[i] = '\0';
    1479         136 :             *fsec = strtoint(fstr, &cp, 10);
    1480         136 :             if (*cp != '\0')
    1481           0 :                 return -1;
    1482             :         }
    1483             :         else
    1484           0 :             return -1;
    1485             :     }
    1486             : 
    1487             :     /* do a sanity check */
    1488         500 :     if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||
    1489         500 :         tm->tm_sec < 0 || tm->tm_sec > 59 || *fsec >= USECS_PER_SEC)
    1490           0 :         return -1;
    1491             : 
    1492         500 :     return 0;
    1493             : }                               /* DecodeTime() */
    1494             : 
    1495             : /* DecodeTimezone()
    1496             :  * Interpret string as a numeric timezone.
    1497             :  *
    1498             :  * Note: we allow timezone offsets up to 13:59.  There are places that
    1499             :  * use +1300 summer time.
    1500             :  */
    1501             : static int
    1502         236 : DecodeTimezone(char *str, int *tzp)
    1503             : {
    1504             :     int         tz;
    1505             :     int         hr,
    1506             :                 min;
    1507             :     char       *cp;
    1508             :     int         len;
    1509             : 
    1510             :     /* assume leading character is "+" or "-" */
    1511         236 :     hr = strtoint(str + 1, &cp, 10);
    1512             : 
    1513             :     /* explicit delimiter? */
    1514         236 :     if (*cp == ':')
    1515          68 :         min = strtoint(cp + 1, &cp, 10);
    1516             :     /* otherwise, might have run things together... */
    1517         168 :     else if (*cp == '\0' && (len = strlen(str)) > 3)
    1518             :     {
    1519          32 :         min = strtoint(str + len - 2, &cp, 10);
    1520          32 :         if (min < 0 || min >= 60)
    1521           0 :             return -1;
    1522             : 
    1523          32 :         *(str + len - 2) = '\0';
    1524          32 :         hr = strtoint(str + 1, &cp, 10);
    1525          32 :         if (hr < 0 || hr > 13)
    1526           0 :             return -1;
    1527             :     }
    1528             :     else
    1529         136 :         min = 0;
    1530             : 
    1531         236 :     tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE;
    1532         236 :     if (*str == '-')
    1533          68 :         tz = -tz;
    1534             : 
    1535         236 :     *tzp = -tz;
    1536         236 :     return *cp != '\0';
    1537             : }                               /* DecodeTimezone() */
    1538             : 
    1539             : 
    1540             : /* DecodePosixTimezone()
    1541             :  * Interpret string as a POSIX-compatible timezone:
    1542             :  *  PST-hh:mm
    1543             :  *  PST+h
    1544             :  * - thomas 2000-03-15
    1545             :  */
    1546             : static int
    1547           0 : DecodePosixTimezone(char *str, int *tzp)
    1548             : {
    1549             :     int         val,
    1550             :                 tz;
    1551             :     int         type;
    1552             :     char       *cp;
    1553             :     char        delim;
    1554             : 
    1555           0 :     cp = str;
    1556           0 :     while (*cp != '\0' && isalpha((unsigned char) *cp))
    1557           0 :         cp++;
    1558             : 
    1559           0 :     if (DecodeTimezone(cp, &tz) != 0)
    1560           0 :         return -1;
    1561             : 
    1562           0 :     delim = *cp;
    1563           0 :     *cp = '\0';
    1564           0 :     type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
    1565           0 :     *cp = delim;
    1566             : 
    1567           0 :     switch (type)
    1568             :     {
    1569           0 :         case DTZ:
    1570             :         case TZ:
    1571           0 :             *tzp = -(val + tz);
    1572           0 :             break;
    1573             : 
    1574           0 :         default:
    1575           0 :             return -1;
    1576             :     }
    1577             : 
    1578           0 :     return 0;
    1579             : }                               /* DecodePosixTimezone() */
    1580             : 
    1581             : /* ParseDateTime()
    1582             :  * Break string into tokens based on a date/time context.
    1583             :  * Several field types are assigned:
    1584             :  *  DTK_NUMBER - digits and (possibly) a decimal point
    1585             :  *  DTK_DATE - digits and two delimiters, or digits and text
    1586             :  *  DTK_TIME - digits, colon delimiters, and possibly a decimal point
    1587             :  *  DTK_STRING - text (no digits)
    1588             :  *  DTK_SPECIAL - leading "+" or "-" followed by text
    1589             :  *  DTK_TZ - leading "+" or "-" followed by digits
    1590             :  * Note that some field types can hold unexpected items:
    1591             :  *  DTK_NUMBER can hold date fields (yy.ddd)
    1592             :  *  DTK_STRING can hold months (January) and time zones (PST)
    1593             :  *  DTK_DATE can hold Posix time zones (GMT-8)
    1594             :  *
    1595             :  * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS
    1596             :  * bytes of space.  On output, field[] entries will point into it.
    1597             :  * The field[] and ftype[] arrays must have at least MAXDATEFIELDS entries.
    1598             :  */
    1599             : int
    1600         836 : ParseDateTime(char *timestr, char *lowstr,
    1601             :               char **field, int *ftype, int *numfields, char **endstr)
    1602             : {
    1603         836 :     int         nf = 0;
    1604         836 :     char       *lp = lowstr;
    1605             : 
    1606         836 :     *endstr = timestr;
    1607             :     /* outer loop through fields */
    1608        4548 :     while (*(*endstr) != '\0')
    1609             :     {
    1610             :         /* Record start of current field */
    1611        3716 :         if (nf >= MAXDATEFIELDS)
    1612           4 :             return -1;
    1613        3712 :         field[nf] = lp;
    1614             : 
    1615             :         /* leading digit? then date or time */
    1616        3712 :         if (isdigit((unsigned char) *(*endstr)))
    1617             :         {
    1618        1448 :             *lp++ = *(*endstr)++;
    1619        3744 :             while (isdigit((unsigned char) *(*endstr)))
    1620        2296 :                 *lp++ = *(*endstr)++;
    1621             : 
    1622             :             /* time field? */
    1623        1448 :             if (*(*endstr) == ':')
    1624             :             {
    1625         500 :                 ftype[nf] = DTK_TIME;
    1626         500 :                 *lp++ = *(*endstr)++;
    1627        3544 :                 while (isdigit((unsigned char) *(*endstr)) ||
    1628        1000 :                        (*(*endstr) == ':') || (*(*endstr) == '.'))
    1629        3044 :                     *lp++ = *(*endstr)++;
    1630             :             }
    1631             :             /* date field? allow embedded text month */
    1632         948 :             else if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
    1633         480 :             {
    1634             :                 /* save delimiting character to use later */
    1635         480 :                 char       *dp = (*endstr);
    1636             : 
    1637         480 :                 *lp++ = *(*endstr)++;
    1638             :                 /* second field is all digits? then no embedded text month */
    1639         480 :                 if (isdigit((unsigned char) *(*endstr)))
    1640             :                 {
    1641         360 :                     ftype[nf] = (*dp == '.') ? DTK_NUMBER : DTK_DATE;
    1642         996 :                     while (isdigit((unsigned char) *(*endstr)))
    1643         636 :                         *lp++ = *(*endstr)++;
    1644             : 
    1645             :                     /*
    1646             :                      * insist that the delimiters match to get a three-field
    1647             :                      * date.
    1648             :                      */
    1649         360 :                     if (*(*endstr) == *dp)
    1650             :                     {
    1651         336 :                         ftype[nf] = DTK_DATE;
    1652         336 :                         *lp++ = *(*endstr)++;
    1653        1144 :                         while (isdigit((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
    1654         808 :                             *lp++ = *(*endstr)++;
    1655             :                     }
    1656             :                 }
    1657             :                 else
    1658             :                 {
    1659         120 :                     ftype[nf] = DTK_DATE;
    1660         888 :                     while (isalnum((unsigned char) *(*endstr)) || (*(*endstr) == *dp))
    1661         768 :                         *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1662             :                 }
    1663             :             }
    1664             : 
    1665             :             /*
    1666             :              * otherwise, number only and will determine year, month, day, or
    1667             :              * concatenated fields later...
    1668             :              */
    1669             :             else
    1670         468 :                 ftype[nf] = DTK_NUMBER;
    1671             :         }
    1672             :         /* Leading decimal point? Then fractional seconds... */
    1673        2264 :         else if (*(*endstr) == '.')
    1674             :         {
    1675         196 :             *lp++ = *(*endstr)++;
    1676         196 :             while (isdigit((unsigned char) *(*endstr)))
    1677           0 :                 *lp++ = *(*endstr)++;
    1678             : 
    1679         196 :             ftype[nf] = DTK_NUMBER;
    1680             :         }
    1681             : 
    1682             :         /*
    1683             :          * text? then date string, month, day of week, special, or timezone
    1684             :          */
    1685        2068 :         else if (isalpha((unsigned char) *(*endstr)))
    1686             :         {
    1687         520 :             ftype[nf] = DTK_STRING;
    1688         520 :             *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1689        2484 :             while (isalpha((unsigned char) *(*endstr)))
    1690        1964 :                 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1691             : 
    1692             :             /*
    1693             :              * Full date string with leading text month? Could also be a POSIX
    1694             :              * time zone...
    1695             :              */
    1696         520 :             if (*(*endstr) == '-' || *(*endstr) == '/' || *(*endstr) == '.')
    1697             :             {
    1698          48 :                 char       *dp = (*endstr);
    1699             : 
    1700          48 :                 ftype[nf] = DTK_DATE;
    1701          48 :                 *lp++ = *(*endstr)++;
    1702         336 :                 while (isdigit((unsigned char) *(*endstr)) || *(*endstr) == *dp)
    1703         288 :                     *lp++ = *(*endstr)++;
    1704             :             }
    1705             :         }
    1706             :         /* skip leading spaces */
    1707        1548 :         else if (isspace((unsigned char) *(*endstr)))
    1708             :         {
    1709        1256 :             (*endstr)++;
    1710        1256 :             continue;
    1711             :         }
    1712             :         /* sign? then special or numeric timezone */
    1713         292 :         else if (*(*endstr) == '+' || *(*endstr) == '-')
    1714             :         {
    1715         204 :             *lp++ = *(*endstr)++;
    1716             :             /* soak up leading whitespace */
    1717         204 :             while (isspace((unsigned char) *(*endstr)))
    1718           0 :                 (*endstr)++;
    1719             :             /* numeric timezone? */
    1720         204 :             if (isdigit((unsigned char) *(*endstr)))
    1721             :             {
    1722         204 :                 ftype[nf] = DTK_TZ;
    1723         204 :                 *lp++ = *(*endstr)++;
    1724         408 :                 while (isdigit((unsigned char) *(*endstr)) ||
    1725         272 :                        (*(*endstr) == ':') || (*(*endstr) == '.'))
    1726         204 :                     *lp++ = *(*endstr)++;
    1727             :             }
    1728             :             /* special? */
    1729           0 :             else if (isalpha((unsigned char) *(*endstr)))
    1730             :             {
    1731           0 :                 ftype[nf] = DTK_SPECIAL;
    1732           0 :                 *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1733           0 :                 while (isalpha((unsigned char) *(*endstr)))
    1734           0 :                     *lp++ = pg_tolower((unsigned char) *(*endstr)++);
    1735             :             }
    1736             :             /* otherwise something wrong... */
    1737             :             else
    1738           0 :                 return -1;
    1739             :         }
    1740             :         /* ignore punctuation but use as delimiter */
    1741          88 :         else if (ispunct((unsigned char) *(*endstr)))
    1742             :         {
    1743          88 :             (*endstr)++;
    1744          88 :             continue;
    1745             :         }
    1746             :         /* otherwise, something is not right... */
    1747             :         else
    1748           0 :             return -1;
    1749             : 
    1750             :         /* force in a delimiter after each field */
    1751        2368 :         *lp++ = '\0';
    1752        2368 :         nf++;
    1753             :     }
    1754             : 
    1755         832 :     *numfields = nf;
    1756             : 
    1757         832 :     return 0;
    1758             : }                               /* ParseDateTime() */
    1759             : 
    1760             : 
    1761             : /* DecodeDateTime()
    1762             :  * Interpret previously parsed fields for general date and time.
    1763             :  * Return 0 if full date, 1 if only time, and -1 if problems.
    1764             :  *      External format(s):
    1765             :  *              "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
    1766             :  *              "Fri Feb-7-1997 15:23:27"
    1767             :  *              "Feb-7-1997 15:23:27"
    1768             :  *              "2-7-1997 15:23:27"
    1769             :  *              "1997-2-7 15:23:27"
    1770             :  *              "1997.038 15:23:27"       (day of year 1-366)
    1771             :  *      Also supports input in compact time:
    1772             :  *              "970207 152327"
    1773             :  *              "97038 152327"
    1774             :  *              "20011225T040506.789-07"
    1775             :  *
    1776             :  * Use the system-provided functions to get the current time zone
    1777             :  *  if not specified in the input string.
    1778             :  * If the date is outside the time_t system-supported time range,
    1779             :  *  then assume UTC time zone. - thomas 1997-05-27
    1780             :  */
    1781             : int
    1782         716 : DecodeDateTime(char **field, int *ftype, int nf,
    1783             :                int *dtype, struct tm *tm, fsec_t *fsec, bool EuroDates)
    1784             : {
    1785         716 :     int         fmask = 0,
    1786             :                 tmask,
    1787             :                 type;
    1788         716 :     int         ptype = 0;      /* "prefix type" for ISO y2001m02d04 format */
    1789             :     int         i;
    1790             :     int         val;
    1791         716 :     int         mer = HR24;
    1792         716 :     bool        haveTextMonth = false;
    1793         716 :     bool        is2digits = false;
    1794         716 :     bool        bc = false;
    1795         716 :     int         t = 0;
    1796         716 :     int        *tzp = &t;
    1797             : 
    1798             :     /***
    1799             :      * We'll insist on at least all of the date fields, but initialize the
    1800             :      * remaining fields in case they are not set later...
    1801             :      ***/
    1802         716 :     *dtype = DTK_DATE;
    1803         716 :     tm->tm_hour = 0;
    1804         716 :     tm->tm_min = 0;
    1805         716 :     tm->tm_sec = 0;
    1806         716 :     *fsec = 0;
    1807             :     /* don't know daylight savings time status apriori */
    1808         716 :     tm->tm_isdst = -1;
    1809         716 :     if (tzp != NULL)
    1810         716 :         *tzp = 0;
    1811             : 
    1812        2496 :     for (i = 0; i < nf; i++)
    1813             :     {
    1814        1800 :         switch (ftype[i])
    1815             :         {
    1816         504 :             case DTK_DATE:
    1817             :                 /***
    1818             :                  * Integral julian day with attached time zone?
    1819             :                  * All other forms with JD will be separated into
    1820             :                  * distinct fields, so we handle just this case here.
    1821             :                  ***/
    1822         504 :                 if (ptype == DTK_JULIAN)
    1823             :                 {
    1824             :                     char       *cp;
    1825             :                     int         jday;
    1826             : 
    1827           0 :                     if (tzp == NULL)
    1828           0 :                         return -1;
    1829             : 
    1830           0 :                     jday = strtoint(field[i], &cp, 10);
    1831           0 :                     if (*cp != '-')
    1832           0 :                         return -1;
    1833             : 
    1834           0 :                     j2date(jday, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    1835             :                     /* Get the time zone from the end of the string */
    1836           0 :                     if (DecodeTimezone(cp, tzp) != 0)
    1837           0 :                         return -1;
    1838             : 
    1839           0 :                     tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
    1840           0 :                     ptype = 0;
    1841           0 :                     break;
    1842             :                 }
    1843             :                 /***
    1844             :                  * Already have a date? Then this might be a POSIX time
    1845             :                  * zone with an embedded dash (e.g. "PST-3" == "EST") or
    1846             :                  * a run-together time with trailing time zone (e.g. hhmmss-zz).
    1847             :                  * - thomas 2001-12-25
    1848             :                  ***/
    1849         504 :                 else if (((fmask & DTK_DATE_M) == DTK_DATE_M)
    1850         504 :                          || (ptype != 0))
    1851             :                 {
    1852             :                     /* No time zone accepted? Then quit... */
    1853           0 :                     if (tzp == NULL)
    1854           0 :                         return -1;
    1855             : 
    1856           0 :                     if (isdigit((unsigned char) *field[i]) || ptype != 0)
    1857           0 :                     {
    1858             :                         char       *cp;
    1859             : 
    1860           0 :                         if (ptype != 0)
    1861             :                         {
    1862             :                             /* Sanity check; should not fail this test */
    1863           0 :                             if (ptype != DTK_TIME)
    1864           0 :                                 return -1;
    1865           0 :                             ptype = 0;
    1866             :                         }
    1867             : 
    1868             :                         /*
    1869             :                          * Starts with a digit but we already have a time
    1870             :                          * field? Then we are in trouble with a date and time
    1871             :                          * already...
    1872             :                          */
    1873           0 :                         if ((fmask & DTK_TIME_M) == DTK_TIME_M)
    1874           0 :                             return -1;
    1875             : 
    1876           0 :                         if ((cp = strchr(field[i], '-')) == NULL)
    1877           0 :                             return -1;
    1878             : 
    1879             :                         /* Get the time zone from the end of the string */
    1880           0 :                         if (DecodeTimezone(cp, tzp) != 0)
    1881           0 :                             return -1;
    1882           0 :                         *cp = '\0';
    1883             : 
    1884             :                         /*
    1885             :                          * Then read the rest of the field as a concatenated
    1886             :                          * time
    1887             :                          */
    1888           0 :                         if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
    1889             :                                                           &tmask, tm, fsec, &is2digits)) < 0)
    1890           0 :                             return -1;
    1891             : 
    1892             :                         /*
    1893             :                          * modify tmask after returning from
    1894             :                          * DecodeNumberField()
    1895             :                          */
    1896           0 :                         tmask |= DTK_M(TZ);
    1897             :                     }
    1898             :                     else
    1899             :                     {
    1900           0 :                         if (DecodePosixTimezone(field[i], tzp) != 0)
    1901           0 :                             return -1;
    1902             : 
    1903           0 :                         ftype[i] = DTK_TZ;
    1904           0 :                         tmask = DTK_M(TZ);
    1905             :                     }
    1906             :                 }
    1907         504 :                 else if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
    1908           0 :                     return -1;
    1909         504 :                 break;
    1910             : 
    1911         496 :             case DTK_TIME:
    1912         496 :                 if (DecodeTime(field[i], &tmask, tm, fsec) != 0)
    1913           0 :                     return -1;
    1914             : 
    1915             :                 /*
    1916             :                  * Check upper limit on hours; other limits checked in
    1917             :                  * DecodeTime()
    1918             :                  */
    1919             :                 /* test for > 24:00:00 */
    1920         496 :                 if (tm->tm_hour > 24 ||
    1921         492 :                     (tm->tm_hour == 24 && (tm->tm_min > 0 || tm->tm_sec > 0)))
    1922           4 :                     return -1;
    1923         492 :                 break;
    1924             : 
    1925         204 :             case DTK_TZ:
    1926             :                 {
    1927             :                     int         tz;
    1928             : 
    1929         204 :                     if (tzp == NULL)
    1930           0 :                         return -1;
    1931             : 
    1932         204 :                     if (DecodeTimezone(field[i], &tz) != 0)
    1933           0 :                         return -1;
    1934             : 
    1935             :                     /*
    1936             :                      * Already have a time zone? Then maybe this is the second
    1937             :                      * field of a POSIX time: EST+3 (equivalent to PST)
    1938             :                      */
    1939         204 :                     if (i > 0 && (fmask & DTK_M(TZ)) != 0 &&
    1940           0 :                         ftype[i - 1] == DTK_TZ &&
    1941           0 :                         isalpha((unsigned char) *field[i - 1]))
    1942             :                     {
    1943           0 :                         *tzp -= tz;
    1944           0 :                         tmask = 0;
    1945             :                     }
    1946             :                     else
    1947             :                     {
    1948         204 :                         *tzp = tz;
    1949         204 :                         tmask = DTK_M(TZ);
    1950             :                     }
    1951             :                 }
    1952         204 :                 break;
    1953             : 
    1954         312 :             case DTK_NUMBER:
    1955             : 
    1956             :                 /*
    1957             :                  * Was this an "ISO date" with embedded field labels? An
    1958             :                  * example is "y2001m02d04" - thomas 2001-02-04
    1959             :                  */
    1960         312 :                 if (ptype != 0)
    1961             :                 {
    1962             :                     char       *cp;
    1963             :                     int         value;
    1964             : 
    1965          24 :                     value = strtoint(field[i], &cp, 10);
    1966             : 
    1967             :                     /*
    1968             :                      * only a few kinds are allowed to have an embedded
    1969             :                      * decimal
    1970             :                      */
    1971          24 :                     if (*cp == '.')
    1972             :                         switch (ptype)
    1973             :                         {
    1974           0 :                             case DTK_JULIAN:
    1975             :                             case DTK_TIME:
    1976             :                             case DTK_SECOND:
    1977           0 :                                 break;
    1978           0 :                             default:
    1979           0 :                                 return 1;
    1980             :                                 break;
    1981             :                         }
    1982          24 :                     else if (*cp != '\0')
    1983           0 :                         return -1;
    1984             : 
    1985             :                     switch (ptype)
    1986             :                     {
    1987           0 :                         case DTK_YEAR:
    1988           0 :                             tm->tm_year = value;
    1989           0 :                             tmask = DTK_M(YEAR);
    1990           0 :                             break;
    1991             : 
    1992           0 :                         case DTK_MONTH:
    1993             : 
    1994             :                             /*
    1995             :                              * already have a month and hour? then assume
    1996             :                              * minutes
    1997             :                              */
    1998           0 :                             if ((fmask & DTK_M(MONTH)) != 0 &&
    1999           0 :                                 (fmask & DTK_M(HOUR)) != 0)
    2000             :                             {
    2001           0 :                                 tm->tm_min = value;
    2002           0 :                                 tmask = DTK_M(MINUTE);
    2003             :                             }
    2004             :                             else
    2005             :                             {
    2006           0 :                                 tm->tm_mon = value;
    2007           0 :                                 tmask = DTK_M(MONTH);
    2008             :                             }
    2009           0 :                             break;
    2010             : 
    2011           0 :                         case DTK_DAY:
    2012           0 :                             tm->tm_mday = value;
    2013           0 :                             tmask = DTK_M(DAY);
    2014           0 :                             break;
    2015             : 
    2016           0 :                         case DTK_HOUR:
    2017           0 :                             tm->tm_hour = value;
    2018           0 :                             tmask = DTK_M(HOUR);
    2019           0 :                             break;
    2020             : 
    2021           0 :                         case DTK_MINUTE:
    2022           0 :                             tm->tm_min = value;
    2023           0 :                             tmask = DTK_M(MINUTE);
    2024           0 :                             break;
    2025             : 
    2026           0 :                         case DTK_SECOND:
    2027           0 :                             tm->tm_sec = value;
    2028           0 :                             tmask = DTK_M(SECOND);
    2029           0 :                             if (*cp == '.')
    2030             :                             {
    2031             :                                 double      frac;
    2032             : 
    2033           0 :                                 frac = strtod(cp, &cp);
    2034           0 :                                 if (*cp != '\0')
    2035           0 :                                     return -1;
    2036           0 :                                 *fsec = frac * 1000000;
    2037             :                             }
    2038           0 :                             break;
    2039             : 
    2040           0 :                         case DTK_TZ:
    2041           0 :                             tmask = DTK_M(TZ);
    2042           0 :                             if (DecodeTimezone(field[i], tzp) != 0)
    2043           0 :                                 return -1;
    2044           0 :                             break;
    2045             : 
    2046          24 :                         case DTK_JULIAN:
    2047             :                             /***
    2048             :                              * previous field was a label for "julian date"?
    2049             :                              ***/
    2050          24 :                             tmask = DTK_DATE_M;
    2051          24 :                             j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    2052             :                             /* fractional Julian Day? */
    2053          24 :                             if (*cp == '.')
    2054             :                             {
    2055             :                                 double      time;
    2056             : 
    2057           0 :                                 time = strtod(cp, &cp);
    2058           0 :                                 if (*cp != '\0')
    2059           0 :                                     return -1;
    2060             : 
    2061           0 :                                 tmask |= DTK_TIME_M;
    2062           0 :                                 dt2time((time * USECS_PER_DAY), &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
    2063             :                             }
    2064          24 :                             break;
    2065             : 
    2066           0 :                         case DTK_TIME:
    2067             :                             /* previous field was "t" for ISO time */
    2068           0 :                             if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
    2069             :                                                               &tmask, tm, fsec, &is2digits)) < 0)
    2070           0 :                                 return -1;
    2071             : 
    2072           0 :                             if (tmask != DTK_TIME_M)
    2073           0 :                                 return -1;
    2074           0 :                             break;
    2075             : 
    2076           0 :                         default:
    2077           0 :                             return -1;
    2078             :                             break;
    2079             :                     }
    2080             : 
    2081          24 :                     ptype = 0;
    2082          24 :                     *dtype = DTK_DATE;
    2083             :                 }
    2084             :                 else
    2085             :                 {
    2086             :                     char       *cp;
    2087             :                     int         flen;
    2088             : 
    2089         288 :                     flen = strlen(field[i]);
    2090         288 :                     cp = strchr(field[i], '.');
    2091             : 
    2092             :                     /* Embedded decimal and no date yet? */
    2093         288 :                     if (cp != NULL && !(fmask & DTK_DATE_M))
    2094             :                     {
    2095          28 :                         if (DecodeDate(field[i], fmask, &tmask, tm, EuroDates) != 0)
    2096           4 :                             return -1;
    2097             :                     }
    2098             :                     /* embedded decimal and several digits before? */
    2099         260 :                     else if (cp != NULL && flen - strlen(cp) > 2)
    2100             :                     {
    2101             :                         /*
    2102             :                          * Interpret as a concatenated date or time Set the
    2103             :                          * type field to allow decoding other fields later.
    2104             :                          * Example: 20011223 or 040506
    2105             :                          */
    2106           0 :                         if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
    2107             :                                                           &tmask, tm, fsec, &is2digits)) < 0)
    2108           0 :                             return -1;
    2109             :                     }
    2110         260 :                     else if (flen > 4)
    2111             :                     {
    2112          60 :                         if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
    2113             :                                                           &tmask, tm, fsec, &is2digits)) < 0)
    2114           0 :                             return -1;
    2115             :                     }
    2116             :                     /* otherwise it is a single date/time field... */
    2117         200 :                     else if (DecodeNumber(flen, field[i], fmask,
    2118             :                                           &tmask, tm, fsec, &is2digits, EuroDates) != 0)
    2119           0 :                         return -1;
    2120             :                 }
    2121         308 :                 break;
    2122             : 
    2123         284 :             case DTK_STRING:
    2124             :             case DTK_SPECIAL:
    2125         284 :                 type = DecodeSpecial(i, field[i], &val);
    2126         284 :                 if (type == IGNORE_DTF)
    2127           0 :                     continue;
    2128             : 
    2129         284 :                 tmask = DTK_M(type);
    2130             :                 switch (type)
    2131             :                 {
    2132           0 :                     case RESERV:
    2133           0 :                         switch (val)
    2134             :                         {
    2135           0 :                             case DTK_NOW:
    2136           0 :                                 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
    2137           0 :                                 *dtype = DTK_DATE;
    2138           0 :                                 GetCurrentDateTime(tm);
    2139           0 :                                 break;
    2140             : 
    2141           0 :                             case DTK_YESTERDAY:
    2142           0 :                                 tmask = DTK_DATE_M;
    2143           0 :                                 *dtype = DTK_DATE;
    2144           0 :                                 GetCurrentDateTime(tm);
    2145           0 :                                 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1,
    2146             :                                        &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    2147           0 :                                 tm->tm_hour = 0;
    2148           0 :                                 tm->tm_min = 0;
    2149           0 :                                 tm->tm_sec = 0;
    2150           0 :                                 break;
    2151             : 
    2152           0 :                             case DTK_TODAY:
    2153           0 :                                 tmask = DTK_DATE_M;
    2154           0 :                                 *dtype = DTK_DATE;
    2155           0 :                                 GetCurrentDateTime(tm);
    2156           0 :                                 tm->tm_hour = 0;
    2157           0 :                                 tm->tm_min = 0;
    2158           0 :                                 tm->tm_sec = 0;
    2159           0 :                                 break;
    2160             : 
    2161           0 :                             case DTK_TOMORROW:
    2162           0 :                                 tmask = DTK_DATE_M;
    2163           0 :                                 *dtype = DTK_DATE;
    2164           0 :                                 GetCurrentDateTime(tm);
    2165           0 :                                 j2date(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1,
    2166             :                                        &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
    2167           0 :                                 tm->tm_hour = 0;
    2168           0 :                                 tm->tm_min = 0;
    2169           0 :                                 tm->tm_sec = 0;
    2170           0 :                                 break;
    2171             : 
    2172           0 :                             case DTK_ZULU:
    2173           0 :                                 tmask = (DTK_TIME_M | DTK_M(TZ));
    2174           0 :                                 *dtype = DTK_DATE;
    2175           0 :                                 tm->tm_hour = 0;
    2176           0 :                                 tm->tm_min = 0;
    2177           0 :                                 tm->tm_sec = 0;
    2178           0 :                                 if (tzp != NULL)
    2179           0 :                                     *tzp = 0;
    2180           0 :                                 break;
    2181             : 
    2182           0 :                             default:
    2183           0 :                                 *dtype = val;
    2184             :                         }
    2185             : 
    2186           0 :                         break;
    2187             : 
    2188         100 :                     case MONTH:
    2189             : 
    2190             :                         /*
    2191             :                          * already have a (numeric) month? then see if we can
    2192             :                          * substitute...
    2193             :                          */
    2194         100 :                         if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
    2195           8 :                             !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 && tm->tm_mon <= 31)
    2196             :                         {
    2197           8 :                             tm->tm_mday = tm->tm_mon;
    2198           8 :                             tmask = DTK_M(DAY);
    2199             :                         }
    2200         100 :                         haveTextMonth = true;
    2201         100 :                         tm->tm_mon = val;
    2202         100 :                         break;
    2203             : 
    2204           0 :                     case DTZMOD:
    2205             : 
    2206             :                         /*
    2207             :                          * daylight savings time modifier (solves "MET DST"
    2208             :                          * syntax)
    2209             :                          */
    2210           0 :                         tmask |= DTK_M(DTZ);
    2211           0 :                         tm->tm_isdst = 1;
    2212           0 :                         if (tzp == NULL)
    2213           0 :                             return -1;
    2214           0 :                         *tzp -= val;
    2215           0 :                         break;
    2216             : 
    2217          68 :                     case DTZ:
    2218             : 
    2219             :                         /*
    2220             :                          * set mask for TZ here _or_ check for DTZ later when
    2221             :                          * getting default timezone
    2222             :                          */
    2223          68 :                         tmask |= DTK_M(TZ);
    2224          68 :                         tm->tm_isdst = 1;
    2225          68 :                         if (tzp == NULL)
    2226           0 :                             return -1;
    2227          68 :                         *tzp = -val;
    2228          68 :                         ftype[i] = DTK_TZ;
    2229          68 :                         break;
    2230             : 
    2231           0 :                     case TZ:
    2232           0 :                         tm->tm_isdst = 0;
    2233           0 :                         if (tzp == NULL)
    2234           0 :                             return -1;
    2235           0 :                         *tzp = -val;
    2236           0 :                         ftype[i] = DTK_TZ;
    2237           0 :                         break;
    2238             : 
    2239           0 :                     case IGNORE_DTF:
    2240           0 :                         break;
    2241             : 
    2242           4 :                     case AMPM:
    2243           4 :                         mer = val;
    2244           4 :                         break;
    2245             : 
    2246          24 :                     case ADBC:
    2247          24 :                         bc = (val == BC);
    2248          24 :                         break;
    2249             : 
    2250          52 :                     case DOW:
    2251          52 :                         tm->tm_wday = val;
    2252          52 :                         break;
    2253             : 
    2254          24 :                     case UNITS:
    2255          24 :                         tmask = 0;
    2256          24 :                         ptype = val;
    2257          24 :                         break;
    2258             : 
    2259           0 :                     case ISOTIME:
    2260             : 
    2261             :                         /*
    2262             :                          * This is a filler field "t" indicating that the next
    2263             :                          * field is time. Try to verify that this is sensible.
    2264             :                          */
    2265           0 :                         tmask = 0;
    2266             : 
    2267             :                         /* No preceding date? Then quit... */
    2268           0 :                         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
    2269           0 :                             return -1;
    2270             : 
    2271             :                         /***
    2272             :                          * We will need one of the following fields:
    2273             :                          *  DTK_NUMBER should be hhmmss.fff
    2274             :                          *  DTK_TIME should be hh:mm:ss.fff
    2275             :                          *  DTK_DATE should be hhmmss-zz
    2276             :                          ***/
    2277           0 :                         if (i >= nf - 1 ||
    2278           0 :                             (ftype[i + 1] != DTK_NUMBER &&
    2279           0 :                              ftype[i + 1] != DTK_TIME &&
    2280           0 :                              ftype[i + 1] != DTK_DATE))
    2281           0 :                             return -1;
    2282             : 
    2283           0 :                         ptype = val;
    2284           0 :                         break;
    2285             : 
    2286          12 :                     default:
    2287          12 :                         return -1;
    2288             :                 }
    2289         272 :                 break;
    2290             : 
    2291           0 :             default:
    2292           0 :                 return -1;
    2293             :         }
    2294             : 
    2295        1780 :         if (tmask & fmask)
    2296           0 :             return -1;
    2297        1780 :         fmask |= tmask;
    2298             :     }
    2299             : 
    2300             :     /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
    2301         696 :     if (bc)
    2302             :     {
    2303          24 :         if (tm->tm_year > 0)
    2304          24 :             tm->tm_year = -(tm->tm_year - 1);
    2305             :         else
    2306           0 :             return -1;
    2307             :     }
    2308         672 :     else if (is2digits)
    2309             :     {
    2310          24 :         if (tm->tm_year < 70)
    2311           0 :             tm->tm_year += 2000;
    2312          24 :         else if (tm->tm_year < 100)
    2313          24 :             tm->tm_year += 1900;
    2314             :     }
    2315             : 
    2316         696 :     if (mer != HR24 && tm->tm_hour > 12)
    2317           0 :         return -1;
    2318         696 :     if (mer == AM && tm->tm_hour == 12)
    2319           0 :         tm->tm_hour = 0;
    2320         696 :     else if (mer == PM && tm->tm_hour != 12)
    2321           0 :         tm->tm_hour += 12;
    2322             : 
    2323             :     /* do additional checking for full date specs... */
    2324         696 :     if (*dtype == DTK_DATE)
    2325             :     {
    2326         696 :         if ((fmask & DTK_DATE_M) != DTK_DATE_M)
    2327           0 :             return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
    2328             : 
    2329             :         /*
    2330             :          * check for valid day of month and month, now that we know for sure
    2331             :          * the month and year...
    2332             :          */
    2333         696 :         if (tm->tm_mon < 1 || tm->tm_mday < 1 || tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
    2334           4 :             return -1;
    2335             : 
    2336             :         /*
    2337             :          * backend tried to find local timezone here but we don't use the
    2338             :          * result afterwards anyway so we only check for this error: daylight
    2339             :          * savings time modifier but no standard timezone?
    2340             :          */
    2341         692 :         if ((fmask & DTK_DATE_M) == DTK_DATE_M && tzp != NULL && !(fmask & DTK_M(TZ)) && (fmask & DTK_M(DTZMOD)))
    2342           0 :             return -1;
    2343             :     }
    2344             : 
    2345         692 :     return 0;
    2346             : }                               /* DecodeDateTime() */
    2347             : 
    2348             : /* Function works as follows:
    2349             :  *
    2350             :  *
    2351             :  * */
    2352             : 
    2353             : static char *
    2354         456 : find_end_token(char *str, char *fmt)
    2355             : {
    2356             :     /*
    2357             :      * str: here is28the day12the hour fmt: here is%dthe day%hthe hour
    2358             :      *
    2359             :      * we extract the 28, we read the percent sign and the type "d" then this
    2360             :      * functions gets called as find_end_token("28the day12the hour", "the
    2361             :      * day%hthehour")
    2362             :      *
    2363             :      * fmt points to "the day%hthehour", next_percent points to %hthehour and
    2364             :      * we have to find a match for everything between these positions ("the
    2365             :      * day"). We look for "the day" in str and know that the pattern we are
    2366             :      * about to scan ends where this string starts (right after the "28")
    2367             :      *
    2368             :      * At the end, *fmt is '\0' and *str isn't. end_position then is
    2369             :      * unchanged.
    2370             :      */
    2371         456 :     char       *end_position = NULL;
    2372             :     char       *next_percent,
    2373         456 :                *subst_location = NULL;
    2374         456 :     int         scan_offset = 0;
    2375             :     char        last_char;
    2376             : 
    2377             :     /* are we at the end? */
    2378         456 :     if (!*fmt)
    2379             :     {
    2380          68 :         end_position = fmt;
    2381          68 :         return end_position;
    2382             :     }
    2383             : 
    2384             :     /* not at the end */
    2385         396 :     while (fmt[scan_offset] == '%' && fmt[scan_offset + 1])
    2386             :     {
    2387             :         /*
    2388             :          * there is no delimiter, skip to the next delimiter if we're reading
    2389             :          * a number and then something that is not a number "9:15pm", we might
    2390             :          * be able to recover with the strtol end pointer. Go for the next
    2391             :          * percent sign
    2392             :          */
    2393           8 :         scan_offset += 2;
    2394             :     }
    2395         388 :     next_percent = strchr(fmt + scan_offset, '%');
    2396         388 :     if (next_percent)
    2397             :     {
    2398             :         /*
    2399             :          * we don't want to allocate extra memory, so we temporarily set the
    2400             :          * '%' sign to '\0' and call strstr However since we allow whitespace
    2401             :          * to float around everything, we have to shorten the pattern until we
    2402             :          * reach a non-whitespace character
    2403             :          */
    2404             : 
    2405         376 :         subst_location = next_percent;
    2406         428 :         while (*(subst_location - 1) == ' ' && subst_location - 1 > fmt + scan_offset)
    2407          52 :             subst_location--;
    2408         376 :         last_char = *subst_location;
    2409         376 :         *subst_location = '\0';
    2410             : 
    2411             :         /*
    2412             :          * the haystack is the str and the needle is the original fmt but it
    2413             :          * ends at the position where the next percent sign would be
    2414             :          */
    2415             : 
    2416             :         /*
    2417             :          * There is one special case. Imagine: str = " 2", fmt = "%d %...",
    2418             :          * since we want to allow blanks as "dynamic" padding we have to
    2419             :          * accept this. Now, we are called with a fmt of " %..." and look for
    2420             :          * " " in str. We find it at the first position and never read the
    2421             :          * 2...
    2422             :          */
    2423         376 :         while (*str == ' ')
    2424           0 :             str++;
    2425         376 :         end_position = strstr(str, fmt + scan_offset);
    2426         376 :         *subst_location = last_char;
    2427             :     }
    2428             :     else
    2429             :     {
    2430             :         /*
    2431             :          * there is no other percent sign. So everything up to the end has to
    2432             :          * match.
    2433             :          */
    2434          12 :         end_position = str + strlen(str);
    2435             :     }
    2436         388 :     if (!end_position)
    2437             :     {
    2438             :         /*
    2439             :          * maybe we have the following case:
    2440             :          *
    2441             :          * str = "4:15am" fmt = "%M:%S %p"
    2442             :          *
    2443             :          * at this place we could have
    2444             :          *
    2445             :          * str = "15am" fmt = " %p"
    2446             :          *
    2447             :          * and have set fmt to " " because overwrote the % sign with a NULL
    2448             :          *
    2449             :          * In this case where we would have to match a space but can't find
    2450             :          * it, set end_position to the end of the string
    2451             :          */
    2452           4 :         if ((fmt + scan_offset)[0] == ' ' && fmt + scan_offset + 1 == subst_location)
    2453           4 :             end_position = str + strlen(str);
    2454             :     }
    2455         388 :     return end_position;
    2456             : }
    2457             : 
    2458             : static int
    2459         456 : pgtypes_defmt_scan(union un_fmt_comb *scan_val, int scan_type, char **pstr, char *pfmt)
    2460             : {
    2461             :     /*
    2462             :      * scan everything between pstr and pstr_end. This is not including the
    2463             :      * last character so we might set it to '\0' for the parsing
    2464             :      */
    2465             : 
    2466             :     char        last_char;
    2467         456 :     int         err = 0;
    2468             :     char       *pstr_end;
    2469         456 :     char       *strtol_end = NULL;
    2470             : 
    2471         456 :     while (**pstr == ' ')
    2472           0 :         pstr++;
    2473         456 :     pstr_end = find_end_token(*pstr, pfmt);
    2474         456 :     if (!pstr_end)
    2475             :     {
    2476             :         /* there was an error, no match */
    2477           0 :         return 1;
    2478             :     }
    2479         456 :     last_char = *pstr_end;
    2480         456 :     *pstr_end = '\0';
    2481             : 
    2482         456 :     switch (scan_type)
    2483             :     {
    2484         404 :         case PGTYPES_TYPE_UINT:
    2485             : 
    2486             :             /*
    2487             :              * numbers may be blank-padded, this is the only deviation from
    2488             :              * the fmt-string we accept
    2489             :              */
    2490         404 :             while (**pstr == ' ')
    2491           0 :                 (*pstr)++;
    2492         404 :             errno = 0;
    2493         404 :             scan_val->uint_val = (unsigned int) strtol(*pstr, &strtol_end, 10);
    2494         404 :             if (errno)
    2495           0 :                 err = 1;
    2496         404 :             break;
    2497           4 :         case PGTYPES_TYPE_UINT_LONG:
    2498           4 :             while (**pstr == ' ')
    2499           0 :                 (*pstr)++;
    2500           4 :             errno = 0;
    2501           4 :             scan_val->luint_val = (unsigned long int) strtol(*pstr, &strtol_end, 10);
    2502           4 :             if (errno)
    2503           0 :                 err = 1;
    2504           4 :             break;
    2505          48 :         case PGTYPES_TYPE_STRING_MALLOCED:
    2506          48 :             scan_val->str_val = pgtypes_strdup(*pstr);
    2507          48 :             if (scan_val->str_val == NULL)
    2508           0 :                 err = 1;
    2509          48 :             break;
    2510             :     }
    2511         456 :     if (strtol_end && *strtol_end)
    2512          36 :         *pstr = strtol_end;
    2513             :     else
    2514         420 :         *pstr = pstr_end;
    2515         456 :     *pstr_end = last_char;
    2516         456 :     return err;
    2517             : }
    2518             : 
    2519             : /* XXX range checking */
    2520             : int
    2521          96 : PGTYPEStimestamp_defmt_scan(char **str, char *fmt, timestamp * d,
    2522             :                             int *year, int *month, int *day,
    2523             :                             int *hour, int *minute, int *second,
    2524             :                             int *tz)
    2525             : {
    2526             :     union un_fmt_comb scan_val;
    2527             :     int         scan_type;
    2528             : 
    2529             :     char       *pstr,
    2530             :                *pfmt,
    2531             :                *tmp;
    2532          96 :     int         err = 1;
    2533             :     unsigned int j;
    2534             :     struct tm   tm;
    2535             : 
    2536          96 :     pfmt = fmt;
    2537          96 :     pstr = *str;
    2538             : 
    2539        1096 :     while (*pfmt)
    2540             :     {
    2541        1008 :         err = 0;
    2542        1416 :         while (*pfmt == ' ')
    2543         408 :             pfmt++;
    2544        1500 :         while (*pstr == ' ')
    2545         492 :             pstr++;
    2546        1008 :         if (*pfmt != '%')
    2547             :         {
    2548         392 :             if (*pfmt == *pstr)
    2549             :             {
    2550         384 :                 pfmt++;
    2551         384 :                 pstr++;
    2552             :             }
    2553             :             else
    2554             :             {
    2555             :                 /* Error: no match */
    2556           8 :                 err = 1;
    2557           8 :                 return err;
    2558             :             }
    2559         384 :             continue;
    2560             :         }
    2561             :         /* here *pfmt equals '%' */
    2562         616 :         pfmt++;
    2563         616 :         switch (*pfmt)
    2564             :         {
    2565          36 :             case 'a':
    2566          36 :                 pfmt++;
    2567             : 
    2568             :                 /*
    2569             :                  * we parse the day and see if it is a week day but we do not
    2570             :                  * check if the week day really matches the date
    2571             :                  */
    2572          36 :                 err = 1;
    2573          36 :                 j = 0;
    2574         100 :                 while (pgtypes_date_weekdays_short[j])
    2575             :                 {
    2576         100 :                     if (strncmp(pgtypes_date_weekdays_short[j], pstr,
    2577         100 :                                 strlen(pgtypes_date_weekdays_short[j])) == 0)
    2578             :                     {
    2579             :                         /* found it */
    2580          36 :                         err = 0;
    2581          36 :                         pstr += strlen(pgtypes_date_weekdays_short[j]);
    2582          36 :                         break;
    2583             :                     }
    2584          64 :                     j++;
    2585             :                 }
    2586          36 :                 break;
    2587           0 :             case 'A':
    2588             :                 /* see note above */
    2589           0 :                 pfmt++;
    2590           0 :                 err = 1;
    2591           0 :                 j = 0;
    2592           0 :                 while (days[j])
    2593             :                 {
    2594           0 :                     if (strncmp(days[j], pstr, strlen(days[j])) == 0)
    2595             :                     {
    2596             :                         /* found it */
    2597           0 :                         err = 0;
    2598           0 :                         pstr += strlen(days[j]);
    2599           0 :                         break;
    2600             :                     }
    2601           0 :                     j++;
    2602             :                 }
    2603           0 :                 break;
    2604          48 :             case 'b':
    2605             :             case 'h':
    2606          48 :                 pfmt++;
    2607          48 :                 err = 1;
    2608          48 :                 j = 0;
    2609         296 :                 while (months[j])
    2610             :                 {
    2611         296 :                     if (strncmp(months[j], pstr, strlen(months[j])) == 0)
    2612             :                     {
    2613             :                         /* found it */
    2614          48 :                         err = 0;
    2615          48 :                         pstr += strlen(months[j]);
    2616          48 :                         *month = j + 1;
    2617          48 :                         break;
    2618             :                     }
    2619         248 :                     j++;
    2620             :                 }
    2621          48 :                 break;
    2622          28 :             case 'B':
    2623             :                 /* see note above */
    2624          28 :                 pfmt++;
    2625          28 :                 err = 1;
    2626          28 :                 j = 0;
    2627         228 :                 while (pgtypes_date_months[j])
    2628             :                 {
    2629         228 :                     if (strncmp(pgtypes_date_months[j], pstr, strlen(pgtypes_date_months[j])) == 0)
    2630             :                     {
    2631             :                         /* found it */
    2632          28 :                         err = 0;
    2633          28 :                         pstr += strlen(pgtypes_date_months[j]);
    2634          28 :                         *month = j + 1;
    2635          28 :                         break;
    2636             :                     }
    2637         200 :                     j++;
    2638             :                 }
    2639          28 :                 break;
    2640           0 :             case 'c':
    2641             :                 /* XXX */
    2642           0 :                 break;
    2643           8 :             case 'C':
    2644           8 :                 pfmt++;
    2645           8 :                 scan_type = PGTYPES_TYPE_UINT;
    2646           8 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2647           8 :                 *year = scan_val.uint_val * 100;
    2648           8 :                 break;
    2649          84 :             case 'd':
    2650             :             case 'e':
    2651          84 :                 pfmt++;
    2652          84 :                 scan_type = PGTYPES_TYPE_UINT;
    2653          84 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2654          84 :                 *day = scan_val.uint_val;
    2655          84 :                 break;
    2656           0 :             case 'D':
    2657             : 
    2658             :                 /*
    2659             :                  * we have to concatenate the strings in order to be able to
    2660             :                  * find the end of the substitution
    2661             :                  */
    2662           0 :                 pfmt++;
    2663           0 :                 tmp = pgtypes_alloc(strlen("%m/%d/%y") + strlen(pstr) + 1);
    2664           0 :                 if (!tmp)
    2665           0 :                     return 1;
    2666           0 :                 strcpy(tmp, "%m/%d/%y");
    2667           0 :                 strcat(tmp, pfmt);
    2668           0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2669           0 :                 free(tmp);
    2670           0 :                 return err;
    2671           8 :             case 'm':
    2672           8 :                 pfmt++;
    2673           8 :                 scan_type = PGTYPES_TYPE_UINT;
    2674           8 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2675           8 :                 *month = scan_val.uint_val;
    2676           8 :                 break;
    2677           8 :             case 'y':
    2678             :             case 'g':           /* XXX difference to y (ISO) */
    2679           8 :                 pfmt++;
    2680           8 :                 scan_type = PGTYPES_TYPE_UINT;
    2681           8 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2682           8 :                 if (*year < 0)
    2683             :                 {
    2684             :                     /* not yet set */
    2685           4 :                     *year = scan_val.uint_val;
    2686             :                 }
    2687             :                 else
    2688           4 :                     *year += scan_val.uint_val;
    2689           8 :                 if (*year < 100)
    2690           4 :                     *year += 1900;
    2691           8 :                 break;
    2692           0 :             case 'G':
    2693             :                 /* XXX difference to %V (ISO) */
    2694           0 :                 pfmt++;
    2695           0 :                 scan_type = PGTYPES_TYPE_UINT;
    2696           0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2697           0 :                 *year = scan_val.uint_val;
    2698           0 :                 break;
    2699          84 :             case 'H':
    2700             :             case 'I':
    2701             :             case 'k':
    2702             :             case 'l':
    2703          84 :                 pfmt++;
    2704          84 :                 scan_type = PGTYPES_TYPE_UINT;
    2705          84 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2706          84 :                 *hour += scan_val.uint_val;
    2707          84 :                 break;
    2708           0 :             case 'j':
    2709           0 :                 pfmt++;
    2710           0 :                 scan_type = PGTYPES_TYPE_UINT;
    2711           0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2712             : 
    2713             :                 /*
    2714             :                  * XXX what should we do with that? We could say that it's
    2715             :                  * sufficient if we have the year and the day within the year
    2716             :                  * to get at least a specific day.
    2717             :                  */
    2718           0 :                 break;
    2719          80 :             case 'M':
    2720          80 :                 pfmt++;
    2721          80 :                 scan_type = PGTYPES_TYPE_UINT;
    2722          80 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2723          80 :                 *minute = scan_val.uint_val;
    2724          80 :                 break;
    2725           8 :             case 'n':
    2726           8 :                 pfmt++;
    2727           8 :                 if (*pstr == '\n')
    2728           8 :                     pstr++;
    2729             :                 else
    2730           0 :                     err = 1;
    2731           8 :                 break;
    2732          16 :             case 'p':
    2733          16 :                 err = 1;
    2734          16 :                 pfmt++;
    2735          16 :                 if (strncmp(pstr, "am", 2) == 0)
    2736             :                 {
    2737          12 :                     *hour += 0;
    2738          12 :                     err = 0;
    2739          12 :                     pstr += 2;
    2740             :                 }
    2741          16 :                 if (strncmp(pstr, "a.m.", 4) == 0)
    2742             :                 {
    2743           0 :                     *hour += 0;
    2744           0 :                     err = 0;
    2745           0 :                     pstr += 4;
    2746             :                 }
    2747          16 :                 if (strncmp(pstr, "pm", 2) == 0)
    2748             :                 {
    2749           4 :                     *hour += 12;
    2750           4 :                     err = 0;
    2751           4 :                     pstr += 2;
    2752             :                 }
    2753          16 :                 if (strncmp(pstr, "p.m.", 4) == 0)
    2754             :                 {
    2755           0 :                     *hour += 12;
    2756           0 :                     err = 0;
    2757           0 :                     pstr += 4;
    2758             :                 }
    2759          16 :                 break;
    2760           4 :             case 'P':
    2761           4 :                 err = 1;
    2762           4 :                 pfmt++;
    2763           4 :                 if (strncmp(pstr, "AM", 2) == 0)
    2764             :                 {
    2765           0 :                     *hour += 0;
    2766           0 :                     err = 0;
    2767           0 :                     pstr += 2;
    2768             :                 }
    2769           4 :                 if (strncmp(pstr, "A.M.", 4) == 0)
    2770             :                 {
    2771           0 :                     *hour += 0;
    2772           0 :                     err = 0;
    2773           0 :                     pstr += 4;
    2774             :                 }
    2775           4 :                 if (strncmp(pstr, "PM", 2) == 0)
    2776             :                 {
    2777           0 :                     *hour += 12;
    2778           0 :                     err = 0;
    2779           0 :                     pstr += 2;
    2780             :                 }
    2781           4 :                 if (strncmp(pstr, "P.M.", 4) == 0)
    2782             :                 {
    2783           4 :                     *hour += 12;
    2784           4 :                     err = 0;
    2785           4 :                     pstr += 4;
    2786             :                 }
    2787           4 :                 break;
    2788           0 :             case 'r':
    2789           0 :                 pfmt++;
    2790           0 :                 tmp = pgtypes_alloc(strlen("%I:%M:%S %p") + strlen(pstr) + 1);
    2791           0 :                 if (!tmp)
    2792           0 :                     return 1;
    2793           0 :                 strcpy(tmp, "%I:%M:%S %p");
    2794           0 :                 strcat(tmp, pfmt);
    2795           0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2796           0 :                 free(tmp);
    2797           0 :                 return err;
    2798           0 :             case 'R':
    2799           0 :                 pfmt++;
    2800           0 :                 tmp = pgtypes_alloc(strlen("%H:%M") + strlen(pstr) + 1);
    2801           0 :                 if (!tmp)
    2802           0 :                     return 1;
    2803           0 :                 strcpy(tmp, "%H:%M");
    2804           0 :                 strcat(tmp, pfmt);
    2805           0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2806           0 :                 free(tmp);
    2807           0 :                 return err;
    2808           4 :             case 's':
    2809           4 :                 pfmt++;
    2810           4 :                 scan_type = PGTYPES_TYPE_UINT_LONG;
    2811           4 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2812             :                 /* number of seconds in scan_val.luint_val */
    2813             :                 {
    2814             :                     struct tm  *tms;
    2815             :                     struct tm   tmbuf;
    2816           4 :                     time_t      et = (time_t) scan_val.luint_val;
    2817             : 
    2818           4 :                     tms = gmtime_r(&et, &tmbuf);
    2819             : 
    2820           4 :                     if (tms)
    2821             :                     {
    2822           4 :                         *year = tms->tm_year + 1900;
    2823           4 :                         *month = tms->tm_mon + 1;
    2824           4 :                         *day = tms->tm_mday;
    2825           4 :                         *hour = tms->tm_hour;
    2826           4 :                         *minute = tms->tm_min;
    2827           4 :                         *second = tms->tm_sec;
    2828             :                     }
    2829             :                     else
    2830           0 :                         err = 1;
    2831             :                 }
    2832           4 :                 break;
    2833          60 :             case 'S':
    2834          60 :                 pfmt++;
    2835          60 :                 scan_type = PGTYPES_TYPE_UINT;
    2836          60 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2837          60 :                 *second = scan_val.uint_val;
    2838          60 :                 break;
    2839           0 :             case 't':
    2840           0 :                 pfmt++;
    2841           0 :                 if (*pstr == '\t')
    2842           0 :                     pstr++;
    2843             :                 else
    2844           0 :                     err = 1;
    2845           0 :                 break;
    2846           0 :             case 'T':
    2847           0 :                 pfmt++;
    2848           0 :                 tmp = pgtypes_alloc(strlen("%H:%M:%S") + strlen(pstr) + 1);
    2849           0 :                 if (!tmp)
    2850           0 :                     return 1;
    2851           0 :                 strcpy(tmp, "%H:%M:%S");
    2852           0 :                 strcat(tmp, pfmt);
    2853           0 :                 err = PGTYPEStimestamp_defmt_scan(&pstr, tmp, d, year, month, day, hour, minute, second, tz);
    2854           0 :                 free(tmp);
    2855           0 :                 return err;
    2856           0 :             case 'u':
    2857           0 :                 pfmt++;
    2858           0 :                 scan_type = PGTYPES_TYPE_UINT;
    2859           0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2860           0 :                 if (scan_val.uint_val < 1 || scan_val.uint_val > 7)
    2861           0 :                     err = 1;
    2862           0 :                 break;
    2863           0 :             case 'U':
    2864           0 :                 pfmt++;
    2865           0 :                 scan_type = PGTYPES_TYPE_UINT;
    2866           0 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2867           0 :                 if (scan_val.uint_val > 53)
    2868           0 :                     err = 1;
    2869           0 :                 break;
    2870           0 :             case 'V':
    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 > 53)
    2875           0 :                     err = 1;
    2876           0 :                 break;
    2877           0 :             case 'w':
    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 > 6)
    2882           0 :                     err = 1;
    2883           0 :                 break;
    2884           0 :             case 'W':
    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 > 53)
    2889           0 :                     err = 1;
    2890           0 :                 break;
    2891           0 :             case 'x':
    2892             :             case 'X':
    2893             :                 /* XXX */
    2894           0 :                 break;
    2895          72 :             case 'Y':
    2896          72 :                 pfmt++;
    2897          72 :                 scan_type = PGTYPES_TYPE_UINT;
    2898          72 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2899          72 :                 *year = scan_val.uint_val;
    2900          72 :                 break;
    2901          32 :             case 'z':
    2902          32 :                 pfmt++;
    2903          32 :                 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
    2904          32 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2905          32 :                 if (!err)
    2906             :                 {
    2907          32 :                     err = DecodeTimezone(scan_val.str_val, tz);
    2908          32 :                     free(scan_val.str_val);
    2909             :                 }
    2910          32 :                 break;
    2911          16 :             case 'Z':
    2912          16 :                 pfmt++;
    2913          16 :                 scan_type = PGTYPES_TYPE_STRING_MALLOCED;
    2914          16 :                 err = pgtypes_defmt_scan(&scan_val, scan_type, &pstr, pfmt);
    2915          16 :                 if (!err)
    2916             :                 {
    2917             :                     /*
    2918             :                      * XXX use DecodeSpecial instead?  Do we need strcasecmp
    2919             :                      * here?
    2920             :                      */
    2921          16 :                     err = 1;
    2922         800 :                     for (j = 0; j < szdatetktbl; j++)
    2923             :                     {
    2924        1440 :                         if ((datetktbl[j].type == TZ || datetktbl[j].type == DTZ) &&
    2925         640 :                             pg_strcasecmp(datetktbl[j].token,
    2926         640 :                                           scan_val.str_val) == 0)
    2927             :                         {
    2928          16 :                             *tz = -datetktbl[j].value;
    2929          16 :                             err = 0;
    2930          16 :                             break;
    2931             :                         }
    2932             :                     }
    2933          16 :                     free(scan_val.str_val);
    2934             :                 }
    2935          16 :                 break;
    2936           0 :             case '+':
    2937             :                 /* XXX */
    2938           0 :                 break;
    2939          16 :             case '%':
    2940          16 :                 pfmt++;
    2941          16 :                 if (*pstr == '%')
    2942          16 :                     pstr++;
    2943             :                 else
    2944           0 :                     err = 1;
    2945          16 :                 break;
    2946           4 :             default:
    2947           4 :                 err = 1;
    2948             :         }
    2949             :     }
    2950          88 :     if (!err)
    2951             :     {
    2952          84 :         if (*second < 0)
    2953          20 :             *second = 0;
    2954          84 :         if (*minute < 0)
    2955           0 :             *minute = 0;
    2956          84 :         if (*hour < 0)
    2957           0 :             *hour = 0;
    2958          84 :         if (*day < 0)
    2959             :         {
    2960           0 :             err = 1;
    2961           0 :             *day = 1;
    2962             :         }
    2963          84 :         if (*month < 0)
    2964             :         {
    2965           0 :             err = 1;
    2966           0 :             *month = 1;
    2967             :         }
    2968          84 :         if (*year < 0)
    2969             :         {
    2970           0 :             err = 1;
    2971           0 :             *year = 1970;
    2972             :         }
    2973             : 
    2974          84 :         if (*second > 59)
    2975             :         {
    2976           0 :             err = 1;
    2977           0 :             *second = 0;
    2978             :         }
    2979          84 :         if (*minute > 59)
    2980             :         {
    2981           0 :             err = 1;
    2982           0 :             *minute = 0;
    2983             :         }
    2984          84 :         if (*hour > 24 ||        /* test for > 24:00:00 */
    2985          84 :             (*hour == 24 && (*minute > 0 || *second > 0)))
    2986             :         {
    2987           0 :             err = 1;
    2988           0 :             *hour = 0;
    2989             :         }
    2990          84 :         if (*month > MONTHS_PER_YEAR)
    2991             :         {
    2992           0 :             err = 1;
    2993           0 :             *month = 1;
    2994             :         }
    2995          84 :         if (*day > day_tab[isleap(*year)][*month - 1])
    2996             :         {
    2997          16 :             *day = day_tab[isleap(*year)][*month - 1];
    2998          16 :             err = 1;
    2999             :         }
    3000             : 
    3001          84 :         tm.tm_sec = *second;
    3002          84 :         tm.tm_min = *minute;
    3003          84 :         tm.tm_hour = *hour;
    3004          84 :         tm.tm_mday = *day;
    3005          84 :         tm.tm_mon = *month;
    3006          84 :         tm.tm_year = *year;
    3007             : 
    3008          84 :         tm2timestamp(&tm, 0, tz, d);
    3009             :     }
    3010          88 :     return err;
    3011             : }
    3012             : 
    3013             : /* XXX: 1900 is compiled in as the base for years */

Generated by: LCOV version 1.14