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