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