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