Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * datetime.c
4 : * Support functions for date/time types.
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/adt/datetime.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <ctype.h>
18 : #include <limits.h>
19 : #include <math.h>
20 :
21 : #include "access/htup_details.h"
22 : #include "access/xact.h"
23 : #include "catalog/pg_type.h"
24 : #include "common/int.h"
25 : #include "common/string.h"
26 : #include "funcapi.h"
27 : #include "miscadmin.h"
28 : #include "nodes/nodeFuncs.h"
29 : #include "parser/scansup.h"
30 : #include "utils/builtins.h"
31 : #include "utils/date.h"
32 : #include "utils/datetime.h"
33 : #include "utils/guc.h"
34 : #include "utils/memutils.h"
35 : #include "utils/tzparser.h"
36 :
37 : static int DecodeNumber(int flen, char *str, bool haveTextMonth,
38 : int fmask, int *tmask,
39 : struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
40 : static int DecodeNumberField(int len, char *str,
41 : int fmask, int *tmask,
42 : struct pg_tm *tm, fsec_t *fsec, bool *is2digits);
43 : static int DecodeTimeCommon(char *str, int fmask, int range,
44 : int *tmask, struct pg_itm *itm);
45 : static int DecodeTime(char *str, int fmask, int range,
46 : int *tmask, struct pg_tm *tm, fsec_t *fsec);
47 : static int DecodeTimeForInterval(char *str, int fmask, int range,
48 : int *tmask, struct pg_itm_in *itm_in);
49 : static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
50 : static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
51 : struct pg_tm *tm);
52 : static char *AppendSeconds(char *cp, int sec, fsec_t fsec,
53 : int precision, bool fillzeros);
54 : static bool int64_multiply_add(int64 val, int64 multiplier, int64 *sum);
55 : static bool AdjustFractMicroseconds(double frac, int64 scale,
56 : struct pg_itm_in *itm_in);
57 : static bool AdjustFractDays(double frac, int scale,
58 : struct pg_itm_in *itm_in);
59 : static bool AdjustFractYears(double frac, int scale,
60 : struct pg_itm_in *itm_in);
61 : static bool AdjustMicroseconds(int64 val, double fval, int64 scale,
62 : struct pg_itm_in *itm_in);
63 : static bool AdjustDays(int64 val, int scale,
64 : struct pg_itm_in *itm_in);
65 : static bool AdjustMonths(int64 val, struct pg_itm_in *itm_in);
66 : static bool AdjustYears(int64 val, int scale,
67 : struct pg_itm_in *itm_in);
68 : static int DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp,
69 : pg_time_t *tp);
70 : static bool DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t,
71 : const char *abbr, pg_tz *tzp,
72 : int *offset, int *isdst);
73 : static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
74 : DateTimeErrorExtra *extra);
75 :
76 :
77 : const int day_tab[2][13] =
78 : {
79 : {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
80 : {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}
81 : };
82 :
83 : const char *const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
84 : "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
85 :
86 : const char *const days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
87 : "Thursday", "Friday", "Saturday", NULL};
88 :
89 :
90 : /*****************************************************************************
91 : * PRIVATE ROUTINES *
92 : *****************************************************************************/
93 :
94 : /*
95 : * datetktbl holds date/time keywords.
96 : *
97 : * Note that this table must be strictly alphabetically ordered to allow an
98 : * O(ln(N)) search algorithm to be used.
99 : *
100 : * The token field must be NUL-terminated; we truncate entries to TOKMAXLEN
101 : * characters to fit.
102 : *
103 : * The static table contains no TZ, DTZ, or DYNTZ entries; rather those
104 : * are loaded from configuration files and stored in zoneabbrevtbl, whose
105 : * abbrevs[] field has the same format as the static datetktbl.
106 : */
107 : static const datetkn datetktbl[] = {
108 : /* token, type, value */
109 : {"+infinity", RESERV, DTK_LATE}, /* same as "infinity" */
110 : {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
111 : {DA_D, ADBC, AD}, /* "ad" for years > 0 */
112 : {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */
113 : {"am", AMPM, AM},
114 : {"apr", MONTH, 4},
115 : {"april", MONTH, 4},
116 : {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */
117 : {"aug", MONTH, 8},
118 : {"august", MONTH, 8},
119 : {DB_C, ADBC, BC}, /* "bc" for years <= 0 */
120 : {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */
121 : {"dec", MONTH, 12},
122 : {"december", MONTH, 12},
123 : {"dow", UNITS, DTK_DOW}, /* day of week */
124 : {"doy", UNITS, DTK_DOY}, /* day of year */
125 : {"dst", DTZMOD, SECS_PER_HOUR},
126 : {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
127 : {"feb", MONTH, 2},
128 : {"february", MONTH, 2},
129 : {"fri", DOW, 5},
130 : {"friday", DOW, 5},
131 : {"h", UNITS, DTK_HOUR}, /* "hour" */
132 : {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */
133 : {"isodow", UNITS, DTK_ISODOW}, /* ISO day of week, Sunday == 7 */
134 : {"isoyear", UNITS, DTK_ISOYEAR}, /* year in terms of the ISO week date */
135 : {"j", UNITS, DTK_JULIAN},
136 : {"jan", MONTH, 1},
137 : {"january", MONTH, 1},
138 : {"jd", UNITS, DTK_JULIAN},
139 : {"jul", MONTH, 7},
140 : {"julian", UNITS, DTK_JULIAN},
141 : {"july", MONTH, 7},
142 : {"jun", MONTH, 6},
143 : {"june", MONTH, 6},
144 : {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */
145 : {"mar", MONTH, 3},
146 : {"march", MONTH, 3},
147 : {"may", MONTH, 5},
148 : {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
149 : {"mon", DOW, 1},
150 : {"monday", DOW, 1},
151 : {"nov", MONTH, 11},
152 : {"november", MONTH, 11},
153 : {NOW, RESERV, DTK_NOW}, /* current transaction time */
154 : {"oct", MONTH, 10},
155 : {"october", MONTH, 10},
156 : {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
157 : {"pm", AMPM, PM},
158 : {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
159 : {"sat", DOW, 6},
160 : {"saturday", DOW, 6},
161 : {"sep", MONTH, 9},
162 : {"sept", MONTH, 9},
163 : {"september", MONTH, 9},
164 : {"sun", DOW, 0},
165 : {"sunday", DOW, 0},
166 : {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */
167 : {"thu", DOW, 4},
168 : {"thur", DOW, 4},
169 : {"thurs", DOW, 4},
170 : {"thursday", DOW, 4},
171 : {TODAY, RESERV, DTK_TODAY}, /* midnight */
172 : {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */
173 : {"tue", DOW, 2},
174 : {"tues", DOW, 2},
175 : {"tuesday", DOW, 2},
176 : {"wed", DOW, 3},
177 : {"wednesday", DOW, 3},
178 : {"weds", DOW, 3},
179 : {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */
180 : {YESTERDAY, RESERV, DTK_YESTERDAY} /* yesterday midnight */
181 : };
182 :
183 : static const int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
184 :
185 : /*
186 : * deltatktbl: same format as datetktbl, but holds keywords used to represent
187 : * time units (eg, for intervals, and for EXTRACT).
188 : */
189 : static const datetkn deltatktbl[] = {
190 : /* token, type, value */
191 : {"@", IGNORE_DTF, 0}, /* postgres relative prefix */
192 : {DAGO, AGO, 0}, /* "ago" indicates negative time offset */
193 : {"c", UNITS, DTK_CENTURY}, /* "century" relative */
194 : {"cent", UNITS, DTK_CENTURY}, /* "century" relative */
195 : {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */
196 : {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */
197 : {"d", UNITS, DTK_DAY}, /* "day" relative */
198 : {DDAY, UNITS, DTK_DAY}, /* "day" relative */
199 : {"days", UNITS, DTK_DAY}, /* "days" relative */
200 : {"dec", UNITS, DTK_DECADE}, /* "decade" relative */
201 : {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */
202 : {"decades", UNITS, DTK_DECADE}, /* "decades" relative */
203 : {"decs", UNITS, DTK_DECADE}, /* "decades" relative */
204 : {"h", UNITS, DTK_HOUR}, /* "hour" relative */
205 : {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */
206 : {"hours", UNITS, DTK_HOUR}, /* "hours" relative */
207 : {"hr", UNITS, DTK_HOUR}, /* "hour" relative */
208 : {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */
209 : {"m", UNITS, DTK_MINUTE}, /* "minute" relative */
210 : {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
211 : {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
212 : {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
213 : {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */
214 : {"millisecon", UNITS, DTK_MILLISEC}, /* relative */
215 : {"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */
216 : {"min", UNITS, DTK_MINUTE}, /* "minute" relative */
217 : {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */
218 : {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */
219 : {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */
220 : {"mon", UNITS, DTK_MONTH}, /* "months" relative */
221 : {"mons", UNITS, DTK_MONTH}, /* "months" relative */
222 : {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */
223 : {"months", UNITS, DTK_MONTH},
224 : {"ms", UNITS, DTK_MILLISEC},
225 : {"msec", UNITS, DTK_MILLISEC},
226 : {DMILLISEC, UNITS, DTK_MILLISEC},
227 : {"mseconds", UNITS, DTK_MILLISEC},
228 : {"msecs", UNITS, DTK_MILLISEC},
229 : {"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */
230 : {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */
231 : {"s", UNITS, DTK_SECOND},
232 : {"sec", UNITS, DTK_SECOND},
233 : {DSECOND, UNITS, DTK_SECOND},
234 : {"seconds", UNITS, DTK_SECOND},
235 : {"secs", UNITS, DTK_SECOND},
236 : {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
237 : {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */
238 : {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */
239 : {"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
240 : {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */
241 : {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */
242 : {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
243 : {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */
244 : {"w", UNITS, DTK_WEEK}, /* "week" relative */
245 : {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */
246 : {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */
247 : {"y", UNITS, DTK_YEAR}, /* "year" relative */
248 : {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */
249 : {"years", UNITS, DTK_YEAR}, /* "years" relative */
250 : {"yr", UNITS, DTK_YEAR}, /* "year" relative */
251 : {"yrs", UNITS, DTK_YEAR} /* "years" relative */
252 : };
253 :
254 : static const int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
255 :
256 : static TimeZoneAbbrevTable *zoneabbrevtbl = NULL;
257 :
258 : /* Caches of recent lookup results in the above tables */
259 :
260 : static const datetkn *datecache[MAXDATEFIELDS] = {NULL};
261 :
262 : static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};
263 :
264 : static const datetkn *abbrevcache[MAXDATEFIELDS] = {NULL};
265 :
266 :
267 : /*
268 : * Calendar time to Julian date conversions.
269 : * Julian date is commonly used in astronomical applications,
270 : * since it is numerically accurate and computationally simple.
271 : * The algorithms here will accurately convert between Julian day
272 : * and calendar date for all non-negative Julian days
273 : * (i.e. from Nov 24, -4713 on).
274 : *
275 : * Rewritten to eliminate overflow problems. This now allows the
276 : * routines to work correctly for all Julian day counts from
277 : * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
278 : * a 32-bit integer. Longer types should also work to the limits
279 : * of their precision.
280 : *
281 : * Actually, date2j() will work sanely, in the sense of producing
282 : * valid negative Julian dates, significantly before Nov 24, -4713.
283 : * We rely on it to do so back to Nov 1, -4713; see IS_VALID_JULIAN()
284 : * and associated commentary in timestamp.h.
285 : */
286 :
287 : int
201 pg 288 GNC 166371 : date2j(int year, int month, int day)
289 : {
290 : int julian;
291 : int century;
7354 bruce 292 ECB :
201 pg 293 GNC 166371 : if (month > 2)
294 : {
295 89639 : month += 1;
296 89639 : year += 4800;
7188 bruce 297 ECB : }
298 : else
299 : {
201 pg 300 GNC 76732 : month += 13;
301 76732 : year += 4799;
302 : }
303 :
304 166371 : century = year / 100;
305 166371 : julian = year * 365 - 32167;
306 166371 : julian += year / 4 - century + century / 4;
307 166371 : julian += 7834 * month / 256 + day;
9522 scrappy 308 ECB :
7354 bruce 309 CBC 166371 : return julian;
2118 tgl 310 ECB : } /* date2j() */
9522 scrappy 311 :
312 : void
8453 lockhart 313 CBC 137458 : j2date(int jd, int *year, int *month, int *day)
314 : {
315 : unsigned int julian;
316 : unsigned int quad;
7188 bruce 317 ECB : unsigned int extra;
318 : int y;
319 :
7354 bruce 320 GIC 137458 : julian = jd;
321 137458 : julian += 32044;
7188 322 137458 : quad = julian / 146097;
323 137458 : extra = (julian - quad * 146097) * 4 + 3;
7188 bruce 324 CBC 137458 : julian += 60 + quad * 3 + extra / 146097;
325 137458 : quad = julian / 1461;
326 137458 : julian -= quad * 1461;
7354 327 137458 : y = julian * 4 / 1461;
328 274916 : julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
329 137458 : + 123;
7188 330 137458 : y += quad * 4;
7354 331 137458 : *year = y - 4800;
332 137458 : quad = julian * 2141 / 65536;
7188 333 137458 : *day = julian - 7834 * quad / 256;
4411 334 137458 : *month = (quad + 10) % MONTHS_PER_YEAR + 1;
2118 tgl 335 137458 : } /* j2date() */
9522 scrappy 336 ECB :
7310 tgl 337 :
338 : /*
339 : * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
340 : *
341 : * Note: various places use the locution j2day(date - 1) to produce a
342 : * result according to the convention 0..6 = Mon..Sun. This is a bit of
343 : * a crock, but will work as long as the computation here is just a modulo.
344 : */
345 : int
8453 lockhart 346 GIC 25104 : j2day(int date)
347 : {
2580 tgl 348 25104 : date += 1;
349 25104 : date %= 7;
2580 tgl 350 ECB : /* Cope if division truncates towards zero, as it probably does */
2580 tgl 351 GIC 25104 : if (date < 0)
2580 tgl 352 LBC 0 : date += 7;
8453 lockhart 353 ECB :
2580 tgl 354 GIC 25104 : return date;
2118 tgl 355 ECB : } /* j2day() */
8453 lockhart 356 EUB :
357 :
6493 tgl 358 ECB : /*
359 : * GetCurrentDateTime()
360 : *
361 : * Get the transaction start time ("now()") broken down as a struct pg_tm,
362 : * converted according to the session timezone setting.
363 : *
364 : * This is just a convenience wrapper for GetCurrentTimeUsec, to cover the
365 : * case where caller doesn't need either fractional seconds or tz offset.
366 : */
367 : void
2118 tgl 368 GIC 1186 : GetCurrentDateTime(struct pg_tm *tm)
369 : {
370 : fsec_t fsec;
371 :
923 tgl 372 CBC 1186 : GetCurrentTimeUsec(tm, &fsec, NULL);
6493 tgl 373 GIC 1186 : }
374 :
375 : /*
6493 tgl 376 ECB : * GetCurrentTimeUsec()
377 : *
378 : * Get the transaction start time ("now()") broken down as a struct pg_tm,
379 : * including fractional seconds and timezone offset. The time is converted
380 : * according to the session timezone setting.
381 : *
382 : * Callers may pass tzp = NULL if they don't need the offset, but this does
383 : * not affect the conversion behavior (unlike timestamp2tm()).
384 : *
385 : * Internally, we cache the result, since this could be called many times
386 : * in a transaction, within which now() doesn't change.
387 : */
388 : void
2118 tgl 389 GIC 1273 : GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
390 : {
923 391 1273 : TimestampTz cur_ts = GetCurrentTransactionStartTimestamp();
392 :
923 tgl 393 ECB : /*
394 : * The cache key must include both current time and current timezone. By
395 : * representing the timezone by just a pointer, we're assuming that
396 : * distinct timezone settings could never have the same pointer value.
397 : * This is true by virtue of the hashtable used inside pg_tzset();
398 : * however, it might need another look if we ever allow entries in that
399 : * hash to be recycled.
400 : */
401 : static TimestampTz cache_ts = 0;
402 : static pg_tz *cache_timezone = NULL;
403 : static struct pg_tm cache_tm;
404 : static fsec_t cache_fsec;
405 : static int cache_tz;
406 :
923 tgl 407 GIC 1273 : if (cur_ts != cache_ts || session_timezone != cache_timezone)
408 : {
409 : /*
410 : * Make sure cache is marked invalid in case of error after partial
923 tgl 411 ECB : * update within timestamp2tm.
412 : */
923 tgl 413 GIC 335 : cache_timezone = NULL;
414 :
415 : /*
416 : * Perform the computation, storing results into cache. We do not
923 tgl 417 ECB : * really expect any error here, since current time surely ought to be
418 : * within range, but check just for sanity's sake.
419 : */
923 tgl 420 GIC 335 : if (timestamp2tm(cur_ts, &cache_tz, &cache_tm, &cache_fsec,
421 : NULL, session_timezone) != 0)
923 tgl 422 UIC 0 : ereport(ERROR,
423 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
923 tgl 424 ECB : errmsg("timestamp out of range")));
425 :
923 tgl 426 EUB : /* OK, so mark the cache valid. */
923 tgl 427 GIC 335 : cache_ts = cur_ts;
428 335 : cache_timezone = session_timezone;
429 : }
430 :
923 tgl 431 CBC 1273 : *tm = cache_tm;
432 1273 : *fsec = cache_fsec;
6493 tgl 433 GIC 1273 : if (tzp != NULL)
923 434 81 : *tzp = cache_tz;
6493 tgl 435 CBC 1273 : }
6493 tgl 436 ECB :
437 :
5261 438 : /*
2619 439 : * Append seconds and fractional seconds (if any) at *cp.
440 : *
441 : * precision is the max number of fraction digits, fillzeros says to
442 : * pad to two integral-seconds digits.
443 : *
444 : * Returns a pointer to the new end of string. No NUL terminator is put
445 : * there; callers are responsible for NUL terminating str themselves.
446 : *
447 : * Note that any sign is stripped from the input sec and fsec values.
448 : */
449 : static char *
5261 tgl 450 GIC 69298 : AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
451 : {
2619 452 69298 : Assert(precision >= 0);
453 :
2619 tgl 454 CBC 69298 : if (fillzeros)
184 peter 455 GNC 67660 : cp = pg_ultostr_zeropad(cp, abs(sec), 2);
2619 tgl 456 ECB : else
184 peter 457 GNC 1638 : cp = pg_ultostr(cp, abs(sec));
2619 tgl 458 ECB :
2236 459 : /* fsec_t is just an int32 */
2619 tgl 460 GIC 69298 : if (fsec != 0)
2619 tgl 461 ECB : {
184 peter 462 GNC 13255 : int32 value = abs(fsec);
2619 tgl 463 GIC 13255 : char *end = &cp[precision + 1];
2619 tgl 464 CBC 13255 : bool gotnonzero = false;
465 :
466 13255 : *cp++ = '.';
2619 tgl 467 ECB :
468 : /*
469 : * Append the fractional seconds part. Note that we don't want any
470 : * trailing zeros here, so since we're building the number in reverse
471 : * we'll skip appending zeros until we've output a non-zero digit.
472 : */
2619 tgl 473 GIC 92785 : while (precision--)
474 : {
475 79530 : int32 oldval = value;
476 : int32 remainder;
2619 tgl 477 ECB :
2619 tgl 478 GIC 79530 : value /= 10;
2619 tgl 479 CBC 79530 : remainder = oldval - value * 10;
480 :
481 : /* check if we got a non-zero */
482 79530 : if (remainder)
483 62258 : gotnonzero = true;
484 :
2619 tgl 485 GIC 79530 : if (gotnonzero)
2619 tgl 486 CBC 71304 : cp[precision] = '0' + remainder;
2619 tgl 487 ECB : else
2619 tgl 488 GIC 8226 : end = &cp[precision];
2619 tgl 489 ECB : }
490 :
491 : /*
492 : * If we still have a non-zero value then precision must have not been
493 : * enough to print the number. We punt the problem to pg_ultostr(),
494 : * which will generate a correct answer in the minimum valid width.
495 : */
2619 tgl 496 GIC 13255 : if (value)
184 peter 497 UNC 0 : return pg_ultostr(cp, abs(fsec));
498 :
2619 tgl 499 GIC 13255 : return end;
2619 tgl 500 ECB : }
2619 tgl 501 EUB : else
2619 tgl 502 GIC 56043 : return cp;
7843 lockhart 503 ECB : }
504 :
505 :
2619 tgl 506 : /*
507 : * Variant of above that's specialized to timestamp case.
508 : *
509 : * Returns a pointer to the new end of string. No NUL terminator is put
510 : * there; callers are responsible for NUL terminating str themselves.
511 : */
512 : static char *
2118 tgl 513 GIC 60294 : AppendTimestampSeconds(char *cp, struct pg_tm *tm, fsec_t fsec)
514 : {
2619 515 60294 : return AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
516 : }
5261 tgl 517 ECB :
518 :
372 519 : /*
520 : * Add val * multiplier to *sum.
521 : * Returns true if successful, false on overflow.
522 : */
523 : static bool
372 tgl 524 GIC 4047 : int64_multiply_add(int64 val, int64 multiplier, int64 *sum)
525 : {
526 : int64 product;
527 :
372 tgl 528 CBC 8064 : if (pg_mul_s64_overflow(val, multiplier, &product) ||
372 tgl 529 GIC 4017 : pg_add_s64_overflow(*sum, product, sum))
530 78 : return false;
531 3969 : return true;
372 tgl 532 ECB : }
533 :
5261 534 : /*
372 535 : * Multiply frac by scale (to produce microseconds) and add to itm_in->tm_usec.
536 : * Returns true if successful, false if itm_in overflows.
537 : */
538 : static bool
372 tgl 539 GIC 4369 : AdjustFractMicroseconds(double frac, int64 scale,
540 : struct pg_itm_in *itm_in)
541 : {
542 : int64 usec;
5261 tgl 543 ECB :
544 : /* Fast path for common case */
5261 tgl 545 GIC 4369 : if (frac == 0)
372 546 4138 : return true;
547 :
548 : /*
372 tgl 549 ECB : * We assume the input frac has abs value less than 1, so overflow of frac
550 : * or usec is not an issue for interesting values of scale.
551 : */
5050 bruce 552 GIC 231 : frac *= scale;
372 tgl 553 231 : usec = (int64) frac;
554 :
555 : /* Round off any fractional microsecond */
372 tgl 556 CBC 231 : frac -= usec;
557 231 : if (frac > 0.5)
372 tgl 558 GIC 12 : usec++;
559 219 : else if (frac < -0.5)
372 tgl 560 CBC 15 : usec--;
372 tgl 561 ECB :
372 tgl 562 CBC 231 : return !pg_add_s64_overflow(itm_in->tm_usec, usec, &itm_in->tm_usec);
5261 tgl 563 ECB : }
564 :
565 : /*
372 566 : * Multiply frac by scale (to produce days). Add the integral part of the
567 : * result to itm_in->tm_mday, the fractional part to itm_in->tm_usec.
568 : * Returns true if successful, false if itm_in overflows.
569 : */
570 : static bool
372 tgl 571 GIC 459 : AdjustFractDays(double frac, int scale,
572 : struct pg_itm_in *itm_in)
573 : {
574 : int extra_days;
5261 tgl 575 ECB :
576 : /* Fast path for common case */
5261 tgl 577 GIC 459 : if (frac == 0)
372 578 366 : return true;
579 :
580 : /*
372 tgl 581 ECB : * We assume the input frac has abs value less than 1, so overflow of frac
582 : * or extra_days is not an issue.
583 : */
5050 bruce 584 GIC 93 : frac *= scale;
585 93 : extra_days = (int) frac;
586 :
587 : /* ... but this could overflow, if tm_mday is already nonzero */
372 tgl 588 CBC 93 : if (pg_add_s32_overflow(itm_in->tm_mday, extra_days, &itm_in->tm_mday))
589 24 : return false;
590 :
591 : /* Handle any fractional day */
5050 bruce 592 69 : frac -= extra_days;
372 tgl 593 69 : return AdjustFractMicroseconds(frac, USECS_PER_DAY, itm_in);
594 : }
595 :
372 tgl 596 ECB : /*
597 : * Multiply frac by scale (to produce years), then further scale up to months.
598 : * Add the integral part of the result to itm_in->tm_mon, discarding any
599 : * fractional part.
600 : * Returns true if successful, false if itm_in overflows.
601 : */
602 : static bool
372 tgl 603 GIC 449 : AdjustFractYears(double frac, int scale,
604 : struct pg_itm_in *itm_in)
605 : {
606 : /*
372 tgl 607 ECB : * As above, we assume abs(frac) < 1, so this can't overflow for any
608 : * interesting value of scale.
609 : */
372 tgl 610 GIC 449 : int extra_months = (int) rint(frac * scale * MONTHS_PER_YEAR);
611 :
612 449 : return !pg_add_s32_overflow(itm_in->tm_mon, extra_months, &itm_in->tm_mon);
613 : }
372 tgl 614 ECB :
615 : /*
616 : * Add (val + fval) * scale to itm_in->tm_usec.
617 : * Returns true if successful, false if itm_in overflows.
618 : */
619 : static bool
372 tgl 620 GIC 1107 : AdjustMicroseconds(int64 val, double fval, int64 scale,
621 : struct pg_itm_in *itm_in)
622 : {
623 : /* Handle the integer part */
372 tgl 624 CBC 1107 : if (!int64_multiply_add(val, scale, &itm_in->tm_usec))
372 tgl 625 GIC 75 : return false;
626 : /* Handle the float part */
627 1032 : return AdjustFractMicroseconds(fval, scale, itm_in);
372 tgl 628 ECB : }
629 :
630 : /*
631 : * Multiply val by scale (to produce days) and add to itm_in->tm_mday.
632 : * Returns true if successful, false if itm_in overflows.
633 : */
634 : static bool
372 tgl 635 GIC 3397 : AdjustDays(int64 val, int scale, struct pg_itm_in *itm_in)
636 : {
637 : int days;
638 :
372 tgl 639 CBC 3397 : if (val < INT_MIN || val > INT_MAX)
372 tgl 640 GIC 18 : return false;
641 6752 : return !pg_mul_s32_overflow((int32) val, scale, &days) &&
642 3373 : !pg_add_s32_overflow(itm_in->tm_mday, days, &itm_in->tm_mday);
372 tgl 643 ECB : }
644 :
645 : /*
646 : * Add val to itm_in->tm_mon (no need for scale here, as val is always
647 : * in months already).
648 : * Returns true if successful, false if itm_in overflows.
649 : */
650 : static bool
372 tgl 651 GIC 444 : AdjustMonths(int64 val, struct pg_itm_in *itm_in)
652 : {
653 444 : if (val < INT_MIN || val > INT_MAX)
654 6 : return false;
372 tgl 655 CBC 438 : return !pg_add_s32_overflow(itm_in->tm_mon, (int32) val, &itm_in->tm_mon);
656 : }
5261 tgl 657 ECB :
372 658 : /*
659 : * Multiply val by scale (to produce years) and add to itm_in->tm_year.
660 : * Returns true if successful, false if itm_in overflows.
661 : */
662 : static bool
372 tgl 663 GIC 518 : AdjustYears(int64 val, int scale,
664 : struct pg_itm_in *itm_in)
665 : {
666 : int years;
372 tgl 667 ECB :
372 tgl 668 GIC 518 : if (val < INT_MIN || val > INT_MAX)
669 12 : return false;
670 994 : return !pg_mul_s32_overflow((int32) val, scale, &years) &&
671 488 : !pg_add_s32_overflow(itm_in->tm_year, years, &itm_in->tm_year);
372 tgl 672 ECB : }
673 :
674 :
371 675 : /*
676 : * Parse the fractional part of a number (decimal point and optional digits,
677 : * followed by end of string). Returns the fractional value into *frac.
678 : *
679 : * Returns 0 if successful, DTERR code if bogus input detected.
680 : */
681 : static int
371 tgl 682 GIC 11142 : ParseFraction(char *cp, double *frac)
683 : {
684 : /* Caller should always pass the start of the fraction part */
685 11142 : Assert(*cp == '.');
371 tgl 686 ECB :
687 : /*
688 : * We want to allow just "." with no digits, but some versions of strtod
689 : * will report EINVAL for that, so special-case it.
690 : */
371 tgl 691 GIC 11142 : if (cp[1] == '\0')
692 : {
371 tgl 693 UIC 0 : *frac = 0;
694 : }
371 tgl 695 ECB : else
696 : {
371 tgl 697 GBC 11142 : errno = 0;
371 tgl 698 GIC 11142 : *frac = strtod(cp, &cp);
699 : /* check for parse failure */
700 11142 : if (*cp != '\0' || errno != 0)
371 tgl 701 CBC 6 : return DTERR_BAD_FORMAT;
371 tgl 702 ECB : }
371 tgl 703 GIC 11136 : return 0;
371 tgl 704 ECB : }
705 :
706 : /*
707 : * Fetch a fractional-second value with suitable error checking.
708 : * Same as ParseFraction except we convert the result to integer microseconds.
709 : */
710 : static int
5261 tgl 711 GIC 10893 : ParseFractionalSecond(char *cp, fsec_t *fsec)
712 : {
713 : double frac;
714 : int dterr;
5261 tgl 715 ECB :
371 tgl 716 GIC 10893 : dterr = ParseFraction(cp, &frac);
717 10893 : if (dterr)
718 6 : return dterr;
5261 719 10887 : *fsec = rint(frac * 1000000);
5261 tgl 720 CBC 10887 : return 0;
5261 tgl 721 ECB : }
722 :
723 :
8453 lockhart 724 : /* ParseDateTime()
725 : * Break string into tokens based on a date/time context.
726 : * Returns 0 if successful, DTERR code if bogus input detected.
727 : *
728 : * timestr - the input string
729 : * workbuf - workspace for field string storage. This must be
730 : * larger than the largest legal input for this datetime type --
731 : * some additional space will be needed to NUL terminate fields.
732 : * buflen - the size of workbuf
733 : * field[] - pointers to field strings are returned in this array
734 : * ftype[] - field type indicators are returned in this array
735 : * maxfields - dimensions of the above two arrays
736 : * *numfields - set to the actual number of fields detected
737 : *
738 : * The fields extracted from the input are stored as separate,
739 : * null-terminated strings in the workspace at workbuf. Any text is
740 : * converted to lower case.
741 : *
742 : * Several field types are assigned:
743 : * DTK_NUMBER - digits and (possibly) a decimal point
744 : * DTK_DATE - digits and two delimiters, or digits and text
745 : * DTK_TIME - digits, colon delimiters, and possibly a decimal point
746 : * DTK_STRING - text (no digits or punctuation)
747 : * DTK_SPECIAL - leading "+" or "-" followed by text
748 : * DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
749 : *
750 : * Note that some field types can hold unexpected items:
751 : * DTK_NUMBER can hold date fields (yy.ddd)
752 : * DTK_STRING can hold months (January) and time zones (PST)
753 : * DTK_DATE can hold time zone names (America/New_York, GMT-8)
754 : */
755 : int
6527 neilc 756 GIC 39951 : ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
757 : char **field, int *ftype, int maxfields, int *numfields)
758 : {
8453 lockhart 759 39951 : int nf = 0;
7187 tgl 760 CBC 39951 : const char *cp = timestr;
6527 neilc 761 GIC 39951 : char *bufp = workbuf;
762 39951 : const char *bufend = workbuf + buflen;
6527 neilc 763 ECB :
764 : /*
6385 bruce 765 : * Set the character pointed-to by "bufptr" to "newchar", and increment
766 : * "bufptr". "end" gives the end of the buffer -- we return an error if
767 : * there is no space left to append a character to the buffer. Note that
768 : * "bufptr" is evaluated twice.
769 : */
770 : #define APPEND_CHAR(bufptr, end, newchar) \
771 : do \
772 : { \
773 : if (((bufptr) + 1) >= (end)) \
774 : return DTERR_BAD_FORMAT; \
775 : *(bufptr)++ = newchar; \
776 : } while (0)
777 :
778 : /* outer loop through fields */
8453 lockhart 779 GIC 179935 : while (*cp != '\0')
780 : {
781 : /* Ignore spaces between fields */
7187 tgl 782 139984 : if (isspace((unsigned char) *cp))
7187 tgl 783 ECB : {
7187 tgl 784 GIC 40092 : cp++;
785 40092 : continue;
7187 tgl 786 ECB : }
787 :
788 : /* Record start of current field */
7187 tgl 789 CBC 99892 : if (nf >= maxfields)
7165 tgl 790 UIC 0 : return DTERR_BAD_FORMAT;
6527 neilc 791 GIC 99892 : field[nf] = bufp;
792 :
8453 lockhart 793 ECB : /* leading digit? then date or time */
7771 lockhart 794 GBC 99892 : if (isdigit((unsigned char) *cp))
8453 lockhart 795 ECB : {
6527 neilc 796 GIC 69446 : APPEND_CHAR(bufp, bufend, *cp++);
8162 tgl 797 204967 : while (isdigit((unsigned char) *cp))
6527 neilc 798 CBC 135521 : APPEND_CHAR(bufp, bufend, *cp++);
799 :
8453 lockhart 800 ECB : /* time field? */
8453 lockhart 801 CBC 69446 : if (*cp == ':')
8453 lockhart 802 ECB : {
8453 lockhart 803 GIC 31149 : ftype[nf] = DTK_TIME;
6527 neilc 804 31149 : APPEND_CHAR(bufp, bufend, *cp++);
8162 tgl 805 CBC 31149 : while (isdigit((unsigned char) *cp) ||
8162 tgl 806 GIC 260126 : (*cp == ':') || (*cp == '.'))
6527 neilc 807 CBC 228977 : APPEND_CHAR(bufp, bufend, *cp++);
8453 lockhart 808 ECB : }
809 : /* date field? allow embedded text month */
6530 bruce 810 CBC 38297 : else if (*cp == '-' || *cp == '/' || *cp == '.')
8453 lockhart 811 31977 : {
812 : /* save delimiting character to use later */
7184 bruce 813 GIC 31977 : char delim = *cp;
7771 lockhart 814 ECB :
6527 neilc 815 CBC 31977 : APPEND_CHAR(bufp, bufend, *cp++);
816 : /* second field is all digits? then no embedded text month */
7863 lockhart 817 31977 : if (isdigit((unsigned char) *cp))
818 : {
7187 tgl 819 31929 : ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
7771 lockhart 820 GIC 95805 : while (isdigit((unsigned char) *cp))
6527 neilc 821 CBC 63876 : APPEND_CHAR(bufp, bufend, *cp++);
822 :
7522 bruce 823 ECB : /*
6385 824 : * insist that the delimiters match to get a three-field
825 : * date.
826 : */
7187 tgl 827 GIC 31929 : if (*cp == delim)
828 : {
7771 lockhart 829 31638 : ftype[nf] = DTK_DATE;
6527 neilc 830 31638 : APPEND_CHAR(bufp, bufend, *cp++);
6530 bruce 831 CBC 96375 : while (isdigit((unsigned char) *cp) || *cp == delim)
6527 neilc 832 GIC 64737 : APPEND_CHAR(bufp, bufend, *cp++);
7771 lockhart 833 ECB : }
7863 834 : }
835 : else
836 : {
7771 lockhart 837 GIC 48 : ftype[nf] = DTK_DATE;
6530 bruce 838 378 : while (isalnum((unsigned char) *cp) || *cp == delim)
6527 neilc 839 330 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
840 : }
8453 lockhart 841 ECB : }
7522 bruce 842 :
843 : /*
844 : * otherwise, number only and will determine year, month, day, or
845 : * concatenated fields later...
846 : */
847 : else
8453 lockhart 848 GIC 6320 : ftype[nf] = DTK_NUMBER;
849 : }
850 : /* Leading decimal point? Then fractional seconds... */
7771 851 30446 : else if (*cp == '.')
7771 lockhart 852 ECB : {
6527 neilc 853 UIC 0 : APPEND_CHAR(bufp, bufend, *cp++);
7771 lockhart 854 0 : while (isdigit((unsigned char) *cp))
6527 neilc 855 LBC 0 : APPEND_CHAR(bufp, bufend, *cp++);
856 :
7771 lockhart 857 UBC 0 : ftype[nf] = DTK_NUMBER;
7771 lockhart 858 EUB : }
7522 bruce 859 :
860 : /*
6385 861 : * text? then date string, month, day of week, special, or timezone
862 : */
8162 tgl 863 GIC 30446 : else if (isalpha((unsigned char) *cp))
864 : {
865 : bool is_date;
866 :
8453 lockhart 867 CBC 9463 : ftype[nf] = DTK_STRING;
6527 neilc 868 GIC 9463 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
8162 tgl 869 36754 : while (isalpha((unsigned char) *cp))
6527 neilc 870 27291 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
8453 lockhart 871 ECB :
8397 bruce 872 : /*
6018 tgl 873 : * Dates can have embedded '-', '/', or '.' separators. It could
5624 bruce 874 : * also be a timezone name containing embedded '/', '+', '-', '_',
875 : * or ':' (but '_' or ':' can't be the first punctuation). If the
876 : * next character is a digit or '+', we need to check whether what
877 : * we have so far is a recognized non-timezone keyword --- if so,
878 : * don't believe that this is the start of a timezone.
879 : */
6018 tgl 880 GIC 9463 : is_date = false;
6530 bruce 881 9463 : if (*cp == '-' || *cp == '/' || *cp == '.')
6018 tgl 882 743 : is_date = true;
883 8720 : else if (*cp == '+' || isdigit((unsigned char) *cp))
8453 lockhart 884 ECB : {
6018 tgl 885 CBC 906 : *bufp = '\0'; /* null-terminate current field value */
6018 tgl 886 ECB : /* we need search only the core token table, not TZ names */
6018 tgl 887 CBC 906 : if (datebsearch(field[nf], datetktbl, szdatetktbl) == NULL)
6018 tgl 888 GIC 753 : is_date = true;
6018 tgl 889 ECB : }
6018 tgl 890 GIC 9463 : if (is_date)
6018 tgl 891 ECB : {
6018 tgl 892 CBC 1496 : ftype[nf] = DTK_DATE;
893 : do
6150 bruce 894 ECB : {
6018 tgl 895 GIC 6953 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
6018 tgl 896 CBC 6953 : } while (*cp == '+' || *cp == '-' ||
6018 tgl 897 GIC 6812 : *cp == '/' || *cp == '_' ||
898 13699 : *cp == '.' || *cp == ':' ||
6018 tgl 899 CBC 6545 : isalnum((unsigned char) *cp));
8453 lockhart 900 ECB : }
901 : }
7771 902 : /* sign? then special or numeric timezone */
6530 bruce 903 CBC 20983 : else if (*cp == '+' || *cp == '-')
904 : {
6527 neilc 905 GIC 20773 : APPEND_CHAR(bufp, bufend, *cp++);
906 : /* soak up leading whitespace */
8162 tgl 907 CBC 20785 : while (isspace((unsigned char) *cp))
8453 lockhart 908 GIC 12 : cp++;
8453 lockhart 909 ECB : /* numeric timezone? */
910 : /* note that "DTK_TZ" could also be a signed float or yyyy-mm */
8162 tgl 911 CBC 20773 : if (isdigit((unsigned char) *cp))
8453 lockhart 912 ECB : {
8453 lockhart 913 GIC 20676 : ftype[nf] = DTK_TZ;
6527 neilc 914 20676 : APPEND_CHAR(bufp, bufend, *cp++);
8162 tgl 915 CBC 20676 : while (isdigit((unsigned char) *cp) ||
5318 tgl 916 GIC 46607 : *cp == ':' || *cp == '.' || *cp == '-')
6527 neilc 917 CBC 25931 : APPEND_CHAR(bufp, bufend, *cp++);
8453 lockhart 918 ECB : }
7771 919 : /* special? */
8162 tgl 920 CBC 97 : else if (isalpha((unsigned char) *cp))
8453 lockhart 921 ECB : {
8453 lockhart 922 GIC 97 : ftype[nf] = DTK_SPECIAL;
6527 neilc 923 97 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
8162 tgl 924 CBC 776 : while (isalpha((unsigned char) *cp))
6527 neilc 925 GIC 679 : APPEND_CHAR(bufp, bufend, pg_tolower((unsigned char) *cp++));
8453 lockhart 926 ECB : }
7771 927 : /* otherwise something wrong... */
8453 928 : else
7165 tgl 929 LBC 0 : return DTERR_BAD_FORMAT;
930 : }
931 : /* ignore other punctuation but use as delimiter */
8162 tgl 932 GIC 210 : else if (ispunct((unsigned char) *cp))
8453 lockhart 933 EUB : {
8453 lockhart 934 GIC 210 : cp++;
935 210 : continue;
8453 lockhart 936 ECB : }
937 : /* otherwise, something is not right... */
938 : else
7165 tgl 939 LBC 0 : return DTERR_BAD_FORMAT;
940 :
941 : /* force in a delimiter after each field */
6527 neilc 942 GIC 99682 : *bufp++ = '\0';
8453 lockhart 943 GBC 99682 : nf++;
944 : }
945 :
8453 lockhart 946 CBC 39951 : *numfields = nf;
9522 scrappy 947 ECB :
8453 lockhart 948 GIC 39951 : return 0;
949 : }
8453 lockhart 950 ECB :
951 :
952 : /* DecodeDateTime()
953 : * Interpret previously parsed fields for general date and time.
954 : * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
955 : * (Currently, all callers treat 1 as an error return too.)
956 : *
957 : * Inputs are field[] and ftype[] arrays, of length nf.
958 : * Other arguments are outputs.
959 : *
960 : * External format(s):
961 : * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
962 : * "Fri Feb-7-1997 15:23:27"
963 : * "Feb-7-1997 15:23:27"
964 : * "2-7-1997 15:23:27"
965 : * "1997-2-7 15:23:27"
966 : * "1997.038 15:23:27" (day of year 1-366)
967 : * Also supports input in compact time:
968 : * "970207 152327"
969 : * "97038 152327"
970 : * "20011225T040506.789-07"
971 : *
972 : * Use the system-provided functions to get the current time zone
973 : * if not specified in the input string.
974 : *
975 : * If the date is outside the range of pg_time_t (in practice that could only
976 : * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
977 : * 1997-05-27
978 : */
979 : int
8453 lockhart 980 GIC 33325 : DecodeDateTime(char **field, int *ftype, int nf,
981 : int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
982 : DateTimeErrorExtra *extra)
983 : {
984 33325 : int fmask = 0,
985 : tmask,
986 : type;
24 tgl 987 GNC 33325 : int ptype = 0; /* "prefix type" for ISO and Julian formats */
8453 lockhart 988 ECB : int i;
989 : int val;
990 : int dterr;
8453 lockhart 991 GIC 33325 : int mer = HR24;
2062 peter_e 992 CBC 33325 : bool haveTextMonth = false;
2062 peter_e 993 GIC 33325 : bool isjulian = false;
994 33325 : bool is2digits = false;
2062 peter_e 995 CBC 33325 : bool bc = false;
6018 tgl 996 GIC 33325 : pg_tz *namedTz = NULL;
3097 997 33325 : pg_tz *abbrevTz = NULL;
998 : pg_tz *valtz;
3097 tgl 999 CBC 33325 : char *abbrev = NULL;
4240 rhaas 1000 ECB : struct pg_tm cur_tm;
8453 lockhart 1001 :
7165 tgl 1002 : /*
7836 bruce 1003 : * We'll insist on at least all of the date fields, but initialize the
1004 : * remaining fields in case they are not set later...
7165 tgl 1005 : */
8453 lockhart 1006 GIC 33325 : *dtype = DTK_DATE;
8453 lockhart 1007 CBC 33325 : tm->tm_hour = 0;
8453 lockhart 1008 GIC 33325 : tm->tm_min = 0;
1009 33325 : tm->tm_sec = 0;
1010 33325 : *fsec = 0;
1011 : /* don't know daylight savings time status apriori */
7771 1012 33325 : tm->tm_isdst = -1;
8453 1013 33325 : if (tzp != NULL)
8453 lockhart 1014 CBC 33325 : *tzp = 0;
8453 lockhart 1015 ECB :
8453 lockhart 1016 CBC 118677 : for (i = 0; i < nf; i++)
8453 lockhart 1017 ECB : {
8453 lockhart 1018 CBC 85508 : switch (ftype[i])
1019 : {
1020 32307 : case DTK_DATE:
3260 bruce 1021 ECB :
3477 1022 : /*
1023 : * Integral julian day with attached time zone? All other
3260 1024 : * forms with JD will be separated into distinct fields, so we
1025 : * handle just this case here.
3477 1026 : */
7771 lockhart 1027 GIC 32307 : if (ptype == DTK_JULIAN)
7863 lockhart 1028 ECB : {
1029 : char *cp;
1030 : int jday;
1031 :
7771 lockhart 1032 GIC 3 : if (tzp == NULL)
7165 tgl 1033 UIC 0 : return DTERR_BAD_FORMAT;
1034 :
6337 bruce 1035 CBC 3 : errno = 0;
186 drowley 1036 GNC 3 : jday = strtoint(field[i], &cp, 10);
1037 3 : if (errno == ERANGE || jday < 0)
6338 tgl 1038 UIC 0 : return DTERR_FIELD_OVERFLOW;
1039 :
186 drowley 1040 GNC 3 : j2date(jday, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2062 peter_e 1041 GBC 3 : isjulian = true;
1042 :
7771 lockhart 1043 ECB : /* Get the time zone from the end of the string */
7165 tgl 1044 CBC 3 : dterr = DecodeTimezone(cp, tzp);
1045 3 : if (dterr)
7165 tgl 1046 UBC 0 : return dterr;
1047 :
7771 lockhart 1048 CBC 3 : tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
1049 3 : ptype = 0;
7771 lockhart 1050 GIC 3 : break;
1051 : }
3260 bruce 1052 ECB :
3477 1053 : /*
6018 tgl 1054 EUB : * Already have a date? Then this might be a time zone name
1055 : * with embedded punctuation (e.g. "America/New_York") or a
5780 tgl 1056 ECB : * run-together time with trailing time zone (e.g. hhmmss-zz).
7771 lockhart 1057 : * - thomas 2001-12-25
5780 tgl 1058 : *
1059 : * We consider it a time zone if we already have month & day.
1060 : * This is to allow the form "mmm dd hhmmss tz year", which
1061 : * we've historically accepted.
1062 : */
5780 tgl 1063 GIC 32304 : else if (ptype != 0 ||
1064 32298 : ((fmask & (DTK_M(MONTH) | DTK_M(DAY))) ==
1065 : (DTK_M(MONTH) | DTK_M(DAY))))
1066 : {
1067 : /* No time zone accepted? Then quit... */
7771 lockhart 1068 693 : if (tzp == NULL)
7165 tgl 1069 UIC 0 : return DTERR_BAD_FORMAT;
1070 :
7770 tgl 1071 CBC 693 : if (isdigit((unsigned char) *field[i]) || ptype != 0)
7771 lockhart 1072 9 : {
1073 : char *cp;
1074 :
1075 : /*
1076 : * Allow a preceding "t" field, but no other units.
1077 : */
7771 lockhart 1078 GIC 9 : if (ptype != 0)
7771 lockhart 1079 ECB : {
7771 lockhart 1080 EUB : /* Sanity check; should not fail this test */
7771 lockhart 1081 GIC 6 : if (ptype != DTK_TIME)
7165 tgl 1082 LBC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 1083 CBC 6 : ptype = 0;
1084 : }
1085 :
1086 : /*
1087 : * Starts with a digit but we already have a time
1088 : * field? Then we are in trouble with a date and time
6385 bruce 1089 ECB : * already...
1090 : */
7771 lockhart 1091 GIC 9 : if ((fmask & DTK_TIME_M) == DTK_TIME_M)
7165 tgl 1092 LBC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 1093 EUB :
7771 lockhart 1094 CBC 9 : if ((cp = strchr(field[i], '-')) == NULL)
7165 tgl 1095 UIC 0 : return DTERR_BAD_FORMAT;
1096 :
1097 : /* Get the time zone from the end of the string */
7165 tgl 1098 GIC 9 : dterr = DecodeTimezone(cp, tzp);
1099 9 : if (dterr)
7165 tgl 1100 UIC 0 : return dterr;
7771 lockhart 1101 GIC 9 : *cp = '\0';
7771 lockhart 1102 ECB :
7522 bruce 1103 EUB : /*
1104 : * Then read the rest of the field as a concatenated
6385 bruce 1105 ECB : * time
7522 bruce 1106 EUB : */
7165 tgl 1107 GIC 9 : dterr = DecodeNumberField(strlen(field[i]), field[i],
1108 : fmask,
7165 tgl 1109 ECB : &tmask, tm,
1110 : fsec, &is2digits);
7165 tgl 1111 GBC 9 : if (dterr < 0)
7165 tgl 1112 LBC 0 : return dterr;
1113 :
1114 : /*
1115 : * modify tmask after returning from
1116 : * DecodeNumberField()
1117 : */
7771 lockhart 1118 CBC 9 : tmask |= DTK_M(TZ);
1119 : }
1120 : else
1121 : {
6018 tgl 1122 684 : namedTz = pg_tzset(field[i]);
6018 tgl 1123 GBC 684 : if (!namedTz)
1124 : {
121 tgl 1125 GNC 18 : extra->dtee_timezone = field[i];
1126 18 : return DTERR_BAD_TIMEZONE;
6018 tgl 1127 ECB : }
1128 : /* we'll apply the zone setting below */
7771 lockhart 1129 CBC 666 : tmask = DTK_M(TZ);
7771 lockhart 1130 ECB : }
1131 : }
1132 : else
7165 tgl 1133 : {
5522 tgl 1134 GIC 31611 : dterr = DecodeDate(field[i], fmask,
1135 : &tmask, &is2digits, tm);
7165 1136 31611 : if (dterr)
1137 18 : return dterr;
7165 tgl 1138 ECB : }
8453 lockhart 1139 GIC 32268 : break;
8453 lockhart 1140 ECB :
8453 lockhart 1141 CBC 28610 : case DTK_TIME:
1142 :
3879 bruce 1143 ECB : /*
1144 : * This might be an ISO time following a "t" field.
1145 : */
3879 bruce 1146 GIC 28610 : if (ptype != 0)
1147 : {
1148 : /* Sanity check; should not fail this test */
1149 6 : if (ptype != DTK_TIME)
3879 bruce 1150 LBC 0 : return DTERR_BAD_FORMAT;
3879 bruce 1151 GIC 6 : ptype = 0;
1152 : }
5324 tgl 1153 CBC 28610 : dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE,
5324 tgl 1154 EUB : &tmask, tm, fsec);
7165 tgl 1155 CBC 28610 : if (dterr)
7165 tgl 1156 UIC 0 : return dterr;
8453 lockhart 1157 ECB :
1158 : /* check for time overflow */
1039 tgl 1159 CBC 28610 : if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec,
1039 tgl 1160 EUB : *fsec))
7165 tgl 1161 UIC 0 : return DTERR_FIELD_OVERFLOW;
8453 lockhart 1162 GIC 28610 : break;
8453 lockhart 1163 ECB :
8453 lockhart 1164 GIC 19253 : case DTK_TZ:
8424 lockhart 1165 EUB : {
7522 bruce 1166 ECB : int tz;
1167 :
7771 lockhart 1168 CBC 19253 : if (tzp == NULL)
7165 tgl 1169 GIC 6 : return DTERR_BAD_FORMAT;
1170 :
6018 1171 19253 : dterr = DecodeTimezone(field[i], &tz);
7165 tgl 1172 CBC 19253 : if (dterr)
1173 6 : return dterr;
6018 tgl 1174 GIC 19247 : *tzp = tz;
6018 tgl 1175 CBC 19247 : tmask = DTK_M(TZ);
8424 lockhart 1176 ECB : }
8453 lockhart 1177 CBC 19247 : break;
8453 lockhart 1178 ECB :
8453 lockhart 1179 CBC 2286 : case DTK_NUMBER:
1180 :
7836 bruce 1181 ECB : /*
1182 : * Deal with cases where previous field labeled this one
1183 : */
7863 lockhart 1184 GIC 2286 : if (ptype != 0)
1185 : {
1186 : char *cp;
1187 : int value;
1188 :
6337 bruce 1189 66 : errno = 0;
186 drowley 1190 GNC 66 : value = strtoint(field[i], &cp, 10);
6338 tgl 1191 GIC 66 : if (errno == ERANGE)
6338 tgl 1192 CBC 6 : return DTERR_FIELD_OVERFLOW;
24 tgl 1193 GNC 66 : if (*cp != '.' && *cp != '\0')
7165 tgl 1194 LBC 0 : return DTERR_BAD_FORMAT;
1195 :
1196 : switch (ptype)
1197 : {
7771 lockhart 1198 CBC 42 : case DTK_JULIAN:
4582 tgl 1199 EUB : /* previous field was a label for "julian date" */
186 drowley 1200 GNC 42 : if (value < 0)
4582 tgl 1201 UIC 0 : return DTERR_FIELD_OVERFLOW;
7771 lockhart 1202 GIC 42 : tmask = DTK_DATE_M;
186 drowley 1203 GNC 42 : j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2062 peter_e 1204 GIC 42 : isjulian = true;
1205 :
1206 : /* fractional Julian Day? */
7771 lockhart 1207 42 : if (*cp == '.')
1208 : {
1209 : double time;
1210 :
371 tgl 1211 CBC 6 : dterr = ParseFraction(cp, &time);
1212 6 : if (dterr)
371 tgl 1213 UIC 0 : return dterr;
5261 tgl 1214 CBC 6 : time *= USECS_PER_DAY;
5261 tgl 1215 GIC 6 : dt2time(time,
1216 : &tm->tm_hour, &tm->tm_min,
5261 tgl 1217 ECB : &tm->tm_sec, fsec);
5261 tgl 1218 GBC 6 : tmask |= DTK_TIME_M;
1219 : }
7771 lockhart 1220 GIC 42 : break;
1221 :
1222 18 : case DTK_TIME:
7771 lockhart 1223 ECB : /* previous field was "t" for ISO time */
7165 tgl 1224 GIC 18 : dterr = DecodeNumberField(strlen(field[i]), field[i],
1225 : (fmask | DTK_DATE_M),
1226 : &tmask, tm,
7165 tgl 1227 ECB : fsec, &is2digits);
7165 tgl 1228 CBC 18 : if (dterr < 0)
7165 tgl 1229 UIC 0 : return dterr;
7771 lockhart 1230 GIC 18 : if (tmask != DTK_TIME_M)
7165 tgl 1231 LBC 0 : return DTERR_BAD_FORMAT;
7863 lockhart 1232 GIC 18 : break;
7863 lockhart 1233 ECB :
7863 lockhart 1234 GIC 6 : default:
7165 tgl 1235 6 : return DTERR_BAD_FORMAT;
7863 lockhart 1236 ECB : break;
1237 : }
1238 :
7863 lockhart 1239 GBC 60 : ptype = 0;
7863 lockhart 1240 CBC 60 : *dtype = DTK_DATE;
7863 lockhart 1241 ECB : }
7771 1242 : else
8453 lockhart 1243 EUB : {
1244 : char *cp;
7522 bruce 1245 ECB : int flen;
7771 lockhart 1246 :
7771 lockhart 1247 GIC 2220 : flen = strlen(field[i]);
7771 lockhart 1248 CBC 2220 : cp = strchr(field[i], '.');
8453 lockhart 1249 ECB :
1250 : /* Embedded decimal and no date yet? */
6530 bruce 1251 CBC 2220 : if (cp != NULL && !(fmask & DTK_DATE_M))
7771 lockhart 1252 ECB : {
5522 tgl 1253 CBC 15 : dterr = DecodeDate(field[i], fmask,
5522 tgl 1254 ECB : &tmask, &is2digits, tm);
7165 tgl 1255 CBC 15 : if (dterr)
7165 tgl 1256 UIC 0 : return dterr;
7771 lockhart 1257 ECB : }
1258 : /* embedded decimal and several digits before? */
6530 bruce 1259 CBC 2205 : else if (cp != NULL && flen - strlen(cp) > 2)
7771 lockhart 1260 ECB : {
7522 bruce 1261 : /*
1262 : * Interpret as a concatenated date or time Set the
6385 1263 : * type field to allow decoding other fields later.
1264 : * Example: 20011223 or 040506
7771 lockhart 1265 : */
7165 tgl 1266 CBC 6 : dterr = DecodeNumberField(flen, field[i], fmask,
7165 tgl 1267 ECB : &tmask, tm,
1268 : fsec, &is2digits);
7165 tgl 1269 CBC 6 : if (dterr < 0)
7165 tgl 1270 LBC 0 : return dterr;
7771 lockhart 1271 ECB : }
3260 bruce 1272 :
1273 : /*
3462 1274 : * Is this a YMD or HMS specification, or a year number?
1275 : * YMD and HMS are required to be six digits or more, so
1276 : * if it is 5 digits, it is a year. If it is six or more
1674 michael 1277 : * digits, we assume it is YMD or HMS unless no date and
1278 : * no time values have been specified. This forces 6+
1279 : * digit years to be at the end of the string, or to use
3462 bruce 1280 : * the ISO date specification.
1281 : */
3462 bruce 1282 CBC 2199 : else if (flen >= 6 && (!(fmask & DTK_DATE_M) ||
3260 1283 48 : !(fmask & DTK_TIME_M)))
7771 lockhart 1284 ECB : {
7165 tgl 1285 CBC 167 : dterr = DecodeNumberField(flen, field[i], fmask,
7165 tgl 1286 ECB : &tmask, tm,
1287 : fsec, &is2digits);
7165 tgl 1288 CBC 167 : if (dterr < 0)
7165 tgl 1289 LBC 0 : return dterr;
7771 lockhart 1290 ECB : }
1291 : /* otherwise it is a single date/time field... */
7165 tgl 1292 : else
1293 : {
7084 tgl 1294 GIC 2032 : dterr = DecodeNumber(flen, field[i],
7084 tgl 1295 ECB : haveTextMonth, fmask,
7165 1296 : &tmask, tm,
1297 : fsec, &is2digits);
7165 tgl 1298 CBC 2032 : if (dterr)
7165 tgl 1299 GIC 6 : return dterr;
7165 tgl 1300 EUB : }
8453 lockhart 1301 : }
8453 lockhart 1302 GIC 2274 : break;
1303 :
1304 3052 : case DTK_STRING:
8453 lockhart 1305 ECB : case DTK_SPECIAL:
1306 : /* timezone abbrevs take precedence over built-in tokens */
121 tgl 1307 GNC 3052 : dterr = DecodeTimezoneAbbrev(i, field[i],
1308 : &type, &val, &valtz, extra);
1309 3052 : if (dterr)
121 tgl 1310 UNC 0 : return dterr;
3097 tgl 1311 GIC 3052 : if (type == UNKNOWN_FIELD)
1312 2276 : type = DecodeSpecial(i, field[i], &val);
7607 JanWieck 1313 3052 : if (type == IGNORE_DTF)
8453 lockhart 1314 UIC 0 : continue;
1315 :
8453 lockhart 1316 CBC 3052 : tmask = DTK_M(type);
1317 3052 : switch (type)
8453 lockhart 1318 ECB : {
8453 lockhart 1319 GIC 569 : case RESERV:
8453 lockhart 1320 CBC 569 : switch (val)
8453 lockhart 1321 ECB : {
8453 lockhart 1322 GIC 57 : case DTK_NOW:
8453 lockhart 1323 CBC 57 : tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
1324 57 : *dtype = DTK_DATE;
7353 tgl 1325 57 : GetCurrentTimeUsec(tm, fsec, tzp);
8453 lockhart 1326 GIC 57 : break;
8453 lockhart 1327 ECB :
8453 lockhart 1328 GIC 51 : case DTK_YESTERDAY:
1329 51 : tmask = DTK_DATE_M;
1330 51 : *dtype = DTK_DATE;
4240 rhaas 1331 51 : GetCurrentDateTime(&cur_tm);
1332 51 : j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) - 1,
2118 tgl 1333 ECB : &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
8453 lockhart 1334 CBC 51 : break;
8453 lockhart 1335 ECB :
8453 lockhart 1336 GBC 72 : case DTK_TODAY:
8453 lockhart 1337 CBC 72 : tmask = DTK_DATE_M;
1338 72 : *dtype = DTK_DATE;
4240 rhaas 1339 GIC 72 : GetCurrentDateTime(&cur_tm);
4240 rhaas 1340 CBC 72 : tm->tm_year = cur_tm.tm_year;
4240 rhaas 1341 GIC 72 : tm->tm_mon = cur_tm.tm_mon;
1342 72 : tm->tm_mday = cur_tm.tm_mday;
8453 lockhart 1343 72 : break;
1344 :
1345 75 : case DTK_TOMORROW:
8453 lockhart 1346 CBC 75 : tmask = DTK_DATE_M;
1347 75 : *dtype = DTK_DATE;
4240 rhaas 1348 75 : GetCurrentDateTime(&cur_tm);
4240 rhaas 1349 GBC 75 : j2date(date2j(cur_tm.tm_year, cur_tm.tm_mon, cur_tm.tm_mday) + 1,
2118 tgl 1350 ECB : &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
8453 lockhart 1351 CBC 75 : break;
1352 :
1353 3 : case DTK_ZULU:
1354 3 : tmask = (DTK_TIME_M | DTK_M(TZ));
1355 3 : *dtype = DTK_DATE;
8453 lockhart 1356 GBC 3 : tm->tm_hour = 0;
8453 lockhart 1357 CBC 3 : tm->tm_min = 0;
1358 3 : tm->tm_sec = 0;
8453 lockhart 1359 GIC 3 : if (tzp != NULL)
8453 lockhart 1360 CBC 3 : *tzp = 0;
1361 3 : break;
8453 lockhart 1362 ECB :
31 tgl 1363 GNC 311 : case DTK_EPOCH:
1364 : case DTK_LATE:
1365 : case DTK_EARLY:
1366 311 : tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
8453 lockhart 1367 GIC 311 : *dtype = val;
1368 : /* caller ignores tm for these dtype codes */
31 tgl 1369 GNC 311 : break;
1370 :
31 tgl 1371 UNC 0 : default:
1372 0 : elog(ERROR, "unrecognized RESERV datetime token: %d",
1373 : val);
8453 lockhart 1374 ECB : }
1375 :
8453 lockhart 1376 CBC 569 : break;
1377 :
1378 896 : case MONTH:
8397 bruce 1379 ECB :
8453 lockhart 1380 : /*
1381 : * already have a (numeric) month? then see if we can
6385 bruce 1382 : * substitute...
8453 lockhart 1383 : */
6530 bruce 1384 CBC 896 : if ((fmask & DTK_M(MONTH)) && !haveTextMonth &&
6530 bruce 1385 GIC 46 : !(fmask & DTK_M(DAY)) && tm->tm_mon >= 1 &&
6530 bruce 1386 CBC 40 : tm->tm_mon <= 31)
8453 lockhart 1387 ECB : {
8453 lockhart 1388 CBC 37 : tm->tm_mday = tm->tm_mon;
8453 lockhart 1389 GIC 37 : tmask = DTK_M(DAY);
8453 lockhart 1390 ECB : }
2062 peter_e 1391 CBC 896 : haveTextMonth = true;
8453 lockhart 1392 GIC 896 : tm->tm_mon = val;
8453 lockhart 1393 CBC 896 : break;
8453 lockhart 1394 ECB :
8453 lockhart 1395 CBC 3 : case DTZMOD:
8453 lockhart 1396 ECB :
1397 : /*
6385 bruce 1398 : * daylight savings time modifier (solves "MET DST"
1399 : * syntax)
1400 : */
8453 lockhart 1401 GIC 3 : tmask |= DTK_M(DTZ);
1402 3 : tm->tm_isdst = 1;
1403 3 : if (tzp == NULL)
7165 tgl 1404 LBC 0 : return DTERR_BAD_FORMAT;
3097 tgl 1405 GIC 3 : *tzp -= val;
8453 lockhart 1406 3 : break;
8453 lockhart 1407 ECB :
8453 lockhart 1408 GBC 472 : case DTZ:
1409 :
1410 : /*
6385 bruce 1411 ECB : * set mask for TZ here _or_ check for DTZ later when
6385 bruce 1412 EUB : * getting default timezone
8453 lockhart 1413 ECB : */
8453 lockhart 1414 CBC 472 : tmask |= DTK_M(TZ);
8453 lockhart 1415 GIC 472 : tm->tm_isdst = 1;
8453 lockhart 1416 CBC 472 : if (tzp == NULL)
7165 tgl 1417 UIC 0 : return DTERR_BAD_FORMAT;
3097 tgl 1418 GIC 472 : *tzp = -val;
8453 lockhart 1419 472 : break;
1420 :
1421 262 : case TZ:
8453 lockhart 1422 CBC 262 : tm->tm_isdst = 0;
1423 262 : if (tzp == NULL)
7165 tgl 1424 LBC 0 : return DTERR_BAD_FORMAT;
3097 tgl 1425 GIC 262 : *tzp = -val;
8453 lockhart 1426 GBC 262 : break;
8453 lockhart 1427 EUB :
3097 tgl 1428 GIC 42 : case DYNTZ:
3097 tgl 1429 GBC 42 : tmask |= DTK_M(TZ);
1430 42 : if (tzp == NULL)
3097 tgl 1431 UIC 0 : return DTERR_BAD_FORMAT;
3097 tgl 1432 ECB : /* we'll determine the actual offset later */
3097 tgl 1433 GIC 42 : abbrevTz = valtz;
3097 tgl 1434 GBC 42 : abbrev = field[i];
8453 lockhart 1435 42 : break;
1436 :
8453 lockhart 1437 GIC 12 : case AMPM:
8453 lockhart 1438 CBC 12 : mer = val;
1439 12 : break;
8453 lockhart 1440 ECB :
8453 lockhart 1441 GIC 129 : case ADBC:
1442 129 : bc = (val == BC);
1443 129 : break;
8453 lockhart 1444 ECB :
8453 lockhart 1445 GBC 550 : case DOW:
8453 lockhart 1446 GIC 550 : tm->tm_wday = val;
1447 550 : break;
8453 lockhart 1448 ECB :
7863 lockhart 1449 GIC 63 : case UNITS:
1450 63 : tmask = 0;
1451 : /* reject consecutive unhandled units */
24 tgl 1452 GNC 63 : if (ptype != 0)
1453 6 : return DTERR_BAD_FORMAT;
7841 lockhart 1454 CBC 57 : ptype = val;
7863 1455 57 : break;
7863 lockhart 1456 ECB :
7768 lockhart 1457 GIC 30 : case ISOTIME:
1458 :
7522 bruce 1459 ECB : /*
6385 bruce 1460 EUB : * This is a filler field "t" indicating that the next
6385 bruce 1461 ECB : * field is time. Try to verify that this is sensible.
7771 lockhart 1462 EUB : */
7841 lockhart 1463 CBC 30 : tmask = 0;
7771 lockhart 1464 ECB :
1465 : /* No preceding date? Then quit... */
7771 lockhart 1466 GIC 30 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
7165 tgl 1467 LBC 0 : return DTERR_BAD_FORMAT;
1468 :
1469 : /* reject consecutive unhandled units */
24 tgl 1470 GNC 30 : if (ptype != 0)
7165 tgl 1471 UIC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 1472 CBC 30 : ptype = val;
7863 lockhart 1473 GBC 30 : break;
1474 :
6017 tgl 1475 CBC 24 : case UNKNOWN_FIELD:
1476 :
1477 : /*
1478 : * Before giving up and declaring error, check to see
1479 : * if it is an all-alpha timezone name.
1480 : */
6017 tgl 1481 GIC 24 : namedTz = pg_tzset(field[i]);
6017 tgl 1482 CBC 24 : if (!namedTz)
6017 tgl 1483 GIC 24 : return DTERR_BAD_FORMAT;
1484 : /* we'll apply the zone setting below */
6017 tgl 1485 LBC 0 : tmask = DTK_M(TZ);
6017 tgl 1486 UBC 0 : break;
1487 :
8453 lockhart 1488 LBC 0 : default:
7165 tgl 1489 UIC 0 : return DTERR_BAD_FORMAT;
1490 : }
8453 lockhart 1491 GIC 3022 : break;
8453 lockhart 1492 ECB :
8453 lockhart 1493 UIC 0 : default:
7165 tgl 1494 0 : return DTERR_BAD_FORMAT;
1495 : }
1496 :
8453 lockhart 1497 GIC 85424 : if (tmask & fmask)
7165 tgl 1498 CBC 72 : return DTERR_BAD_FORMAT;
8453 lockhart 1499 GBC 85352 : fmask |= tmask;
1500 : } /* end loop over fields */
8453 lockhart 1501 ECB :
1502 : /* reject if prefix type appeared and was never handled */
24 tgl 1503 GNC 33169 : if (ptype != 0)
24 tgl 1504 UNC 0 : return DTERR_BAD_FORMAT;
1505 :
1506 : /* do additional checking for normal date specs (but not "infinity" etc) */
8453 lockhart 1507 GIC 33169 : if (*dtype == DTK_DATE)
1508 : {
1509 : /* do final checking/adjustment of Y/M/D fields */
31 tgl 1510 GNC 32930 : dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
1511 32930 : if (dterr)
1512 99 : return dterr;
1513 :
1514 : /* handle AM/PM */
1515 32831 : if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
31 tgl 1516 UNC 0 : return DTERR_FIELD_OVERFLOW;
31 tgl 1517 GNC 32831 : if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
31 tgl 1518 UNC 0 : tm->tm_hour = 0;
31 tgl 1519 GNC 32831 : else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
1520 12 : tm->tm_hour += HOURS_PER_DAY / 2;
1521 :
1522 : /* check for incomplete input */
8453 lockhart 1523 GIC 32831 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1524 : {
7165 tgl 1525 3 : if ((fmask & DTK_TIME_M) == DTK_TIME_M)
7165 tgl 1526 UIC 0 : return 1;
7165 tgl 1527 CBC 3 : return DTERR_BAD_FORMAT;
1528 : }
1529 :
1530 : /*
5624 bruce 1531 ECB : * If we had a full timezone spec, compute the offset (we could not do
1532 : * it before, because we need the date to resolve DST status).
1533 : */
6018 tgl 1534 GIC 32828 : if (namedTz != NULL)
1535 : {
1536 : /* daylight savings time modifier disallowed with full TZ */
1537 666 : if (fmask & DTK_M(DTZMOD))
6018 tgl 1538 UIC 0 : return DTERR_BAD_FORMAT;
1539 :
6018 tgl 1540 GIC 666 : *tzp = DetermineTimeZoneOffset(tm, namedTz);
1541 : }
1542 :
1543 : /*
1544 : * Likewise, if we had a dynamic timezone abbreviation, resolve it
1545 : * now.
1546 : */
3097 1547 32828 : if (abbrevTz != NULL)
1548 : {
3097 tgl 1549 ECB : /* daylight savings time modifier disallowed with dynamic TZ */
3097 tgl 1550 GIC 42 : if (fmask & DTK_M(DTZMOD))
3097 tgl 1551 UIC 0 : return DTERR_BAD_FORMAT;
1552 :
3097 tgl 1553 GIC 42 : *tzp = DetermineTimeZoneAbbrevOffset(tm, abbrev, abbrevTz);
1554 : }
1555 :
1556 : /* timezone not specified? then use session timezone */
6532 bruce 1557 32828 : if (tzp != NULL && !(fmask & DTK_M(TZ)))
1558 : {
1559 : /*
1560 : * daylight savings time modifier but no standard timezone? then
1561 : * error
1562 : */
8453 lockhart 1563 12079 : if (fmask & DTK_M(DTZMOD))
7165 tgl 1564 UIC 0 : return DTERR_BAD_FORMAT;
1565 :
5727 tgl 1566 GIC 12079 : *tzp = DetermineTimeZoneOffset(tm, session_timezone);
1567 : }
1568 : }
1569 :
8011 1570 33067 : return 0;
7165 tgl 1571 ECB : }
8011 1572 :
1573 :
1574 : /* DetermineTimeZoneOffset()
7523 1575 : *
3097 1576 : * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min,
3097 tgl 1577 EUB : * and tm_sec fields are set, and a zic-style time zone definition, determine
3097 tgl 1578 ECB : * the applicable GMT offset and daylight-savings status at that time.
1579 : * Set the struct pg_tm's tm_isdst field accordingly, and return the GMT
1580 : * offset as the function result.
1581 : *
3097 tgl 1582 EUB : * Note: if the date is out of the range we can deal with, we return zero
1583 : * as the GMT offset and set tm_isdst = 0. We don't throw an error here,
1584 : * though probably some higher-level code will.
1585 : */
1586 : int
2118 tgl 1587 GIC 25995 : DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
1588 : {
1589 : pg_time_t t;
3097 tgl 1590 ECB :
3097 tgl 1591 CBC 25995 : return DetermineTimeZoneOffsetInternal(tm, tzp, &t);
3097 tgl 1592 EUB : }
1593 :
3097 tgl 1594 ECB :
1595 : /* DetermineTimeZoneOffsetInternal()
1596 : *
1597 : * As above, but also return the actual UTC time imputed to the date/time
1598 : * into *tp.
1599 : *
3097 tgl 1600 EUB : * In event of an out-of-range date, we punt by returning zero into *tp.
1601 : * This is okay for the immediate callers but is a good reason for not
3097 tgl 1602 ECB : * exposing this worker function globally.
1603 : *
1604 : * Note: it might seem that we should use mktime() for this, but bitter
6733 1605 : * experience teaches otherwise. This code is much faster than most versions
1606 : * of mktime(), anyway.
8011 1607 : */
1608 : static int
2118 tgl 1609 GIC 26085 : DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp, pg_time_t *tp)
1610 : {
1611 : int date,
1612 : sec;
6884 tgl 1613 ECB : pg_time_t day,
6733 1614 : mytime,
1615 : prevtime,
1616 : boundary,
1617 : beforetime,
6733 tgl 1618 EUB : aftertime;
6733 tgl 1619 ECB : long int before_gmtoff,
1620 : after_gmtoff;
1621 : int before_isdst,
1622 : after_isdst;
1623 : int res;
8011 tgl 1624 EUB :
1625 : /*
1626 : * First, generate the pg_time_t value corresponding to the given
1627 : * y/m/d/h/m/s taken as GMT time. If this overflows, punt and decide the
1628 : * timezone is GMT. (For a valid Julian date, integer overflow should be
1629 : * impossible with 64-bit pg_time_t, but let's check for safety.)
1630 : */
6884 tgl 1631 CBC 26085 : if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
6884 tgl 1632 GIC 12 : goto overflow;
6884 tgl 1633 CBC 26073 : date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
6796 bruce 1634 ECB :
6385 bruce 1635 CBC 26073 : day = ((pg_time_t) date) * SECS_PER_DAY;
6530 bruce 1636 GIC 26073 : if (day / SECS_PER_DAY != date)
6884 tgl 1637 LBC 0 : goto overflow;
6471 bruce 1638 GIC 26073 : sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * MINS_PER_HOUR) * SECS_PER_MINUTE;
6733 tgl 1639 CBC 26073 : mytime = day + sec;
6733 tgl 1640 ECB : /* since sec >= 0, overflow could only be from +day to -mytime */
6733 tgl 1641 CBC 26073 : if (mytime < 0 && day > 0)
6884 tgl 1642 UIC 0 : goto overflow;
1643 :
1644 : /*
1645 : * Find the DST time boundary just before or following the target time. We
1646 : * assume that all zones have GMT offsets less than 24 hours, and that DST
1647 : * boundaries can't be closer together than 48 hours, so backing up 24
1648 : * hours and finding the "next" boundary will work.
1649 : */
6471 bruce 1650 GIC 26073 : prevtime = mytime - SECS_PER_DAY;
6733 tgl 1651 26073 : if (mytime < 0 && prevtime > 0)
6733 tgl 1652 UIC 0 : goto overflow;
1653 :
6733 tgl 1654 GIC 26073 : res = pg_next_dst_boundary(&prevtime,
6733 tgl 1655 ECB : &before_gmtoff, &before_isdst,
1656 : &boundary,
6564 bruce 1657 : &after_gmtoff, &after_isdst,
6385 1658 : tzp);
6733 tgl 1659 CBC 26073 : if (res < 0)
6733 tgl 1660 UIC 0 : goto overflow; /* failure? */
6733 tgl 1661 ECB :
6733 tgl 1662 CBC 26073 : if (res == 0)
6733 tgl 1663 ECB : {
1664 : /* Non-DST zone, life is simple */
6733 tgl 1665 CBC 1301 : tm->tm_isdst = before_isdst;
3097 tgl 1666 GIC 1301 : *tp = mytime - before_gmtoff;
6385 bruce 1667 CBC 1301 : return -(int) before_gmtoff;
6733 tgl 1668 ECB : }
6884 1669 :
1670 : /*
1671 : * Form the candidate pg_time_t values with local-time adjustment
1672 : */
6733 tgl 1673 GIC 24772 : beforetime = mytime - before_gmtoff;
6530 bruce 1674 24772 : if ((before_gmtoff > 0 &&
1675 6 : mytime < 0 && beforetime > 0) ||
1676 24772 : (before_gmtoff <= 0 &&
1677 18402 : mytime > 0 && beforetime < 0))
6733 tgl 1678 UIC 0 : goto overflow;
6733 tgl 1679 GIC 24772 : aftertime = mytime - after_gmtoff;
6530 bruce 1680 24772 : if ((after_gmtoff > 0 &&
1681 6 : mytime < 0 && aftertime > 0) ||
1682 24772 : (after_gmtoff <= 0 &&
1683 18402 : mytime > 0 && aftertime < 0))
6884 tgl 1684 UIC 0 : goto overflow;
1685 :
1686 : /*
1687 : * If both before or both after the boundary time, we know what to do. The
3097 tgl 1688 ECB : * boundary time itself is considered to be after the transition, which
1689 : * means we can accept aftertime == boundary in the second case.
1690 : */
3097 tgl 1691 GIC 24772 : if (beforetime < boundary && aftertime < boundary)
1692 : {
6733 1693 24483 : tm->tm_isdst = before_isdst;
3097 1694 24483 : *tp = beforetime;
6385 bruce 1695 24483 : return -(int) before_gmtoff;
1696 : }
6733 tgl 1697 289 : if (beforetime > boundary && aftertime >= boundary)
1698 : {
6733 tgl 1699 CBC 214 : tm->tm_isdst = after_isdst;
3097 tgl 1700 GIC 214 : *tp = aftertime;
6385 bruce 1701 214 : return -(int) after_gmtoff;
1702 : }
1703 :
6733 tgl 1704 ECB : /*
1705 : * It's an invalid or ambiguous time due to timezone transition. In a
1706 : * spring-forward transition, prefer the "before" interpretation; in a
1707 : * fall-back transition, prefer "after". (We used to define and implement
3097 1708 : * this test as "prefer the standard-time interpretation", but that rule
1709 : * does not help to resolve the behavior when both times are reported as
1710 : * standard time; which does happen, eg Europe/Moscow in Oct 2014. Also,
1711 : * in some zones such as Europe/Dublin, there is widespread confusion
1712 : * about which time offset is "standard" time, so it's fortunate that our
1713 : * behavior doesn't depend on that.)
1714 : */
3097 tgl 1715 GIC 75 : if (beforetime > aftertime)
6733 tgl 1716 EUB : {
3097 tgl 1717 GIC 36 : tm->tm_isdst = before_isdst;
1718 36 : *tp = beforetime;
1719 36 : return -(int) before_gmtoff;
1720 : }
1721 39 : tm->tm_isdst = after_isdst;
1722 39 : *tp = aftertime;
1723 39 : return -(int) after_gmtoff;
1724 :
6884 1725 12 : overflow:
6884 tgl 1726 ECB : /* Given date is out of range, so assume UTC */
6884 tgl 1727 GIC 12 : tm->tm_isdst = 0;
3097 1728 12 : *tp = 0;
6884 tgl 1729 CBC 12 : return 0;
1730 : }
1731 :
1732 :
1733 : /* DetermineTimeZoneAbbrevOffset()
1734 : *
1735 : * Determine the GMT offset and DST flag to be attributed to a dynamic
1736 : * time zone abbreviation, that is one whose meaning has changed over time.
1737 : * *tm contains the local time at which the meaning should be determined,
1738 : * and tm->tm_isdst receives the DST flag.
3097 tgl 1739 ECB : *
1740 : * This differs from the behavior of DetermineTimeZoneOffset() in that a
1741 : * standard-time or daylight-time abbreviation forces use of the corresponding
1742 : * GMT offset even when the zone was then in DS or standard time respectively.
1743 : * (However, that happens only if we can match the given abbreviation to some
1744 : * abbreviation that appears in the IANA timezone data. Otherwise, we fall
1745 : * back to doing DetermineTimeZoneOffset().)
1746 : */
3097 tgl 1747 EUB : int
2118 tgl 1748 GIC 90 : DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_tz *tzp)
1749 : {
1750 : pg_time_t t;
2410 tgl 1751 ECB : int zone_offset;
1752 : int abbr_offset;
1753 : int abbr_isdst;
1754 :
1755 : /*
1756 : * Compute the UTC time we want to probe at. (In event of overflow, we'll
1757 : * probe at the epoch, which is a bit random but probably doesn't matter.)
1758 : */
2410 tgl 1759 GIC 90 : zone_offset = DetermineTimeZoneOffsetInternal(tm, tzp, &t);
1760 :
1761 : /*
1762 : * Try to match the abbreviation to something in the zone definition.
2410 tgl 1763 ECB : */
2410 tgl 1764 GIC 90 : if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1765 : &abbr_offset, &abbr_isdst))
1766 : {
1767 : /* Success, so use the abbrev-specific answers. */
1768 90 : tm->tm_isdst = abbr_isdst;
1769 90 : return abbr_offset;
1770 : }
2410 tgl 1771 ECB :
1772 : /*
1773 : * No match, so use the answers we already got from
1774 : * DetermineTimeZoneOffsetInternal.
1775 : */
2410 tgl 1776 LBC 0 : return zone_offset;
1777 : }
1778 :
1779 :
1780 : /* DetermineTimeZoneAbbrevOffsetTS()
1781 : *
1782 : * As above but the probe time is specified as a TimestampTz (hence, UTC time),
3097 tgl 1783 ECB : * and DST status is returned into *isdst rather than into tm->tm_isdst.
1784 : */
1785 : int
3097 tgl 1786 CBC 492 : DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
1787 : pg_tz *tzp, int *isdst)
1788 : {
3097 tgl 1789 GIC 492 : pg_time_t t = timestamptz_to_time_t(ts);
1790 : int zone_offset;
1791 : int abbr_offset;
1792 : int tz;
1793 : struct pg_tm tm;
1794 : fsec_t fsec;
1795 :
1796 : /*
1797 : * If the abbrev matches anything in the zone data, this is pretty easy.
1798 : */
2410 1799 492 : if (DetermineTimeZoneAbbrevOffsetInternal(t, abbr, tzp,
1800 : &abbr_offset, isdst))
1801 45 : return abbr_offset;
1802 :
1803 : /*
1804 : * Else, break down the timestamp so we can use DetermineTimeZoneOffset.
1805 : */
2410 tgl 1806 CBC 447 : if (timestamp2tm(ts, &tz, &tm, &fsec, NULL, tzp) != 0)
2410 tgl 1807 UIC 0 : ereport(ERROR,
1808 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1809 : errmsg("timestamp out of range")));
2410 tgl 1810 ECB :
2410 tgl 1811 GIC 447 : zone_offset = DetermineTimeZoneOffset(&tm, tzp);
1812 447 : *isdst = tm.tm_isdst;
2410 tgl 1813 CBC 447 : return zone_offset;
1814 : }
1815 :
1816 :
3097 tgl 1817 ECB : /* DetermineTimeZoneAbbrevOffsetInternal()
1818 : *
1819 : * Workhorse for above two functions: work from a pg_time_t probe instant.
2410 1820 : * On success, return GMT offset and DST status into *offset and *isdst.
3097 1821 : */
2410 1822 : static bool
2410 tgl 1823 CBC 582 : DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp,
1824 : int *offset, int *isdst)
1825 : {
3097 tgl 1826 ECB : char upabbr[TZ_STRLEN_MAX + 1];
1827 : unsigned char *p;
1828 : long int gmtoff;
1829 :
1830 : /* We need to force the abbrev to upper case */
3097 tgl 1831 GIC 582 : strlcpy(upabbr, abbr, sizeof(upabbr));
3097 tgl 1832 CBC 2721 : for (p = (unsigned char *) upabbr; *p; p++)
3097 tgl 1833 GIC 2139 : *p = pg_toupper(*p);
3097 tgl 1834 ECB :
1835 : /* Look up the abbrev's meaning at this time in this zone */
2410 tgl 1836 GIC 582 : if (pg_interpret_timezone_abbrev(upabbr,
2410 tgl 1837 ECB : &t,
1838 : &gmtoff,
1839 : isdst,
1840 : tzp))
1841 : {
1842 : /* Change sign to agree with DetermineTimeZoneOffset() */
2410 tgl 1843 GIC 135 : *offset = (int) -gmtoff;
1844 135 : return true;
1845 : }
1846 447 : return false;
3097 tgl 1847 ECB : }
3097 tgl 1848 EUB :
1849 :
1850 : /* DecodeTimeOnly()
8453 lockhart 1851 ECB : * Interpret parsed string as time fields only.
7165 tgl 1852 : * Returns 0 if successful, DTERR code if bogus input detected.
1853 : *
1854 : * Inputs are field[] and ftype[] arrays, of length nf.
1855 : * Other arguments are outputs.
1856 : *
8426 lockhart 1857 : * Note that support for time zone is here for
1858 : * SQL TIME WITH TIME ZONE, but it reveals
3641 peter_e 1859 : * bogosity with SQL date/time standards, since
8426 lockhart 1860 EUB : * we must infer a time zone from current time.
1861 : * - thomas 2000-03-10
1862 : * Allow specifying date to get a better time zone,
1863 : * if time zones are allowed. - thomas 2001-12-26
1864 : */
8453 lockhart 1865 ECB : int
8426 lockhart 1866 GIC 1973 : DecodeTimeOnly(char **field, int *ftype, int nf,
1867 : int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
1868 : DateTimeErrorExtra *extra)
1869 : {
7771 1870 1973 : int fmask = 0,
1871 : tmask,
1872 : type;
24 tgl 1873 GNC 1973 : int ptype = 0; /* "prefix type" for ISO and Julian formats */
8453 lockhart 1874 EUB : int i;
7771 1875 : int val;
1876 : int dterr;
2062 peter_e 1877 GIC 1973 : bool isjulian = false;
1878 1973 : bool is2digits = false;
1879 1973 : bool bc = false;
8453 lockhart 1880 GBC 1973 : int mer = HR24;
6018 tgl 1881 1973 : pg_tz *namedTz = NULL;
3097 tgl 1882 GIC 1973 : pg_tz *abbrevTz = NULL;
1883 1973 : char *abbrev = NULL;
3097 tgl 1884 EUB : pg_tz *valtz;
8453 lockhart 1885 :
8453 lockhart 1886 GBC 1973 : *dtype = DTK_TIME;
1887 1973 : tm->tm_hour = 0;
8453 lockhart 1888 GIC 1973 : tm->tm_min = 0;
1889 1973 : tm->tm_sec = 0;
1890 1973 : *fsec = 0;
1891 : /* don't know daylight savings time status apriori */
7771 1892 1973 : tm->tm_isdst = -1;
7771 lockhart 1893 EUB :
8426 lockhart 1894 GIC 1973 : if (tzp != NULL)
1895 1973 : *tzp = 0;
1896 :
8453 lockhart 1897 GBC 5082 : for (i = 0; i < nf; i++)
8453 lockhart 1898 EUB : {
8453 lockhart 1899 GBC 3115 : switch (ftype[i])
1900 : {
8424 1901 695 : case DTK_DATE:
1902 :
1903 : /*
1904 : * Time zone not allowed? Then should not accept dates or time
6385 bruce 1905 ECB : * zones no matter what else!
8424 lockhart 1906 : */
7771 lockhart 1907 GIC 695 : if (tzp == NULL)
7165 tgl 1908 UBC 0 : return DTERR_BAD_FORMAT;
8424 lockhart 1909 EUB :
1910 : /* Under limited circumstances, we will accept a date... */
6530 bruce 1911 GIC 695 : if (i == 0 && nf >= 2 &&
6530 bruce 1912 CBC 99 : (ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME))
7771 lockhart 1913 ECB : {
5522 tgl 1914 GIC 99 : dterr = DecodeDate(field[i], fmask,
1915 : &tmask, &is2digits, tm);
7165 tgl 1916 CBC 99 : if (dterr)
7165 tgl 1917 UIC 0 : return dterr;
7771 lockhart 1918 ECB : }
1919 : /* otherwise, this is a time and/or time zone */
1920 : else
1921 : {
7770 tgl 1922 CBC 596 : if (isdigit((unsigned char) *field[i]))
7771 lockhart 1923 EUB : {
7522 bruce 1924 ECB : char *cp;
1925 :
1926 : /*
1927 : * Starts with a digit but we already have a time
1928 : * field? Then we are in trouble with time already...
1929 : */
7771 lockhart 1930 LBC 0 : if ((fmask & DTK_TIME_M) == DTK_TIME_M)
7165 tgl 1931 UBC 0 : return DTERR_BAD_FORMAT;
1932 :
7522 bruce 1933 ECB : /*
6385 1934 : * Should not get here and fail. Sanity check only...
7522 bruce 1935 EUB : */
7771 lockhart 1936 LBC 0 : if ((cp = strchr(field[i], '-')) == NULL)
7165 tgl 1937 0 : return DTERR_BAD_FORMAT;
1938 :
7771 lockhart 1939 ECB : /* Get the time zone from the end of the string */
7165 tgl 1940 UIC 0 : dterr = DecodeTimezone(cp, tzp);
7165 tgl 1941 LBC 0 : if (dterr)
7165 tgl 1942 UIC 0 : return dterr;
7771 lockhart 1943 0 : *cp = '\0';
1944 :
1945 : /*
6385 bruce 1946 ECB : * Then read the rest of the field as a concatenated
1947 : * time
1948 : */
7165 tgl 1949 UIC 0 : dterr = DecodeNumberField(strlen(field[i]), field[i],
1950 : (fmask | DTK_DATE_M),
7165 tgl 1951 ECB : &tmask, tm,
1952 : fsec, &is2digits);
7165 tgl 1953 LBC 0 : if (dterr < 0)
1954 0 : return dterr;
1955 0 : ftype[i] = dterr;
7771 lockhart 1956 EUB :
7771 lockhart 1957 UIC 0 : tmask |= DTK_M(TZ);
1958 : }
1959 : else
7771 lockhart 1960 ECB : {
6018 tgl 1961 GIC 596 : namedTz = pg_tzset(field[i]);
6018 tgl 1962 CBC 596 : if (!namedTz)
6018 tgl 1963 EUB : {
121 tgl 1964 UNC 0 : extra->dtee_timezone = field[i];
1965 0 : return DTERR_BAD_TIMEZONE;
1966 : }
6018 tgl 1967 EUB : /* we'll apply the zone setting below */
7771 lockhart 1968 GBC 596 : ftype[i] = DTK_TZ;
1969 596 : tmask = DTK_M(TZ);
7771 lockhart 1970 EUB : }
1971 : }
8424 lockhart 1972 GIC 695 : break;
1973 :
8453 lockhart 1974 GBC 1928 : case DTK_TIME:
7165 tgl 1975 GIC 1928 : dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
5324 tgl 1976 ECB : INTERVAL_FULL_RANGE,
1977 : &tmask, tm, fsec);
7165 tgl 1978 CBC 1928 : if (dterr)
7165 tgl 1979 UIC 0 : return dterr;
8453 lockhart 1980 CBC 1928 : break;
1981 :
8426 lockhart 1982 GIC 270 : case DTK_TZ:
1983 : {
8397 bruce 1984 ECB : int tz;
8424 lockhart 1985 EUB :
7165 tgl 1986 CBC 270 : if (tzp == NULL)
7165 tgl 1987 UIC 0 : return DTERR_BAD_FORMAT;
7165 tgl 1988 ECB :
6018 tgl 1989 GBC 270 : dterr = DecodeTimezone(field[i], &tz);
7165 tgl 1990 CBC 270 : if (dterr)
7165 tgl 1991 UIC 0 : return dterr;
6018 tgl 1992 CBC 270 : *tzp = tz;
1993 270 : tmask = DTK_M(TZ);
1994 : }
8426 lockhart 1995 GIC 270 : break;
1996 :
8453 lockhart 1997 CBC 48 : case DTK_NUMBER:
7522 bruce 1998 ECB :
1999 : /*
2000 : * Deal with cases where previous field labeled this one
2001 : */
7771 lockhart 2002 GIC 48 : if (ptype != 0)
2003 : {
7771 lockhart 2004 ECB : char *cp;
2005 : int value;
8453 lockhart 2006 EUB :
6337 bruce 2007 GIC 36 : errno = 0;
186 drowley 2008 GNC 36 : value = strtoint(field[i], &cp, 10);
6338 tgl 2009 CBC 36 : if (errno == ERANGE)
6338 tgl 2010 GIC 6 : return DTERR_FIELD_OVERFLOW;
24 tgl 2011 GNC 36 : if (*cp != '.' && *cp != '\0')
7165 tgl 2012 UIC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 2013 EUB :
2014 : switch (ptype)
2015 : {
7771 lockhart 2016 GBC 3 : case DTK_JULIAN:
2017 : /* previous field was a label for "julian date" */
24 tgl 2018 GNC 3 : if (tzp == NULL)
24 tgl 2019 UNC 0 : return DTERR_BAD_FORMAT;
186 drowley 2020 GNC 3 : if (value < 0)
4582 tgl 2021 UIC 0 : return DTERR_FIELD_OVERFLOW;
7771 lockhart 2022 GIC 3 : tmask = DTK_DATE_M;
186 drowley 2023 GNC 3 : j2date(value, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2062 peter_e 2024 GBC 3 : isjulian = true;
4582 tgl 2025 EUB :
7771 lockhart 2026 GBC 3 : if (*cp == '.')
7771 lockhart 2027 EUB : {
7522 bruce 2028 : double time;
7771 lockhart 2029 :
371 tgl 2030 UIC 0 : dterr = ParseFraction(cp, &time);
371 tgl 2031 LBC 0 : if (dterr)
371 tgl 2032 UIC 0 : return dterr;
5261 2033 0 : time *= USECS_PER_DAY;
2034 0 : dt2time(time,
2035 : &tm->tm_hour, &tm->tm_min,
2036 : &tm->tm_sec, fsec);
5261 tgl 2037 LBC 0 : tmask |= DTK_TIME_M;
7771 lockhart 2038 ECB : }
7771 lockhart 2039 CBC 3 : break;
7771 lockhart 2040 EUB :
7771 lockhart 2041 CBC 27 : case DTK_TIME:
7771 lockhart 2042 ECB : /* previous field was "t" for ISO time */
7165 tgl 2043 CBC 27 : dterr = DecodeNumberField(strlen(field[i]), field[i],
2044 : (fmask | DTK_DATE_M),
7165 tgl 2045 ECB : &tmask, tm,
2046 : fsec, &is2digits);
7165 tgl 2047 CBC 27 : if (dterr < 0)
7165 tgl 2048 UBC 0 : return dterr;
7165 tgl 2049 CBC 27 : ftype[i] = dterr;
7771 lockhart 2050 ECB :
7771 lockhart 2051 CBC 27 : if (tmask != DTK_TIME_M)
7165 tgl 2052 UIC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 2053 CBC 27 : break;
7771 lockhart 2054 ECB :
7771 lockhart 2055 CBC 6 : default:
7165 tgl 2056 GBC 6 : return DTERR_BAD_FORMAT;
2057 : break;
7771 lockhart 2058 ECB : }
2059 :
7771 lockhart 2060 CBC 30 : ptype = 0;
2061 30 : *dtype = DTK_DATE;
2062 : }
7771 lockhart 2063 ECB : else
2064 : {
7522 bruce 2065 : char *cp;
2066 : int flen;
7771 lockhart 2067 EUB :
7771 lockhart 2068 GBC 12 : flen = strlen(field[i]);
2069 12 : cp = strchr(field[i], '.');
2070 :
7771 lockhart 2071 ECB : /* Embedded decimal? */
7771 lockhart 2072 CBC 12 : if (cp != NULL)
2073 : {
7522 bruce 2074 ECB : /*
7522 bruce 2075 EUB : * Under limited circumstances, we will accept a
7522 bruce 2076 ECB : * date...
2077 : */
6530 bruce 2078 GIC 12 : if (i == 0 && nf >= 2 && ftype[nf - 1] == DTK_DATE)
7771 lockhart 2079 ECB : {
5522 tgl 2080 LBC 0 : dterr = DecodeDate(field[i], fmask,
2081 : &tmask, &is2digits, tm);
7165 2082 0 : if (dterr)
7165 tgl 2083 UBC 0 : return dterr;
7771 lockhart 2084 ECB : }
2085 : /* embedded decimal and several digits before? */
6530 bruce 2086 GIC 12 : else if (flen - strlen(cp) > 2)
7771 lockhart 2087 EUB : {
2088 : /*
2089 : * Interpret as a concatenated date or time Set
2090 : * the type field to allow decoding other fields
2091 : * later. Example: 20011223 or 040506
2092 : */
7165 tgl 2093 GBC 12 : dterr = DecodeNumberField(flen, field[i],
6385 bruce 2094 EUB : (fmask | DTK_DATE_M),
7165 tgl 2095 : &tmask, tm,
2096 : fsec, &is2digits);
7165 tgl 2097 GBC 12 : if (dterr < 0)
7165 tgl 2098 UBC 0 : return dterr;
7165 tgl 2099 GIC 12 : ftype[i] = dterr;
7771 lockhart 2100 EUB : }
2101 : else
7165 tgl 2102 UIC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 2103 ECB : }
7771 lockhart 2104 UIC 0 : else if (flen > 4)
7771 lockhart 2105 EUB : {
7165 tgl 2106 UBC 0 : dterr = DecodeNumberField(flen, field[i],
2107 : (fmask | DTK_DATE_M),
2108 : &tmask, tm,
7165 tgl 2109 ECB : fsec, &is2digits);
7165 tgl 2110 UBC 0 : if (dterr < 0)
7165 tgl 2111 LBC 0 : return dterr;
7165 tgl 2112 UIC 0 : ftype[i] = dterr;
2113 : }
2114 : /* otherwise it is a single date/time field... */
7165 tgl 2115 ECB : else
7165 tgl 2116 EUB : {
7165 tgl 2117 UIC 0 : dterr = DecodeNumber(flen, field[i],
2118 : false,
7165 tgl 2119 ECB : (fmask | DTK_DATE_M),
2120 : &tmask, tm,
7165 tgl 2121 EUB : fsec, &is2digits);
7165 tgl 2122 UIC 0 : if (dterr)
2123 0 : return dterr;
7165 tgl 2124 ECB : }
7771 lockhart 2125 EUB : }
8453 lockhart 2126 CBC 42 : break;
8453 lockhart 2127 EUB :
8453 lockhart 2128 CBC 174 : case DTK_STRING:
8453 lockhart 2129 ECB : case DTK_SPECIAL:
2130 : /* timezone abbrevs take precedence over built-in tokens */
121 tgl 2131 GNC 174 : dterr = DecodeTimezoneAbbrev(i, field[i],
2132 : &type, &val, &valtz, extra);
2133 174 : if (dterr)
121 tgl 2134 UNC 0 : return dterr;
3097 tgl 2135 CBC 174 : if (type == UNKNOWN_FIELD)
2136 48 : type = DecodeSpecial(i, field[i], &val);
7607 JanWieck 2137 GIC 174 : if (type == IGNORE_DTF)
8453 lockhart 2138 LBC 0 : continue;
8453 lockhart 2139 EUB :
8453 lockhart 2140 GIC 174 : tmask = DTK_M(type);
2141 174 : switch (type)
2142 : {
2143 6 : case RESERV:
2144 6 : switch (val)
8453 lockhart 2145 ECB : {
8453 lockhart 2146 GIC 6 : case DTK_NOW:
2147 6 : tmask = DTK_TIME_M;
2148 6 : *dtype = DTK_TIME;
7353 tgl 2149 6 : GetCurrentTimeUsec(tm, fsec, NULL);
8453 lockhart 2150 CBC 6 : break;
8453 lockhart 2151 ECB :
8453 lockhart 2152 UIC 0 : case DTK_ZULU:
2153 0 : tmask = (DTK_TIME_M | DTK_M(TZ));
8453 lockhart 2154 LBC 0 : *dtype = DTK_TIME;
8453 lockhart 2155 UIC 0 : tm->tm_hour = 0;
8453 lockhart 2156 LBC 0 : tm->tm_min = 0;
8453 lockhart 2157 UIC 0 : tm->tm_sec = 0;
2158 0 : tm->tm_isdst = 0;
2159 0 : break;
2160 :
8453 lockhart 2161 LBC 0 : default:
7165 tgl 2162 0 : return DTERR_BAD_FORMAT;
8453 lockhart 2163 ECB : }
2164 :
8453 lockhart 2165 GIC 6 : break;
2166 :
8424 lockhart 2167 UIC 0 : case DTZMOD:
2168 :
2169 : /*
6385 bruce 2170 ECB : * daylight savings time modifier (solves "MET DST"
2171 : * syntax)
2172 : */
8424 lockhart 2173 UBC 0 : tmask |= DTK_M(DTZ);
8424 lockhart 2174 UIC 0 : tm->tm_isdst = 1;
2175 0 : if (tzp == NULL)
7165 tgl 2176 0 : return DTERR_BAD_FORMAT;
3097 2177 0 : *tzp -= val;
8424 lockhart 2178 UBC 0 : break;
8424 lockhart 2179 EUB :
8424 lockhart 2180 GIC 93 : case DTZ:
8424 lockhart 2181 EUB :
2182 : /*
2183 : * set mask for TZ here _or_ check for DTZ later when
2184 : * getting default timezone
2185 : */
8424 lockhart 2186 GBC 93 : tmask |= DTK_M(TZ);
2187 93 : tm->tm_isdst = 1;
2188 93 : if (tzp == NULL)
7165 tgl 2189 UBC 0 : return DTERR_BAD_FORMAT;
3097 tgl 2190 GBC 93 : *tzp = -val;
8424 lockhart 2191 GIC 93 : ftype[i] = DTK_TZ;
8424 lockhart 2192 GBC 93 : break;
8424 lockhart 2193 EUB :
8424 lockhart 2194 GBC 30 : case TZ:
2195 30 : tm->tm_isdst = 0;
2196 30 : if (tzp == NULL)
7165 tgl 2197 UIC 0 : return DTERR_BAD_FORMAT;
3097 tgl 2198 GIC 30 : *tzp = -val;
8424 lockhart 2199 30 : ftype[i] = DTK_TZ;
8424 lockhart 2200 CBC 30 : break;
2201 :
3097 tgl 2202 GIC 3 : case DYNTZ:
3097 tgl 2203 CBC 3 : tmask |= DTK_M(TZ);
3097 tgl 2204 GIC 3 : if (tzp == NULL)
3097 tgl 2205 UIC 0 : return DTERR_BAD_FORMAT;
2206 : /* we'll determine the actual offset later */
3097 tgl 2207 GIC 3 : abbrevTz = valtz;
3097 tgl 2208 CBC 3 : abbrev = field[i];
3097 tgl 2209 GBC 3 : ftype[i] = DTK_TZ;
8453 lockhart 2210 GIC 3 : break;
8453 lockhart 2211 ECB :
8453 lockhart 2212 CBC 6 : case AMPM:
8453 lockhart 2213 GIC 6 : mer = val;
2214 6 : break;
2215 :
5522 tgl 2216 LBC 0 : case ADBC:
5522 tgl 2217 UBC 0 : bc = (val == BC);
5522 tgl 2218 LBC 0 : break;
5522 tgl 2219 ECB :
7771 lockhart 2220 CBC 9 : case UNITS:
7771 lockhart 2221 GIC 9 : tmask = 0;
2222 : /* reject consecutive unhandled units */
24 tgl 2223 GNC 9 : if (ptype != 0)
24 tgl 2224 UNC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 2225 CBC 9 : ptype = val;
2226 9 : break;
7771 lockhart 2227 ECB :
7768 lockhart 2228 CBC 27 : case ISOTIME:
7771 2229 27 : tmask = 0;
2230 : /* reject consecutive unhandled units */
24 tgl 2231 GNC 27 : if (ptype != 0)
7165 tgl 2232 UIC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 2233 GIC 27 : ptype = val;
2234 27 : break;
2235 :
6017 tgl 2236 LBC 0 : case UNKNOWN_FIELD:
2237 :
2238 : /*
2239 : * Before giving up and declaring error, check to see
6017 tgl 2240 ECB : * if it is an all-alpha timezone name.
2241 : */
6017 tgl 2242 UIC 0 : namedTz = pg_tzset(field[i]);
2243 0 : if (!namedTz)
6017 tgl 2244 LBC 0 : return DTERR_BAD_FORMAT;
2245 : /* we'll apply the zone setting below */
6017 tgl 2246 UIC 0 : tmask = DTK_M(TZ);
6017 tgl 2247 LBC 0 : break;
2248 :
8453 lockhart 2249 UIC 0 : default:
7165 tgl 2250 LBC 0 : return DTERR_BAD_FORMAT;
2251 : }
8453 lockhart 2252 GIC 174 : break;
8453 lockhart 2253 ECB :
8453 lockhart 2254 UIC 0 : default:
7165 tgl 2255 0 : return DTERR_BAD_FORMAT;
8453 lockhart 2256 ECB : }
8453 lockhart 2257 EUB :
8453 lockhart 2258 GIC 3109 : if (tmask & fmask)
7165 tgl 2259 LBC 0 : return DTERR_BAD_FORMAT;
8453 lockhart 2260 GBC 3109 : fmask |= tmask;
2261 : } /* end loop over fields */
5522 tgl 2262 ECB :
2263 : /* reject if prefix type appeared and was never handled */
24 tgl 2264 GNC 1967 : if (ptype != 0)
24 tgl 2265 UNC 0 : return DTERR_BAD_FORMAT;
2266 :
5522 tgl 2267 ECB : /* do final checking/adjustment of Y/M/D fields */
4582 tgl 2268 GIC 1967 : dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm);
5522 tgl 2269 CBC 1967 : if (dterr)
5522 tgl 2270 LBC 0 : return dterr;
2271 :
5522 tgl 2272 ECB : /* handle AM/PM */
4411 bruce 2273 GIC 1967 : if (mer != HR24 && tm->tm_hour > HOURS_PER_DAY / 2)
7165 tgl 2274 LBC 0 : return DTERR_FIELD_OVERFLOW;
4411 bruce 2275 CBC 1967 : if (mer == AM && tm->tm_hour == HOURS_PER_DAY / 2)
8453 lockhart 2276 UIC 0 : tm->tm_hour = 0;
4411 bruce 2277 GIC 1967 : else if (mer == PM && tm->tm_hour != HOURS_PER_DAY / 2)
2278 6 : tm->tm_hour += HOURS_PER_DAY / 2;
8453 lockhart 2279 ECB :
1039 tgl 2280 : /* check for time overflow */
1039 tgl 2281 CBC 1967 : if (time_overflows(tm->tm_hour, tm->tm_min, tm->tm_sec, *fsec))
6018 tgl 2282 GIC 36 : return DTERR_FIELD_OVERFLOW;
2283 :
8453 lockhart 2284 1931 : if ((fmask & DTK_TIME_M) != DTK_TIME_M)
7165 tgl 2285 LBC 0 : return DTERR_BAD_FORMAT;
2286 :
6018 tgl 2287 ECB : /*
2288 : * If we had a full timezone spec, compute the offset (we could not do it
5624 bruce 2289 : * before, because we may need the date to resolve DST status).
6018 tgl 2290 : */
6018 tgl 2291 GBC 1931 : if (namedTz != NULL)
2292 : {
6017 tgl 2293 ECB : long int gmtoff;
2294 :
2295 : /* daylight savings time modifier disallowed with full TZ */
6018 tgl 2296 CBC 596 : if (fmask & DTK_M(DTZMOD))
2297 21 : return DTERR_BAD_FORMAT;
6018 tgl 2298 ECB :
6017 2299 : /* if non-DST zone, we do not need to know the date */
6017 tgl 2300 GIC 596 : if (pg_get_timezone_offset(namedTz, &gmtoff))
6017 tgl 2301 EUB : {
6017 tgl 2302 GBC 557 : *tzp = -(int) gmtoff;
2303 : }
6017 tgl 2304 ECB : else
6017 tgl 2305 EUB : {
2306 : /* a date has to be specified */
6017 tgl 2307 CBC 39 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
2308 21 : return DTERR_BAD_FORMAT;
6017 tgl 2309 GIC 18 : *tzp = DetermineTimeZoneOffset(tm, namedTz);
2310 : }
6018 tgl 2311 ECB : }
2312 :
2313 : /*
2314 : * Likewise, if we had a dynamic timezone abbreviation, resolve it now.
2315 : */
3097 tgl 2316 CBC 1910 : if (abbrevTz != NULL)
2317 : {
3097 tgl 2318 ECB : struct pg_tm tt,
3097 tgl 2319 LBC 0 : *tmp = &tt;
2320 :
3097 tgl 2321 ECB : /*
3097 tgl 2322 EUB : * daylight savings time modifier but no standard timezone? then error
2323 : */
3097 tgl 2324 LBC 0 : if (fmask & DTK_M(DTZMOD))
3097 tgl 2325 UIC 0 : return DTERR_BAD_FORMAT;
2326 :
3097 tgl 2327 LBC 0 : if ((fmask & DTK_DATE_M) == 0)
3097 tgl 2328 UBC 0 : GetCurrentDateTime(tmp);
2329 : else
3097 tgl 2330 ECB : {
1341 michael 2331 EUB : /* a date has to be specified */
1341 michael 2332 UIC 0 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1341 michael 2333 LBC 0 : return DTERR_BAD_FORMAT;
3097 tgl 2334 0 : tmp->tm_year = tm->tm_year;
3097 tgl 2335 UIC 0 : tmp->tm_mon = tm->tm_mon;
2336 0 : tmp->tm_mday = tm->tm_mday;
3097 tgl 2337 ECB : }
3097 tgl 2338 LBC 0 : tmp->tm_hour = tm->tm_hour;
3097 tgl 2339 UIC 0 : tmp->tm_min = tm->tm_min;
2340 0 : tmp->tm_sec = tm->tm_sec;
2341 0 : *tzp = DetermineTimeZoneAbbrevOffset(tmp, abbrev, abbrevTz);
3097 tgl 2342 LBC 0 : tm->tm_isdst = tmp->tm_isdst;
2343 : }
2344 :
2345 : /* timezone not specified? then use session timezone */
6532 bruce 2346 GIC 1910 : if (tzp != NULL && !(fmask & DTK_M(TZ)))
2347 : {
2348 : struct pg_tm tt,
8397 2349 960 : *tmp = &tt;
8426 lockhart 2350 ECB :
2351 : /*
2352 : * daylight savings time modifier but no standard timezone? then error
2353 : */
8426 lockhart 2354 GIC 960 : if (fmask & DTK_M(DTZMOD))
7165 tgl 2355 LBC 0 : return DTERR_BAD_FORMAT;
2356 :
7771 lockhart 2357 GIC 960 : if ((fmask & DTK_DATE_M) == 0)
7607 JanWieck 2358 921 : GetCurrentDateTime(tmp);
7771 lockhart 2359 ECB : else
2360 : {
2361 : /* a date has to be specified */
1341 michael 2362 CBC 39 : if ((fmask & DTK_DATE_M) != DTK_DATE_M)
1341 michael 2363 UBC 0 : return DTERR_BAD_FORMAT;
7771 lockhart 2364 GIC 39 : tmp->tm_year = tm->tm_year;
7771 lockhart 2365 CBC 39 : tmp->tm_mon = tm->tm_mon;
7771 lockhart 2366 GIC 39 : tmp->tm_mday = tm->tm_mday;
7771 lockhart 2367 ECB : }
8426 lockhart 2368 GIC 960 : tmp->tm_hour = tm->tm_hour;
2369 960 : tmp->tm_min = tm->tm_min;
8426 lockhart 2370 CBC 960 : tmp->tm_sec = tm->tm_sec;
5727 tgl 2371 GBC 960 : *tzp = DetermineTimeZoneOffset(tmp, session_timezone);
8426 lockhart 2372 CBC 960 : tm->tm_isdst = tmp->tm_isdst;
8426 lockhart 2373 ECB : }
2374 :
8453 lockhart 2375 CBC 1910 : return 0;
2376 : }
2377 :
2378 : /* DecodeDate()
2379 : * Decode date string which includes delimiters.
7165 tgl 2380 ECB : * Return 0 if okay, a DTERR code if not.
2381 : *
2382 : * str: field to be parsed
2383 : * fmask: bitmask for field types already seen
2384 : * *tmask: receives bitmask for fields found here
2385 : * *is2digits: set to true if we find 2-digit year
5522 2386 : * *tm: field values are stored into appropriate members of this struct
2387 : */
8340 bruce 2388 : static int
5522 tgl 2389 GIC 31725 : DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
2390 : struct pg_tm *tm)
2391 : {
2392 : fsec_t fsec;
8453 lockhart 2393 CBC 31725 : int nf = 0;
2394 : int i,
8453 lockhart 2395 ECB : len;
7165 tgl 2396 : int dterr;
2062 peter_e 2397 GIC 31725 : bool haveTextMonth = false;
2398 : int type,
2399 : val,
8453 lockhart 2400 CBC 31725 : dmask = 0;
2401 : char *field[MAXDATEFIELDS];
8453 lockhart 2402 ECB :
5522 tgl 2403 CBC 31725 : *tmask = 0;
2404 :
2405 : /* parse this string... */
6530 bruce 2406 126867 : while (*str != '\0' && nf < MAXDATEFIELDS)
2407 : {
2408 : /* skip field separators */
3841 heikki.linnakangas 2409 GIC 95142 : while (*str != '\0' && !isalnum((unsigned char) *str))
8453 lockhart 2410 UIC 0 : str++;
2411 :
3841 heikki.linnakangas 2412 GIC 95142 : if (*str == '\0')
3602 bruce 2413 LBC 0 : return DTERR_BAD_FORMAT; /* end of string after separator */
3841 heikki.linnakangas 2414 ECB :
8453 lockhart 2415 GIC 95142 : field[nf] = str;
8162 tgl 2416 95142 : if (isdigit((unsigned char) *str))
8453 lockhart 2417 ECB : {
8162 tgl 2418 GIC 348659 : while (isdigit((unsigned char) *str))
8453 lockhart 2419 253589 : str++;
2420 : }
8162 tgl 2421 72 : else if (isalpha((unsigned char) *str))
2422 : {
2423 288 : while (isalpha((unsigned char) *str))
8453 lockhart 2424 216 : str++;
2425 : }
2426 :
2427 : /* Just get rid of any non-digit, non-alpha characters... */
2428 95142 : if (*str != '\0')
2429 63435 : *str++ = '\0';
2430 95142 : nf++;
2431 : }
8453 lockhart 2432 ECB :
2433 : /* look first for text fields, since that will be unambiguous month */
8453 lockhart 2434 GIC 126867 : for (i = 0; i < nf; i++)
2435 : {
8162 tgl 2436 95142 : if (isalpha((unsigned char) *field[i]))
8453 lockhart 2437 ECB : {
8453 lockhart 2438 GIC 72 : type = DecodeSpecial(i, field[i], &val);
7607 JanWieck 2439 CBC 72 : if (type == IGNORE_DTF)
8453 lockhart 2440 UIC 0 : continue;
8453 lockhart 2441 ECB :
8453 lockhart 2442 CBC 72 : dmask = DTK_M(type);
2443 72 : switch (type)
8453 lockhart 2444 EUB : {
8453 lockhart 2445 CBC 72 : case MONTH:
8453 lockhart 2446 GBC 72 : tm->tm_mon = val;
2062 peter_e 2447 CBC 72 : haveTextMonth = true;
8453 lockhart 2448 72 : break;
8453 lockhart 2449 ECB :
8453 lockhart 2450 UBC 0 : default:
7165 tgl 2451 LBC 0 : return DTERR_BAD_FORMAT;
2452 : }
8453 lockhart 2453 CBC 72 : if (fmask & dmask)
7165 tgl 2454 UIC 0 : return DTERR_BAD_FORMAT;
8453 lockhart 2455 ECB :
8453 lockhart 2456 GIC 72 : fmask |= dmask;
8453 lockhart 2457 CBC 72 : *tmask |= dmask;
8453 lockhart 2458 EUB :
8453 lockhart 2459 ECB : /* mark this field as being completed */
8453 lockhart 2460 CBC 72 : field[i] = NULL;
8453 lockhart 2461 ECB : }
2462 : }
2463 :
2464 : /* now pick up remaining numeric fields */
8453 lockhart 2465 GIC 126867 : for (i = 0; i < nf; i++)
2466 : {
8453 lockhart 2467 CBC 95142 : if (field[i] == NULL)
2468 72 : continue;
8453 lockhart 2469 ECB :
8453 lockhart 2470 CBC 95070 : if ((len = strlen(field[i])) <= 0)
7165 tgl 2471 UBC 0 : return DTERR_BAD_FORMAT;
8453 lockhart 2472 ECB :
7084 tgl 2473 CBC 95070 : dterr = DecodeNumber(len, field[i], haveTextMonth, fmask,
7165 tgl 2474 ECB : &dmask, tm,
2475 : &fsec, is2digits);
7165 tgl 2476 CBC 95070 : if (dterr)
7165 tgl 2477 UIC 0 : return dterr;
8453 lockhart 2478 ECB :
8453 lockhart 2479 CBC 95070 : if (fmask & dmask)
7165 tgl 2480 LBC 0 : return DTERR_BAD_FORMAT;
8453 lockhart 2481 EUB :
8453 lockhart 2482 CBC 95070 : fmask |= dmask;
8453 lockhart 2483 GIC 95070 : *tmask |= dmask;
8453 lockhart 2484 ECB : }
2485 :
8453 lockhart 2486 GBC 31725 : if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
7165 tgl 2487 GIC 18 : return DTERR_BAD_FORMAT;
8453 lockhart 2488 ECB :
5522 tgl 2489 EUB : /* validation of the field values must wait until ValidateDate() */
2490 :
5522 tgl 2491 GIC 31707 : return 0;
5522 tgl 2492 EUB : }
2493 :
2494 : /* ValidateDate()
5522 tgl 2495 ECB : * Check valid year/month/day values, handle BC and DOY cases
2496 : * Return 0 if okay, a DTERR code if not.
2497 : */
3430 2498 : int
4582 tgl 2499 GBC 36418 : ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
2500 : struct pg_tm *tm)
5522 tgl 2501 ECB : {
5522 tgl 2502 GIC 36418 : if (fmask & DTK_M(YEAR))
8453 lockhart 2503 ECB : {
4582 tgl 2504 GIC 34547 : if (isjulian)
2505 : {
2506 : /* tm_year is correct and should not be touched */
2507 : }
2508 33104 : else if (bc)
2509 : {
2510 : /* there is no year zero in AD/BC notation */
5522 2511 129 : if (tm->tm_year <= 0)
5522 tgl 2512 UIC 0 : return DTERR_FIELD_OVERFLOW;
2513 : /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
5522 tgl 2514 CBC 129 : tm->tm_year = -(tm->tm_year - 1);
2515 : }
5522 tgl 2516 GIC 32975 : else if (is2digits)
2517 : {
2518 : /* process 1 or 2-digit input as 1970-2069 AD, allow '0' and '00' */
5050 bruce 2519 177 : if (tm->tm_year < 0) /* just paranoia */
5522 tgl 2520 LBC 0 : return DTERR_FIELD_OVERFLOW;
5522 tgl 2521 GIC 177 : if (tm->tm_year < 70)
5522 tgl 2522 CBC 87 : tm->tm_year += 2000;
5522 tgl 2523 GBC 90 : else if (tm->tm_year < 100)
5522 tgl 2524 GIC 90 : tm->tm_year += 1900;
5522 tgl 2525 ECB : }
5522 tgl 2526 EUB : else
5522 tgl 2527 ECB : {
2528 : /* there is no year zero in AD/BC notation */
5522 tgl 2529 CBC 32798 : if (tm->tm_year <= 0)
2530 6 : return DTERR_FIELD_OVERFLOW;
2531 : }
8453 lockhart 2532 ECB : }
2533 :
2534 : /* now that we have correct year, decode DOY */
7194 tgl 2535 GIC 36412 : if (fmask & DTK_M(DOY))
2536 : {
2537 15 : j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,
2538 : &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2539 : }
2540 :
2541 : /* check for valid month */
5522 2542 36412 : if (fmask & DTK_M(MONTH))
5522 tgl 2543 ECB : {
5522 tgl 2544 GIC 34529 : if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)
2545 39 : return DTERR_MD_FIELD_OVERFLOW;
2546 : }
2547 :
2548 : /* minimal check for valid day */
5522 tgl 2549 CBC 36373 : if (fmask & DTK_M(DAY))
2550 : {
2551 34472 : if (tm->tm_mday < 1 || tm->tm_mday > 31)
2552 69 : return DTERR_MD_FIELD_OVERFLOW;
2553 : }
7165 tgl 2554 ECB :
5522 tgl 2555 CBC 36304 : if ((fmask & DTK_DATE_M) == DTK_DATE_M)
5522 tgl 2556 ECB : {
2557 : /*
2558 : * Check for valid day of month, now that we know for sure the month
2559 : * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems
2560 : * unlikely that "Feb 29" is a YMD-order error.
2561 : */
5522 tgl 2562 GIC 34394 : if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2563 24 : return DTERR_FIELD_OVERFLOW;
2564 : }
2565 :
8453 lockhart 2566 36280 : return 0;
2567 : }
2568 :
8453 lockhart 2569 ECB :
2570 : /* DecodeTimeCommon()
2571 : * Decode time string which includes delimiters.
2572 : * Return 0 if okay, a DTERR code if not.
2573 : * tmask and itm are output parameters.
2574 : *
2575 : * This code is shared between the timestamp and interval cases.
372 tgl 2576 : * We return a struct pg_itm (of which only the tm_usec, tm_sec, tm_min,
2577 : * and tm_hour fields are used) and let the wrapper functions below
2578 : * convert and range-check as necessary.
8453 lockhart 2579 : */
8340 bruce 2580 : static int
372 tgl 2581 GBC 31524 : DecodeTimeCommon(char *str, int fmask, int range,
372 tgl 2582 ECB : int *tmask, struct pg_itm *itm)
8453 lockhart 2583 EUB : {
2584 : char *cp;
5261 tgl 2585 ECB : int dterr;
372 tgl 2586 GIC 31524 : fsec_t fsec = 0;
2587 :
8453 lockhart 2588 31524 : *tmask = DTK_TIME_M;
2589 :
6337 bruce 2590 31524 : errno = 0;
372 tgl 2591 GBC 31524 : itm->tm_hour = strtoi64(str, &cp, 10);
6338 tgl 2592 GIC 31524 : if (errno == ERANGE)
6338 tgl 2593 UBC 0 : return DTERR_FIELD_OVERFLOW;
8453 lockhart 2594 GIC 31524 : if (*cp != ':')
7165 tgl 2595 UIC 0 : return DTERR_BAD_FORMAT;
6337 bruce 2596 GIC 31524 : errno = 0;
372 tgl 2597 GBC 31524 : itm->tm_min = strtoint(cp + 1, &cp, 10);
6338 2598 31524 : if (errno == ERANGE)
6338 tgl 2599 UBC 0 : return DTERR_FIELD_OVERFLOW;
8453 lockhart 2600 GIC 31524 : if (*cp == '\0')
2601 : {
372 tgl 2602 GBC 744 : itm->tm_sec = 0;
5324 tgl 2603 EUB : /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
5324 tgl 2604 GBC 744 : if (range == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND)))
2605 : {
372 tgl 2606 CBC 9 : if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
372 tgl 2607 UBC 0 : return DTERR_FIELD_OVERFLOW;
372 tgl 2608 GIC 9 : itm->tm_sec = itm->tm_min;
2609 9 : itm->tm_min = (int) itm->tm_hour;
372 tgl 2610 CBC 9 : itm->tm_hour = 0;
2611 : }
2612 : }
5323 2613 30780 : else if (*cp == '.')
5323 tgl 2614 ECB : {
2615 : /* always assume mm:ss.sss is MINUTE TO SECOND */
372 tgl 2616 CBC 24 : dterr = ParseFractionalSecond(cp, &fsec);
5261 tgl 2617 GIC 24 : if (dterr)
2618 6 : return dterr;
372 2619 18 : if (itm->tm_hour > INT_MAX || itm->tm_hour < INT_MIN)
372 tgl 2620 LBC 0 : return DTERR_FIELD_OVERFLOW;
372 tgl 2621 GIC 18 : itm->tm_sec = itm->tm_min;
372 tgl 2622 CBC 18 : itm->tm_min = (int) itm->tm_hour;
372 tgl 2623 GIC 18 : itm->tm_hour = 0;
2624 : }
5323 2625 30756 : else if (*cp == ':')
2626 : {
6337 bruce 2627 30756 : errno = 0;
372 tgl 2628 30756 : itm->tm_sec = strtoint(cp + 1, &cp, 10);
6338 2629 30756 : if (errno == ERANGE)
6338 tgl 2630 UIC 0 : return DTERR_FIELD_OVERFLOW;
372 tgl 2631 CBC 30756 : if (*cp == '.')
2632 : {
2633 10869 : dterr = ParseFractionalSecond(cp, &fsec);
5261 2634 10869 : if (dterr)
5261 tgl 2635 UIC 0 : return dterr;
8453 lockhart 2636 ECB : }
372 tgl 2637 GIC 19887 : else if (*cp != '\0')
7165 tgl 2638 LBC 0 : return DTERR_BAD_FORMAT;
9345 bruce 2639 ECB : }
2640 : else
5323 tgl 2641 UIC 0 : return DTERR_BAD_FORMAT;
2642 :
372 tgl 2643 ECB : /* do a sanity check; but caller must check the range of tm_hour */
372 tgl 2644 CBC 31518 : if (itm->tm_hour < 0 ||
372 tgl 2645 GIC 31518 : itm->tm_min < 0 || itm->tm_min > MINS_PER_HOUR - 1 ||
372 tgl 2646 CBC 31518 : itm->tm_sec < 0 || itm->tm_sec > SECS_PER_MINUTE ||
372 tgl 2647 GIC 31518 : fsec < 0 || fsec > USECS_PER_SEC)
372 tgl 2648 LBC 0 : return DTERR_FIELD_OVERFLOW;
2649 :
372 tgl 2650 CBC 31518 : itm->tm_usec = (int) fsec;
372 tgl 2651 ECB :
372 tgl 2652 CBC 31518 : return 0;
2653 : }
372 tgl 2654 ECB :
2655 : /* DecodeTime()
2656 : * Decode time string which includes delimiters.
2657 : * Return 0 if okay, a DTERR code if not.
2658 : *
2659 : * This version is used for timestamps. The results are returned into
2660 : * the tm_hour/tm_min/tm_sec fields of *tm, and microseconds into *fsec.
2661 : */
2662 : static int
372 tgl 2663 GIC 30538 : DecodeTime(char *str, int fmask, int range,
372 tgl 2664 ECB : int *tmask, struct pg_tm *tm, fsec_t *fsec)
2665 : {
2666 : struct pg_itm itm;
2667 : int dterr;
2668 :
372 tgl 2669 GIC 30538 : dterr = DecodeTimeCommon(str, fmask, range,
2670 : tmask, &itm);
372 tgl 2671 CBC 30538 : if (dterr)
372 tgl 2672 LBC 0 : return dterr;
2673 :
372 tgl 2674 GIC 30538 : if (itm.tm_hour > INT_MAX)
372 tgl 2675 UIC 0 : return DTERR_FIELD_OVERFLOW;
372 tgl 2676 GIC 30538 : tm->tm_hour = (int) itm.tm_hour;
2677 30538 : tm->tm_min = itm.tm_min;
372 tgl 2678 CBC 30538 : tm->tm_sec = itm.tm_sec;
2679 30538 : *fsec = itm.tm_usec;
2680 :
2681 30538 : return 0;
2682 : }
372 tgl 2683 ECB :
2684 : /* DecodeTimeForInterval()
2685 : * Decode time string which includes delimiters.
2686 : * Return 0 if okay, a DTERR code if not.
2687 : *
2688 : * This version is used for intervals. The results are returned into
2689 : * itm_in->tm_usec.
2690 : */
2691 : static int
372 tgl 2692 CBC 986 : DecodeTimeForInterval(char *str, int fmask, int range,
372 tgl 2693 ECB : int *tmask, struct pg_itm_in *itm_in)
2694 : {
2695 : struct pg_itm itm;
2696 : int dterr;
2697 :
372 tgl 2698 CBC 986 : dterr = DecodeTimeCommon(str, fmask, range,
2699 : tmask, &itm);
372 tgl 2700 GIC 986 : if (dterr)
2701 6 : return dterr;
2702 :
2703 980 : itm_in->tm_usec = itm.tm_usec;
372 tgl 2704 CBC 980 : if (!int64_multiply_add(itm.tm_hour, USECS_PER_HOUR, &itm_in->tm_usec) ||
2705 980 : !int64_multiply_add(itm.tm_min, USECS_PER_MINUTE, &itm_in->tm_usec) ||
372 tgl 2706 GIC 980 : !int64_multiply_add(itm.tm_sec, USECS_PER_SEC, &itm_in->tm_usec))
7165 tgl 2707 CBC 3 : return DTERR_FIELD_OVERFLOW;
2708 :
8453 lockhart 2709 977 : return 0;
2710 : }
9503 scrappy 2711 ECB :
2712 :
8453 lockhart 2713 : /* DecodeNumber()
2714 : * Interpret plain numeric field as a date value in context.
7165 tgl 2715 : * Return 0 if okay, a DTERR code if not.
2716 : */
8340 bruce 2717 : static int
7084 tgl 2718 CBC 97102 : DecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,
2118 tgl 2719 ECB : int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
2720 : {
8453 lockhart 2721 : int val;
2722 : char *cp;
7165 tgl 2723 : int dterr;
2724 :
8453 lockhart 2725 GIC 97102 : *tmask = 0;
9345 bruce 2726 ECB :
6337 bruce 2727 CBC 97102 : errno = 0;
2542 tgl 2728 GBC 97102 : val = strtoint(str, &cp, 10);
6338 tgl 2729 GIC 97102 : if (errno == ERANGE)
6338 tgl 2730 UBC 0 : return DTERR_FIELD_OVERFLOW;
8453 lockhart 2731 GIC 97102 : if (cp == str)
7165 tgl 2732 UBC 0 : return DTERR_BAD_FORMAT;
2733 :
8453 lockhart 2734 GIC 97102 : if (*cp == '.')
2735 : {
2736 : /*
2737 : * More than two digits before decimal point? Then could be a date or
2738 : * a run-together time: 2001.360 20011225 040506.789
7771 lockhart 2739 ECB : */
6530 bruce 2740 LBC 0 : if (cp - str > 2)
2741 : {
7165 tgl 2742 0 : dterr = DecodeNumberField(flen, str,
2743 : (fmask | DTK_DATE_M),
2744 : tmask, tm,
2745 : fsec, is2digits);
7165 tgl 2746 UIC 0 : if (dterr < 0)
2747 0 : return dterr;
7167 2748 0 : return 0;
2749 : }
2750 :
5261 2751 0 : dterr = ParseFractionalSecond(cp, fsec);
2752 0 : if (dterr)
2753 0 : return dterr;
8453 lockhart 2754 ECB : }
7771 lockhart 2755 GIC 97102 : else if (*cp != '\0')
7165 tgl 2756 UIC 0 : return DTERR_BAD_FORMAT;
2757 :
2758 : /* Special case for day of year */
6530 bruce 2759 GIC 97102 : if (flen == 3 && (fmask & DTK_DATE_M) == DTK_M(YEAR) && val >= 1 &&
2760 : val <= 366)
2761 : {
8453 lockhart 2762 27 : *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
8453 lockhart 2763 CBC 27 : tm->tm_yday = val;
2764 : /* tm_mon and tm_mday can't actually be set yet ... */
7194 tgl 2765 GIC 27 : return 0;
2766 : }
2767 :
2768 : /* Switch based on what we have so far */
7194 tgl 2769 CBC 97075 : switch (fmask & DTK_DATE_M)
2770 : {
7194 tgl 2771 GIC 31820 : case 0:
7188 bruce 2772 EUB :
2773 : /*
2774 : * Nothing so far; make a decision about what we think the input
2775 : * is. There used to be lots of heuristics here, but the
2776 : * consensus now is to be paranoid. It *must* be either
2777 : * YYYY-MM-DD (with a more-than-two-digit year field), or the
7194 tgl 2778 ECB : * field order defined by DateOrder.
2779 : */
7194 tgl 2780 CBC 31820 : if (flen >= 3 || DateOrder == DATEORDER_YMD)
7194 tgl 2781 EUB : {
7194 tgl 2782 CBC 30974 : *tmask = DTK_M(YEAR);
7194 tgl 2783 GIC 30974 : tm->tm_year = val;
2784 : }
7194 tgl 2785 CBC 846 : else if (DateOrder == DATEORDER_DMY)
7194 tgl 2786 ECB : {
7194 tgl 2787 GIC 82 : *tmask = DTK_M(DAY);
2788 82 : tm->tm_mday = val;
7194 tgl 2789 ECB : }
2790 : else
2791 : {
7194 tgl 2792 GIC 764 : *tmask = DTK_M(MONTH);
7194 tgl 2793 CBC 764 : tm->tm_mon = val;
2794 : }
7194 tgl 2795 GIC 31820 : break;
2796 :
2797 30920 : case (DTK_M(YEAR)):
2798 : /* Must be at second field of YY-MM-DD */
7194 tgl 2799 CBC 30920 : *tmask = DTK_M(MONTH);
2800 30920 : tm->tm_mon = val;
2801 30920 : break;
7194 tgl 2802 ECB :
7084 tgl 2803 CBC 1622 : case (DTK_M(MONTH)):
2804 1622 : if (haveTextMonth)
7084 tgl 2805 ECB : {
2806 : /*
6385 bruce 2807 : * We are at the first numeric field of a date that included a
2808 : * textual month name. We want to support the variants
2809 : * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
2810 : * inputs. We will also accept MON-DD-YY or DD-MON-YY in
2811 : * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
7084 tgl 2812 : */
7084 tgl 2813 GIC 886 : if (flen >= 3 || DateOrder == DATEORDER_YMD)
2814 : {
7084 tgl 2815 CBC 36 : *tmask = DTK_M(YEAR);
7084 tgl 2816 GIC 36 : tm->tm_year = val;
7084 tgl 2817 ECB : }
2818 : else
2819 : {
7084 tgl 2820 CBC 850 : *tmask = DTK_M(DAY);
2821 850 : tm->tm_mday = val;
7084 tgl 2822 ECB : }
2823 : }
2824 : else
2825 : {
2826 : /* Must be at second field of MM-DD-YY */
7084 tgl 2827 CBC 736 : *tmask = DTK_M(DAY);
7084 tgl 2828 GIC 736 : tm->tm_mday = val;
7084 tgl 2829 EUB : }
7084 tgl 2830 GBC 1622 : break;
7084 tgl 2831 EUB :
7194 tgl 2832 GBC 30959 : case (DTK_M(YEAR) | DTK_M(MONTH)):
7084 2833 30959 : if (haveTextMonth)
2834 : {
7084 tgl 2835 EUB : /* Need to accept DD-MON-YYYY even in YMD mode */
7084 tgl 2836 GIC 63 : if (flen >= 3 && *is2digits)
2837 : {
2838 : /* Guess that first numeric field is day was wrong */
2118 tgl 2839 CBC 15 : *tmask = DTK_M(DAY); /* YEAR is already set */
7084 tgl 2840 GIC 15 : tm->tm_mday = tm->tm_year;
2841 15 : tm->tm_year = val;
2062 peter_e 2842 15 : *is2digits = false;
2843 : }
2844 : else
2845 : {
7084 tgl 2846 48 : *tmask = DTK_M(DAY);
2847 48 : tm->tm_mday = val;
2848 : }
7084 tgl 2849 ECB : }
2850 : else
2851 : {
2852 : /* Must be at third field of YY-MM-DD */
7084 tgl 2853 GIC 30896 : *tmask = DTK_M(DAY);
7084 tgl 2854 CBC 30896 : tm->tm_mday = val;
2855 : }
7194 tgl 2856 GIC 30959 : break;
2857 :
7194 tgl 2858 CBC 73 : case (DTK_M(DAY)):
7194 tgl 2859 ECB : /* Must be at second field of DD-MM-YY */
7194 tgl 2860 GIC 73 : *tmask = DTK_M(MONTH);
7194 tgl 2861 CBC 73 : tm->tm_mon = val;
2862 73 : break;
8411 lockhart 2863 ECB :
7194 tgl 2864 GBC 1675 : case (DTK_M(MONTH) | DTK_M(DAY)):
2865 : /* Must be at third field of DD-MM-YY or MM-DD-YY */
7194 tgl 2866 GIC 1675 : *tmask = DTK_M(YEAR);
7194 tgl 2867 CBC 1675 : tm->tm_year = val;
7194 tgl 2868 GIC 1675 : break;
7194 tgl 2869 ECB :
7167 tgl 2870 CBC 6 : case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
7167 tgl 2871 ECB : /* we have all the date, so it must be a time field */
7165 tgl 2872 GBC 6 : dterr = DecodeNumberField(flen, str, fmask,
7165 tgl 2873 ECB : tmask, tm,
2874 : fsec, is2digits);
7165 tgl 2875 CBC 6 : if (dterr < 0)
2876 6 : return dterr;
7167 tgl 2877 LBC 0 : return 0;
7167 tgl 2878 EUB :
7194 tgl 2879 UIC 0 : default:
2880 : /* Anything else is bogus input */
7165 2881 0 : return DTERR_BAD_FORMAT;
8453 lockhart 2882 ECB : }
2883 :
8397 bruce 2884 : /*
6385 2885 : * When processing a year field, mark it for adjustment if it's only one
2886 : * or two digits.
2887 : */
7194 tgl 2888 GIC 97069 : if (*tmask == DTK_M(YEAR))
7084 tgl 2889 CBC 32685 : *is2digits = (flen <= 2);
2890 :
8453 lockhart 2891 GIC 97069 : return 0;
7194 tgl 2892 ECB : }
9503 scrappy 2893 :
9459 lockhart 2894 :
8453 2895 : /* DecodeNumberField()
7771 2896 : * Interpret numeric string as a concatenated date or time field.
7165 tgl 2897 EUB : * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
2898 : *
7771 lockhart 2899 ECB : * Use the context of previously decoded fields to help with
2900 : * the interpretation.
9459 2901 : */
2902 : static int
8453 lockhart 2903 CBC 245 : DecodeNumberField(int len, char *str, int fmask,
2904 : int *tmask, struct pg_tm *tm, fsec_t *fsec, bool *is2digits)
9459 lockhart 2905 ECB : {
8453 lockhart 2906 EUB : char *cp;
2907 :
7522 bruce 2908 ECB : /*
2909 : * Have a decimal point? Then this is a date or something with a seconds
2910 : * field...
2911 : */
7771 lockhart 2912 GIC 245 : if ((cp = strchr(str, '.')) != NULL)
2913 : {
2914 : /*
2915 : * Can we use ParseFractionalSecond here? Not clear whether trailing
2916 : * junk should be rejected ...
2917 : */
371 tgl 2918 57 : if (cp[1] == '\0')
2919 : {
2920 : /* avoid assuming that strtod will accept "." */
371 tgl 2921 UIC 0 : *fsec = 0;
2922 : }
2923 : else
2924 : {
2925 : double frac;
2926 :
371 tgl 2927 GIC 57 : errno = 0;
2928 57 : frac = strtod(cp, NULL);
2929 57 : if (errno != 0)
371 tgl 2930 UIC 0 : return DTERR_BAD_FORMAT;
371 tgl 2931 GIC 57 : *fsec = rint(frac * 1000000);
2932 : }
5261 tgl 2933 ECB : /* Now truncate off the fraction for further processing */
7771 lockhart 2934 GIC 57 : *cp = '\0';
2935 57 : len = strlen(str);
2936 : }
2937 : /* No decimal point and no complete date yet? */
2938 188 : else if ((fmask & DTK_DATE_M) != DTK_DATE_M)
8453 lockhart 2939 ECB : {
3462 bruce 2940 GIC 122 : if (len >= 6)
8453 lockhart 2941 ECB : {
8453 lockhart 2942 GIC 122 : *tmask = DTK_DATE_M;
3260 bruce 2943 ECB :
3462 2944 : /*
2945 : * Start from end and consider first 2 as Day, next 2 as Month,
2946 : * and the rest as Year.
3462 bruce 2947 EUB : */
3462 bruce 2948 GIC 122 : tm->tm_mday = atoi(str + (len - 2));
3462 bruce 2949 CBC 122 : *(str + (len - 2)) = '\0';
3462 bruce 2950 GIC 122 : tm->tm_mon = atoi(str + (len - 4));
3462 bruce 2951 CBC 122 : *(str + (len - 4)) = '\0';
2952 122 : tm->tm_year = atoi(str);
2953 122 : if ((len - 4) == 2)
2062 peter_e 2954 GIC 9 : *is2digits = true;
2955 :
7771 lockhart 2956 122 : return DTK_DATE;
7771 lockhart 2957 ECB : }
8453 2958 : }
7771 2959 :
2960 : /* not all time fields are specified? */
7771 lockhart 2961 CBC 123 : if ((fmask & DTK_TIME_M) != DTK_TIME_M)
8453 lockhart 2962 ECB : {
7771 2963 : /* hhmmss */
7771 lockhart 2964 GBC 123 : if (len == 6)
2965 : {
7771 lockhart 2966 GIC 117 : *tmask = DTK_TIME_M;
2967 117 : tm->tm_sec = atoi(str + 4);
7771 lockhart 2968 CBC 117 : *(str + 4) = '\0';
2969 117 : tm->tm_min = atoi(str + 2);
7771 lockhart 2970 GIC 117 : *(str + 2) = '\0';
3462 bruce 2971 117 : tm->tm_hour = atoi(str);
2972 :
7771 lockhart 2973 CBC 117 : return DTK_TIME;
2974 : }
2975 : /* hhmm? */
7771 lockhart 2976 GIC 6 : else if (len == 4)
2977 : {
7771 lockhart 2978 UIC 0 : *tmask = DTK_TIME_M;
2979 0 : tm->tm_sec = 0;
2980 0 : tm->tm_min = atoi(str + 2);
2981 0 : *(str + 2) = '\0';
3462 bruce 2982 0 : tm->tm_hour = atoi(str);
2983 :
7771 lockhart 2984 0 : return DTK_TIME;
2985 : }
2986 : }
2987 :
7165 tgl 2988 GIC 6 : return DTERR_BAD_FORMAT;
2989 : }
9459 lockhart 2990 ECB :
2991 :
2992 : /* DecodeTimezone()
2993 : * Interpret string as a numeric timezone.
2994 : *
7165 tgl 2995 : * Return 0 if okay (and set *tzp), a DTERR code if not okay.
2996 : */
3323 alvherre 2997 : int
121 tgl 2998 GNC 20375 : DecodeTimezone(const char *str, int *tzp)
8453 lockhart 2999 ECB : {
3000 : int tz;
3001 : int hr,
3002 : min,
6018 tgl 3003 CBC 20375 : sec = 0;
8453 lockhart 3004 ECB : char *cp;
3005 :
3006 : /* leading character must be "+" or "-" */
7266 tgl 3007 GIC 20375 : if (*str != '+' && *str != '-')
7165 tgl 3008 CBC 30 : return DTERR_BAD_FORMAT;
7266 tgl 3009 ECB :
6337 bruce 3010 CBC 20345 : errno = 0;
2542 tgl 3011 GIC 20345 : hr = strtoint(str + 1, &cp, 10);
6338 3012 20345 : if (errno == ERANGE)
6338 tgl 3013 LBC 0 : return DTERR_TZDISP_OVERFLOW;
3014 :
3015 : /* explicit delimiter? */
8453 lockhart 3016 GIC 20345 : if (*cp == ':')
3017 : {
6337 bruce 3018 804 : errno = 0;
2542 tgl 3019 804 : min = strtoint(cp + 1, &cp, 10);
6338 3020 804 : if (errno == ERANGE)
6338 tgl 3021 UIC 0 : return DTERR_TZDISP_OVERFLOW;
6018 tgl 3022 GIC 804 : if (*cp == ':')
3023 : {
3024 12 : errno = 0;
2542 3025 12 : sec = strtoint(cp + 1, &cp, 10);
6018 3026 12 : if (errno == ERANGE)
6018 tgl 3027 UIC 0 : return DTERR_TZDISP_OVERFLOW;
3028 : }
3029 : }
3030 : /* otherwise, might have run things together... */
6530 bruce 3031 GIC 19541 : else if (*cp == '\0' && strlen(str) > 3)
8453 lockhart 3032 ECB : {
7165 tgl 3033 GIC 36 : min = hr % 100;
3034 36 : hr = hr / 100;
3035 : /* we could, but don't, support a run-together hhmmss format */
3036 : }
3037 : else
8453 lockhart 3038 19505 : min = 0;
3039 :
3040 : /* Range-check the values; see notes in datatype/timestamp.h */
3966 tgl 3041 20345 : if (hr < 0 || hr > MAX_TZDISP_HOUR)
7165 3042 6 : return DTERR_TZDISP_OVERFLOW;
4411 bruce 3043 20339 : if (min < 0 || min >= MINS_PER_HOUR)
7165 tgl 3044 6 : return DTERR_TZDISP_OVERFLOW;
4411 bruce 3045 20333 : if (sec < 0 || sec >= SECS_PER_MINUTE)
6018 tgl 3046 UIC 0 : return DTERR_TZDISP_OVERFLOW;
3047 :
6018 tgl 3048 GIC 20333 : tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec;
8453 lockhart 3049 CBC 20333 : if (*str == '-')
3050 10501 : tz = -tz;
3051 :
8453 lockhart 3052 GIC 20333 : *tzp = -tz;
7165 tgl 3053 ECB :
7165 tgl 3054 CBC 20333 : if (*cp != '\0')
7165 tgl 3055 UBC 0 : return DTERR_BAD_FORMAT;
3056 :
7165 tgl 3057 CBC 20333 : return 0;
3058 : }
3059 :
3097 tgl 3060 ECB :
3061 : /* DecodeTimezoneAbbrev()
3062 : * Interpret string as a timezone abbreviation, if possible.
3063 : *
3064 : * Sets *ftype to an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if
3065 : * string is not any known abbreviation. On success, set *offset and *tz to
3066 : * represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ).
3067 : * Note that full timezone names (such as America/New_York) are not handled
3068 : * here, mostly for historical reasons.
3069 : *
3070 : * The function result is 0 or a DTERR code; in the latter case, *extra
3071 : * is filled as needed. Note that unknown-abbreviation is not considered
3072 : * an error case. Also note that many callers assume that the DTERR code
3073 : * is one that DateTimeParseError does not require "str" or "datatype"
3074 : * strings for.
3075 : *
3076 : * Given string must be lowercased already.
3077 : *
3078 : * Implement a cache lookup since it is likely that dates
3079 : * will be related in format.
3080 : */
3081 : int
121 tgl 3082 GNC 3469 : DecodeTimezoneAbbrev(int field, const char *lowtoken,
3083 : int *ftype, int *offset, pg_tz **tz,
3084 : DateTimeErrorExtra *extra)
3085 : {
3086 : const datetkn *tp;
3087 :
3097 tgl 3088 GIC 3469 : tp = abbrevcache[field];
3089 : /* use strncmp so that we match truncated tokens */
3090 3469 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3091 : {
3092 2629 : if (zoneabbrevtbl)
3097 tgl 3093 CBC 2629 : tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
3097 tgl 3094 GIC 2629 : zoneabbrevtbl->numabbrevs);
3095 : else
3097 tgl 3096 UIC 0 : tp = NULL;
3097 : }
3097 tgl 3098 CBC 3469 : if (tp == NULL)
3099 : {
121 tgl 3100 GNC 2459 : *ftype = UNKNOWN_FIELD;
3097 tgl 3101 CBC 2459 : *offset = 0;
3097 tgl 3102 GIC 2459 : *tz = NULL;
3097 tgl 3103 ECB : }
3104 : else
3105 : {
3097 tgl 3106 GIC 1010 : abbrevcache[field] = tp;
121 tgl 3107 GNC 1010 : *ftype = tp->type;
3108 1010 : if (tp->type == DYNTZ)
3109 : {
3097 tgl 3110 GIC 132 : *offset = 0;
121 tgl 3111 GNC 132 : *tz = FetchDynamicTimeZone(zoneabbrevtbl, tp, extra);
3112 132 : if (*tz == NULL)
121 tgl 3113 UNC 0 : return DTERR_BAD_ZONE_ABBREV;
3097 tgl 3114 ECB : }
3115 : else
3116 : {
3097 tgl 3117 CBC 878 : *offset = tp->value;
3118 878 : *tz = NULL;
3097 tgl 3119 ECB : }
3120 : }
3121 :
121 tgl 3122 GNC 3469 : return 0;
3123 : }
3124 :
3125 :
3126 : /* DecodeSpecial()
3127 : * Decode text string using lookup table.
3128 : *
3129 : * Recognizes the keywords listed in datetktbl.
3130 : * Note: at one time this would also recognize timezone abbreviations,
3131 : * but no more; use DecodeTimezoneAbbrev for that.
3132 : *
3133 : * Given string must be lowercased already.
3134 : *
8453 lockhart 3135 ECB : * Implement a cache lookup since it is likely that dates
3136 : * will be related in format.
3137 : */
3138 : int
121 tgl 3139 GNC 19327 : DecodeSpecial(int field, const char *lowtoken, int *val)
3140 : {
8453 lockhart 3141 ECB : int type;
3142 : const datetkn *tp;
3143 :
6102 tgl 3144 GIC 19327 : tp = datecache[field];
3145 : /* use strncmp so that we match truncated tokens */
3146 19327 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
3147 : {
3097 3148 4334 : tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
3149 : }
8453 lockhart 3150 CBC 19327 : if (tp == NULL)
8453 lockhart 3151 ECB : {
7863 lockhart 3152 CBC 36 : type = UNKNOWN_FIELD;
8453 lockhart 3153 GIC 36 : *val = 0;
3154 : }
3155 : else
3156 : {
6102 tgl 3157 19291 : datecache[field] = tp;
8453 lockhart 3158 19291 : type = tp->type;
3097 tgl 3159 19291 : *val = tp->value;
3160 : }
3161 :
8453 lockhart 3162 19327 : return type;
3163 : }
3164 :
3165 :
3166 : /* DecodeTimezoneName()
3167 : * Interpret string as a timezone abbreviation or name.
3168 : * Throw error if the name is not recognized.
3169 : *
3170 : * The return value indicates what kind of zone identifier it is:
3171 : * TZNAME_FIXED_OFFSET: fixed offset from UTC
3172 : * TZNAME_DYNTZ: dynamic timezone abbreviation
3173 : * TZNAME_ZONE: full tzdb zone name
3174 : *
3175 : * For TZNAME_FIXED_OFFSET, *offset receives the UTC offset (in seconds,
3176 : * with ISO sign convention: positive is east of Greenwich).
3177 : * For the other two cases, *tz receives the timezone struct representing
3178 : * the zone name or the abbreviation's underlying zone.
3179 : */
3180 : int
23 tgl 3181 GNC 243 : DecodeTimezoneName(const char *tzname, int *offset, pg_tz **tz)
3182 : {
3183 : char *lowzone;
3184 : int dterr,
3185 : type;
3186 : DateTimeErrorExtra extra;
3187 :
3188 : /*
3189 : * First we look in the timezone abbreviation table (to handle cases like
3190 : * "EST"), and if that fails, we look in the timezone database (to handle
3191 : * cases like "America/New_York"). This matches the order in which
3192 : * timestamp input checks the cases; it's important because the timezone
3193 : * database unwisely uses a few zone names that are identical to offset
3194 : * abbreviations.
3195 : */
3196 :
3197 : /* DecodeTimezoneAbbrev requires lowercase input */
3198 243 : lowzone = downcase_truncate_identifier(tzname,
3199 243 : strlen(tzname),
3200 : false);
3201 :
3202 243 : dterr = DecodeTimezoneAbbrev(0, lowzone, &type, offset, tz, &extra);
3203 243 : if (dterr)
23 tgl 3204 UNC 0 : DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
3205 :
23 tgl 3206 GNC 243 : if (type == TZ || type == DTZ)
3207 : {
3208 : /* fixed-offset abbreviation, return the offset */
3209 21 : return TZNAME_FIXED_OFFSET;
3210 : }
3211 222 : else if (type == DYNTZ)
3212 : {
3213 : /* dynamic-offset abbreviation, return its referenced timezone */
3214 87 : return TZNAME_DYNTZ;
3215 : }
3216 : else
3217 : {
3218 : /* try it as a full zone name */
3219 135 : *tz = pg_tzset(tzname);
3220 135 : if (*tz == NULL)
3221 6 : ereport(ERROR,
3222 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3223 : errmsg("time zone \"%s\" not recognized", tzname)));
3224 129 : return TZNAME_ZONE;
3225 : }
3226 : }
3227 :
3228 : /* DecodeTimezoneNameToTz()
3229 : * Interpret string as a timezone abbreviation or name.
3230 : * Throw error if the name is not recognized.
3231 : *
3232 : * This is a simple wrapper for DecodeTimezoneName that produces a pg_tz *
3233 : * result in all cases.
3234 : */
3235 : pg_tz *
3236 36 : DecodeTimezoneNameToTz(const char *tzname)
3237 : {
3238 : pg_tz *result;
3239 : int offset;
3240 :
3241 36 : if (DecodeTimezoneName(tzname, &offset, &result) == TZNAME_FIXED_OFFSET)
3242 : {
3243 : /* fixed-offset abbreviation, get a pg_tz descriptor for that */
3244 3 : result = pg_tzset_offset(-offset); /* flip to POSIX sign convention */
3245 : }
3246 36 : return result;
3247 : }
3248 :
3249 :
3250 : /* ClearPgItmIn
3251 : *
3252 : * Zero out a pg_itm_in
3253 : */
5050 bruce 3254 ECB : static inline void
372 tgl 3255 GIC 4920 : ClearPgItmIn(struct pg_itm_in *itm_in)
5261 tgl 3256 ECB : {
372 tgl 3257 GIC 4920 : itm_in->tm_usec = 0;
372 tgl 3258 CBC 4920 : itm_in->tm_mday = 0;
372 tgl 3259 GIC 4920 : itm_in->tm_mon = 0;
372 tgl 3260 CBC 4920 : itm_in->tm_year = 0;
5261 tgl 3261 GIC 4920 : }
5261 tgl 3262 ECB :
3263 :
3264 : /* DecodeInterval()
3265 : * Interpret previously parsed fields for general time interval.
3266 : * Returns 0 if successful, DTERR code if bogus input detected.
3267 : * dtype and itm_in are output parameters.
3268 : *
8453 lockhart 3269 : * Allow "date" field DTK_DATE since this could be just
3270 : * an unsigned floating point number. - thomas 1997-11-16
3271 : *
3272 : * Allow ISO-style time span, with implicit units on number of days
7809 tgl 3273 : * preceding an hh:mm:ss field. - thomas 1998-04-30
8453 lockhart 3274 : */
3275 : int
5324 tgl 3276 CBC 4653 : DecodeInterval(char **field, int *ftype, int nf, int range,
372 tgl 3277 ECB : int *dtype, struct pg_itm_in *itm_in)
9522 scrappy 3278 : {
372 tgl 3279 CBC 4653 : bool force_negative = false;
2062 peter_e 3280 4653 : bool is_before = false;
8453 lockhart 3281 ECB : char *cp;
8453 lockhart 3282 CBC 4653 : int fmask = 0,
3283 : tmask,
372 tgl 3284 ECB : type,
3285 : uval;
3286 : int i;
3287 : int dterr;
3288 : int64 val;
3289 : double fval;
3290 :
8453 lockhart 3291 CBC 4653 : *dtype = DTK_DELTA;
7607 JanWieck 3292 GIC 4653 : type = IGNORE_DTF;
372 tgl 3293 4653 : ClearPgItmIn(itm_in);
3294 :
3295 : /*----------
3296 : * The SQL standard defines the interval literal
372 tgl 3297 ECB : * '-1 1:00:00'
3298 : * to mean "negative 1 days and negative 1 hours", while Postgres
3299 : * traditionally treats this as meaning "negative 1 days and positive
3300 : * 1 hours". In SQL_STANDARD intervalstyle, we apply the leading sign
3301 : * to all fields if there are no other explicit signs.
3302 : *
3303 : * We leave the signs alone if there are additional explicit signs.
3304 : * This protects us against misinterpreting postgres-style dump output,
372 tgl 3305 EUB : * since the postgres-style output code has always put an explicit sign on
372 tgl 3306 ECB : * all fields following a negative field. But note that SQL-spec output
3307 : * is ambiguous and can be misinterpreted on load! (So it's best practice
3308 : * to dump in postgres style, not SQL style.)
3309 : *----------
372 tgl 3310 EUB : */
56 tgl 3311 GBC 4653 : if (IntervalStyle == INTSTYLE_SQL_STANDARD && nf > 0 && *field[0] == '-')
3312 : {
372 tgl 3313 GIC 19 : force_negative = true;
3314 : /* Check for additional explicit signs */
3315 122 : for (i = 1; i < nf; i++)
3316 : {
3317 112 : if (*field[i] == '-' || *field[i] == '+')
372 tgl 3318 ECB : {
372 tgl 3319 CBC 9 : force_negative = false;
372 tgl 3320 GIC 9 : break;
3321 : }
3322 : }
3323 : }
3324 :
3325 : /* read through list backwards to pick up units before values */
8453 lockhart 3326 15088 : for (i = nf - 1; i >= 0; i--)
3327 : {
3328 10945 : switch (ftype[i])
3329 : {
3330 611 : case DTK_TIME:
372 tgl 3331 CBC 611 : dterr = DecodeTimeForInterval(field[i], fmask, range,
3332 : &tmask, itm_in);
7165 tgl 3333 GIC 611 : if (dterr)
3334 9 : return dterr;
372 3335 602 : if (force_negative &&
372 tgl 3336 CBC 1 : itm_in->tm_usec > 0)
3337 1 : itm_in->tm_usec = -itm_in->tm_usec;
8453 lockhart 3338 602 : type = DTK_DAY;
3339 602 : break;
3340 :
3341 1147 : case DTK_TZ:
8053 bruce 3342 ECB :
8453 lockhart 3343 : /*
3901 tgl 3344 : * Timezone means a token with a leading sign character and at
5050 bruce 3345 : * least one digit; there could be ':', '.', '-' embedded in
3346 : * it as well.
3347 : */
6530 bruce 3348 CBC 1147 : Assert(*field[i] == '-' || *field[i] == '+');
8053 bruce 3349 ECB :
3350 : /*
3351 : * Check for signed hh:mm or hh:mm:ss. If so, process exactly
3352 : * like DTK_TIME case above, plus handling the sign.
8189 lockhart 3353 : */
5318 tgl 3354 CBC 1522 : if (strchr(field[i] + 1, ':') != NULL &&
372 3355 375 : DecodeTimeForInterval(field[i] + 1, fmask, range,
3356 : &tmask, itm_in) == 0)
3357 : {
8053 bruce 3358 GIC 375 : if (*field[i] == '-')
8053 bruce 3359 ECB : {
372 tgl 3360 : /* flip the sign on time field */
372 tgl 3361 CBC 345 : if (itm_in->tm_usec == PG_INT64_MIN)
372 tgl 3362 LBC 0 : return DTERR_FIELD_OVERFLOW;
372 tgl 3363 CBC 345 : itm_in->tm_usec = -itm_in->tm_usec;
3364 : }
3365 :
372 tgl 3366 GIC 375 : if (force_negative &&
372 tgl 3367 LBC 0 : itm_in->tm_usec > 0)
3368 0 : itm_in->tm_usec = -itm_in->tm_usec;
372 tgl 3369 ECB :
8053 bruce 3370 : /*
3371 : * Set the next type to be a day, if units are not
6385 3372 : * specified. This handles the case of '1 +02:03' since we
3373 : * are reading right to left.
3374 : */
8189 lockhart 3375 GIC 375 : type = DTK_DAY;
3376 375 : break;
8053 bruce 3377 ECB : }
3901 tgl 3378 :
3901 tgl 3379 EUB : /*
3901 tgl 3380 ECB : * Otherwise, fall through to DTK_NUMBER case, which can
3901 tgl 3381 EUB : * handle signed float numbers and signed year-month values.
3901 tgl 3382 ECB : */
3383 :
1804 3384 : /* FALLTHROUGH */
8189 lockhart 3385 :
8453 lockhart 3386 EUB : case DTK_DATE:
8453 lockhart 3387 ECB : case DTK_NUMBER:
5324 tgl 3388 GBC 5178 : if (type == IGNORE_DTF)
5324 tgl 3389 ECB : {
3390 : /* use typmod to decide what rightmost field is */
3391 : switch (range)
3392 : {
5324 tgl 3393 CBC 3 : case INTERVAL_MASK(YEAR):
3394 3 : type = DTK_YEAR;
5324 tgl 3395 GBC 3 : break;
5324 tgl 3396 CBC 15 : case INTERVAL_MASK(MONTH):
5324 tgl 3397 ECB : case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
5324 tgl 3398 GIC 15 : type = DTK_MONTH;
5324 tgl 3399 CBC 15 : break;
3400 9 : case INTERVAL_MASK(DAY):
5324 tgl 3401 GIC 9 : type = DTK_DAY;
5324 tgl 3402 CBC 9 : break;
5324 tgl 3403 GIC 12 : case INTERVAL_MASK(HOUR):
5324 tgl 3404 ECB : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
5324 tgl 3405 GIC 12 : type = DTK_HOUR;
5324 tgl 3406 CBC 12 : break;
5324 tgl 3407 GIC 12 : case INTERVAL_MASK(MINUTE):
3408 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
5051 tgl 3409 ECB : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
5324 tgl 3410 CBC 12 : type = DTK_MINUTE;
3411 12 : break;
3412 30 : case INTERVAL_MASK(SECOND):
3413 : case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3414 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
3415 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
5324 tgl 3416 GIC 30 : type = DTK_SECOND;
5324 tgl 3417 CBC 30 : break;
3418 219 : default:
3419 219 : type = DTK_SECOND;
3420 219 : break;
5324 tgl 3421 ECB : }
3422 : }
3423 :
6337 bruce 3424 CBC 5178 : errno = 0;
372 tgl 3425 5178 : val = strtoi64(field[i], &cp, 10);
6338 3426 5178 : if (errno == ERANGE)
3427 6 : return DTERR_FIELD_OVERFLOW;
3428 :
5324 3429 5172 : if (*cp == '-')
5324 tgl 3430 ECB : {
3431 : /* SQL "years-months" syntax */
3432 : int val2;
3433 :
2542 tgl 3434 GIC 30 : val2 = strtoint(cp + 1, &cp, 10);
5324 3435 30 : if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR)
5324 tgl 3436 UIC 0 : return DTERR_FIELD_OVERFLOW;
5324 tgl 3437 CBC 30 : if (*cp != '\0')
5324 tgl 3438 LBC 0 : return DTERR_BAD_FORMAT;
5324 tgl 3439 GIC 30 : type = DTK_MONTH;
5265 tgl 3440 CBC 30 : if (*field[i] == '-')
5318 3441 3 : val2 = -val2;
372 tgl 3442 GIC 30 : if (pg_mul_s64_overflow(val, MONTHS_PER_YEAR, &val))
372 tgl 3443 LBC 0 : return DTERR_FIELD_OVERFLOW;
372 tgl 3444 CBC 30 : if (pg_add_s64_overflow(val, val2, &val))
3356 bruce 3445 LBC 0 : return DTERR_FIELD_OVERFLOW;
5324 tgl 3446 CBC 30 : fval = 0;
5324 tgl 3447 ECB : }
5324 tgl 3448 GIC 5142 : else if (*cp == '.')
8453 lockhart 3449 ECB : {
371 tgl 3450 CBC 243 : dterr = ParseFraction(cp, &fval);
3451 243 : if (dterr)
371 tgl 3452 LBC 0 : return dterr;
7053 tgl 3453 CBC 243 : if (*field[i] == '-')
6529 bruce 3454 69 : fval = -fval;
3455 : }
8453 lockhart 3456 4899 : else if (*cp == '\0')
3457 4707 : fval = 0;
8453 lockhart 3458 ECB : else
7165 tgl 3459 CBC 192 : return DTERR_BAD_FORMAT;
8453 lockhart 3460 ECB :
8453 lockhart 3461 CBC 4980 : tmask = 0; /* DTK_M(type); */
3462 :
372 tgl 3463 4980 : if (force_negative)
372 tgl 3464 ECB : {
3465 : /* val and fval should be of same sign, but test anyway */
372 tgl 3466 CBC 40 : if (val > 0)
3467 30 : val = -val;
3468 40 : if (fval > 0)
372 tgl 3469 GIC 9 : fval = -fval;
372 tgl 3470 ECB : }
3471 :
8453 lockhart 3472 : switch (type)
3473 : {
8453 lockhart 3474 CBC 93 : case DTK_MICROSEC:
372 tgl 3475 93 : if (!AdjustMicroseconds(val, fval, 1, itm_in))
372 tgl 3476 GIC 18 : return DTERR_FIELD_OVERFLOW;
5794 neilc 3477 CBC 75 : tmask = DTK_M(MICROSECOND);
8453 lockhart 3478 75 : break;
8453 lockhart 3479 ECB :
8453 lockhart 3480 CBC 51 : case DTK_MILLISEC:
372 tgl 3481 51 : if (!AdjustMicroseconds(val, fval, 1000, itm_in))
3482 6 : return DTERR_FIELD_OVERFLOW;
5794 neilc 3483 GIC 45 : tmask = DTK_M(MILLISECOND);
8453 lockhart 3484 CBC 45 : break;
8453 lockhart 3485 ECB :
8453 lockhart 3486 CBC 297 : case DTK_SECOND:
372 tgl 3487 297 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3488 6 : return DTERR_FIELD_OVERFLOW;
5624 bruce 3489 ECB :
3490 : /*
3491 : * If any subseconds were specified, consider this
3492 : * microsecond and millisecond input as well.
5794 neilc 3493 : */
5794 neilc 3494 CBC 291 : if (fval == 0)
3495 210 : tmask = DTK_M(SECOND);
5794 neilc 3496 ECB : else
5794 neilc 3497 GIC 81 : tmask = DTK_ALL_SECS_M;
8453 lockhart 3498 CBC 291 : break;
8453 lockhart 3499 ECB :
8453 lockhart 3500 CBC 167 : case DTK_MINUTE:
372 tgl 3501 167 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3502 6 : return DTERR_FIELD_OVERFLOW;
8453 lockhart 3503 161 : tmask = DTK_M(MINUTE);
8453 lockhart 3504 GIC 161 : break;
8453 lockhart 3505 EUB :
8453 lockhart 3506 GBC 304 : case DTK_HOUR:
372 tgl 3507 GIC 304 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
372 tgl 3508 CBC 6 : return DTERR_FIELD_OVERFLOW;
8453 lockhart 3509 GIC 298 : tmask = DTK_M(HOUR);
5050 bruce 3510 CBC 298 : type = DTK_DAY; /* set for next field */
8453 lockhart 3511 GIC 298 : break;
8453 lockhart 3512 ECB :
8453 lockhart 3513 CBC 3232 : case DTK_DAY:
372 tgl 3514 GBC 3232 : if (!AdjustDays(val, 1, itm_in) ||
372 tgl 3515 GIC 3196 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
372 tgl 3516 CBC 45 : return DTERR_FIELD_OVERFLOW;
5060 tgl 3517 GIC 3187 : tmask = DTK_M(DAY);
8453 lockhart 3518 3187 : break;
8453 lockhart 3519 ECB :
8453 lockhart 3520 CBC 48 : case DTK_WEEK:
372 tgl 3521 48 : if (!AdjustDays(val, 7, itm_in) ||
372 tgl 3522 GIC 36 : !AdjustFractDays(fval, 7, itm_in))
372 tgl 3523 CBC 24 : return DTERR_FIELD_OVERFLOW;
5060 3524 24 : tmask = DTK_M(WEEK);
8453 lockhart 3525 24 : break;
8453 lockhart 3526 ECB :
8453 lockhart 3527 GIC 357 : case DTK_MONTH:
372 tgl 3528 GBC 357 : if (!AdjustMonths(val, itm_in) ||
3529 327 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3530 42 : return DTERR_FIELD_OVERFLOW;
8453 lockhart 3531 315 : tmask = DTK_M(MONTH);
8453 lockhart 3532 GIC 315 : break;
8453 lockhart 3533 ECB :
8453 lockhart 3534 CBC 332 : case DTK_YEAR:
372 tgl 3535 GIC 332 : if (!AdjustYears(val, 1, itm_in) ||
372 tgl 3536 CBC 308 : !AdjustFractYears(fval, 1, itm_in))
372 tgl 3537 GIC 30 : return DTERR_FIELD_OVERFLOW;
5060 tgl 3538 GBC 302 : tmask = DTK_M(YEAR);
8453 lockhart 3539 302 : break;
3540 :
8453 lockhart 3541 GIC 33 : case DTK_DECADE:
372 tgl 3542 CBC 33 : if (!AdjustYears(val, 10, itm_in) ||
3543 21 : !AdjustFractYears(fval, 10, itm_in))
3544 18 : return DTERR_FIELD_OVERFLOW;
5060 tgl 3545 GIC 15 : tmask = DTK_M(DECADE);
8453 lockhart 3546 15 : break;
3547 :
8453 lockhart 3548 CBC 33 : case DTK_CENTURY:
372 tgl 3549 33 : if (!AdjustYears(val, 100, itm_in) ||
372 tgl 3550 GIC 21 : !AdjustFractYears(fval, 100, itm_in))
3551 18 : return DTERR_FIELD_OVERFLOW;
5060 tgl 3552 CBC 15 : tmask = DTK_M(CENTURY);
8453 lockhart 3553 GIC 15 : break;
8453 lockhart 3554 ECB :
8395 lockhart 3555 CBC 33 : case DTK_MILLENNIUM:
372 tgl 3556 33 : if (!AdjustYears(val, 1000, itm_in) ||
3557 21 : !AdjustFractYears(fval, 1000, itm_in))
3558 18 : return DTERR_FIELD_OVERFLOW;
5060 tgl 3559 GIC 15 : tmask = DTK_M(MILLENNIUM);
8453 lockhart 3560 CBC 15 : break;
8453 lockhart 3561 ECB :
8453 lockhart 3562 LBC 0 : default:
7165 tgl 3563 0 : return DTERR_BAD_FORMAT;
3564 : }
8453 lockhart 3565 GIC 4743 : break;
8453 lockhart 3566 ECB :
8453 lockhart 3567 GIC 4781 : case DTK_STRING:
3568 : case DTK_SPECIAL:
372 tgl 3569 4781 : type = DecodeUnits(i, field[i], &uval);
7607 JanWieck 3570 4781 : if (type == IGNORE_DTF)
8453 lockhart 3571 UIC 0 : continue;
3572 :
8453 lockhart 3573 GIC 4781 : tmask = 0; /* DTK_M(type); */
3574 : switch (type)
3575 : {
3576 4724 : case UNITS:
372 tgl 3577 4724 : type = uval;
8453 lockhart 3578 CBC 4724 : break;
3579 :
8453 lockhart 3580 GIC 39 : case AGO:
2062 peter_e 3581 39 : is_before = true;
372 tgl 3582 39 : type = uval;
8453 lockhart 3583 39 : break;
3584 :
8453 lockhart 3585 UIC 0 : case RESERV:
4633 tgl 3586 0 : tmask = (DTK_DATE_M | DTK_TIME_M);
372 3587 0 : *dtype = uval;
8453 lockhart 3588 0 : break;
3589 :
8453 lockhart 3590 GIC 18 : default:
7165 tgl 3591 18 : return DTERR_BAD_FORMAT;
3592 : }
8453 lockhart 3593 4763 : break;
8453 lockhart 3594 ECB :
8453 lockhart 3595 UBC 0 : default:
7165 tgl 3596 LBC 0 : return DTERR_BAD_FORMAT;
8453 lockhart 3597 ECB : }
3598 :
8453 lockhart 3599 CBC 10483 : if (tmask & fmask)
7165 tgl 3600 48 : return DTERR_BAD_FORMAT;
8453 lockhart 3601 GIC 10435 : fmask |= tmask;
8453 lockhart 3602 ECB : }
9522 scrappy 3603 EUB :
3604 : /* ensure that at least one time field has been found */
5264 tgl 3605 CBC 4143 : if (fmask == 0)
3606 3 : return DTERR_BAD_FORMAT;
3607 :
5264 tgl 3608 ECB : /* finally, AGO negates everything */
8453 lockhart 3609 CBC 4140 : if (is_before)
3610 : {
372 tgl 3611 21 : if (itm_in->tm_usec == PG_INT64_MIN ||
3612 15 : itm_in->tm_mday == INT_MIN ||
372 tgl 3613 GIC 12 : itm_in->tm_mon == INT_MIN ||
3614 9 : itm_in->tm_year == INT_MIN)
3615 12 : return DTERR_FIELD_OVERFLOW;
3616 :
3617 9 : itm_in->tm_usec = -itm_in->tm_usec;
3618 9 : itm_in->tm_mday = -itm_in->tm_mday;
3619 9 : itm_in->tm_mon = -itm_in->tm_mon;
372 tgl 3620 CBC 9 : itm_in->tm_year = -itm_in->tm_year;
3621 : }
3622 :
7165 3623 4128 : return 0;
7165 tgl 3624 ECB : }
9522 scrappy 3625 :
3626 :
3627 : /*
3628 : * Helper functions to avoid duplicated code in DecodeISO8601Interval.
3629 : *
3630 : * Parse a decimal value and break it into integer and fractional parts.
3631 : * Set *endptr to end+1 of the parsed substring.
3632 : * Returns 0 or DTERR code.
3633 : */
3634 : static int
372 tgl 3635 GIC 477 : ParseISO8601Number(char *str, char **endptr, int64 *ipart, double *fpart)
3636 : {
3637 : double val;
3638 :
3639 : /*
3640 : * Historically this has accepted anything that strtod() would take,
3641 : * notably including "e" notation, so continue doing that. This is
3642 : * slightly annoying because the precision of double is less than that of
3643 : * int64, so we would lose accuracy for inputs larger than 2^53 or so.
3644 : * However, historically we rejected inputs outside the int32 range,
3645 : * making that concern moot. What we do now is reject abs(val) above
3646 : * 1.0e15 (a round number a bit less than 2^50), so that any accepted
48 tgl 3647 ECB : * value will have an exact integer part, and thereby a fraction part with
3648 : * abs(*fpart) less than 1. In the absence of field complaints it doesn't
3649 : * seem worth working harder.
3650 : */
48 tgl 3651 CBC 477 : if (!(isdigit((unsigned char) *str) || *str == '-' || *str == '.'))
48 tgl 3652 UIC 0 : return DTERR_BAD_FORMAT;
5262 tgl 3653 CBC 477 : errno = 0;
48 3654 477 : val = strtod(str, endptr);
3655 : /* did we not see anything that looks like a double? */
5262 3656 477 : if (*endptr == str || errno != 0)
3657 3 : return DTERR_BAD_FORMAT;
3658 : /* watch out for overflow, including infinities; reject NaN too */
48 3659 474 : if (isnan(val) || val < -1.0e15 || val > 1.0e15)
48 tgl 3660 LBC 0 : return DTERR_FIELD_OVERFLOW;
3661 : /* be very sure we truncate towards zero (cf dtrunc()) */
48 tgl 3662 GIC 474 : if (val >= 0)
3663 366 : *ipart = (int64) floor(val);
3664 : else
3665 108 : *ipart = (int64) -floor(-val);
3666 474 : *fpart = val - *ipart;
3667 : /* Callers expect this to hold */
48 tgl 3668 CBC 474 : Assert(*fpart > -1.0 && *fpart < 1.0);
5262 tgl 3669 GIC 474 : return 0;
5262 tgl 3670 ECB : }
3671 :
3672 : /*
3673 : * Determine number of integral digits in a valid ISO 8601 number field
3674 : * (we should ignore sign and any fraction part)
3675 : */
3676 : static int
5262 tgl 3677 CBC 33 : ISO8601IntegerWidth(char *fieldstart)
5262 tgl 3678 ECB : {
3679 : /* We might have had a leading '-' */
5262 tgl 3680 GIC 33 : if (*fieldstart == '-')
3681 9 : fieldstart++;
3682 33 : return strspn(fieldstart, "0123456789");
3683 : }
3684 :
5262 tgl 3685 ECB :
3686 : /* DecodeISO8601Interval()
5050 bruce 3687 : * Decode an ISO 8601 time interval of the "format with designators"
3688 : * (section 4.4.3.2) or "alternative format" (section 4.4.3.3)
3689 : * Examples: P1D for 1 day
3690 : * PT1H for 1 hour
3691 : * P2Y6M7DT1H30M for 2 years, 6 months, 7 days 1 hour 30 min
3692 : * P0002-06-07T01:30:00 the same value in alternative format
5262 tgl 3693 : *
3694 : * Returns 0 if successful, DTERR code if bogus input detected.
3695 : * Note: error code should be DTERR_BAD_FORMAT if input doesn't look like
3696 : * ISO8601, otherwise this could cause unexpected error messages.
372 3697 : * dtype and itm_in are output parameters.
5262 3698 : *
5050 bruce 3699 : * A couple exceptions from the spec:
3700 : * - a week field ('W') may coexist with other units
3701 : * - allows decimals in fields other than the least significant unit.
5262 tgl 3702 : */
3703 : int
5262 tgl 3704 CBC 267 : DecodeISO8601Interval(char *str,
372 tgl 3705 ECB : int *dtype, struct pg_itm_in *itm_in)
5262 3706 : {
5050 bruce 3707 CBC 267 : bool datepart = true;
3708 267 : bool havefield = false;
5262 tgl 3709 ECB :
5262 tgl 3710 CBC 267 : *dtype = DTK_DELTA;
372 3711 267 : ClearPgItmIn(itm_in);
3712 :
5262 3713 267 : if (strlen(str) < 2 || str[0] != 'P')
5262 tgl 3714 GIC 75 : return DTERR_BAD_FORMAT;
5262 tgl 3715 ECB :
5262 tgl 3716 CBC 192 : str++;
3717 561 : while (*str)
5262 tgl 3718 ECB : {
5050 bruce 3719 EUB : char *fieldstart;
372 tgl 3720 ECB : int64 val;
5050 bruce 3721 EUB : double fval;
5050 bruce 3722 ECB : char unit;
3723 : int dterr;
5262 tgl 3724 :
5050 bruce 3725 GIC 507 : if (*str == 'T') /* T indicates the beginning of the time part */
3726 : {
5262 tgl 3727 99 : datepart = false;
3728 99 : havefield = false;
3729 99 : str++;
5262 tgl 3730 CBC 120 : continue;
5262 tgl 3731 EUB : }
3732 :
5262 tgl 3733 CBC 408 : fieldstart = str;
3734 408 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3735 408 : if (dterr)
3736 138 : return dterr;
5262 tgl 3737 ECB :
3738 : /*
3739 : * Note: we could step off the end of the string here. Code below
3740 : * *must* exit the loop if unit == '\0'.
3741 : */
5262 tgl 3742 CBC 405 : unit = *str++;
3743 :
5262 tgl 3744 GIC 405 : if (datepart)
5262 tgl 3745 ECB : {
5050 bruce 3746 CBC 234 : switch (unit) /* before T: Y M W D */
5262 tgl 3747 EUB : {
5262 tgl 3748 CBC 42 : case 'Y':
372 3749 42 : if (!AdjustYears(val, 1, itm_in) ||
3750 42 : !AdjustFractYears(fval, 1, itm_in))
3751 6 : return DTERR_FIELD_OVERFLOW;
5262 3752 36 : break;
3753 54 : case 'M':
372 tgl 3754 GIC 54 : if (!AdjustMonths(val, itm_in) ||
372 tgl 3755 CBC 48 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
3756 12 : return DTERR_FIELD_OVERFLOW;
5262 3757 42 : break;
5262 tgl 3758 GIC 27 : case 'W':
372 tgl 3759 CBC 27 : if (!AdjustDays(val, 7, itm_in) ||
372 tgl 3760 GBC 21 : !AdjustFractDays(fval, 7, itm_in))
372 tgl 3761 CBC 12 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3762 GIC 15 : break;
5262 tgl 3763 CBC 66 : case 'D':
372 3764 66 : if (!AdjustDays(val, 1, itm_in) ||
372 tgl 3765 GBC 48 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
372 tgl 3766 CBC 18 : return DTERR_FIELD_OVERFLOW;
5262 3767 48 : break;
5050 bruce 3768 15 : case 'T': /* ISO 8601 4.4.3.3 Alternative Format / Basic */
5262 tgl 3769 ECB : case '\0':
5262 tgl 3770 CBC 15 : if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
5262 tgl 3771 ECB : {
372 tgl 3772 GIC 3 : if (!AdjustYears(val / 10000, 1, itm_in) ||
372 tgl 3773 CBC 3 : !AdjustMonths((val / 100) % 100, itm_in) ||
3774 3 : !AdjustDays(val % 100, 1, itm_in) ||
3775 3 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
372 tgl 3776 UIC 0 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3777 GBC 3 : if (unit == '\0')
5262 tgl 3778 UBC 0 : return 0;
5262 tgl 3779 GIC 3 : datepart = false;
5262 tgl 3780 GBC 3 : havefield = false;
5262 tgl 3781 GIC 3 : continue;
3782 : }
3783 : /* Else fall through to extended alternative format */
3784 : /* FALLTHROUGH */
5050 bruce 3785 ECB : case '-': /* ISO 8601 4.4.3.3 Alternative Format,
3786 : * Extended */
5262 tgl 3787 CBC 42 : if (havefield)
5262 tgl 3788 LBC 0 : return DTERR_BAD_FORMAT;
5262 tgl 3789 ECB :
372 tgl 3790 CBC 42 : if (!AdjustYears(val, 1, itm_in) ||
3791 36 : !AdjustFractYears(fval, 1, itm_in))
3792 6 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3793 GBC 36 : if (unit == '\0')
5262 tgl 3794 CBC 3 : return 0;
3795 33 : if (unit == 'T')
5262 tgl 3796 ECB : {
5262 tgl 3797 CBC 3 : datepart = false;
3798 3 : havefield = false;
3799 3 : continue;
5262 tgl 3800 ECB : }
3801 :
5262 tgl 3802 CBC 30 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3803 30 : if (dterr)
5262 tgl 3804 LBC 0 : return dterr;
372 tgl 3805 CBC 30 : if (!AdjustMonths(val, itm_in) ||
372 tgl 3806 GBC 27 : !AdjustFractDays(fval, DAYS_PER_MONTH, itm_in))
372 tgl 3807 CBC 3 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3808 GIC 27 : if (*str == '\0')
3809 3 : return 0;
3810 24 : if (*str == 'T')
3811 : {
3812 3 : datepart = false;
5262 tgl 3813 CBC 3 : havefield = false;
5262 tgl 3814 GBC 3 : continue;
3815 : }
5262 tgl 3816 CBC 21 : if (*str != '-')
5262 tgl 3817 LBC 0 : return DTERR_BAD_FORMAT;
5262 tgl 3818 CBC 21 : str++;
5050 bruce 3819 ECB :
5262 tgl 3820 GIC 21 : dterr = ParseISO8601Number(str, &str, &val, &fval);
5262 tgl 3821 CBC 21 : if (dterr)
5262 tgl 3822 LBC 0 : return dterr;
372 tgl 3823 GBC 21 : if (!AdjustDays(val, 1, itm_in) ||
372 tgl 3824 CBC 18 : !AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in))
3825 3 : return DTERR_FIELD_OVERFLOW;
5262 3826 18 : if (*str == '\0')
3827 6 : return 0;
3828 12 : if (*str == 'T')
5262 tgl 3829 EUB : {
5262 tgl 3830 CBC 12 : datepart = false;
5262 tgl 3831 GIC 12 : havefield = false;
5262 tgl 3832 CBC 12 : continue;
5262 tgl 3833 ECB : }
5262 tgl 3834 UBC 0 : return DTERR_BAD_FORMAT;
5262 tgl 3835 LBC 0 : default:
5262 tgl 3836 EUB : /* not a valid date unit suffix */
5262 tgl 3837 LBC 0 : return DTERR_BAD_FORMAT;
5262 tgl 3838 ECB : }
5262 tgl 3839 EUB : }
3840 : else
3841 : {
5050 bruce 3842 GIC 171 : switch (unit) /* after T: H M S */
5262 tgl 3843 EUB : {
5262 tgl 3844 GIC 54 : case 'H':
372 3845 54 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
3846 18 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3847 CBC 36 : break;
5262 tgl 3848 GIC 30 : case 'M':
372 3849 30 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
372 tgl 3850 LBC 0 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3851 GIC 30 : break;
3852 48 : case 'S':
372 3853 48 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
3854 6 : return DTERR_FIELD_OVERFLOW;
5262 3855 42 : break;
5050 bruce 3856 18 : case '\0': /* ISO 8601 4.4.3.3 Alternative Format */
3857 18 : if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
3858 : {
372 tgl 3859 3 : if (!AdjustMicroseconds(val / 10000, 0, USECS_PER_HOUR, itm_in) ||
3860 3 : !AdjustMicroseconds((val / 100) % 100, 0, USECS_PER_MINUTE, itm_in) ||
3861 3 : !AdjustMicroseconds(val % 100, 0, USECS_PER_SEC, itm_in) ||
3862 3 : !AdjustFractMicroseconds(fval, 1, itm_in))
372 tgl 3863 UIC 0 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3864 GIC 3 : return 0;
5262 tgl 3865 ECB : }
3866 : /* Else fall through to extended alternative format */
3867 : /* FALLTHROUGH */
3868 : case ':': /* ISO 8601 4.4.3.3 Alternative Format,
3869 : * Extended */
5262 tgl 3870 CBC 36 : if (havefield)
5262 tgl 3871 UIC 0 : return DTERR_BAD_FORMAT;
5262 tgl 3872 ECB :
372 tgl 3873 GIC 36 : if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in))
372 tgl 3874 CBC 15 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3875 GIC 21 : if (unit == '\0')
5262 tgl 3876 CBC 9 : return 0;
3877 :
3878 12 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3879 12 : if (dterr)
5262 tgl 3880 UIC 0 : return dterr;
372 tgl 3881 GIC 12 : if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in))
3882 3 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3883 CBC 9 : if (*str == '\0')
3884 3 : return 0;
3885 6 : if (*str != ':')
5262 tgl 3886 UIC 0 : return DTERR_BAD_FORMAT;
5262 tgl 3887 GIC 6 : str++;
5050 bruce 3888 ECB :
5262 tgl 3889 GIC 6 : dterr = ParseISO8601Number(str, &str, &val, &fval);
3890 6 : if (dterr)
5262 tgl 3891 UIC 0 : return dterr;
372 tgl 3892 GIC 6 : if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in))
372 tgl 3893 UIC 0 : return DTERR_FIELD_OVERFLOW;
5262 tgl 3894 GIC 6 : if (*str == '\0')
3895 6 : return 0;
5262 tgl 3896 UIC 0 : return DTERR_BAD_FORMAT;
3897 :
3898 0 : default:
3899 : /* not a valid time unit suffix */
3900 0 : return DTERR_BAD_FORMAT;
3901 : }
3902 : }
3903 :
5262 tgl 3904 GIC 249 : havefield = true;
3905 : }
3906 :
3907 54 : return 0;
3908 : }
3909 :
5262 tgl 3910 ECB :
3911 : /* DecodeUnits()
3912 : * Decode text string using lookup table.
3913 : *
3097 3914 : * This routine recognizes keywords associated with time interval units.
3915 : *
3916 : * Given string must be lowercased already.
3917 : *
3918 : * Implement a cache lookup since it is likely that dates
3919 : * will be related in format.
3920 : */
8453 lockhart 3921 : int
121 tgl 3922 GNC 31150 : DecodeUnits(int field, const char *lowtoken, int *val)
3923 : {
8453 lockhart 3924 ECB : int type;
3925 : const datetkn *tp;
3926 :
6102 tgl 3927 GIC 31150 : tp = deltacache[field];
3928 : /* use strncmp so that we match truncated tokens */
6102 tgl 3929 GBC 31150 : if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
6102 tgl 3930 ECB : {
8453 lockhart 3931 CBC 25801 : tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
3932 : }
8453 lockhart 3933 GIC 31150 : if (tp == NULL)
3934 : {
7863 lockhart 3935 GBC 16949 : type = UNKNOWN_FIELD;
8453 lockhart 3936 CBC 16949 : *val = 0;
8453 lockhart 3937 ECB : }
3938 : else
3939 : {
6102 tgl 3940 GIC 14201 : deltacache[field] = tp;
8453 lockhart 3941 GBC 14201 : type = tp->type;
3097 tgl 3942 CBC 14201 : *val = tp->value;
8453 lockhart 3943 ECB : }
3944 :
8453 lockhart 3945 GIC 31150 : return type;
3946 : } /* DecodeUnits() */
9522 scrappy 3947 ECB :
7165 tgl 3948 EUB : /*
3949 : * Report an error detected by one of the datetime input processing routines.
3950 : *
3951 : * dterr is the error code, and *extra contains any auxiliary info we need
3952 : * for the error report. extra can be NULL if not needed for the particular
3953 : * dterr value.
3954 : *
3955 : * str is the original input string, and datatype is the name of the datatype
3956 : * we were trying to accept. (For some DTERR codes, these are not used and
3957 : * can be NULL.)
3958 : *
3959 : * If escontext points to an ErrorSaveContext node, that is filled instead
3960 : * of throwing an error.
3961 : *
3962 : * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
3963 : * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
7165 tgl 3964 ECB : * separate SQLSTATE codes, so ...
3965 : */
3966 : void
121 tgl 3967 GNC 801 : DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
3968 : const char *str, const char *datatype,
3969 : Node *escontext)
3970 : {
7165 tgl 3971 GIC 801 : switch (dterr)
7165 tgl 3972 ECB : {
7165 tgl 3973 GIC 87 : case DTERR_FIELD_OVERFLOW:
121 tgl 3974 GNC 87 : errsave(escontext,
3975 : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
3976 : errmsg("date/time field value out of range: \"%s\"",
3977 : str)));
7165 tgl 3978 GIC 12 : break;
3979 90 : case DTERR_MD_FIELD_OVERFLOW:
3980 : /* <nanny>same as above, but add hint about DateStyle</nanny> */
121 tgl 3981 GNC 90 : errsave(escontext,
3982 : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
7165 tgl 3983 ECB : errmsg("date/time field value out of range: \"%s\"",
3984 : str),
2118 3985 : errhint("Perhaps you need a different \"datestyle\" setting.")));
7165 tgl 3986 UIC 0 : break;
7165 tgl 3987 GIC 360 : case DTERR_INTERVAL_OVERFLOW:
121 tgl 3988 GNC 360 : errsave(escontext,
7165 tgl 3989 ECB : (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
3990 : errmsg("interval field value out of range: \"%s\"",
3991 : str)));
7165 tgl 3992 UIC 0 : break;
7165 tgl 3993 CBC 6 : case DTERR_TZDISP_OVERFLOW:
121 tgl 3994 GNC 6 : errsave(escontext,
3995 : (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
3996 : errmsg("time zone displacement out of range: \"%s\"",
6385 bruce 3997 ECB : str)));
7165 tgl 3998 LBC 0 : break;
121 tgl 3999 GNC 18 : case DTERR_BAD_TIMEZONE:
4000 18 : errsave(escontext,
4001 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4002 : errmsg("time zone \"%s\" not recognized",
4003 : extra->dtee_timezone)));
4004 12 : break;
121 tgl 4005 UNC 0 : case DTERR_BAD_ZONE_ABBREV:
4006 0 : errsave(escontext,
4007 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
4008 : errmsg("time zone \"%s\" not recognized",
4009 : extra->dtee_timezone),
4010 : errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".",
4011 : extra->dtee_abbrev)));
4012 0 : break;
7165 tgl 4013 CBC 240 : case DTERR_BAD_FORMAT:
4014 : default:
121 tgl 4015 GNC 240 : errsave(escontext,
7165 tgl 4016 ECB : (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
4017 : errmsg("invalid input syntax for type %s: \"%s\"",
4018 : datatype, str)));
7165 tgl 4019 GIC 42 : break;
4020 : }
7165 tgl 4021 CBC 66 : }
4022 :
4023 : /* datebsearch()
4024 : * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
4025 : * is WAY faster than the generic bsearch().
4026 : */
4027 : static const datetkn *
6102 tgl 4028 GIC 33670 : datebsearch(const char *key, const datetkn *base, int nel)
4029 : {
4352 4030 33670 : if (nel > 0)
8453 lockhart 4031 ECB : {
4352 tgl 4032 GIC 33670 : const datetkn *last = base + nel - 1,
4033 : *position;
4034 : int result;
4035 :
4036 212330 : while (last >= base)
8453 lockhart 4037 ECB : {
4352 tgl 4038 CBC 192133 : position = base + ((last - base) >> 1);
3097 tgl 4039 ECB : /* precheck the first character for a bit of extra speed */
3097 tgl 4040 CBC 192133 : result = (int) key[0] - (int) position->token[0];
8453 lockhart 4041 192133 : if (result == 0)
4042 : {
4043 : /* use strncmp so that we match truncated tokens */
4352 tgl 4044 48121 : result = strncmp(key, position->token, TOKMAXLEN);
4352 tgl 4045 GIC 48121 : if (result == 0)
4352 tgl 4046 CBC 13473 : return position;
4047 : }
4048 178660 : if (result < 0)
4049 90195 : last = position - 1;
4352 tgl 4050 ECB : else
4352 tgl 4051 CBC 88465 : base = position + 1;
8453 lockhart 4052 ECB : }
4053 : }
8453 lockhart 4054 CBC 20197 : return NULL;
4055 : }
9522 scrappy 4056 ECB :
6018 tgl 4057 : /* EncodeTimezone()
2619 4058 : * Copies representation of a numeric timezone offset to str.
4059 : *
4060 : * Returns a pointer to the new end of string. No NUL terminator is put
4061 : * there; callers are responsible for NUL terminating str themselves.
6018 4062 : */
4063 : static char *
5883 peter_e 4064 GIC 32694 : EncodeTimezone(char *str, int tz, int style)
4065 : {
4066 : int hour,
4067 : min,
4068 : sec;
6018 tgl 4069 ECB :
6018 tgl 4070 GIC 32694 : sec = abs(tz);
6018 tgl 4071 CBC 32694 : min = sec / SECS_PER_MINUTE;
6018 tgl 4072 GIC 32694 : sec -= min * SECS_PER_MINUTE;
6018 tgl 4073 CBC 32694 : hour = min / MINS_PER_HOUR;
6018 tgl 4074 GIC 32694 : min -= hour * MINS_PER_HOUR;
6018 tgl 4075 ECB :
4076 : /* TZ is negated compared to sign we wish to display ... */
6018 tgl 4077 GIC 32694 : *str++ = (tz <= 0 ? '+' : '-');
6018 tgl 4078 ECB :
6018 tgl 4079 CBC 32694 : if (sec != 0)
2619 tgl 4080 ECB : {
1163 rhodiumtoad 4081 CBC 24 : str = pg_ultostr_zeropad(str, hour, 2);
2619 tgl 4082 24 : *str++ = ':';
1163 rhodiumtoad 4083 24 : str = pg_ultostr_zeropad(str, min, 2);
2619 tgl 4084 24 : *str++ = ':';
1163 rhodiumtoad 4085 GIC 24 : str = pg_ultostr_zeropad(str, sec, 2);
2619 tgl 4086 EUB : }
5883 peter_e 4087 GIC 32670 : else if (min != 0 || style == USE_XSD_DATES)
2619 tgl 4088 EUB : {
1163 rhodiumtoad 4089 GIC 156 : str = pg_ultostr_zeropad(str, hour, 2);
2619 tgl 4090 GBC 156 : *str++ = ':';
1163 rhodiumtoad 4091 156 : str = pg_ultostr_zeropad(str, min, 2);
2619 tgl 4092 EUB : }
4093 : else
1163 rhodiumtoad 4094 GIC 32514 : str = pg_ultostr_zeropad(str, hour, 2);
2619 tgl 4095 32694 : return str;
6018 tgl 4096 EUB : }
9522 scrappy 4097 :
8453 lockhart 4098 : /* EncodeDateOnly()
4099 : * Encode date as local time.
4100 : */
5066 tgl 4101 : void
2118 tgl 4102 GBC 4369 : EncodeDateOnly(struct pg_tm *tm, int style, char *str)
8453 lockhart 4103 EUB : {
5066 tgl 4104 GIC 4369 : Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
9459 lockhart 4105 ECB :
8453 lockhart 4106 GIC 4369 : switch (style)
8453 lockhart 4107 ECB : {
8453 lockhart 4108 CBC 2271 : case USE_ISO_DATES:
5883 peter_e 4109 ECB : case USE_XSD_DATES:
7863 lockhart 4110 : /* compatible with ISO date formats */
1163 rhodiumtoad 4111 CBC 2271 : str = pg_ultostr_zeropad(str,
1060 tgl 4112 2271 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
2619 4113 2271 : *str++ = '-';
1163 rhodiumtoad 4114 GIC 2271 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4115 CBC 2271 : *str++ = '-';
1163 rhodiumtoad 4116 GIC 2271 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
8453 lockhart 4117 2271 : break;
9459 lockhart 4118 ECB :
8453 lockhart 4119 UIC 0 : case USE_SQL_DATES:
7863 lockhart 4120 EUB : /* compatible with Oracle/Ingres date formats */
7194 tgl 4121 UBC 0 : if (DateOrder == DATEORDER_DMY)
2619 tgl 4122 EUB : {
1163 rhodiumtoad 4123 UIC 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4124 0 : *str++ = '/';
1163 rhodiumtoad 4125 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4126 ECB : }
8453 lockhart 4127 : else
2619 tgl 4128 : {
1163 rhodiumtoad 4129 UIC 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4130 LBC 0 : *str++ = '/';
1163 rhodiumtoad 4131 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4132 ECB : }
2619 tgl 4133 LBC 0 : *str++ = '/';
1163 rhodiumtoad 4134 UIC 0 : str = pg_ultostr_zeropad(str,
1060 tgl 4135 0 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
8453 lockhart 4136 LBC 0 : break;
4137 :
8453 lockhart 4138 CBC 2 : case USE_GERMAN_DATES:
7863 lockhart 4139 ECB : /* German-style date format */
1163 rhodiumtoad 4140 GIC 2 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4141 CBC 2 : *str++ = '.';
1163 rhodiumtoad 4142 2 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4143 GIC 2 : *str++ = '.';
1163 rhodiumtoad 4144 2 : str = pg_ultostr_zeropad(str,
1060 tgl 4145 2 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
8453 lockhart 4146 2 : break;
4147 :
4148 2096 : case USE_POSTGRES_DATES:
4149 : default:
4150 : /* traditional date-only style for Postgres */
7194 tgl 4151 2096 : if (DateOrder == DATEORDER_DMY)
4152 : {
1163 rhodiumtoad 4153 UIC 0 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4154 LBC 0 : *str++ = '-';
1163 rhodiumtoad 4155 UIC 0 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4156 ECB : }
8453 lockhart 4157 : else
2619 tgl 4158 : {
1163 rhodiumtoad 4159 CBC 2096 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4160 2096 : *str++ = '-';
1163 rhodiumtoad 4161 2096 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4162 ECB : }
2619 tgl 4163 CBC 2096 : *str++ = '-';
1163 rhodiumtoad 4164 2096 : str = pg_ultostr_zeropad(str,
1060 tgl 4165 GIC 2096 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
8453 lockhart 4166 2096 : break;
4167 : }
4168 :
2619 tgl 4169 4369 : if (tm->tm_year <= 0)
4170 : {
4171 40 : memcpy(str, " BC", 3); /* Don't copy NUL */
4172 40 : str += 3;
4173 : }
4174 4369 : *str = '\0';
5066 4175 4369 : }
4176 :
4177 :
4178 : /* EncodeTimeOnly()
4179 : * Encode time fields only.
4180 : *
4181 : * tm and fsec are the value to encode, print_tz determines whether to include
4182 : * a time zone (the difference between time and timetz types), tz is the
4183 : * numeric time zone offset, style is the date style, str is where to write the
4043 peter_e 4184 ECB : * output.
4185 : */
4186 : void
2118 tgl 4187 GIC 5466 : EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
9522 scrappy 4188 ECB : {
1163 rhodiumtoad 4189 GIC 5466 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
2619 tgl 4190 5466 : *str++ = ':';
1163 rhodiumtoad 4191 5466 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
2619 tgl 4192 5466 : *str++ = ':';
2619 tgl 4193 CBC 5466 : str = AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
4043 peter_e 4194 5466 : if (print_tz)
2619 tgl 4195 GIC 2806 : str = EncodeTimezone(str, tz, style);
2619 tgl 4196 CBC 5466 : *str = '\0';
5066 tgl 4197 GIC 5466 : }
9522 scrappy 4198 ECB :
4199 :
4200 : /* EncodeDateTime()
8453 lockhart 4201 : * Encode date and time interpreted as local time.
4043 peter_e 4202 : *
4203 : * tm and fsec are the value to encode, print_tz determines whether to include
4204 : * a time zone (the difference between timestamp and timestamptz types), tz is
4205 : * the numeric time zone offset, tzn is the textual time zone, which if
4206 : * specified will be used instead of tz by some styles, style is the date
4207 : * style, str is where to write the output.
4208 : *
4209 : * Supported date styles:
8453 lockhart 4210 : * Postgres - day mon hh:mm:ss yyyy tz
4211 : * SQL - mm/dd/yyyy hh:mm:ss.ss tz
4212 : * ISO - yyyy-mm-dd hh:mm:ss+/-tz
7771 4213 : * German - dd.mm.yyyy hh:mm:ss tz
5883 peter_e 4214 : * XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
8453 lockhart 4215 : */
4216 : void
2118 tgl 4217 CBC 60294 : EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str)
4218 : {
6018 tgl 4219 ECB : int day;
4220 :
6471 bruce 4221 CBC 60294 : Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR);
9522 scrappy 4222 ECB :
4043 peter_e 4223 : /*
4224 : * Negative tm_isdst means we have no valid time zone translation.
4225 : */
4043 peter_e 4226 GIC 60294 : if (tm->tm_isdst < 0)
4043 peter_e 4227 CBC 21528 : print_tz = false;
4043 peter_e 4228 ECB :
8453 lockhart 4229 CBC 60294 : switch (style)
4230 : {
4231 46080 : case USE_ISO_DATES:
5883 peter_e 4232 ECB : case USE_XSD_DATES:
7658 lockhart 4233 : /* Compatible with ISO-8601 date formats */
1163 rhodiumtoad 4234 CBC 46080 : str = pg_ultostr_zeropad(str,
1060 tgl 4235 46080 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
2619 4236 46080 : *str++ = '-';
1163 rhodiumtoad 4237 46080 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4238 46080 : *str++ = '-';
1163 rhodiumtoad 4239 46080 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4240 GIC 46080 : *str++ = (style == USE_ISO_DATES) ? ' ' : 'T';
1163 rhodiumtoad 4241 46080 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
2619 tgl 4242 46080 : *str++ = ':';
1163 rhodiumtoad 4243 46080 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
2619 tgl 4244 46080 : *str++ = ':';
4245 46080 : str = AppendTimestampSeconds(str, tm, fsec);
4043 peter_e 4246 46080 : if (print_tz)
2619 tgl 4247 CBC 29888 : str = EncodeTimezone(str, tz, style);
8453 lockhart 4248 GIC 46080 : break;
9522 scrappy 4249 ECB :
8453 lockhart 4250 GIC 390 : case USE_SQL_DATES:
7658 lockhart 4251 ECB : /* Compatible with Oracle/Ingres date formats */
7194 tgl 4252 CBC 390 : if (DateOrder == DATEORDER_DMY)
4253 : {
1163 rhodiumtoad 4254 GIC 192 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4255 GBC 192 : *str++ = '/';
1163 rhodiumtoad 4256 GIC 192 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4257 ECB : }
4258 : else
4259 : {
1163 rhodiumtoad 4260 GIC 198 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4261 CBC 198 : *str++ = '/';
1163 rhodiumtoad 4262 198 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4263 ECB : }
2619 tgl 4264 CBC 390 : *str++ = '/';
1163 rhodiumtoad 4265 390 : str = pg_ultostr_zeropad(str,
1060 tgl 4266 390 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
2619 4267 390 : *str++ = ' ';
1163 rhodiumtoad 4268 390 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
2619 tgl 4269 390 : *str++ = ':';
1163 rhodiumtoad 4270 390 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
2619 tgl 4271 390 : *str++ = ':';
4272 390 : str = AppendTimestampSeconds(str, tm, fsec);
4273 :
4719 tgl 4274 ECB : /*
4275 : * Note: the uses of %.*s in this function would be risky if the
1014 4276 : * timezone names ever contain non-ASCII characters, since we are
4277 : * not being careful to do encoding-aware clipping. However, all
2619 4278 : * TZ abbreviations in the IANA database are plain ASCII.
4719 4279 : */
4043 peter_e 4280 GIC 390 : if (print_tz)
4281 : {
4043 peter_e 4282 GBC 9 : if (tzn)
4283 : {
2619 tgl 4284 CBC 9 : sprintf(str, " %.*s", MAXTZLEN, tzn);
2619 tgl 4285 GIC 9 : str += strlen(str);
2619 tgl 4286 ECB : }
4287 : else
2619 tgl 4288 UIC 0 : str = EncodeTimezone(str, tz, style);
8453 lockhart 4289 ECB : }
8453 lockhart 4290 CBC 390 : break;
9522 scrappy 4291 ECB :
8453 lockhart 4292 CBC 12 : case USE_GERMAN_DATES:
7658 lockhart 4293 ECB : /* German variant on European style */
1163 rhodiumtoad 4294 CBC 12 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4295 GIC 12 : *str++ = '.';
1163 rhodiumtoad 4296 CBC 12 : str = pg_ultostr_zeropad(str, tm->tm_mon, 2);
2619 tgl 4297 12 : *str++ = '.';
1163 rhodiumtoad 4298 12 : str = pg_ultostr_zeropad(str,
1060 tgl 4299 12 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
2619 tgl 4300 GIC 12 : *str++ = ' ';
1163 rhodiumtoad 4301 12 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
2619 tgl 4302 12 : *str++ = ':';
1163 rhodiumtoad 4303 CBC 12 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
2619 tgl 4304 12 : *str++ = ':';
4305 12 : str = AppendTimestampSeconds(str, tm, fsec);
7858 lockhart 4306 ECB :
4043 peter_e 4307 GIC 12 : if (print_tz)
7658 lockhart 4308 ECB : {
4043 peter_e 4309 CBC 12 : if (tzn)
2619 tgl 4310 ECB : {
2619 tgl 4311 CBC 12 : sprintf(str, " %.*s", MAXTZLEN, tzn);
4312 12 : str += strlen(str);
2619 tgl 4313 ECB : }
7658 lockhart 4314 : else
2619 tgl 4315 LBC 0 : str = EncodeTimezone(str, tz, style);
8453 lockhart 4316 ECB : }
8453 lockhart 4317 GIC 12 : break;
9522 scrappy 4318 ECB :
8453 lockhart 4319 GIC 13812 : case USE_POSTGRES_DATES:
8453 lockhart 4320 ECB : default:
4321 : /* Backward-compatible with traditional Postgres abstime dates */
8453 lockhart 4322 CBC 13812 : day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4323 13812 : tm->tm_wday = j2day(day);
2997 tgl 4324 GIC 13812 : memcpy(str, days[tm->tm_wday], 3);
2619 4325 13812 : str += 3;
4326 13812 : *str++ = ' ';
7194 4327 13812 : if (DateOrder == DATEORDER_DMY)
4328 : {
1163 rhodiumtoad 4329 197 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4330 197 : *str++ = ' ';
4331 197 : memcpy(str, months[tm->tm_mon - 1], 3);
4332 197 : str += 3;
2619 tgl 4333 EUB : }
8453 lockhart 4334 : else
4335 : {
2619 tgl 4336 GIC 13615 : memcpy(str, months[tm->tm_mon - 1], 3);
2619 tgl 4337 CBC 13615 : str += 3;
2619 tgl 4338 GIC 13615 : *str++ = ' ';
1163 rhodiumtoad 4339 13615 : str = pg_ultostr_zeropad(str, tm->tm_mday, 2);
2619 tgl 4340 ECB : }
2619 tgl 4341 GIC 13812 : *str++ = ' ';
1163 rhodiumtoad 4342 CBC 13812 : str = pg_ultostr_zeropad(str, tm->tm_hour, 2);
2619 tgl 4343 13812 : *str++ = ':';
1163 rhodiumtoad 4344 GIC 13812 : str = pg_ultostr_zeropad(str, tm->tm_min, 2);
2619 tgl 4345 CBC 13812 : *str++ = ':';
4346 13812 : str = AppendTimestampSeconds(str, tm, fsec);
2619 tgl 4347 GIC 13812 : *str++ = ' ';
1163 rhodiumtoad 4348 13812 : str = pg_ultostr_zeropad(str,
1060 tgl 4349 13812 : (tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
4350 :
4043 peter_e 4351 13812 : if (print_tz)
4352 : {
4353 8857 : if (tzn)
4354 : {
2619 tgl 4355 CBC 8857 : sprintf(str, " %.*s", MAXTZLEN, tzn);
2619 tgl 4356 GIC 8857 : str += strlen(str);
2619 tgl 4357 ECB : }
7658 lockhart 4358 : else
7843 4359 : {
7658 4360 : /*
4361 : * We have a time zone, but no string version. Use the
4362 : * numeric form, but be sure to include a leading space to
4363 : * avoid formatting something which would be rejected by
4364 : * the date/time parser later. - thomas 2001-10-19
4365 : */
2619 tgl 4366 UIC 0 : *str++ = ' ';
4367 0 : str = EncodeTimezone(str, tz, style);
7843 lockhart 4368 ECB : }
8453 4369 : }
8453 lockhart 4370 CBC 13812 : break;
8453 lockhart 4371 ECB : }
2619 tgl 4372 :
2619 tgl 4373 GIC 60294 : if (tm->tm_year <= 0)
4374 : {
4375 137 : memcpy(str, " BC", 3); /* Don't copy NUL */
4376 137 : str += 3;
4377 : }
4378 60294 : *str = '\0';
6846 4379 60294 : }
4380 :
8453 lockhart 4381 ECB :
5264 tgl 4382 : /*
5262 4383 : * Helper functions to avoid duplicated code in EncodeInterval.
4384 : */
4385 :
4386 : /* Append an ISO-8601-style interval field, but only if value isn't zero */
4387 : static char *
372 tgl 4388 CBC 105 : AddISO8601IntPart(char *cp, int64 value, char units)
4389 : {
5261 tgl 4390 GIC 105 : if (value == 0)
5261 tgl 4391 CBC 27 : return cp;
372 4392 78 : sprintf(cp, "%lld%c", (long long) value, units);
5261 tgl 4393 GIC 78 : return cp + strlen(cp);
5264 tgl 4394 ECB : }
4395 :
5261 4396 : /* Append a postgres-style interval field, but only if value isn't zero */
5262 4397 : static char *
372 tgl 4398 GIC 6774 : AddPostgresIntPart(char *cp, int64 value, const char *units,
5261 tgl 4399 ECB : bool *is_zero, bool *is_before)
5262 4400 : {
5262 tgl 4401 CBC 6774 : if (value == 0)
4402 3656 : return cp;
372 4403 9354 : sprintf(cp, "%s%s%lld %s%s",
5261 tgl 4404 GIC 3118 : (!*is_zero) ? " " : "",
4405 3118 : (*is_before && value > 0) ? "+" : "",
4406 : (long long) value,
4407 : units,
4408 : (value != 1) ? "s" : "");
4409 :
4410 : /*
4411 : * Each nonzero field sets is_before for (only) the next one. This is a
4412 : * tad bizarre but it's how it worked before...
4413 : */
4414 3118 : *is_before = (value < 0);
2062 peter_e 4415 3118 : *is_zero = false;
5261 tgl 4416 3118 : return cp + strlen(cp);
4417 : }
4418 :
4419 : /* Append a verbose-style interval field, but only if value isn't zero */
4420 : static char *
372 4421 19855 : AddVerboseIntPart(char *cp, int64 value, const char *units,
4422 : bool *is_zero, bool *is_before)
4423 : {
5261 4424 19855 : if (value == 0)
4425 13061 : return cp;
4426 : /* first nonzero value sets is_before */
5261 tgl 4427 CBC 6794 : if (*is_zero)
4428 : {
4429 3665 : *is_before = (value < 0);
181 peter 4430 GNC 3665 : value = i64abs(value);
5261 tgl 4431 ECB : }
5261 tgl 4432 CBC 3129 : else if (*is_before)
4433 648 : value = -value;
372 4434 6794 : sprintf(cp, " %lld %s%s", (long long) value, units, (value == 1) ? "" : "s");
2062 peter_e 4435 6794 : *is_zero = false;
5262 tgl 4436 6794 : return cp + strlen(cp);
5262 tgl 4437 ECB : }
4438 :
4439 :
4440 : /* EncodeInterval()
4441 : * Interpret time structure as a delta time and convert to string.
4442 : *
4443 : * Support "traditional Postgres" and ISO-8601 styles.
4444 : * Actually, afaik ISO does not address time interval formatting,
4445 : * but this looks similar to the spec for absolute date/time.
8453 lockhart 4446 : * - thomas 1998-04-30
4447 : *
4448 : * Actually, afaik, ISO 8601 does specify formats for "time
5264 tgl 4449 : * intervals...[of the]...format with time-unit designators", which
4450 : * are pretty ugly. The format looks something like
5050 bruce 4451 : * P1Y1M1DT1H1M1.12345S
5264 tgl 4452 : * but useful for exchanging data with computers instead of humans.
4453 : * - ron 2003-07-14
4454 : *
4455 : * And ISO's SQL 2008 standard specifies standards for
4456 : * "year-month literal"s (that look like '2-3') and
4457 : * "day-time literal"s (that look like ('4 5:6:7')
9297 lockhart 4458 : */
5066 tgl 4459 : void
372 tgl 4460 CBC 6316 : EncodeInterval(struct pg_itm *itm, int style, char *str)
9297 lockhart 4461 ECB : {
5264 tgl 4462 CBC 6316 : char *cp = str;
372 tgl 4463 GIC 6316 : int year = itm->tm_year;
4464 6316 : int mon = itm->tm_mon;
4465 6316 : int64 mday = itm->tm_mday; /* tm_mday could be INT_MIN */
4466 6316 : int64 hour = itm->tm_hour;
4467 6316 : int min = itm->tm_min;
372 tgl 4468 CBC 6316 : int sec = itm->tm_sec;
372 tgl 4469 GIC 6316 : int fsec = itm->tm_usec;
2062 peter_e 4470 CBC 6316 : bool is_before = false;
4471 6316 : bool is_zero = true;
9297 lockhart 4472 ECB :
8053 bruce 4473 : /*
4474 : * The sign of year and month are guaranteed to match, since they are
6385 4475 : * stored internally as "month". But we'll need to check for is_before and
5261 tgl 4476 : * is_zero when determining the signs of day and hour/minute/seconds
5264 4477 : * fields.
4478 : */
8453 lockhart 4479 GIC 6316 : switch (style)
8453 lockhart 4480 ECB : {
4481 : /* SQL Standard interval format */
5264 tgl 4482 CBC 63 : case INTSTYLE_SQL_STANDARD:
4483 : {
5050 bruce 4484 48 : bool has_negative = year < 0 || mon < 0 ||
5050 bruce 4485 GIC 33 : mday < 0 || hour < 0 ||
4486 111 : min < 0 || sec < 0 || fsec < 0;
4487 51 : bool has_positive = year > 0 || mon > 0 ||
4488 33 : mday > 0 || hour > 0 ||
4489 114 : min > 0 || sec > 0 || fsec > 0;
4490 63 : bool has_year_month = year != 0 || mon != 0;
5050 bruce 4491 CBC 21 : bool has_day_time = mday != 0 || hour != 0 ||
4492 84 : min != 0 || sec != 0 || fsec != 0;
4493 63 : bool has_day = mday != 0;
4494 111 : bool sql_standard_value = !(has_negative && has_positive) &&
5050 bruce 4495 GIC 48 : !(has_year_month && has_day_time);
5264 tgl 4496 ECB :
4497 : /*
4498 : * SQL Standard wants only 1 "<sign>" preceding the whole
4499 : * interval ... but can't do that if mixed signs.
4500 : */
5264 tgl 4501 CBC 63 : if (has_negative && sql_standard_value)
5264 tgl 4502 ECB : {
5264 tgl 4503 GIC 15 : *cp++ = '-';
5264 tgl 4504 CBC 15 : year = -year;
5050 bruce 4505 GIC 15 : mon = -mon;
5264 tgl 4506 CBC 15 : mday = -mday;
5264 tgl 4507 GIC 15 : hour = -hour;
5050 bruce 4508 CBC 15 : min = -min;
5050 bruce 4509 GIC 15 : sec = -sec;
5264 tgl 4510 CBC 15 : fsec = -fsec;
4511 : }
5264 tgl 4512 ECB :
5264 tgl 4513 CBC 63 : if (!has_negative && !has_positive)
5264 tgl 4514 ECB : {
5264 tgl 4515 GIC 6 : sprintf(cp, "0");
4516 : }
4517 57 : else if (!sql_standard_value)
5264 tgl 4518 ECB : {
4519 : /*
5050 bruce 4520 : * For non sql-standard interval values, force outputting
4521 : * the signs to avoid ambiguities with intervals with
4522 : * mixed sign components.
4523 : */
5050 bruce 4524 CBC 27 : char year_sign = (year < 0 || mon < 0) ? '-' : '+';
5050 bruce 4525 GIC 27 : char day_sign = (mday < 0) ? '-' : '+';
4526 39 : char sec_sign = (hour < 0 || min < 0 ||
5050 bruce 4527 CBC 12 : sec < 0 || fsec < 0) ? '-' : '+';
4528 :
372 tgl 4529 27 : sprintf(cp, "%c%d-%d %c%lld %c%lld:%02d:",
5264 tgl 4530 ECB : year_sign, abs(year), abs(mon),
181 peter 4531 GNC 27 : day_sign, (long long) i64abs(mday),
4532 27 : sec_sign, (long long) i64abs(hour), abs(min));
5264 tgl 4533 CBC 27 : cp += strlen(cp);
2619 tgl 4534 GIC 27 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
2619 tgl 4535 CBC 27 : *cp = '\0';
5264 tgl 4536 ECB : }
5264 tgl 4537 CBC 30 : else if (has_year_month)
5264 tgl 4538 ECB : {
5264 tgl 4539 CBC 9 : sprintf(cp, "%d-%d", year, mon);
5264 tgl 4540 ECB : }
5264 tgl 4541 CBC 21 : else if (has_day)
5264 tgl 4542 ECB : {
372 tgl 4543 CBC 15 : sprintf(cp, "%lld %lld:%02d:",
4544 : (long long) mday, (long long) hour, min);
5264 4545 15 : cp += strlen(cp);
2619 4546 15 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
4547 15 : *cp = '\0';
5264 tgl 4548 ECB : }
4549 : else
4550 : {
372 tgl 4551 CBC 6 : sprintf(cp, "%lld:%02d:", (long long) hour, min);
5264 tgl 4552 GIC 6 : cp += strlen(cp);
2619 4553 6 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
2619 tgl 4554 CBC 6 : *cp = '\0';
5264 tgl 4555 ECB : }
4556 : }
5264 tgl 4557 GIC 63 : break;
4558 :
4559 : /* ISO 8601 "time-intervals by duration only" */
5262 4560 24 : case INTSTYLE_ISO_8601:
4561 : /* special-case zero to avoid printing nothing */
5262 tgl 4562 CBC 24 : if (year == 0 && mon == 0 && mday == 0 &&
5050 bruce 4563 3 : hour == 0 && min == 0 && sec == 0 && fsec == 0)
5262 tgl 4564 ECB : {
5262 tgl 4565 GIC 3 : sprintf(cp, "PT0S");
5262 tgl 4566 CBC 3 : break;
4567 : }
4568 21 : *cp++ = 'P';
5261 4569 21 : cp = AddISO8601IntPart(cp, year, 'Y');
5050 bruce 4570 21 : cp = AddISO8601IntPart(cp, mon, 'M');
5261 tgl 4571 21 : cp = AddISO8601IntPart(cp, mday, 'D');
5262 4572 21 : if (hour != 0 || min != 0 || sec != 0 || fsec != 0)
4573 18 : *cp++ = 'T';
5261 4574 21 : cp = AddISO8601IntPart(cp, hour, 'H');
5050 bruce 4575 GIC 21 : cp = AddISO8601IntPart(cp, min, 'M');
5262 tgl 4576 CBC 21 : if (sec != 0 || fsec != 0)
4577 : {
5262 tgl 4578 GIC 18 : if (sec < 0 || fsec < 0)
5262 tgl 4579 CBC 6 : *cp++ = '-';
2619 tgl 4580 GIC 18 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
5262 tgl 4581 CBC 18 : *cp++ = 'S';
4582 18 : *cp++ = '\0';
5262 tgl 4583 ECB : }
5262 tgl 4584 CBC 21 : break;
5262 tgl 4585 ECB :
5050 bruce 4586 : /* Compatible with postgresql < 8.4 when DateStyle = 'iso' */
5264 tgl 4587 CBC 2258 : case INTSTYLE_POSTGRES:
5261 4588 2258 : cp = AddPostgresIntPart(cp, year, "year", &is_zero, &is_before);
4589 :
4338 bruce 4590 ECB : /*
4322 4591 : * Ideally we should spell out "month" like we do for "year" and
4592 : * "day". However, for backward compatibility, we can't easily
4593 : * fix this. bjm 2011-05-24
4338 4594 : */
5261 tgl 4595 CBC 2258 : cp = AddPostgresIntPart(cp, mon, "mon", &is_zero, &is_before);
4596 2258 : cp = AddPostgresIntPart(cp, mday, "day", &is_zero, &is_before);
5261 tgl 4597 GIC 2258 : if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
8453 lockhart 4598 ECB : {
5050 bruce 4599 CBC 1852 : bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
8189 lockhart 4600 ECB :
372 tgl 4601 GIC 3704 : sprintf(cp, "%s%s%02lld:%02d:",
5261 tgl 4602 CBC 1852 : is_zero ? "" : " ",
8116 lockhart 4603 1766 : (minus ? "-" : (is_before ? "+" : "")),
181 peter 4604 GNC 1852 : (long long) i64abs(hour), abs(min));
5264 tgl 4605 GIC 1852 : cp += strlen(cp);
2619 4606 1852 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
2619 tgl 4607 CBC 1852 : *cp = '\0';
8453 lockhart 4608 ECB : }
8453 lockhart 4609 CBC 2258 : break;
9297 lockhart 4610 ECB :
5050 bruce 4611 : /* Compatible with postgresql < 8.4 when DateStyle != 'iso' */
5264 tgl 4612 GIC 3971 : case INTSTYLE_POSTGRES_VERBOSE:
8453 lockhart 4613 ECB : default:
5261 tgl 4614 GIC 3971 : strcpy(cp, "@");
4615 3971 : cp++;
4616 3971 : cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
4617 3971 : cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
4618 3971 : cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
4619 3971 : cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
4620 3971 : cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before);
5261 tgl 4621 CBC 3971 : if (sec != 0 || fsec != 0)
4622 : {
4623 1620 : *cp++ = ' ';
5261 tgl 4624 GIC 1620 : if (sec < 0 || (sec == 0 && fsec < 0))
4625 : {
5261 tgl 4626 CBC 498 : if (is_zero)
2062 peter_e 4627 GIC 171 : is_before = true;
5261 tgl 4628 327 : else if (!is_before)
5261 tgl 4629 CBC 3 : *cp++ = '-';
4630 : }
5261 tgl 4631 GIC 1122 : else if (is_before)
5261 tgl 4632 GBC 6 : *cp++ = '-';
2619 tgl 4633 GIC 1620 : cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
4634 : /* We output "ago", not negatives, so use abs(). */
5261 tgl 4635 GBC 1620 : sprintf(cp, " sec%s",
4636 1620 : (abs(sec) != 1 || fsec != 0) ? "s" : "");
2062 peter_e 4637 GIC 1620 : is_zero = false;
4638 : }
5264 tgl 4639 ECB : /* identically zero? then put in a unitless zero... */
5261 tgl 4640 CBC 3971 : if (is_zero)
5261 tgl 4641 GIC 112 : strcat(cp, " 0");
5264 tgl 4642 GBC 3971 : if (is_before)
5264 tgl 4643 GIC 657 : strcat(cp, " ago");
8453 lockhart 4644 3971 : break;
4645 : }
5066 tgl 4646 GBC 6316 : }
4647 :
4648 :
7388 tgl 4649 ECB : /*
4650 : * We've been burnt by stupid errors in the ordering of the datetkn tables
4651 : * once too often. Arrange to check them during postmaster start.
4652 : */
4653 : static bool
6102 tgl 4654 GIC 7775 : CheckDateTokenTable(const char *tablename, const datetkn *base, int nel)
7388 tgl 4655 ECB : {
7388 tgl 4656 GIC 7775 : bool ok = true;
6102 tgl 4657 ECB : int i;
7388 4658 :
3097 tgl 4659 GIC 1376540 : for (i = 0; i < nel; i++)
7388 tgl 4660 ECB : {
3097 4661 : /* check for token strings that don't fit */
3097 tgl 4662 CBC 1368765 : if (strlen(base[i].token) > TOKMAXLEN)
4663 : {
4664 : /* %.*s is safe since all our tokens are ASCII */
3097 tgl 4665 UIC 0 : elog(LOG, "token too long in %s table: \"%.*s\"",
4666 : tablename,
4667 : TOKMAXLEN + 1, base[i].token);
4668 0 : ok = false;
4669 0 : break; /* don't risk applying strcmp */
4670 : }
4671 : /* check for out of order */
3097 tgl 4672 GIC 1368765 : if (i > 0 &&
4673 1360990 : strcmp(base[i - 1].token, base[i].token) >= 0)
4674 : {
3097 tgl 4675 UIC 0 : elog(LOG, "ordering error in %s table: \"%s\" >= \"%s\"",
4676 : tablename,
4677 : base[i - 1].token,
4678 : base[i].token);
7388 4679 0 : ok = false;
4680 : }
4681 : }
7388 tgl 4682 CBC 7775 : return ok;
4683 : }
7388 tgl 4684 ECB :
4685 : bool
7388 tgl 4686 GIC 599 : CheckDateTokenTables(void)
4687 : {
7388 tgl 4688 CBC 599 : bool ok = true;
4689 :
7310 4690 599 : Assert(UNIX_EPOCH_JDATE == date2j(1970, 1, 1));
7310 tgl 4691 GIC 599 : Assert(POSTGRES_EPOCH_JDATE == date2j(2000, 1, 1));
7310 tgl 4692 ECB :
7388 tgl 4693 GIC 599 : ok &= CheckDateTokenTable("datetktbl", datetktbl, szdatetktbl);
7388 tgl 4694 CBC 599 : ok &= CheckDateTokenTable("deltatktbl", deltatktbl, szdeltatktbl);
4695 599 : return ok;
7388 tgl 4696 ECB : }
4697 :
4078 rhaas 4698 : /*
1520 tgl 4699 EUB : * Common code for temporal prosupport functions: simplify, if possible,
4700 : * a call to a temporal type's length-coercion function.
4701 : *
4702 : * Types time, timetz, timestamp and timestamptz each have a range of allowed
1520 tgl 4703 ECB : * precisions. An unspecified precision is rigorously equivalent to the
4704 : * highest specifiable precision. We can replace the function call with a
4705 : * no-op RelabelType if it is coercing to the same or higher precision as the
4706 : * input is known to have.
4707 : *
4708 : * The input Node is always a FuncExpr, but to reduce the #include footprint
4709 : * of datetime.h, we declare it as Node *.
4710 : *
4711 : * Note: timestamp_scale throws an error when the typmod is out of range, but
4712 : * we can't get there from a cast: our typmodin will have caught it already.
4713 : */
4714 : Node *
1520 tgl 4715 CBC 12 : TemporalSimplify(int32 max_precis, Node *node)
4716 : {
2238 peter_e 4717 GIC 12 : FuncExpr *expr = castNode(FuncExpr, node);
4078 rhaas 4718 12 : Node *ret = NULL;
4719 : Node *typmod;
4720 :
4034 tgl 4721 12 : Assert(list_length(expr->args) >= 2);
4078 rhaas 4722 ECB :
4034 tgl 4723 CBC 12 : typmod = (Node *) lsecond(expr->args);
4078 rhaas 4724 ECB :
1058 tgl 4725 GIC 12 : if (IsA(typmod, Const) && !((Const *) typmod)->constisnull)
4078 rhaas 4726 ECB : {
4034 tgl 4727 GIC 12 : Node *source = (Node *) linitial(expr->args);
4078 rhaas 4728 CBC 12 : int32 old_precis = exprTypmod(source);
4078 rhaas 4729 GIC 12 : int32 new_precis = DatumGetInt32(((Const *) typmod)->constvalue);
4078 rhaas 4730 ECB :
4034 tgl 4731 GIC 12 : if (new_precis < 0 || new_precis == max_precis ||
4034 tgl 4732 UIC 0 : (old_precis >= 0 && new_precis >= old_precis))
4078 rhaas 4733 0 : ret = relabel_to_typmod(source, new_precis);
4078 rhaas 4734 ECB : }
4735 :
4078 rhaas 4736 CBC 12 : return ret;
4737 : }
4738 :
4739 : /*
4740 : * This function gets called during timezone config file load or reload
6102 tgl 4741 ECB : * to create the final array of timezone tokens. The argument array
3097 4742 : * is already sorted in name order.
3097 tgl 4743 EUB : *
4744 : * The result is a TimeZoneAbbrevTable (which must be a single guc_malloc'd
4745 : * chunk) or NULL on alloc failure. No other error conditions are defined.
6102 tgl 4746 ECB : */
3097 4747 : TimeZoneAbbrevTable *
3097 tgl 4748 GIC 6577 : ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs, int n)
6102 tgl 4749 ECB : {
3097 4750 : TimeZoneAbbrevTable *tbl;
4751 : Size tbl_size;
6102 4752 : int i;
4753 :
3097 4754 : /* Space for fixed fields and datetkn array */
3097 tgl 4755 CBC 6577 : tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
3097 tgl 4756 GIC 6577 : n * sizeof(datetkn);
4757 6577 : tbl_size = MAXALIGN(tbl_size);
3097 tgl 4758 ECB : /* Count up space for dynamic abbreviations */
3097 tgl 4759 CBC 1295675 : for (i = 0; i < n; i++)
4760 : {
3097 tgl 4761 GIC 1289098 : struct tzEntry *abbr = abbrevs + i;
4762 :
4763 1289098 : if (abbr->zone != NULL)
4764 : {
3097 tgl 4765 ECB : Size dsize;
4766 :
3097 tgl 4767 CBC 335424 : dsize = offsetof(DynamicZoneAbbrev, zone) +
3097 tgl 4768 GIC 335424 : strlen(abbr->zone) + 1;
3097 tgl 4769 CBC 335424 : tbl_size += MAXALIGN(dsize);
4770 : }
3097 tgl 4771 ECB : }
4772 :
4773 : /* Alloc the result ... */
177 tgl 4774 GNC 6577 : tbl = guc_malloc(LOG, tbl_size);
3097 tgl 4775 CBC 6577 : if (!tbl)
3097 tgl 4776 UIC 0 : return NULL;
4777 :
4778 : /* ... and fill it in */
3097 tgl 4779 CBC 6577 : tbl->tblsize = tbl_size;
4385 4780 6577 : tbl->numabbrevs = n;
4781 : /* in this loop, tbl_size reprises the space calculation above */
3097 tgl 4782 GIC 6577 : tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
4783 6577 : n * sizeof(datetkn);
4784 6577 : tbl_size = MAXALIGN(tbl_size);
6102 tgl 4785 CBC 1295675 : for (i = 0; i < n; i++)
4786 : {
3097 tgl 4787 GIC 1289098 : struct tzEntry *abbr = abbrevs + i;
3097 tgl 4788 CBC 1289098 : datetkn *dtoken = tbl->abbrevs + i;
4789 :
3097 tgl 4790 ECB : /* use strlcpy to truncate name if necessary */
3097 tgl 4791 GIC 1289098 : strlcpy(dtoken->token, abbr->abbrev, TOKMAXLEN + 1);
4792 1289098 : if (abbr->zone != NULL)
4793 : {
4794 : /* Allocate a DynamicZoneAbbrev for this abbreviation */
4795 : DynamicZoneAbbrev *dtza;
4796 : Size dsize;
4797 :
4798 335424 : dtza = (DynamicZoneAbbrev *) ((char *) tbl + tbl_size);
3097 tgl 4799 CBC 335424 : dtza->tz = NULL;
3097 tgl 4800 GIC 335424 : strcpy(dtza->zone, abbr->zone);
3097 tgl 4801 ECB :
3097 tgl 4802 GIC 335424 : dtoken->type = DYNTZ;
3097 tgl 4803 ECB : /* value is offset from table start to DynamicZoneAbbrev */
3097 tgl 4804 CBC 335424 : dtoken->value = (int32) tbl_size;
4805 :
3097 tgl 4806 GIC 335424 : dsize = offsetof(DynamicZoneAbbrev, zone) +
4807 335424 : strlen(abbr->zone) + 1;
4808 335424 : tbl_size += MAXALIGN(dsize);
4809 : }
4810 : else
4811 : {
3097 tgl 4812 CBC 953674 : dtoken->type = abbr->is_dst ? DTZ : TZ;
3097 tgl 4813 GIC 953674 : dtoken->value = abbr->offset;
4814 : }
4815 : }
4816 :
4817 : /* Assert the two loops above agreed on size calculations */
3097 tgl 4818 CBC 6577 : Assert(tbl->tblsize == tbl_size);
3097 tgl 4819 ECB :
4820 : /* Check the ordering, if testing */
3097 tgl 4821 CBC 6577 : Assert(CheckDateTokenTable("timezone abbreviations", tbl->abbrevs, n));
4822 :
3097 tgl 4823 GIC 6577 : return tbl;
4385 tgl 4824 ECB : }
4825 :
4826 : /*
4827 : * Install a TimeZoneAbbrevTable as the active table.
4828 : *
4829 : * Caller is responsible that the passed table doesn't go away while in use.
4385 tgl 4830 EUB : */
4831 : void
4385 tgl 4832 GIC 6489 : InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
4833 : {
3097 tgl 4834 CBC 6489 : zoneabbrevtbl = tbl;
4835 : /* reset abbrevcache, which may contain pointers into old table */
3097 tgl 4836 GIC 6489 : memset(abbrevcache, 0, sizeof(abbrevcache));
4837 6489 : }
4838 :
4839 : /*
4840 : * Helper subroutine to locate pg_tz timezone for a dynamic abbreviation.
4841 : *
4842 : * On failure, returns NULL and fills *extra for a DTERR_BAD_ZONE_ABBREV error.
4843 : */
4844 : static pg_tz *
121 tgl 4845 GNC 588 : FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp,
4846 : DateTimeErrorExtra *extra)
4847 : {
4848 : DynamicZoneAbbrev *dtza;
4849 :
4850 : /* Just some sanity checks to prevent indexing off into nowhere */
3097 tgl 4851 GIC 588 : Assert(tp->type == DYNTZ);
4852 588 : Assert(tp->value > 0 && tp->value < tbl->tblsize);
3097 tgl 4853 ECB :
3097 tgl 4854 GIC 588 : dtza = (DynamicZoneAbbrev *) ((char *) tbl + tp->value);
4855 :
4856 : /* Look up the underlying zone if we haven't already */
4857 588 : if (dtza->tz == NULL)
4858 : {
4859 465 : dtza->tz = pg_tzset(dtza->zone);
4860 465 : if (dtza->tz == NULL)
4861 : {
4862 : /* Ooops, bogus zone name in config file entry */
121 tgl 4863 UNC 0 : extra->dtee_timezone = dtza->zone;
4864 0 : extra->dtee_abbrev = tp->token;
4865 : }
4866 : }
3097 tgl 4867 GIC 588 : return dtza->tz;
6102 tgl 4868 ECB : }
4869 :
4870 :
4871 : /*
4872 : * This set-returning function reads all the available time zone abbreviations
6049 4873 : * and returns a set of (abbrev, utc_offset, is_dst).
4874 : */
6102 4875 : Datum
6049 tgl 4876 GBC 1779 : pg_timezone_abbrevs(PG_FUNCTION_ARGS)
6102 tgl 4877 ECB : {
4878 : FuncCallContext *funcctx;
6031 bruce 4879 : int *pindex;
4880 : Datum result;
4881 : HeapTuple tuple;
4882 : Datum values[3];
267 peter 4883 GNC 1779 : bool nulls[3] = {0};
3097 tgl 4884 ECB : const datetkn *tp;
4885 : char buffer[TOKMAXLEN + 1];
4886 : int gmtoffset;
4887 : bool is_dst;
6031 bruce 4888 : unsigned char *p;
4889 : struct pg_itm_in itm_in;
4890 : Interval *resInterval;
4891 :
6102 tgl 4892 : /* stuff done only on the first call of the function */
6102 tgl 4893 GIC 1779 : if (SRF_IS_FIRSTCALL())
6102 tgl 4894 ECB : {
6031 bruce 4895 : TupleDesc tupdesc;
4896 : MemoryContext oldcontext;
6102 tgl 4897 :
4898 : /* create a function context for cross-call persistence */
6102 tgl 4899 CBC 9 : funcctx = SRF_FIRSTCALL_INIT();
6102 tgl 4900 ECB :
4901 : /*
6031 bruce 4902 : * switch to memory context appropriate for multiple function calls
4903 : */
6102 tgl 4904 GIC 9 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4905 :
4906 : /* allocate memory for user context */
4907 9 : pindex = (int *) palloc(sizeof(int));
4908 9 : *pindex = 0;
4909 9 : funcctx->user_fctx = (void *) pindex;
6102 tgl 4910 ECB :
109 michael 4911 GNC 9 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
109 michael 4912 UNC 0 : elog(ERROR, "return type must be a row type");
109 michael 4913 GNC 9 : funcctx->tuple_desc = tupdesc;
109 michael 4914 EUB :
6102 tgl 4915 GIC 9 : MemoryContextSwitchTo(oldcontext);
4916 : }
4917 :
4918 : /* stuff done on every call of the function */
4919 1779 : funcctx = SRF_PERCALL_SETUP();
4920 1779 : pindex = (int *) funcctx->user_fctx;
4921 :
3097 4922 1779 : if (zoneabbrevtbl == NULL ||
4923 1779 : *pindex >= zoneabbrevtbl->numabbrevs)
6102 tgl 4924 CBC 9 : SRF_RETURN_DONE(funcctx);
6102 tgl 4925 ECB :
3097 tgl 4926 CBC 1770 : tp = zoneabbrevtbl->abbrevs + *pindex;
4927 :
4928 1770 : switch (tp->type)
4929 : {
3097 tgl 4930 GIC 882 : case TZ:
3097 tgl 4931 CBC 882 : gmtoffset = tp->value;
4932 882 : is_dst = false;
4933 882 : break;
4934 432 : case DTZ:
4935 432 : gmtoffset = tp->value;
3097 tgl 4936 GIC 432 : is_dst = true;
3097 tgl 4937 CBC 432 : break;
3097 tgl 4938 GIC 456 : case DYNTZ:
3097 tgl 4939 ECB : {
4940 : /* Determine the current meaning of the abbrev */
4941 : pg_tz *tzp;
4942 : DateTimeErrorExtra extra;
4943 : TimestampTz now;
4944 : int isdst;
4945 :
121 tgl 4946 GNC 456 : tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
4947 456 : if (tzp == NULL)
121 tgl 4948 UNC 0 : DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
4949 : NULL, NULL, NULL);
3097 tgl 4950 GIC 456 : now = GetCurrentTransactionStartTimestamp();
4951 912 : gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
4952 456 : tp->token,
4953 : tzp,
4954 : &isdst);
4955 456 : is_dst = (bool) isdst;
3097 tgl 4956 CBC 456 : break;
4957 : }
3097 tgl 4958 LBC 0 : default:
3097 tgl 4959 UIC 0 : elog(ERROR, "unrecognized timezone type %d", (int) tp->type);
4960 : gmtoffset = 0; /* keep compiler quiet */
4961 : is_dst = false;
3097 tgl 4962 ECB : break;
4963 : }
4964 :
4965 : /*
4966 : * Convert name to text, using upcasing conversion that is the inverse of
4967 : * what ParseDateTime() uses.
6102 4968 : */
3097 tgl 4969 GIC 1770 : strlcpy(buffer, tp->token, sizeof(buffer));
6102 4970 8205 : for (p = (unsigned char *) buffer; *p; p++)
6102 tgl 4971 CBC 6435 : *p = pg_toupper(*p);
4972 :
5493 tgl 4973 GIC 1770 : values[0] = CStringGetTextDatum(buffer);
4974 :
4975 : /* Convert offset (in seconds) to an interval; can't overflow */
372 tgl 4976 CBC 7080 : MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
4977 1770 : itm_in.tm_usec = (int64) gmtoffset * USECS_PER_SEC;
6102 4978 1770 : resInterval = (Interval *) palloc(sizeof(Interval));
372 tgl 4979 GIC 1770 : (void) itmin2interval(&itm_in, resInterval);
6102 4980 1770 : values[1] = IntervalPGetDatum(resInterval);
6102 tgl 4981 ECB :
3097 tgl 4982 GIC 1770 : values[2] = BoolGetDatum(is_dst);
6102 tgl 4983 EUB :
6102 tgl 4984 GIC 1770 : (*pindex)++;
4985 :
4986 1770 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
4987 1770 : result = HeapTupleGetDatum(tuple);
4988 :
4989 1770 : SRF_RETURN_NEXT(funcctx, result);
4990 : }
4991 :
4992 : /*
4993 : * This set-returning function reads all the available full time zones
6049 tgl 4994 ECB : * and returns a set of (name, abbrev, utc_offset, is_dst).
6049 tgl 4995 EUB : */
4996 : Datum
6049 tgl 4997 CBC 8 : pg_timezone_names(PG_FUNCTION_ARGS)
6049 tgl 4998 ECB : {
1119 tgl 4999 GIC 8 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
5000 : pg_tzenum *tzenum;
6031 bruce 5001 ECB : pg_tz *tz;
5002 : Datum values[4];
267 peter 5003 GNC 8 : bool nulls[4] = {0};
6049 tgl 5004 ECB : int tzoff;
6031 bruce 5005 : struct pg_tm tm;
5006 : fsec_t fsec;
4042 peter_e 5007 : const char *tzn;
5008 : Interval *resInterval;
372 tgl 5009 : struct pg_itm_in itm_in;
5010 :
173 michael 5011 GIC 8 : InitMaterializedSRF(fcinfo, 0);
6049 tgl 5012 ECB :
1119 5013 : /* initialize timezone scanning code */
1119 tgl 5014 GIC 8 : tzenum = pg_tzenumerate_start();
5015 :
5016 : /* search for another zone to display */
5017 : for (;;)
5018 : {
6049 5019 4784 : tz = pg_tzenumerate_next(tzenum);
5020 4784 : if (!tz)
1119 5021 8 : break;
5022 :
5023 : /* Convert now() to local time in this zone */
6049 5024 4776 : if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
5025 : &tzoff, &tm, &fsec, &tzn, tz) != 0)
6049 tgl 5026 UIC 0 : continue; /* ignore if conversion fails */
5027 :
5028 : /*
5029 : * IANA's rather silly "Factory" time zone used to emit ridiculously
5030 : * long "abbreviations" such as "Local time zone must be set--see zic
5031 : * manual page" or "Local time zone must be set--use tzsetup". While
5032 : * modern versions of tzdb emit the much saner "-00", it seems some
5033 : * benighted packagers are hacking the IANA data so that it continues
5034 : * to produce these strings. To prevent producing a weirdly wide
5035 : * abbrev column, reject ridiculously long abbreviations.
5036 : */
1353 tgl 5037 GIC 4776 : if (tzn && strlen(tzn) > 31)
6049 tgl 5038 UIC 0 : continue;
5039 :
1119 tgl 5040 GIC 4776 : values[0] = CStringGetTextDatum(pg_get_timezone_name(tz));
5041 4776 : values[1] = CStringGetTextDatum(tzn ? tzn : "");
5042 :
5043 : /* Convert tzoff to an interval; can't overflow */
372 5044 19104 : MemSet(&itm_in, 0, sizeof(struct pg_itm_in));
5045 4776 : itm_in.tm_usec = (int64) -tzoff * USECS_PER_SEC;
1119 5046 4776 : resInterval = (Interval *) palloc(sizeof(Interval));
372 5047 4776 : (void) itmin2interval(&itm_in, resInterval);
1119 5048 4776 : values[2] = IntervalPGetDatum(resInterval);
5049 :
5050 4776 : values[3] = BoolGetDatum(tm.tm_isdst > 0);
5051 :
398 michael 5052 4776 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
5053 : }
5054 :
1119 tgl 5055 8 : pg_tzenumerate_end(tzenum);
5056 8 : return (Datum) 0;
5057 : }
|