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