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