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