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