1 /*-------------------------------------------------------------------------
4 * Support functions for date/time types.
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
22 #include "access/xact.h"
23 #include "catalog/pg_type.h"
25 #include "miscadmin.h"
26 #include "utils/builtins.h"
27 #include "utils/datetime.h"
28 #include "utils/memutils.h"
29 #include "utils/tzparser.h"
32 static int DecodeNumber(int flen
, char *field
, bool haveTextMonth
,
33 int fmask
, int *tmask
,
34 struct pg_tm
* tm
, fsec_t
*fsec
, bool *is2digits
);
35 static int DecodeNumberField(int len
, char *str
,
36 int fmask
, int *tmask
,
37 struct pg_tm
* tm
, fsec_t
*fsec
, bool *is2digits
);
38 static int DecodeTime(char *str
, int fmask
, int range
,
39 int *tmask
, struct pg_tm
* tm
, fsec_t
*fsec
);
40 static int DecodeTimezone(char *str
, int *tzp
);
41 static const datetkn
*datebsearch(const char *key
, const datetkn
*base
, int nel
);
42 static int DecodeDate(char *str
, int fmask
, int *tmask
, bool *is2digits
,
44 static int ValidateDate(int fmask
, bool is2digits
, bool bc
,
46 static void TrimTrailingZeros(char *str
);
49 const int day_tab
[2][13] =
51 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
52 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}
55 char *months
[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
56 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
};
58 char *days
[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
59 "Thursday", "Friday", "Saturday", NULL
};
62 /*****************************************************************************
64 *****************************************************************************/
67 * Definitions for squeezing values into "value"
68 * We set aside a high bit for a sign, and scale the timezone offsets
69 * in minutes by a factor of 15 (so can represent quarter-hour increments).
71 #define ABS_SIGNBIT ((char) 0200)
72 #define VALMASK ((char) 0177)
74 #define NEG(n) ((n)|ABS_SIGNBIT)
75 #define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
76 #define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */
77 #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))
80 * datetktbl holds date/time keywords.
82 * Note that this table must be strictly alphabetically ordered to allow an
83 * O(ln(N)) search algorithm to be used.
85 * The text field is NOT guaranteed to be NULL-terminated.
87 * To keep this table reasonably small, we divide the lexval for TZ and DTZ
88 * entries by 15 (so they are on 15 minute boundaries) and truncate the text
89 * field at TOKMAXLEN characters.
90 * Formerly, we divided by 10 rather than 15 but there are a few time zones
91 * which are 30 or 45 minutes away from an even hour, most are on an hour
92 * boundary, and none on other boundaries.
94 * The static table contains no TZ or DTZ entries, rather those are loaded
95 * from configuration files and stored in timezonetktbl, which has the same
96 * format as the static datetktbl.
98 static datetkn
*timezonetktbl
= NULL
;
100 static int sztimezonetktbl
= 0;
102 static const datetkn datetktbl
[] = {
103 /* text, token, lexval */
104 {EARLY
, RESERV
, DTK_EARLY
}, /* "-infinity" reserved for "early time" */
105 {"abstime", IGNORE_DTF
, 0}, /* for pre-v6.1 "Invalid Abstime" */
106 {DA_D
, ADBC
, AD
}, /* "ad" for years > 0 */
107 {"allballs", RESERV
, DTK_ZULU
}, /* 00:00:00 */
111 {"at", IGNORE_DTF
, 0}, /* "at" (throwaway) */
113 {"august", MONTH
, 8},
114 {DB_C
, ADBC
, BC
}, /* "bc" for years <= 0 */
115 {DCURRENT
, RESERV
, DTK_CURRENT
}, /* "current" is always now */
116 {"d", UNITS
, DTK_DAY
}, /* "day of month" for ISO input */
118 {"december", MONTH
, 12},
119 {"dow", RESERV
, DTK_DOW
}, /* day of week */
120 {"doy", RESERV
, DTK_DOY
}, /* day of year */
122 {EPOCH
, RESERV
, DTK_EPOCH
}, /* "epoch" reserved for system epoch time */
124 {"february", MONTH
, 2},
127 {"h", UNITS
, DTK_HOUR
}, /* "hour" */
128 {LATE
, RESERV
, DTK_LATE
}, /* "infinity" reserved for "late time" */
129 {INVALID
, RESERV
, DTK_INVALID
}, /* "invalid" reserved for bad time */
130 {"isodow", RESERV
, DTK_ISODOW
}, /* ISO day of week, Sunday == 7 */
131 {"isoyear", UNITS
, DTK_ISOYEAR
}, /* year in terms of the ISO week date */
132 {"j", UNITS
, DTK_JULIAN
},
134 {"january", MONTH
, 1},
135 {"jd", UNITS
, DTK_JULIAN
},
137 {"julian", UNITS
, DTK_JULIAN
},
141 {"m", UNITS
, DTK_MONTH
}, /* "month" for ISO input */
145 {"mm", UNITS
, DTK_MINUTE
}, /* "minute" for ISO input */
149 {"november", MONTH
, 11},
150 {NOW
, RESERV
, DTK_NOW
}, /* current transaction time */
152 {"october", MONTH
, 10},
153 {"on", IGNORE_DTF
, 0}, /* "on" (throwaway) */
155 {"s", UNITS
, DTK_SECOND
}, /* "seconds" for ISO input */
157 {"saturday", DOW
, 6},
160 {"september", MONTH
, 9},
163 {"t", ISOTIME
, DTK_TIME
}, /* Filler for ISO time fields */
167 {"thursday", DOW
, 4},
168 {TODAY
, RESERV
, DTK_TODAY
}, /* midnight */
169 {TOMORROW
, RESERV
, DTK_TOMORROW
}, /* tomorrow midnight */
173 {"undefined", RESERV
, DTK_INVALID
}, /* pre-v6.1 invalid time */
175 {"wednesday", DOW
, 3},
177 {"y", UNITS
, DTK_YEAR
}, /* "year" for ISO input */
178 {YESTERDAY
, RESERV
, DTK_YESTERDAY
} /* yesterday midnight */
181 static int szdatetktbl
= sizeof datetktbl
/ sizeof datetktbl
[0];
183 static datetkn deltatktbl
[] = {
184 /* text, token, lexval */
185 {"@", IGNORE_DTF
, 0}, /* postgres relative prefix */
186 {DAGO
, AGO
, 0}, /* "ago" indicates negative time offset */
187 {"c", UNITS
, DTK_CENTURY
}, /* "century" relative */
188 {"cent", UNITS
, DTK_CENTURY
}, /* "century" relative */
189 {"centuries", UNITS
, DTK_CENTURY
}, /* "centuries" relative */
190 {DCENTURY
, UNITS
, DTK_CENTURY
}, /* "century" relative */
191 {"d", UNITS
, DTK_DAY
}, /* "day" relative */
192 {DDAY
, UNITS
, DTK_DAY
}, /* "day" relative */
193 {"days", UNITS
, DTK_DAY
}, /* "days" relative */
194 {"dec", UNITS
, DTK_DECADE
}, /* "decade" relative */
195 {DDECADE
, UNITS
, DTK_DECADE
}, /* "decade" relative */
196 {"decades", UNITS
, DTK_DECADE
}, /* "decades" relative */
197 {"decs", UNITS
, DTK_DECADE
}, /* "decades" relative */
198 {"h", UNITS
, DTK_HOUR
}, /* "hour" relative */
199 {DHOUR
, UNITS
, DTK_HOUR
}, /* "hour" relative */
200 {"hours", UNITS
, DTK_HOUR
}, /* "hours" relative */
201 {"hr", UNITS
, DTK_HOUR
}, /* "hour" relative */
202 {"hrs", UNITS
, DTK_HOUR
}, /* "hours" relative */
203 {INVALID
, RESERV
, DTK_INVALID
}, /* reserved for invalid time */
204 {"m", UNITS
, DTK_MINUTE
}, /* "minute" relative */
205 {"microsecon", UNITS
, DTK_MICROSEC
}, /* "microsecond" relative */
206 {"mil", UNITS
, DTK_MILLENNIUM
}, /* "millennium" relative */
207 {"millennia", UNITS
, DTK_MILLENNIUM
}, /* "millennia" relative */
208 {DMILLENNIUM
, UNITS
, DTK_MILLENNIUM
}, /* "millennium" relative */
209 {"millisecon", UNITS
, DTK_MILLISEC
}, /* relative */
210 {"mils", UNITS
, DTK_MILLENNIUM
}, /* "millennia" relative */
211 {"min", UNITS
, DTK_MINUTE
}, /* "minute" relative */
212 {"mins", UNITS
, DTK_MINUTE
}, /* "minutes" relative */
213 {DMINUTE
, UNITS
, DTK_MINUTE
}, /* "minute" relative */
214 {"minutes", UNITS
, DTK_MINUTE
}, /* "minutes" relative */
215 {"mon", UNITS
, DTK_MONTH
}, /* "months" relative */
216 {"mons", UNITS
, DTK_MONTH
}, /* "months" relative */
217 {DMONTH
, UNITS
, DTK_MONTH
}, /* "month" relative */
218 {"months", UNITS
, DTK_MONTH
},
219 {"ms", UNITS
, DTK_MILLISEC
},
220 {"msec", UNITS
, DTK_MILLISEC
},
221 {DMILLISEC
, UNITS
, DTK_MILLISEC
},
222 {"mseconds", UNITS
, DTK_MILLISEC
},
223 {"msecs", UNITS
, DTK_MILLISEC
},
224 {"qtr", UNITS
, DTK_QUARTER
}, /* "quarter" relative */
225 {DQUARTER
, UNITS
, DTK_QUARTER
}, /* "quarter" relative */
226 {"reltime", IGNORE_DTF
, 0}, /* pre-v6.1 "Undefined Reltime" */
227 {"s", UNITS
, DTK_SECOND
},
228 {"sec", UNITS
, DTK_SECOND
},
229 {DSECOND
, UNITS
, DTK_SECOND
},
230 {"seconds", UNITS
, DTK_SECOND
},
231 {"secs", UNITS
, DTK_SECOND
},
232 {DTIMEZONE
, UNITS
, DTK_TZ
}, /* "timezone" time offset */
233 {"timezone_h", UNITS
, DTK_TZ_HOUR
}, /* timezone hour units */
234 {"timezone_m", UNITS
, DTK_TZ_MINUTE
}, /* timezone minutes units */
235 {"undefined", RESERV
, DTK_INVALID
}, /* pre-v6.1 invalid time */
236 {"us", UNITS
, DTK_MICROSEC
}, /* "microsecond" relative */
237 {"usec", UNITS
, DTK_MICROSEC
}, /* "microsecond" relative */
238 {DMICROSEC
, UNITS
, DTK_MICROSEC
}, /* "microsecond" relative */
239 {"useconds", UNITS
, DTK_MICROSEC
}, /* "microseconds" relative */
240 {"usecs", UNITS
, DTK_MICROSEC
}, /* "microseconds" relative */
241 {"w", UNITS
, DTK_WEEK
}, /* "week" relative */
242 {DWEEK
, UNITS
, DTK_WEEK
}, /* "week" relative */
243 {"weeks", UNITS
, DTK_WEEK
}, /* "weeks" relative */
244 {"y", UNITS
, DTK_YEAR
}, /* "year" relative */
245 {DYEAR
, UNITS
, DTK_YEAR
}, /* "year" relative */
246 {"years", UNITS
, DTK_YEAR
}, /* "years" relative */
247 {"yr", UNITS
, DTK_YEAR
}, /* "year" relative */
248 {"yrs", UNITS
, DTK_YEAR
} /* "years" relative */
251 static int szdeltatktbl
= sizeof deltatktbl
/ sizeof deltatktbl
[0];
253 static const datetkn
*datecache
[MAXDATEFIELDS
] = {NULL
};
255 static const datetkn
*deltacache
[MAXDATEFIELDS
] = {NULL
};
259 * strtoi --- just like strtol, but returns int not long
262 strtoi(const char *nptr
, char **endptr
, int base
)
266 val
= strtol(nptr
, endptr
, base
);
267 #ifdef HAVE_LONG_INT_64
268 if (val
!= (long) ((int32
) val
))
276 * Calendar time to Julian date conversions.
277 * Julian date is commonly used in astronomical applications,
278 * since it is numerically accurate and computationally simple.
279 * The algorithms here will accurately convert between Julian day
280 * and calendar date for all non-negative Julian days
281 * (i.e. from Nov 24, -4713 on).
283 * These routines will be used by other date/time packages
286 * Rewritten to eliminate overflow problems. This now allows the
287 * routines to work correctly for all Julian day counts from
288 * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
289 * a 32-bit integer. Longer types should also work to the limits
290 * of their precision.
294 date2j(int y
, int m
, int d
)
311 julian
= y
* 365 - 32167;
312 julian
+= y
/ 4 - century
+ century
/ 4;
313 julian
+= 7834 * m
/ 256 + d
;
319 j2date(int jd
, int *year
, int *month
, int *day
)
328 quad
= julian
/ 146097;
329 extra
= (julian
- quad
* 146097) * 4 + 3;
330 julian
+= 60 + quad
* 3 + extra
/ 146097;
331 quad
= julian
/ 1461;
332 julian
-= quad
* 1461;
333 y
= julian
* 4 / 1461;
334 julian
= ((y
!= 0) ? ((julian
+ 305) % 365) : ((julian
+ 306) % 366))
338 quad
= julian
* 2141 / 65536;
339 *day
= julian
- 7834 * quad
/ 256;
340 *month
= (quad
+ 10) % 12 + 1;
347 * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
349 * Note: various places use the locution j2day(date - 1) to produce a
350 * result according to the convention 0..6 = Mon..Sun. This is a bit of
351 * a crock, but will work as long as the computation here is just a modulo.
368 * GetCurrentDateTime()
370 * Get the transaction start time ("now()") broken down as a struct pg_tm.
373 GetCurrentDateTime(struct pg_tm
* tm
)
378 timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz
, tm
, &fsec
,
380 /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
384 * GetCurrentTimeUsec()
386 * Get the transaction start time ("now()") broken down as a struct pg_tm,
387 * including fractional seconds and timezone offset.
390 GetCurrentTimeUsec(struct pg_tm
* tm
, fsec_t
*fsec
, int *tzp
)
394 timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz
, tm
, fsec
,
396 /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
402 /* TrimTrailingZeros()
403 * ... resulting from printing numbers with full precision.
406 TrimTrailingZeros(char *str
)
408 int len
= strlen(str
);
411 /* chop off trailing one to cope with interval rounding */
412 if (strcmp(str
+ len
- 4, "0001") == 0)
419 /* chop off trailing zeros... but leave at least 2 fractional digits */
420 while (*(str
+ len
- 1) == '0' && *(str
+ len
- 3) != '.')
428 * Break string into tokens based on a date/time context.
429 * Returns 0 if successful, DTERR code if bogus input detected.
431 * timestr - the input string
432 * workbuf - workspace for field string storage. This must be
433 * larger than the largest legal input for this datetime type --
434 * some additional space will be needed to NUL terminate fields.
435 * buflen - the size of workbuf
436 * field[] - pointers to field strings are returned in this array
437 * ftype[] - field type indicators are returned in this array
438 * maxfields - dimensions of the above two arrays
439 * *numfields - set to the actual number of fields detected
441 * The fields extracted from the input are stored as separate,
442 * null-terminated strings in the workspace at workbuf. Any text is
443 * converted to lower case.
445 * Several field types are assigned:
446 * DTK_NUMBER - digits and (possibly) a decimal point
447 * DTK_DATE - digits and two delimiters, or digits and text
448 * DTK_TIME - digits, colon delimiters, and possibly a decimal point
449 * DTK_STRING - text (no digits or punctuation)
450 * DTK_SPECIAL - leading "+" or "-" followed by text
451 * DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
453 * Note that some field types can hold unexpected items:
454 * DTK_NUMBER can hold date fields (yy.ddd)
455 * DTK_STRING can hold months (January) and time zones (PST)
456 * DTK_DATE can hold time zone names (America/New_York, GMT-8)
459 ParseDateTime(const char *timestr
, char *workbuf
, size_t buflen
,
460 char **field
, int *ftype
, int maxfields
, int *numfields
)
463 const char *cp
= timestr
;
464 char *bufp
= workbuf
;
465 const char *bufend
= workbuf
+ buflen
;
468 * Set the character pointed-to by "bufptr" to "newchar", and increment
469 * "bufptr". "end" gives the end of the buffer -- we return an error if
470 * there is no space left to append a character to the buffer. Note that
471 * "bufptr" is evaluated twice.
473 #define APPEND_CHAR(bufptr, end, newchar) \
476 if (((bufptr) + 1) >= (end)) \
477 return DTERR_BAD_FORMAT; \
478 *(bufptr)++ = newchar; \
481 /* outer loop through fields */
484 /* Ignore spaces between fields */
485 if (isspace((unsigned char) *cp
))
491 /* Record start of current field */
493 return DTERR_BAD_FORMAT
;
496 /* leading digit? then date or time */
497 if (isdigit((unsigned char) *cp
))
499 APPEND_CHAR(bufp
, bufend
, *cp
++);
500 while (isdigit((unsigned char) *cp
))
501 APPEND_CHAR(bufp
, bufend
, *cp
++);
506 ftype
[nf
] = DTK_TIME
;
507 APPEND_CHAR(bufp
, bufend
, *cp
++);
508 while (isdigit((unsigned char) *cp
) ||
509 (*cp
== ':') || (*cp
== '.'))
510 APPEND_CHAR(bufp
, bufend
, *cp
++);
512 /* date field? allow embedded text month */
513 else if (*cp
== '-' || *cp
== '/' || *cp
== '.')
515 /* save delimiting character to use later */
518 APPEND_CHAR(bufp
, bufend
, *cp
++);
519 /* second field is all digits? then no embedded text month */
520 if (isdigit((unsigned char) *cp
))
522 ftype
[nf
] = ((delim
== '.') ? DTK_NUMBER
: DTK_DATE
);
523 while (isdigit((unsigned char) *cp
))
524 APPEND_CHAR(bufp
, bufend
, *cp
++);
527 * insist that the delimiters match to get a three-field
532 ftype
[nf
] = DTK_DATE
;
533 APPEND_CHAR(bufp
, bufend
, *cp
++);
534 while (isdigit((unsigned char) *cp
) || *cp
== delim
)
535 APPEND_CHAR(bufp
, bufend
, *cp
++);
540 ftype
[nf
] = DTK_DATE
;
541 while (isalnum((unsigned char) *cp
) || *cp
== delim
)
542 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
547 * otherwise, number only and will determine year, month, day, or
548 * concatenated fields later...
551 ftype
[nf
] = DTK_NUMBER
;
553 /* Leading decimal point? Then fractional seconds... */
556 APPEND_CHAR(bufp
, bufend
, *cp
++);
557 while (isdigit((unsigned char) *cp
))
558 APPEND_CHAR(bufp
, bufend
, *cp
++);
560 ftype
[nf
] = DTK_NUMBER
;
564 * text? then date string, month, day of week, special, or timezone
566 else if (isalpha((unsigned char) *cp
))
570 ftype
[nf
] = DTK_STRING
;
571 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
572 while (isalpha((unsigned char) *cp
))
573 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
576 * Dates can have embedded '-', '/', or '.' separators. It could
577 * also be a timezone name containing embedded '/', '+', '-', '_',
578 * or ':' (but '_' or ':' can't be the first punctuation). If the
579 * next character is a digit or '+', we need to check whether what
580 * we have so far is a recognized non-timezone keyword --- if so,
581 * don't believe that this is the start of a timezone.
584 if (*cp
== '-' || *cp
== '/' || *cp
== '.')
586 else if (*cp
== '+' || isdigit((unsigned char) *cp
))
588 *bufp
= '\0'; /* null-terminate current field value */
589 /* we need search only the core token table, not TZ names */
590 if (datebsearch(field
[nf
], datetktbl
, szdatetktbl
) == NULL
)
595 ftype
[nf
] = DTK_DATE
;
598 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
599 } while (*cp
== '+' || *cp
== '-' ||
600 *cp
== '/' || *cp
== '_' ||
601 *cp
== '.' || *cp
== ':' ||
602 isalnum((unsigned char) *cp
));
605 /* sign? then special or numeric timezone */
606 else if (*cp
== '+' || *cp
== '-')
608 APPEND_CHAR(bufp
, bufend
, *cp
++);
609 /* soak up leading whitespace */
610 while (isspace((unsigned char) *cp
))
612 /* numeric timezone? */
613 /* note that "DTK_TZ" could also be a signed float or yyyy-mm */
614 if (isdigit((unsigned char) *cp
))
617 APPEND_CHAR(bufp
, bufend
, *cp
++);
618 while (isdigit((unsigned char) *cp
) ||
619 *cp
== ':' || *cp
== '.' || *cp
== '-')
620 APPEND_CHAR(bufp
, bufend
, *cp
++);
623 else if (isalpha((unsigned char) *cp
))
625 ftype
[nf
] = DTK_SPECIAL
;
626 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
627 while (isalpha((unsigned char) *cp
))
628 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
630 /* otherwise something wrong... */
632 return DTERR_BAD_FORMAT
;
634 /* ignore other punctuation but use as delimiter */
635 else if (ispunct((unsigned char) *cp
))
640 /* otherwise, something is not right... */
642 return DTERR_BAD_FORMAT
;
644 /* force in a delimiter after each field */
656 * Interpret previously parsed fields for general date and time.
657 * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
658 * (Currently, all callers treat 1 as an error return too.)
660 * External format(s):
661 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
662 * "Fri Feb-7-1997 15:23:27"
663 * "Feb-7-1997 15:23:27"
664 * "2-7-1997 15:23:27"
665 * "1997-2-7 15:23:27"
666 * "1997.038 15:23:27" (day of year 1-366)
667 * Also supports input in compact time:
670 * "20011225T040506.789-07"
672 * Use the system-provided functions to get the current time zone
673 * if not specified in the input string.
675 * If the date is outside the range of pg_time_t (in practice that could only
676 * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
680 DecodeDateTime(char **field
, int *ftype
, int nf
,
681 int *dtype
, struct pg_tm
* tm
, fsec_t
*fsec
, int *tzp
)
686 int ptype
= 0; /* "prefix type" for ISO y2001m02d04 format */
691 bool haveTextMonth
= FALSE
;
692 bool is2digits
= FALSE
;
694 pg_tz
*namedTz
= NULL
;
697 * We'll insist on at least all of the date fields, but initialize the
698 * remaining fields in case they are not set later...
705 /* don't know daylight savings time status apriori */
710 for (i
= 0; i
< nf
; i
++)
716 * Integral julian day with attached time zone?
717 * All other forms with JD will be separated into
718 * distinct fields, so we handle just this case here.
720 if (ptype
== DTK_JULIAN
)
726 return DTERR_BAD_FORMAT
;
729 val
= strtoi(field
[i
], &cp
, 10);
731 return DTERR_FIELD_OVERFLOW
;
733 j2date(val
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
734 /* Get the time zone from the end of the string */
735 dterr
= DecodeTimezone(cp
, tzp
);
739 tmask
= DTK_DATE_M
| DTK_TIME_M
| DTK_M(TZ
);
744 * Already have a date? Then this might be a time zone name
745 * with embedded punctuation (e.g. "America/New_York") or a
746 * run-together time with trailing time zone (e.g. hhmmss-zz).
747 * - thomas 2001-12-25
749 * We consider it a time zone if we already have month & day.
750 * This is to allow the form "mmm dd hhmmss tz year", which
751 * we've historically accepted.
753 else if (ptype
!= 0 ||
754 ((fmask
& (DTK_M(MONTH
) | DTK_M(DAY
))) ==
755 (DTK_M(MONTH
) | DTK_M(DAY
))))
757 /* No time zone accepted? Then quit... */
759 return DTERR_BAD_FORMAT
;
761 if (isdigit((unsigned char) *field
[i
]) || ptype
!= 0)
767 /* Sanity check; should not fail this test */
768 if (ptype
!= DTK_TIME
)
769 return DTERR_BAD_FORMAT
;
774 * Starts with a digit but we already have a time
775 * field? Then we are in trouble with a date and time
778 if ((fmask
& DTK_TIME_M
) == DTK_TIME_M
)
779 return DTERR_BAD_FORMAT
;
781 if ((cp
= strchr(field
[i
], '-')) == NULL
)
782 return DTERR_BAD_FORMAT
;
784 /* Get the time zone from the end of the string */
785 dterr
= DecodeTimezone(cp
, tzp
);
791 * Then read the rest of the field as a concatenated
794 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
802 * modify tmask after returning from
803 * DecodeNumberField()
809 namedTz
= pg_tzset(field
[i
]);
813 * We should return an error code instead of
814 * ereport'ing directly, but then there is no way
815 * to report the bad time zone name.
818 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
819 errmsg("time zone \"%s\" not recognized",
822 /* we'll apply the zone setting below */
828 dterr
= DecodeDate(field
[i
], fmask
,
829 &tmask
, &is2digits
, tm
);
836 dterr
= DecodeTime(field
[i
], fmask
, INTERVAL_FULL_RANGE
,
842 * Check upper limit on hours; other limits checked in
845 /* test for > 24:00:00 */
846 if (tm
->tm_hour
> 24 ||
847 (tm
->tm_hour
== 24 && (tm
->tm_min
> 0 || tm
->tm_sec
> 0)))
848 return DTERR_FIELD_OVERFLOW
;
856 return DTERR_BAD_FORMAT
;
858 dterr
= DecodeTimezone(field
[i
], &tz
);
869 * Was this an "ISO date" with embedded field labels? An
870 * example is "y2001m02d04" - thomas 2001-02-04
878 val
= strtoi(field
[i
], &cp
, 10);
880 return DTERR_FIELD_OVERFLOW
;
883 * only a few kinds are allowed to have an embedded
894 return DTERR_BAD_FORMAT
;
897 else if (*cp
!= '\0')
898 return DTERR_BAD_FORMAT
;
910 * already have a month and hour? then assume
913 if ((fmask
& DTK_M(MONTH
)) != 0 &&
914 (fmask
& DTK_M(HOUR
)) != 0)
917 tmask
= DTK_M(MINUTE
);
922 tmask
= DTK_M(MONTH
);
938 tmask
= DTK_M(MINUTE
);
943 tmask
= DTK_M(SECOND
);
948 frac
= strtod(cp
, &cp
);
950 return DTERR_BAD_FORMAT
;
951 #ifdef HAVE_INT64_TIMESTAMP
952 *fsec
= rint(frac
* 1000000);
956 tmask
= DTK_ALL_SECS_M
;
962 dterr
= DecodeTimezone(field
[i
], tzp
);
969 * previous field was a label for "julian date"?
972 j2date(val
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
973 /* fractional Julian Day? */
978 time
= strtod(cp
, &cp
);
980 return DTERR_BAD_FORMAT
;
983 #ifdef HAVE_INT64_TIMESTAMP
984 dt2time(time
* USECS_PER_DAY
,
985 &tm
->tm_hour
, &tm
->tm_min
,
988 dt2time(time
* SECS_PER_DAY
, &tm
->tm_hour
,
989 &tm
->tm_min
, &tm
->tm_sec
, fsec
);
995 /* previous field was "t" for ISO time */
996 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
997 (fmask
| DTK_DATE_M
),
1002 if (tmask
!= DTK_TIME_M
)
1003 return DTERR_BAD_FORMAT
;
1007 return DTERR_BAD_FORMAT
;
1019 flen
= strlen(field
[i
]);
1020 cp
= strchr(field
[i
], '.');
1022 /* Embedded decimal and no date yet? */
1023 if (cp
!= NULL
&& !(fmask
& DTK_DATE_M
))
1025 dterr
= DecodeDate(field
[i
], fmask
,
1026 &tmask
, &is2digits
, tm
);
1030 /* embedded decimal and several digits before? */
1031 else if (cp
!= NULL
&& flen
- strlen(cp
) > 2)
1034 * Interpret as a concatenated date or time Set the
1035 * type field to allow decoding other fields later.
1036 * Example: 20011223 or 040506
1038 dterr
= DecodeNumberField(flen
, field
[i
], fmask
,
1046 dterr
= DecodeNumberField(flen
, field
[i
], fmask
,
1052 /* otherwise it is a single date/time field... */
1055 dterr
= DecodeNumber(flen
, field
[i
],
1056 haveTextMonth
, fmask
,
1067 type
= DecodeSpecial(i
, field
[i
], &val
);
1068 if (type
== IGNORE_DTF
)
1071 tmask
= DTK_M(type
);
1079 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1080 errmsg("date/time value \"current\" is no longer supported")));
1082 return DTERR_BAD_FORMAT
;
1086 tmask
= (DTK_DATE_M
| DTK_TIME_M
| DTK_M(TZ
));
1088 GetCurrentTimeUsec(tm
, fsec
, tzp
);
1094 GetCurrentDateTime(tm
);
1095 j2date(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) - 1,
1096 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1105 GetCurrentDateTime(tm
);
1114 GetCurrentDateTime(tm
);
1115 j2date(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) + 1,
1116 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1123 tmask
= (DTK_TIME_M
| DTK_M(TZ
));
1141 * already have a (numeric) month? then see if we can
1144 if ((fmask
& DTK_M(MONTH
)) && !haveTextMonth
&&
1145 !(fmask
& DTK_M(DAY
)) && tm
->tm_mon
>= 1 &&
1148 tm
->tm_mday
= tm
->tm_mon
;
1151 haveTextMonth
= TRUE
;
1158 * daylight savings time modifier (solves "MET DST"
1161 tmask
|= DTK_M(DTZ
);
1164 return DTERR_BAD_FORMAT
;
1165 *tzp
+= val
* MINS_PER_HOUR
;
1171 * set mask for TZ here _or_ check for DTZ later when
1172 * getting default timezone
1177 return DTERR_BAD_FORMAT
;
1178 *tzp
= val
* MINS_PER_HOUR
;
1184 return DTERR_BAD_FORMAT
;
1185 *tzp
= val
* MINS_PER_HOUR
;
1211 * This is a filler field "t" indicating that the next
1212 * field is time. Try to verify that this is sensible.
1216 /* No preceding date? Then quit... */
1217 if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
1218 return DTERR_BAD_FORMAT
;
1221 * We will need one of the following fields:
1222 * DTK_NUMBER should be hhmmss.fff
1223 * DTK_TIME should be hh:mm:ss.fff
1224 * DTK_DATE should be hhmmss-zz
1227 (ftype
[i
+ 1] != DTK_NUMBER
&&
1228 ftype
[i
+ 1] != DTK_TIME
&&
1229 ftype
[i
+ 1] != DTK_DATE
))
1230 return DTERR_BAD_FORMAT
;
1238 * Before giving up and declaring error, check to see
1239 * if it is an all-alpha timezone name.
1241 namedTz
= pg_tzset(field
[i
]);
1243 return DTERR_BAD_FORMAT
;
1244 /* we'll apply the zone setting below */
1249 return DTERR_BAD_FORMAT
;
1254 return DTERR_BAD_FORMAT
;
1258 return DTERR_BAD_FORMAT
;
1260 } /* end loop over fields */
1262 /* do final checking/adjustment of Y/M/D fields */
1263 dterr
= ValidateDate(fmask
, is2digits
, bc
, tm
);
1268 if (mer
!= HR24
&& tm
->tm_hour
> 12)
1269 return DTERR_FIELD_OVERFLOW
;
1270 if (mer
== AM
&& tm
->tm_hour
== 12)
1272 else if (mer
== PM
&& tm
->tm_hour
!= 12)
1275 /* do additional checking for full date specs... */
1276 if (*dtype
== DTK_DATE
)
1278 if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
1280 if ((fmask
& DTK_TIME_M
) == DTK_TIME_M
)
1282 return DTERR_BAD_FORMAT
;
1286 * If we had a full timezone spec, compute the offset (we could not do
1287 * it before, because we need the date to resolve DST status).
1289 if (namedTz
!= NULL
)
1291 /* daylight savings time modifier disallowed with full TZ */
1292 if (fmask
& DTK_M(DTZMOD
))
1293 return DTERR_BAD_FORMAT
;
1295 *tzp
= DetermineTimeZoneOffset(tm
, namedTz
);
1298 /* timezone not specified? then find local timezone if possible */
1299 if (tzp
!= NULL
&& !(fmask
& DTK_M(TZ
)))
1302 * daylight savings time modifier but no standard timezone? then
1305 if (fmask
& DTK_M(DTZMOD
))
1306 return DTERR_BAD_FORMAT
;
1308 *tzp
= DetermineTimeZoneOffset(tm
, session_timezone
);
1316 /* DetermineTimeZoneOffset()
1318 * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
1319 * tm_sec fields are set, attempt to determine the applicable time zone
1320 * (ie, regular or daylight-savings time) at that time. Set the struct pg_tm's
1321 * tm_isdst field accordingly, and return the actual timezone offset.
1323 * Note: it might seem that we should use mktime() for this, but bitter
1324 * experience teaches otherwise. This code is much faster than most versions
1325 * of mktime(), anyway.
1328 DetermineTimeZoneOffset(struct pg_tm
* tm
, pg_tz
*tzp
)
1338 long int before_gmtoff
,
1344 if (tzp
== session_timezone
&& HasCTZSet
)
1346 tm
->tm_isdst
= 0; /* for lack of a better idea */
1351 * First, generate the pg_time_t value corresponding to the given
1352 * y/m/d/h/m/s taken as GMT time. If this overflows, punt and decide the
1353 * timezone is GMT. (We only need to worry about overflow on machines
1354 * where pg_time_t is 32 bits.)
1356 if (!IS_VALID_JULIAN(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
))
1358 date
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) - UNIX_EPOCH_JDATE
;
1360 day
= ((pg_time_t
) date
) * SECS_PER_DAY
;
1361 if (day
/ SECS_PER_DAY
!= date
)
1363 sec
= tm
->tm_sec
+ (tm
->tm_min
+ tm
->tm_hour
* MINS_PER_HOUR
) * SECS_PER_MINUTE
;
1365 /* since sec >= 0, overflow could only be from +day to -mytime */
1366 if (mytime
< 0 && day
> 0)
1370 * Find the DST time boundary just before or following the target time. We
1371 * assume that all zones have GMT offsets less than 24 hours, and that DST
1372 * boundaries can't be closer together than 48 hours, so backing up 24
1373 * hours and finding the "next" boundary will work.
1375 prevtime
= mytime
- SECS_PER_DAY
;
1376 if (mytime
< 0 && prevtime
> 0)
1379 res
= pg_next_dst_boundary(&prevtime
,
1380 &before_gmtoff
, &before_isdst
,
1382 &after_gmtoff
, &after_isdst
,
1385 goto overflow
; /* failure? */
1389 /* Non-DST zone, life is simple */
1390 tm
->tm_isdst
= before_isdst
;
1391 return -(int) before_gmtoff
;
1395 * Form the candidate pg_time_t values with local-time adjustment
1397 beforetime
= mytime
- before_gmtoff
;
1398 if ((before_gmtoff
> 0 &&
1399 mytime
< 0 && beforetime
> 0) ||
1400 (before_gmtoff
<= 0 &&
1401 mytime
> 0 && beforetime
< 0))
1403 aftertime
= mytime
- after_gmtoff
;
1404 if ((after_gmtoff
> 0 &&
1405 mytime
< 0 && aftertime
> 0) ||
1406 (after_gmtoff
<= 0 &&
1407 mytime
> 0 && aftertime
< 0))
1411 * If both before or both after the boundary time, we know what to do
1413 if (beforetime
<= boundary
&& aftertime
< boundary
)
1415 tm
->tm_isdst
= before_isdst
;
1416 return -(int) before_gmtoff
;
1418 if (beforetime
> boundary
&& aftertime
>= boundary
)
1420 tm
->tm_isdst
= after_isdst
;
1421 return -(int) after_gmtoff
;
1425 * It's an invalid or ambiguous time due to timezone transition. Prefer
1426 * the standard-time interpretation.
1428 if (after_isdst
== 0)
1430 tm
->tm_isdst
= after_isdst
;
1431 return -(int) after_gmtoff
;
1433 tm
->tm_isdst
= before_isdst
;
1434 return -(int) before_gmtoff
;
1437 /* Given date is out of range, so assume UTC */
1444 * Interpret parsed string as time fields only.
1445 * Returns 0 if successful, DTERR code if bogus input detected.
1447 * Note that support for time zone is here for
1448 * SQL92 TIME WITH TIME ZONE, but it reveals
1449 * bogosity with SQL92 date/time standards, since
1450 * we must infer a time zone from current time.
1451 * - thomas 2000-03-10
1452 * Allow specifying date to get a better time zone,
1453 * if time zones are allowed. - thomas 2001-12-26
1456 DecodeTimeOnly(char **field
, int *ftype
, int nf
,
1457 int *dtype
, struct pg_tm
* tm
, fsec_t
*fsec
, int *tzp
)
1462 int ptype
= 0; /* "prefix type" for ISO h04mm05s06 format */
1466 bool is2digits
= FALSE
;
1469 pg_tz
*namedTz
= NULL
;
1476 /* don't know daylight savings time status apriori */
1482 for (i
= 0; i
< nf
; i
++)
1489 * Time zone not allowed? Then should not accept dates or time
1490 * zones no matter what else!
1493 return DTERR_BAD_FORMAT
;
1495 /* Under limited circumstances, we will accept a date... */
1496 if (i
== 0 && nf
>= 2 &&
1497 (ftype
[nf
- 1] == DTK_DATE
|| ftype
[1] == DTK_TIME
))
1499 dterr
= DecodeDate(field
[i
], fmask
,
1500 &tmask
, &is2digits
, tm
);
1504 /* otherwise, this is a time and/or time zone */
1507 if (isdigit((unsigned char) *field
[i
]))
1512 * Starts with a digit but we already have a time
1513 * field? Then we are in trouble with time already...
1515 if ((fmask
& DTK_TIME_M
) == DTK_TIME_M
)
1516 return DTERR_BAD_FORMAT
;
1519 * Should not get here and fail. Sanity check only...
1521 if ((cp
= strchr(field
[i
], '-')) == NULL
)
1522 return DTERR_BAD_FORMAT
;
1524 /* Get the time zone from the end of the string */
1525 dterr
= DecodeTimezone(cp
, tzp
);
1531 * Then read the rest of the field as a concatenated
1534 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
1535 (fmask
| DTK_DATE_M
),
1546 namedTz
= pg_tzset(field
[i
]);
1550 * We should return an error code instead of
1551 * ereport'ing directly, but then there is no way
1552 * to report the bad time zone name.
1555 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1556 errmsg("time zone \"%s\" not recognized",
1559 /* we'll apply the zone setting below */
1567 dterr
= DecodeTime(field
[i
], (fmask
| DTK_DATE_M
),
1568 INTERVAL_FULL_RANGE
,
1579 return DTERR_BAD_FORMAT
;
1581 dterr
= DecodeTimezone(field
[i
], &tz
);
1592 * Was this an "ISO time" with embedded field labels? An
1593 * example is "h04m05s06" - thomas 2001-02-04
1600 /* Only accept a date under limited circumstances */
1608 return DTERR_BAD_FORMAT
;
1614 val
= strtoi(field
[i
], &cp
, 10);
1615 if (errno
== ERANGE
)
1616 return DTERR_FIELD_OVERFLOW
;
1619 * only a few kinds are allowed to have an embedded
1630 return DTERR_BAD_FORMAT
;
1633 else if (*cp
!= '\0')
1634 return DTERR_BAD_FORMAT
;
1640 tmask
= DTK_M(YEAR
);
1646 * already have a month and hour? then assume
1649 if ((fmask
& DTK_M(MONTH
)) != 0 &&
1650 (fmask
& DTK_M(HOUR
)) != 0)
1653 tmask
= DTK_M(MINUTE
);
1658 tmask
= DTK_M(MONTH
);
1669 tmask
= DTK_M(HOUR
);
1674 tmask
= DTK_M(MINUTE
);
1679 tmask
= DTK_M(SECOND
);
1684 frac
= strtod(cp
, &cp
);
1686 return DTERR_BAD_FORMAT
;
1687 #ifdef HAVE_INT64_TIMESTAMP
1688 *fsec
= rint(frac
* 1000000);
1692 tmask
= DTK_ALL_SECS_M
;
1698 dterr
= DecodeTimezone(field
[i
], tzp
);
1705 * previous field was a label for "julian date"?
1708 j2date(val
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1713 time
= strtod(cp
, &cp
);
1715 return DTERR_BAD_FORMAT
;
1717 tmask
|= DTK_TIME_M
;
1718 #ifdef HAVE_INT64_TIMESTAMP
1719 dt2time(time
* USECS_PER_DAY
,
1720 &tm
->tm_hour
, &tm
->tm_min
, &tm
->tm_sec
, fsec
);
1722 dt2time(time
* SECS_PER_DAY
,
1723 &tm
->tm_hour
, &tm
->tm_min
, &tm
->tm_sec
, fsec
);
1729 /* previous field was "t" for ISO time */
1730 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
1731 (fmask
| DTK_DATE_M
),
1738 if (tmask
!= DTK_TIME_M
)
1739 return DTERR_BAD_FORMAT
;
1743 return DTERR_BAD_FORMAT
;
1755 flen
= strlen(field
[i
]);
1756 cp
= strchr(field
[i
], '.');
1758 /* Embedded decimal? */
1762 * Under limited circumstances, we will accept a
1765 if (i
== 0 && nf
>= 2 && ftype
[nf
- 1] == DTK_DATE
)
1767 dterr
= DecodeDate(field
[i
], fmask
,
1768 &tmask
, &is2digits
, tm
);
1772 /* embedded decimal and several digits before? */
1773 else if (flen
- strlen(cp
) > 2)
1776 * Interpret as a concatenated date or time Set
1777 * the type field to allow decoding other fields
1778 * later. Example: 20011223 or 040506
1780 dterr
= DecodeNumberField(flen
, field
[i
],
1781 (fmask
| DTK_DATE_M
),
1789 return DTERR_BAD_FORMAT
;
1793 dterr
= DecodeNumberField(flen
, field
[i
],
1794 (fmask
| DTK_DATE_M
),
1801 /* otherwise it is a single date/time field... */
1804 dterr
= DecodeNumber(flen
, field
[i
],
1806 (fmask
| DTK_DATE_M
),
1817 type
= DecodeSpecial(i
, field
[i
], &val
);
1818 if (type
== IGNORE_DTF
)
1821 tmask
= DTK_M(type
);
1829 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1830 errmsg("date/time value \"current\" is no longer supported")));
1831 return DTERR_BAD_FORMAT
;
1837 GetCurrentTimeUsec(tm
, fsec
, NULL
);
1841 tmask
= (DTK_TIME_M
| DTK_M(TZ
));
1850 return DTERR_BAD_FORMAT
;
1858 * daylight savings time modifier (solves "MET DST"
1861 tmask
|= DTK_M(DTZ
);
1864 return DTERR_BAD_FORMAT
;
1865 *tzp
+= val
* MINS_PER_HOUR
;
1871 * set mask for TZ here _or_ check for DTZ later when
1872 * getting default timezone
1877 return DTERR_BAD_FORMAT
;
1878 *tzp
= val
* MINS_PER_HOUR
;
1885 return DTERR_BAD_FORMAT
;
1886 *tzp
= val
* MINS_PER_HOUR
;
1910 * We will need one of the following fields:
1911 * DTK_NUMBER should be hhmmss.fff
1912 * DTK_TIME should be hh:mm:ss.fff
1913 * DTK_DATE should be hhmmss-zz
1916 (ftype
[i
+ 1] != DTK_NUMBER
&&
1917 ftype
[i
+ 1] != DTK_TIME
&&
1918 ftype
[i
+ 1] != DTK_DATE
))
1919 return DTERR_BAD_FORMAT
;
1927 * Before giving up and declaring error, check to see
1928 * if it is an all-alpha timezone name.
1930 namedTz
= pg_tzset(field
[i
]);
1932 return DTERR_BAD_FORMAT
;
1933 /* we'll apply the zone setting below */
1938 return DTERR_BAD_FORMAT
;
1943 return DTERR_BAD_FORMAT
;
1947 return DTERR_BAD_FORMAT
;
1949 } /* end loop over fields */
1951 /* do final checking/adjustment of Y/M/D fields */
1952 dterr
= ValidateDate(fmask
, is2digits
, bc
, tm
);
1957 if (mer
!= HR24
&& tm
->tm_hour
> 12)
1958 return DTERR_FIELD_OVERFLOW
;
1959 if (mer
== AM
&& tm
->tm_hour
== 12)
1961 else if (mer
== PM
&& tm
->tm_hour
!= 12)
1964 if (tm
->tm_hour
< 0 || tm
->tm_min
< 0 || tm
->tm_min
> 59 ||
1965 tm
->tm_sec
< 0 || tm
->tm_sec
> 60 || tm
->tm_hour
> 24 ||
1966 /* test for > 24:00:00 */
1967 #ifdef HAVE_INT64_TIMESTAMP
1968 (tm
->tm_hour
== 24 && (tm
->tm_min
> 0 || tm
->tm_sec
> 0 ||
1969 *fsec
> INT64CONST(0))) ||
1970 *fsec
< INT64CONST(0) || *fsec
>= USECS_PER_SEC
1972 (tm
->tm_hour
== 24 && (tm
->tm_min
> 0 || tm
->tm_sec
> 0 ||
1974 *fsec
< 0 || *fsec
>= 1
1977 return DTERR_FIELD_OVERFLOW
;
1979 if ((fmask
& DTK_TIME_M
) != DTK_TIME_M
)
1980 return DTERR_BAD_FORMAT
;
1983 * If we had a full timezone spec, compute the offset (we could not do it
1984 * before, because we may need the date to resolve DST status).
1986 if (namedTz
!= NULL
)
1990 /* daylight savings time modifier disallowed with full TZ */
1991 if (fmask
& DTK_M(DTZMOD
))
1992 return DTERR_BAD_FORMAT
;
1994 /* if non-DST zone, we do not need to know the date */
1995 if (pg_get_timezone_offset(namedTz
, &gmtoff
))
1997 *tzp
= -(int) gmtoff
;
2001 /* a date has to be specified */
2002 if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
2003 return DTERR_BAD_FORMAT
;
2004 *tzp
= DetermineTimeZoneOffset(tm
, namedTz
);
2008 /* timezone not specified? then find local timezone if possible */
2009 if (tzp
!= NULL
&& !(fmask
& DTK_M(TZ
)))
2015 * daylight savings time modifier but no standard timezone? then error
2017 if (fmask
& DTK_M(DTZMOD
))
2018 return DTERR_BAD_FORMAT
;
2020 if ((fmask
& DTK_DATE_M
) == 0)
2021 GetCurrentDateTime(tmp
);
2024 tmp
->tm_year
= tm
->tm_year
;
2025 tmp
->tm_mon
= tm
->tm_mon
;
2026 tmp
->tm_mday
= tm
->tm_mday
;
2028 tmp
->tm_hour
= tm
->tm_hour
;
2029 tmp
->tm_min
= tm
->tm_min
;
2030 tmp
->tm_sec
= tm
->tm_sec
;
2031 *tzp
= DetermineTimeZoneOffset(tmp
, session_timezone
);
2032 tm
->tm_isdst
= tmp
->tm_isdst
;
2039 * Decode date string which includes delimiters.
2040 * Return 0 if okay, a DTERR code if not.
2042 * str: field to be parsed
2043 * fmask: bitmask for field types already seen
2044 * *tmask: receives bitmask for fields found here
2045 * *is2digits: set to TRUE if we find 2-digit year
2046 * *tm: field values are stored into appropriate members of this struct
2049 DecodeDate(char *str
, int fmask
, int *tmask
, bool *is2digits
,
2057 bool haveTextMonth
= FALSE
;
2061 char *field
[MAXDATEFIELDS
];
2065 /* parse this string... */
2066 while (*str
!= '\0' && nf
< MAXDATEFIELDS
)
2068 /* skip field separators */
2069 while (!isalnum((unsigned char) *str
))
2073 if (isdigit((unsigned char) *str
))
2075 while (isdigit((unsigned char) *str
))
2078 else if (isalpha((unsigned char) *str
))
2080 while (isalpha((unsigned char) *str
))
2084 /* Just get rid of any non-digit, non-alpha characters... */
2090 /* look first for text fields, since that will be unambiguous month */
2091 for (i
= 0; i
< nf
; i
++)
2093 if (isalpha((unsigned char) *field
[i
]))
2095 type
= DecodeSpecial(i
, field
[i
], &val
);
2096 if (type
== IGNORE_DTF
)
2099 dmask
= DTK_M(type
);
2104 haveTextMonth
= TRUE
;
2108 return DTERR_BAD_FORMAT
;
2111 return DTERR_BAD_FORMAT
;
2116 /* mark this field as being completed */
2121 /* now pick up remaining numeric fields */
2122 for (i
= 0; i
< nf
; i
++)
2124 if (field
[i
] == NULL
)
2127 if ((len
= strlen(field
[i
])) <= 0)
2128 return DTERR_BAD_FORMAT
;
2130 dterr
= DecodeNumber(len
, field
[i
], haveTextMonth
, fmask
,
2137 return DTERR_BAD_FORMAT
;
2143 if ((fmask
& ~(DTK_M(DOY
) | DTK_M(TZ
))) != DTK_DATE_M
)
2144 return DTERR_BAD_FORMAT
;
2146 /* validation of the field values must wait until ValidateDate() */
2152 * Check valid year/month/day values, handle BC and DOY cases
2153 * Return 0 if okay, a DTERR code if not.
2156 ValidateDate(int fmask
, bool is2digits
, bool bc
, struct pg_tm
* tm
)
2158 if (fmask
& DTK_M(YEAR
))
2162 /* there is no year zero in AD/BC notation */
2163 if (tm
->tm_year
<= 0)
2164 return DTERR_FIELD_OVERFLOW
;
2165 /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
2166 tm
->tm_year
= -(tm
->tm_year
- 1);
2170 /* allow 2-digit input for 1970-2069 AD; 00 is allowed */
2171 if (tm
->tm_year
< 0) /* just paranoia */
2172 return DTERR_FIELD_OVERFLOW
;
2173 if (tm
->tm_year
< 70)
2174 tm
->tm_year
+= 2000;
2175 else if (tm
->tm_year
< 100)
2176 tm
->tm_year
+= 1900;
2180 /* there is no year zero in AD/BC notation */
2181 if (tm
->tm_year
<= 0)
2182 return DTERR_FIELD_OVERFLOW
;
2186 /* now that we have correct year, decode DOY */
2187 if (fmask
& DTK_M(DOY
))
2189 j2date(date2j(tm
->tm_year
, 1, 1) + tm
->tm_yday
- 1,
2190 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
2193 /* check for valid month */
2194 if (fmask
& DTK_M(MONTH
))
2196 if (tm
->tm_mon
< 1 || tm
->tm_mon
> MONTHS_PER_YEAR
)
2197 return DTERR_MD_FIELD_OVERFLOW
;
2200 /* minimal check for valid day */
2201 if (fmask
& DTK_M(DAY
))
2203 if (tm
->tm_mday
< 1 || tm
->tm_mday
> 31)
2204 return DTERR_MD_FIELD_OVERFLOW
;
2207 if ((fmask
& DTK_DATE_M
) == DTK_DATE_M
)
2210 * Check for valid day of month, now that we know for sure the month
2211 * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems
2212 * unlikely that "Feb 29" is a YMD-order error.
2214 if (tm
->tm_mday
> day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1])
2215 return DTERR_FIELD_OVERFLOW
;
2223 * Decode time string which includes delimiters.
2224 * Return 0 if okay, a DTERR code if not.
2226 * Only check the lower limit on hours, since this same code can be
2227 * used to represent time spans.
2230 DecodeTime(char *str
, int fmask
, int range
,
2231 int *tmask
, struct pg_tm
* tm
, fsec_t
*fsec
)
2235 *tmask
= DTK_TIME_M
;
2238 tm
->tm_hour
= strtoi(str
, &cp
, 10);
2239 if (errno
== ERANGE
)
2240 return DTERR_FIELD_OVERFLOW
;
2242 return DTERR_BAD_FORMAT
;
2245 tm
->tm_min
= strtoi(str
, &cp
, 10);
2246 if (errno
== ERANGE
)
2247 return DTERR_FIELD_OVERFLOW
;
2252 /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */
2253 if (range
== (INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
)))
2255 tm
->tm_sec
= tm
->tm_min
;
2256 tm
->tm_min
= tm
->tm_hour
;
2260 else if (*cp
== '.')
2262 /* always assume mm:ss.sss is MINUTE TO SECOND */
2266 frac
= strtod(str
, &cp
);
2268 return DTERR_BAD_FORMAT
;
2269 #ifdef HAVE_INT64_TIMESTAMP
2270 *fsec
= rint(frac
* 1000000);
2274 tm
->tm_sec
= tm
->tm_min
;
2275 tm
->tm_min
= tm
->tm_hour
;
2278 else if (*cp
== ':')
2282 tm
->tm_sec
= strtoi(str
, &cp
, 10);
2283 if (errno
== ERANGE
)
2284 return DTERR_FIELD_OVERFLOW
;
2287 else if (*cp
== '.')
2292 frac
= strtod(str
, &cp
);
2294 return DTERR_BAD_FORMAT
;
2295 #ifdef HAVE_INT64_TIMESTAMP
2296 *fsec
= rint(frac
* 1000000);
2302 return DTERR_BAD_FORMAT
;
2305 return DTERR_BAD_FORMAT
;
2307 /* do a sanity check */
2308 #ifdef HAVE_INT64_TIMESTAMP
2309 if (tm
->tm_hour
< 0 || tm
->tm_min
< 0 || tm
->tm_min
> 59 ||
2310 tm
->tm_sec
< 0 || tm
->tm_sec
> 60 || *fsec
< INT64CONST(0) ||
2311 *fsec
>= USECS_PER_SEC
)
2312 return DTERR_FIELD_OVERFLOW
;
2314 if (tm
->tm_hour
< 0 || tm
->tm_min
< 0 || tm
->tm_min
> 59 ||
2315 tm
->tm_sec
< 0 || tm
->tm_sec
> 60 || *fsec
< 0 || *fsec
>= 1)
2316 return DTERR_FIELD_OVERFLOW
;
2324 * Interpret plain numeric field as a date value in context.
2325 * Return 0 if okay, a DTERR code if not.
2328 DecodeNumber(int flen
, char *str
, bool haveTextMonth
, int fmask
,
2329 int *tmask
, struct pg_tm
* tm
, fsec_t
*fsec
, bool *is2digits
)
2338 val
= strtoi(str
, &cp
, 10);
2339 if (errno
== ERANGE
)
2340 return DTERR_FIELD_OVERFLOW
;
2342 return DTERR_BAD_FORMAT
;
2349 * More than two digits before decimal point? Then could be a date or
2350 * a run-together time: 2001.360 20011225 040506.789
2354 dterr
= DecodeNumberField(flen
, str
,
2355 (fmask
| DTK_DATE_M
),
2363 frac
= strtod(cp
, &cp
);
2365 return DTERR_BAD_FORMAT
;
2366 #ifdef HAVE_INT64_TIMESTAMP
2367 *fsec
= rint(frac
* 1000000);
2372 else if (*cp
!= '\0')
2373 return DTERR_BAD_FORMAT
;
2375 /* Special case for day of year */
2376 if (flen
== 3 && (fmask
& DTK_DATE_M
) == DTK_M(YEAR
) && val
>= 1 &&
2379 *tmask
= (DTK_M(DOY
) | DTK_M(MONTH
) | DTK_M(DAY
));
2381 /* tm_mon and tm_mday can't actually be set yet ... */
2385 /* Switch based on what we have so far */
2386 switch (fmask
& DTK_DATE_M
)
2391 * Nothing so far; make a decision about what we think the input
2392 * is. There used to be lots of heuristics here, but the
2393 * consensus now is to be paranoid. It *must* be either
2394 * YYYY-MM-DD (with a more-than-two-digit year field), or the
2395 * field order defined by DateOrder.
2397 if (flen
>= 3 || DateOrder
== DATEORDER_YMD
)
2399 *tmask
= DTK_M(YEAR
);
2402 else if (DateOrder
== DATEORDER_DMY
)
2404 *tmask
= DTK_M(DAY
);
2409 *tmask
= DTK_M(MONTH
);
2415 /* Must be at second field of YY-MM-DD */
2416 *tmask
= DTK_M(MONTH
);
2420 case (DTK_M(MONTH
)):
2424 * We are at the first numeric field of a date that included a
2425 * textual month name. We want to support the variants
2426 * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
2427 * inputs. We will also accept MON-DD-YY or DD-MON-YY in
2428 * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
2430 if (flen
>= 3 || DateOrder
== DATEORDER_YMD
)
2432 *tmask
= DTK_M(YEAR
);
2437 *tmask
= DTK_M(DAY
);
2443 /* Must be at second field of MM-DD-YY */
2444 *tmask
= DTK_M(DAY
);
2449 case (DTK_M(YEAR
) | DTK_M(MONTH
)):
2452 /* Need to accept DD-MON-YYYY even in YMD mode */
2453 if (flen
>= 3 && *is2digits
)
2455 /* Guess that first numeric field is day was wrong */
2456 *tmask
= DTK_M(DAY
); /* YEAR is already set */
2457 tm
->tm_mday
= tm
->tm_year
;
2463 *tmask
= DTK_M(DAY
);
2469 /* Must be at third field of YY-MM-DD */
2470 *tmask
= DTK_M(DAY
);
2476 /* Must be at second field of DD-MM-YY */
2477 *tmask
= DTK_M(MONTH
);
2481 case (DTK_M(MONTH
) | DTK_M(DAY
)):
2482 /* Must be at third field of DD-MM-YY or MM-DD-YY */
2483 *tmask
= DTK_M(YEAR
);
2487 case (DTK_M(YEAR
) | DTK_M(MONTH
) | DTK_M(DAY
)):
2488 /* we have all the date, so it must be a time field */
2489 dterr
= DecodeNumberField(flen
, str
, fmask
,
2497 /* Anything else is bogus input */
2498 return DTERR_BAD_FORMAT
;
2502 * When processing a year field, mark it for adjustment if it's only one
2505 if (*tmask
== DTK_M(YEAR
))
2506 *is2digits
= (flen
<= 2);
2512 /* DecodeNumberField()
2513 * Interpret numeric string as a concatenated date or time field.
2514 * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
2516 * Use the context of previously decoded fields to help with
2517 * the interpretation.
2520 DecodeNumberField(int len
, char *str
, int fmask
,
2521 int *tmask
, struct pg_tm
* tm
, fsec_t
*fsec
, bool *is2digits
)
2526 * Have a decimal point? Then this is a date or something with a seconds
2529 if ((cp
= strchr(str
, '.')) != NULL
)
2533 frac
= strtod(cp
, NULL
);
2534 #ifdef HAVE_INT64_TIMESTAMP
2535 *fsec
= rint(frac
* 1000000);
2542 /* No decimal point and no complete date yet? */
2543 else if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
2548 *tmask
= DTK_DATE_M
;
2550 tm
->tm_mday
= atoi(str
+ 6);
2552 tm
->tm_mon
= atoi(str
+ 4);
2554 tm
->tm_year
= atoi(str
+ 0);
2561 *tmask
= DTK_DATE_M
;
2562 tm
->tm_mday
= atoi(str
+ 4);
2564 tm
->tm_mon
= atoi(str
+ 2);
2566 tm
->tm_year
= atoi(str
+ 0);
2573 /* not all time fields are specified? */
2574 if ((fmask
& DTK_TIME_M
) != DTK_TIME_M
)
2579 *tmask
= DTK_TIME_M
;
2580 tm
->tm_sec
= atoi(str
+ 4);
2582 tm
->tm_min
= atoi(str
+ 2);
2584 tm
->tm_hour
= atoi(str
+ 0);
2591 *tmask
= DTK_TIME_M
;
2593 tm
->tm_min
= atoi(str
+ 2);
2595 tm
->tm_hour
= atoi(str
+ 0);
2601 return DTERR_BAD_FORMAT
;
2606 * Interpret string as a numeric timezone.
2608 * Return 0 if okay (and set *tzp), a DTERR code if not okay.
2610 * NB: this must *not* ereport on failure; see commands/variable.c.
2612 * Note: we allow timezone offsets up to 13:59. There are places that
2613 * use +1300 summer time.
2616 DecodeTimezone(char *str
, int *tzp
)
2624 /* leading character must be "+" or "-" */
2625 if (*str
!= '+' && *str
!= '-')
2626 return DTERR_BAD_FORMAT
;
2629 hr
= strtoi(str
+ 1, &cp
, 10);
2630 if (errno
== ERANGE
)
2631 return DTERR_TZDISP_OVERFLOW
;
2633 /* explicit delimiter? */
2637 min
= strtoi(cp
+ 1, &cp
, 10);
2638 if (errno
== ERANGE
)
2639 return DTERR_TZDISP_OVERFLOW
;
2643 sec
= strtoi(cp
+ 1, &cp
, 10);
2644 if (errno
== ERANGE
)
2645 return DTERR_TZDISP_OVERFLOW
;
2648 /* otherwise, might have run things together... */
2649 else if (*cp
== '\0' && strlen(str
) > 3)
2653 /* we could, but don't, support a run-together hhmmss format */
2658 if (hr
< 0 || hr
> 14)
2659 return DTERR_TZDISP_OVERFLOW
;
2660 if (min
< 0 || min
>= 60)
2661 return DTERR_TZDISP_OVERFLOW
;
2662 if (sec
< 0 || sec
>= 60)
2663 return DTERR_TZDISP_OVERFLOW
;
2665 tz
= (hr
* MINS_PER_HOUR
+ min
) * SECS_PER_MINUTE
+ sec
;
2672 return DTERR_BAD_FORMAT
;
2678 * Decode text string using lookup table.
2680 * Implement a cache lookup since it is likely that dates
2681 * will be related in format.
2683 * NB: this must *not* ereport on failure;
2684 * see commands/variable.c.
2687 DecodeSpecial(int field
, char *lowtoken
, int *val
)
2692 tp
= datecache
[field
];
2693 if (tp
== NULL
|| strncmp(lowtoken
, tp
->token
, TOKMAXLEN
) != 0)
2695 tp
= datebsearch(lowtoken
, timezonetktbl
, sztimezonetktbl
);
2697 tp
= datebsearch(lowtoken
, datetktbl
, szdatetktbl
);
2701 type
= UNKNOWN_FIELD
;
2706 datecache
[field
] = tp
;
2727 * Interpret previously parsed fields for general time interval.
2728 * Returns 0 if successful, DTERR code if bogus input detected.
2730 * Allow "date" field DTK_DATE since this could be just
2731 * an unsigned floating point number. - thomas 1997-11-16
2733 * Allow ISO-style time span, with implicit units on number of days
2734 * preceding an hh:mm:ss field. - thomas 1998-04-30
2737 DecodeInterval(char **field
, int *ftype
, int nf
, int range
,
2738 int *dtype
, struct pg_tm
* tm
, fsec_t
*fsec
)
2740 bool is_before
= FALSE
;
2761 /* read through list backwards to pick up units before values */
2762 for (i
= nf
- 1; i
>= 0; i
--)
2767 dterr
= DecodeTime(field
[i
], fmask
, range
,
2777 * Timezone is a token with a leading sign character and
2778 * at least one digit; there could be ':', '.', '-'
2779 * embedded in it as well.
2781 Assert(*field
[i
] == '-' || *field
[i
] == '+');
2784 * Try for hh:mm or hh:mm:ss. If not, fall through to
2785 * DTK_NUMBER case, which can handle signed float numbers
2786 * and signed year-month values.
2788 if (strchr(field
[i
] + 1, ':') != NULL
&&
2789 DecodeTime(field
[i
] + 1, fmask
, INTERVAL_FULL_RANGE
,
2790 &tmask
, tm
, fsec
) == 0)
2792 if (*field
[i
] == '-')
2794 /* flip the sign on all fields */
2795 tm
->tm_hour
= -tm
->tm_hour
;
2796 tm
->tm_min
= -tm
->tm_min
;
2797 tm
->tm_sec
= -tm
->tm_sec
;
2802 * Set the next type to be a day, if units are not
2803 * specified. This handles the case of '1 +02:03' since we
2804 * are reading right to left.
2814 if (type
== IGNORE_DTF
)
2816 /* use typmod to decide what rightmost field is */
2819 case INTERVAL_MASK(YEAR
):
2822 case INTERVAL_MASK(MONTH
):
2823 case INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
):
2826 case INTERVAL_MASK(DAY
):
2829 case INTERVAL_MASK(HOUR
):
2830 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
):
2831 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
2832 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
2835 case INTERVAL_MASK(MINUTE
):
2836 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
2839 case INTERVAL_MASK(SECOND
):
2840 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
2841 case INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
2851 val
= strtoi(field
[i
], &cp
, 10);
2852 if (errno
== ERANGE
)
2853 return DTERR_FIELD_OVERFLOW
;
2857 /* SQL "years-months" syntax */
2860 val2
= strtoi(cp
+ 1, &cp
, 10);
2861 if (errno
== ERANGE
|| val2
< 0 || val2
>= MONTHS_PER_YEAR
)
2862 return DTERR_FIELD_OVERFLOW
;
2864 return DTERR_BAD_FORMAT
;
2868 val
= val
* MONTHS_PER_YEAR
+ val2
;
2871 else if (*cp
== '.')
2873 fval
= strtod(cp
, &cp
);
2875 return DTERR_BAD_FORMAT
;
2877 if (*field
[i
] == '-')
2880 else if (*cp
== '\0')
2883 return DTERR_BAD_FORMAT
;
2885 tmask
= 0; /* DTK_M(type); */
2890 #ifdef HAVE_INT64_TIMESTAMP
2891 *fsec
+= rint(val
+ fval
);
2893 *fsec
+= (val
+ fval
) * 1e-6;
2895 tmask
= DTK_M(MICROSECOND
);
2899 #ifdef HAVE_INT64_TIMESTAMP
2900 *fsec
+= rint((val
+ fval
) * 1000);
2902 *fsec
+= (val
+ fval
) * 1e-3;
2904 tmask
= DTK_M(MILLISECOND
);
2909 #ifdef HAVE_INT64_TIMESTAMP
2910 *fsec
+= rint(fval
* 1000000);
2916 * If any subseconds were specified, consider this
2917 * microsecond and millisecond input as well.
2920 tmask
= DTK_M(SECOND
);
2922 tmask
= DTK_ALL_SECS_M
;
2931 fval
*= SECS_PER_MINUTE
;
2934 #ifdef HAVE_INT64_TIMESTAMP
2935 *fsec
+= rint((fval
- sec
) * 1000000);
2937 *fsec
+= fval
- sec
;
2940 tmask
= DTK_M(MINUTE
);
2949 fval
*= SECS_PER_HOUR
;
2952 #ifdef HAVE_INT64_TIMESTAMP
2953 *fsec
+= rint((fval
- sec
) * 1000000);
2955 *fsec
+= fval
- sec
;
2958 tmask
= DTK_M(HOUR
);
2968 fval
*= SECS_PER_DAY
;
2971 #ifdef HAVE_INT64_TIMESTAMP
2972 *fsec
+= rint((fval
- sec
) * 1000000);
2974 *fsec
+= fval
- sec
;
2977 tmask
= (fmask
& DTK_M(DAY
)) ? 0 : DTK_M(DAY
);
2981 tm
->tm_mday
+= val
* 7;
2987 extra_days
= (int32
) fval
;
2988 tm
->tm_mday
+= extra_days
;
2994 fval
*= SECS_PER_DAY
;
2997 #ifdef HAVE_INT64_TIMESTAMP
2998 *fsec
+= rint((fval
- sec
) * 1000000);
3000 *fsec
+= fval
- sec
;
3004 tmask
= (fmask
& DTK_M(DAY
)) ? 0 : DTK_M(DAY
);
3013 fval
*= DAYS_PER_MONTH
;
3021 fval
*= SECS_PER_DAY
;
3024 #ifdef HAVE_INT64_TIMESTAMP
3025 *fsec
+= rint((fval
- sec
) * 1000000);
3027 *fsec
+= fval
- sec
;
3031 tmask
= DTK_M(MONTH
);
3037 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
;
3038 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
3042 tm
->tm_year
+= val
* 10;
3044 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
* 10;
3045 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
3049 tm
->tm_year
+= val
* 100;
3051 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
* 100;
3052 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
3055 case DTK_MILLENNIUM
:
3056 tm
->tm_year
+= val
* 1000;
3058 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
* 1000;
3059 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
3063 return DTERR_BAD_FORMAT
;
3069 type
= DecodeUnits(i
, field
[i
], &val
);
3070 if (type
== IGNORE_DTF
)
3073 tmask
= 0; /* DTK_M(type); */
3086 tmask
= (DTK_DATE_M
|| DTK_TIME_M
);
3091 return DTERR_BAD_FORMAT
;
3096 return DTERR_BAD_FORMAT
;
3100 return DTERR_BAD_FORMAT
;
3108 #ifdef HAVE_INT64_TIMESTAMP
3109 sec
= *fsec
/ USECS_PER_SEC
;
3110 *fsec
-= sec
* USECS_PER_SEC
;
3112 TMODULO(*fsec
, sec
, 1.0);
3120 tm
->tm_sec
= -tm
->tm_sec
;
3121 tm
->tm_min
= -tm
->tm_min
;
3122 tm
->tm_hour
= -tm
->tm_hour
;
3123 tm
->tm_mday
= -tm
->tm_mday
;
3124 tm
->tm_mon
= -tm
->tm_mon
;
3125 tm
->tm_year
= -tm
->tm_year
;
3128 /* ensure that at least one time field has been found */
3130 return DTERR_BAD_FORMAT
;
3137 * Decode text string using lookup table.
3138 * This routine supports time interval decoding
3139 * (hence, it need not recognize timezone names).
3142 DecodeUnits(int field
, char *lowtoken
, int *val
)
3147 tp
= deltacache
[field
];
3148 if (tp
== NULL
|| strncmp(lowtoken
, tp
->token
, TOKMAXLEN
) != 0)
3150 tp
= datebsearch(lowtoken
, deltatktbl
, szdeltatktbl
);
3154 type
= UNKNOWN_FIELD
;
3159 deltacache
[field
] = tp
;
3161 if (type
== TZ
|| type
== DTZ
)
3168 } /* DecodeUnits() */
3171 * Report an error detected by one of the datetime input processing routines.
3173 * dterr is the error code, str is the original input string, datatype is
3174 * the name of the datatype we were trying to accept.
3176 * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
3177 * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
3178 * separate SQLSTATE codes, so ...
3181 DateTimeParseError(int dterr
, const char *str
, const char *datatype
)
3185 case DTERR_FIELD_OVERFLOW
:
3187 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW
),
3188 errmsg("date/time field value out of range: \"%s\"",
3191 case DTERR_MD_FIELD_OVERFLOW
:
3192 /* <nanny>same as above, but add hint about DateStyle</nanny> */
3194 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW
),
3195 errmsg("date/time field value out of range: \"%s\"",
3197 errhint("Perhaps you need a different \"datestyle\" setting.")));
3199 case DTERR_INTERVAL_OVERFLOW
:
3201 (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW
),
3202 errmsg("interval field value out of range: \"%s\"",
3205 case DTERR_TZDISP_OVERFLOW
:
3207 (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE
),
3208 errmsg("time zone displacement out of range: \"%s\"",
3211 case DTERR_BAD_FORMAT
:
3214 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
3215 errmsg("invalid input syntax for type %s: \"%s\"",
3222 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
3223 * is WAY faster than the generic bsearch().
3225 static const datetkn
*
3226 datebsearch(const char *key
, const datetkn
*base
, int nel
)
3228 const datetkn
*last
= base
+ nel
- 1,
3232 while (last
>= base
)
3234 position
= base
+ ((last
- base
) >> 1);
3235 result
= key
[0] - position
->token
[0];
3238 result
= strncmp(key
, position
->token
, TOKMAXLEN
);
3243 last
= position
- 1;
3245 base
= position
+ 1;
3251 * Append representation of a numeric timezone offset to str.
3254 EncodeTimezone(char *str
, int tz
, int style
)
3261 min
= sec
/ SECS_PER_MINUTE
;
3262 sec
-= min
* SECS_PER_MINUTE
;
3263 hour
= min
/ MINS_PER_HOUR
;
3264 min
-= hour
* MINS_PER_HOUR
;
3267 /* TZ is negated compared to sign we wish to display ... */
3268 *str
++ = (tz
<= 0 ? '+' : '-');
3271 sprintf(str
, "%02d:%02d:%02d", hour
, min
, sec
);
3272 else if (min
!= 0 || style
== USE_XSD_DATES
)
3273 sprintf(str
, "%02d:%02d", hour
, min
);
3275 sprintf(str
, "%02d", hour
);
3279 * Encode date as local time.
3282 EncodeDateOnly(struct pg_tm
* tm
, int style
, char *str
)
3284 if (tm
->tm_mon
< 1 || tm
->tm_mon
> MONTHS_PER_YEAR
)
3291 /* compatible with ISO date formats */
3292 if (tm
->tm_year
> 0)
3293 sprintf(str
, "%04d-%02d-%02d",
3294 tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3296 sprintf(str
, "%04d-%02d-%02d %s",
3297 -(tm
->tm_year
- 1), tm
->tm_mon
, tm
->tm_mday
, "BC");
3301 /* compatible with Oracle/Ingres date formats */
3302 if (DateOrder
== DATEORDER_DMY
)
3303 sprintf(str
, "%02d/%02d", tm
->tm_mday
, tm
->tm_mon
);
3305 sprintf(str
, "%02d/%02d", tm
->tm_mon
, tm
->tm_mday
);
3306 if (tm
->tm_year
> 0)
3307 sprintf(str
+ 5, "/%04d", tm
->tm_year
);
3309 sprintf(str
+ 5, "/%04d %s", -(tm
->tm_year
- 1), "BC");
3312 case USE_GERMAN_DATES
:
3313 /* German-style date format */
3314 sprintf(str
, "%02d.%02d", tm
->tm_mday
, tm
->tm_mon
);
3315 if (tm
->tm_year
> 0)
3316 sprintf(str
+ 5, ".%04d", tm
->tm_year
);
3318 sprintf(str
+ 5, ".%04d %s", -(tm
->tm_year
- 1), "BC");
3321 case USE_POSTGRES_DATES
:
3323 /* traditional date-only style for Postgres */
3324 if (DateOrder
== DATEORDER_DMY
)
3325 sprintf(str
, "%02d-%02d", tm
->tm_mday
, tm
->tm_mon
);
3327 sprintf(str
, "%02d-%02d", tm
->tm_mon
, tm
->tm_mday
);
3328 if (tm
->tm_year
> 0)
3329 sprintf(str
+ 5, "-%04d", tm
->tm_year
);
3331 sprintf(str
+ 5, "-%04d %s", -(tm
->tm_year
- 1), "BC");
3336 } /* EncodeDateOnly() */
3340 * Encode time fields only.
3343 EncodeTimeOnly(struct pg_tm
* tm
, fsec_t fsec
, int *tzp
, int style
, char *str
)
3345 if (tm
->tm_hour
< 0 || tm
->tm_hour
> HOURS_PER_DAY
)
3348 sprintf(str
, "%02d:%02d", tm
->tm_hour
, tm
->tm_min
);
3351 * Print fractional seconds if any. The fractional field widths here
3352 * should be equal to the larger of MAX_TIME_PRECISION and
3353 * MAX_TIMESTAMP_PRECISION.
3357 #ifdef HAVE_INT64_TIMESTAMP
3358 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3360 sprintf(str
+ strlen(str
), ":%013.10f", tm
->tm_sec
+ fsec
);
3362 TrimTrailingZeros(str
);
3365 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3368 EncodeTimezone(str
, *tzp
, style
);
3371 } /* EncodeTimeOnly() */
3375 * Encode date and time interpreted as local time.
3376 * Support several date styles:
3377 * Postgres - day mon hh:mm:ss yyyy tz
3378 * SQL - mm/dd/yyyy hh:mm:ss.ss tz
3379 * ISO - yyyy-mm-dd hh:mm:ss+/-tz
3380 * German - dd.mm.yyyy hh:mm:ss tz
3381 * XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
3382 * Variants (affects order of month and day for Postgres and SQL styles):
3384 * European - dd/mm/yyyy
3387 EncodeDateTime(struct pg_tm
* tm
, fsec_t fsec
, int *tzp
, char **tzn
, int style
, char *str
)
3392 * Why are we checking only the month field? Change this to an assert...
3393 * if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR) return -1;
3395 Assert(tm
->tm_mon
>= 1 && tm
->tm_mon
<= MONTHS_PER_YEAR
);
3401 /* Compatible with ISO-8601 date formats */
3403 if (style
== USE_ISO_DATES
)
3404 sprintf(str
, "%04d-%02d-%02d %02d:%02d",
3405 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3406 tm
->tm_mon
, tm
->tm_mday
, tm
->tm_hour
, tm
->tm_min
);
3408 sprintf(str
, "%04d-%02d-%02dT%02d:%02d",
3409 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3410 tm
->tm_mon
, tm
->tm_mday
, tm
->tm_hour
, tm
->tm_min
);
3414 * Print fractional seconds if any. The field widths here should
3415 * be at least equal to MAX_TIMESTAMP_PRECISION.
3417 * In float mode, don't print fractional seconds before 1 AD,
3418 * since it's unlikely there's any precision left ...
3420 #ifdef HAVE_INT64_TIMESTAMP
3423 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3424 TrimTrailingZeros(str
);
3427 if (fsec
!= 0 && tm
->tm_year
> 0)
3429 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3430 TrimTrailingZeros(str
);
3434 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3437 * tzp == NULL indicates that we don't want *any* time zone info
3438 * in the output string. *tzn != NULL indicates that we have alpha
3439 * time zone info available. tm_isdst != -1 indicates that we have
3440 * a valid time zone translation.
3442 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3443 EncodeTimezone(str
, *tzp
, style
);
3445 if (tm
->tm_year
<= 0)
3446 sprintf(str
+ strlen(str
), " BC");
3450 /* Compatible with Oracle/Ingres date formats */
3452 if (DateOrder
== DATEORDER_DMY
)
3453 sprintf(str
, "%02d/%02d", tm
->tm_mday
, tm
->tm_mon
);
3455 sprintf(str
, "%02d/%02d", tm
->tm_mon
, tm
->tm_mday
);
3457 sprintf(str
+ 5, "/%04d %02d:%02d",
3458 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3459 tm
->tm_hour
, tm
->tm_min
);
3462 * Print fractional seconds if any. The field widths here should
3463 * be at least equal to MAX_TIMESTAMP_PRECISION.
3465 * In float mode, don't print fractional seconds before 1 AD,
3466 * since it's unlikely there's any precision left ...
3468 #ifdef HAVE_INT64_TIMESTAMP
3471 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3472 TrimTrailingZeros(str
);
3475 if (fsec
!= 0 && tm
->tm_year
> 0)
3477 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3478 TrimTrailingZeros(str
);
3482 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3484 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3487 sprintf(str
+ strlen(str
), " %.*s", MAXTZLEN
, *tzn
);
3489 EncodeTimezone(str
, *tzp
, style
);
3492 if (tm
->tm_year
<= 0)
3493 sprintf(str
+ strlen(str
), " BC");
3496 case USE_GERMAN_DATES
:
3497 /* German variant on European style */
3499 sprintf(str
, "%02d.%02d", tm
->tm_mday
, tm
->tm_mon
);
3501 sprintf(str
+ 5, ".%04d %02d:%02d",
3502 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3503 tm
->tm_hour
, tm
->tm_min
);
3506 * Print fractional seconds if any. The field widths here should
3507 * be at least equal to MAX_TIMESTAMP_PRECISION.
3509 * In float mode, don't print fractional seconds before 1 AD,
3510 * since it's unlikely there's any precision left ...
3512 #ifdef HAVE_INT64_TIMESTAMP
3515 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3516 TrimTrailingZeros(str
);
3519 if (fsec
!= 0 && tm
->tm_year
> 0)
3521 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3522 TrimTrailingZeros(str
);
3526 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3528 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3531 sprintf(str
+ strlen(str
), " %.*s", MAXTZLEN
, *tzn
);
3533 EncodeTimezone(str
, *tzp
, style
);
3536 if (tm
->tm_year
<= 0)
3537 sprintf(str
+ strlen(str
), " BC");
3540 case USE_POSTGRES_DATES
:
3542 /* Backward-compatible with traditional Postgres abstime dates */
3544 day
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3545 tm
->tm_wday
= j2day(day
);
3547 strncpy(str
, days
[tm
->tm_wday
], 3);
3548 strcpy(str
+ 3, " ");
3550 if (DateOrder
== DATEORDER_DMY
)
3551 sprintf(str
+ 4, "%02d %3s", tm
->tm_mday
, months
[tm
->tm_mon
- 1]);
3553 sprintf(str
+ 4, "%3s %02d", months
[tm
->tm_mon
- 1], tm
->tm_mday
);
3555 sprintf(str
+ 10, " %02d:%02d", tm
->tm_hour
, tm
->tm_min
);
3558 * Print fractional seconds if any. The field widths here should
3559 * be at least equal to MAX_TIMESTAMP_PRECISION.
3561 * In float mode, don't print fractional seconds before 1 AD,
3562 * since it's unlikely there's any precision left ...
3564 #ifdef HAVE_INT64_TIMESTAMP
3567 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3568 TrimTrailingZeros(str
);
3571 if (fsec
!= 0 && tm
->tm_year
> 0)
3573 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3574 TrimTrailingZeros(str
);
3578 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3580 sprintf(str
+ strlen(str
), " %04d",
3581 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1));
3583 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3586 sprintf(str
+ strlen(str
), " %.*s", MAXTZLEN
, *tzn
);
3590 * We have a time zone, but no string version. Use the
3591 * numeric form, but be sure to include a leading space to
3592 * avoid formatting something which would be rejected by
3593 * the date/time parser later. - thomas 2001-10-19
3595 sprintf(str
+ strlen(str
), " ");
3596 EncodeTimezone(str
, *tzp
, style
);
3600 if (tm
->tm_year
<= 0)
3601 sprintf(str
+ strlen(str
), " BC");
3610 * Interpret time structure as a delta time and convert to string.
3612 * Support "traditional Postgres" and ISO-8601 styles.
3613 * Actually, afaik ISO does not address time interval formatting,
3614 * but this looks similar to the spec for absolute date/time.
3615 * - thomas 1998-04-30
3618 EncodeInterval(struct pg_tm
* tm
, fsec_t fsec
, int style
, char *str
)
3620 bool is_before
= FALSE
;
3621 bool is_nonzero
= FALSE
;
3625 * The sign of year and month are guaranteed to match, since they are
3626 * stored internally as "month". But we'll need to check for is_before and
3627 * is_nonzero when determining the signs of hour/minute/seconds fields.
3631 /* compatible with ISO date formats */
3633 if (tm
->tm_year
!= 0)
3635 sprintf(cp
, "%d year%s",
3636 tm
->tm_year
, (tm
->tm_year
!= 1) ? "s" : "");
3638 is_before
= (tm
->tm_year
< 0);
3642 if (tm
->tm_mon
!= 0)
3644 sprintf(cp
, "%s%s%d mon%s", is_nonzero
? " " : "",
3645 (is_before
&& tm
->tm_mon
> 0) ? "+" : "",
3646 tm
->tm_mon
, (tm
->tm_mon
!= 1) ? "s" : "");
3648 is_before
= (tm
->tm_mon
< 0);
3652 if (tm
->tm_mday
!= 0)
3654 sprintf(cp
, "%s%s%d day%s", is_nonzero
? " " : "",
3655 (is_before
&& tm
->tm_mday
> 0) ? "+" : "",
3656 tm
->tm_mday
, (tm
->tm_mday
!= 1) ? "s" : "");
3658 is_before
= (tm
->tm_mday
< 0);
3662 if (!is_nonzero
|| tm
->tm_hour
!= 0 || tm
->tm_min
!= 0 ||
3663 tm
->tm_sec
!= 0 || fsec
!= 0)
3665 int minus
= (tm
->tm_hour
< 0 || tm
->tm_min
< 0 ||
3666 tm
->tm_sec
< 0 || fsec
< 0);
3668 sprintf(cp
, "%s%s%02d:%02d", is_nonzero
? " " : "",
3669 (minus
? "-" : (is_before
? "+" : "")),
3670 abs(tm
->tm_hour
), abs(tm
->tm_min
));
3672 /* Mark as "non-zero" since the fields are now filled in */
3675 /* need fractional seconds? */
3678 #ifdef HAVE_INT64_TIMESTAMP
3679 sprintf(cp
, ":%02d", abs(tm
->tm_sec
));
3681 sprintf(cp
, ".%06d", Abs(fsec
));
3684 sprintf(cp
, ":%012.9f", fabs(fsec
));
3686 TrimTrailingZeros(cp
);
3691 sprintf(cp
, ":%02d", abs(tm
->tm_sec
));
3697 case USE_POSTGRES_DATES
:
3702 if (tm
->tm_year
!= 0)
3704 int year
= tm
->tm_year
;
3706 if (tm
->tm_year
< 0)
3709 sprintf(cp
, "%d year%s", year
,
3710 (year
!= 1) ? "s" : "");
3712 is_before
= (tm
->tm_year
< 0);
3716 if (tm
->tm_mon
!= 0)
3718 int mon
= tm
->tm_mon
;
3720 if (is_before
|| (!is_nonzero
&& tm
->tm_mon
< 0))
3723 sprintf(cp
, "%s%d mon%s", is_nonzero
? " " : "", mon
,
3724 (mon
!= 1) ? "s" : "");
3727 is_before
= (tm
->tm_mon
< 0);
3731 if (tm
->tm_mday
!= 0)
3733 int day
= tm
->tm_mday
;
3735 if (is_before
|| (!is_nonzero
&& tm
->tm_mday
< 0))
3738 sprintf(cp
, "%s%d day%s", is_nonzero
? " " : "", day
,
3739 (day
!= 1) ? "s" : "");
3742 is_before
= (tm
->tm_mday
< 0);
3745 if (tm
->tm_hour
!= 0)
3747 int hour
= tm
->tm_hour
;
3749 if (is_before
|| (!is_nonzero
&& tm
->tm_hour
< 0))
3752 sprintf(cp
, "%s%d hour%s", is_nonzero
? " " : "", hour
,
3753 (hour
!= 1) ? "s" : "");
3756 is_before
= (tm
->tm_hour
< 0);
3760 if (tm
->tm_min
!= 0)
3762 int min
= tm
->tm_min
;
3764 if (is_before
|| (!is_nonzero
&& tm
->tm_min
< 0))
3767 sprintf(cp
, "%s%d min%s", is_nonzero
? " " : "", min
,
3768 (min
!= 1) ? "s" : "");
3771 is_before
= (tm
->tm_min
< 0);
3775 /* fractional seconds? */
3780 #ifdef HAVE_INT64_TIMESTAMP
3782 if (is_before
|| (!is_nonzero
&& tm
->tm_sec
< 0))
3784 tm
->tm_sec
= -tm
->tm_sec
;
3788 else if (!is_nonzero
&& tm
->tm_sec
== 0 && fsec
< 0)
3793 sprintf(cp
, "%s%d.%02d secs", is_nonzero
? " " : "",
3794 tm
->tm_sec
, ((int) sec
) / 10000);
3799 if (is_before
|| (!is_nonzero
&& fsec
< 0))
3802 sprintf(cp
, "%s%.2f secs", is_nonzero
? " " : "", sec
);
3805 is_before
= (fsec
< 0);
3809 /* otherwise, integer seconds only? */
3810 else if (tm
->tm_sec
!= 0)
3812 int sec
= tm
->tm_sec
;
3814 if (is_before
|| (!is_nonzero
&& tm
->tm_sec
< 0))
3817 sprintf(cp
, "%s%d sec%s", is_nonzero
? " " : "", sec
,
3818 (sec
!= 1) ? "s" : "");
3821 is_before
= (tm
->tm_sec
< 0);
3827 /* identically zero? then put in a unitless zero... */
3834 if (is_before
&& (style
!= USE_ISO_DATES
))
3841 } /* EncodeInterval() */
3845 * We've been burnt by stupid errors in the ordering of the datetkn tables
3846 * once too often. Arrange to check them during postmaster start.
3849 CheckDateTokenTable(const char *tablename
, const datetkn
*base
, int nel
)
3854 for (i
= 1; i
< nel
; i
++)
3856 if (strncmp(base
[i
- 1].token
, base
[i
].token
, TOKMAXLEN
) >= 0)
3858 elog(LOG
, "ordering error in %s table: \"%.*s\" >= \"%.*s\"",
3860 TOKMAXLEN
, base
[i
- 1].token
,
3861 TOKMAXLEN
, base
[i
].token
);
3869 CheckDateTokenTables(void)
3873 Assert(UNIX_EPOCH_JDATE
== date2j(1970, 1, 1));
3874 Assert(POSTGRES_EPOCH_JDATE
== date2j(2000, 1, 1));
3876 ok
&= CheckDateTokenTable("datetktbl", datetktbl
, szdatetktbl
);
3877 ok
&= CheckDateTokenTable("deltatktbl", deltatktbl
, szdeltatktbl
);
3882 * This function gets called during timezone config file load or reload
3883 * to create the final array of timezone tokens. The argument array
3884 * is already sorted in name order. This data is in a temporary memory
3885 * context and must be copied to somewhere permanent.
3888 InstallTimeZoneAbbrevs(tzEntry
*abbrevs
, int n
)
3894 * Copy the data into TopMemoryContext and convert to datetkn format.
3896 newtbl
= (datetkn
*) MemoryContextAlloc(TopMemoryContext
,
3897 n
* sizeof(datetkn
));
3898 for (i
= 0; i
< n
; i
++)
3900 strncpy(newtbl
[i
].token
, abbrevs
[i
].abbrev
, TOKMAXLEN
);
3901 newtbl
[i
].type
= abbrevs
[i
].is_dst
? DTZ
: TZ
;
3902 TOVAL(&newtbl
[i
], abbrevs
[i
].offset
/ 60);
3905 /* Check the ordering, if testing */
3906 Assert(CheckDateTokenTable("timezone offset", newtbl
, n
));
3908 /* Now safe to replace existing table (if any) */
3910 pfree(timezonetktbl
);
3911 timezonetktbl
= newtbl
;
3912 sztimezonetktbl
= n
;
3914 /* clear date cache in case it contains any stale timezone names */
3915 for (i
= 0; i
< MAXDATEFIELDS
; i
++)
3916 datecache
[i
] = NULL
;
3920 * This set-returning function reads all the available time zone abbreviations
3921 * and returns a set of (abbrev, utc_offset, is_dst).
3924 pg_timezone_abbrevs(PG_FUNCTION_ARGS
)
3926 FuncCallContext
*funcctx
;
3932 char buffer
[TOKMAXLEN
+ 1];
3935 Interval
*resInterval
;
3937 /* stuff done only on the first call of the function */
3938 if (SRF_IS_FIRSTCALL())
3941 MemoryContext oldcontext
;
3943 /* create a function context for cross-call persistence */
3944 funcctx
= SRF_FIRSTCALL_INIT();
3947 * switch to memory context appropriate for multiple function calls
3949 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
3951 /* allocate memory for user context */
3952 pindex
= (int *) palloc(sizeof(int));
3954 funcctx
->user_fctx
= (void *) pindex
;
3957 * build tupdesc for result tuples. This must match this function's
3960 tupdesc
= CreateTemplateTupleDesc(3, false);
3961 TupleDescInitEntry(tupdesc
, (AttrNumber
) 1, "abbrev",
3963 TupleDescInitEntry(tupdesc
, (AttrNumber
) 2, "utc_offset",
3964 INTERVALOID
, -1, 0);
3965 TupleDescInitEntry(tupdesc
, (AttrNumber
) 3, "is_dst",
3968 funcctx
->tuple_desc
= BlessTupleDesc(tupdesc
);
3969 MemoryContextSwitchTo(oldcontext
);
3972 /* stuff done on every call of the function */
3973 funcctx
= SRF_PERCALL_SETUP();
3974 pindex
= (int *) funcctx
->user_fctx
;
3976 if (*pindex
>= sztimezonetktbl
)
3977 SRF_RETURN_DONE(funcctx
);
3979 MemSet(nulls
, 0, sizeof(nulls
));
3982 * Convert name to text, using upcasing conversion that is the inverse of
3983 * what ParseDateTime() uses.
3985 strncpy(buffer
, timezonetktbl
[*pindex
].token
, TOKMAXLEN
);
3986 buffer
[TOKMAXLEN
] = '\0'; /* may not be null-terminated */
3987 for (p
= (unsigned char *) buffer
; *p
; p
++)
3988 *p
= pg_toupper(*p
);
3990 values
[0] = CStringGetTextDatum(buffer
);
3992 MemSet(&tm
, 0, sizeof(struct pg_tm
));
3993 tm
.tm_min
= (-1) * FROMVAL(&timezonetktbl
[*pindex
]);
3994 resInterval
= (Interval
*) palloc(sizeof(Interval
));
3995 tm2interval(&tm
, 0, resInterval
);
3996 values
[1] = IntervalPGetDatum(resInterval
);
3998 Assert(timezonetktbl
[*pindex
].type
== DTZ
||
3999 timezonetktbl
[*pindex
].type
== TZ
);
4000 values
[2] = BoolGetDatum(timezonetktbl
[*pindex
].type
== DTZ
);
4004 tuple
= heap_form_tuple(funcctx
->tuple_desc
, values
, nulls
);
4005 result
= HeapTupleGetDatum(tuple
);
4007 SRF_RETURN_NEXT(funcctx
, result
);
4011 * This set-returning function reads all the available full time zones
4012 * and returns a set of (name, abbrev, utc_offset, is_dst).
4015 pg_timezone_names(PG_FUNCTION_ARGS
)
4017 MemoryContext oldcontext
;
4018 FuncCallContext
*funcctx
;
4029 Interval
*resInterval
;
4032 /* stuff done only on the first call of the function */
4033 if (SRF_IS_FIRSTCALL())
4037 /* create a function context for cross-call persistence */
4038 funcctx
= SRF_FIRSTCALL_INIT();
4041 * switch to memory context appropriate for multiple function calls
4043 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4045 /* initialize timezone scanning code */
4046 tzenum
= pg_tzenumerate_start();
4047 funcctx
->user_fctx
= (void *) tzenum
;
4050 * build tupdesc for result tuples. This must match this function's
4053 tupdesc
= CreateTemplateTupleDesc(4, false);
4054 TupleDescInitEntry(tupdesc
, (AttrNumber
) 1, "name",
4056 TupleDescInitEntry(tupdesc
, (AttrNumber
) 2, "abbrev",
4058 TupleDescInitEntry(tupdesc
, (AttrNumber
) 3, "utc_offset",
4059 INTERVALOID
, -1, 0);
4060 TupleDescInitEntry(tupdesc
, (AttrNumber
) 4, "is_dst",
4063 funcctx
->tuple_desc
= BlessTupleDesc(tupdesc
);
4064 MemoryContextSwitchTo(oldcontext
);
4067 /* stuff done on every call of the function */
4068 funcctx
= SRF_PERCALL_SETUP();
4069 tzenum
= (pg_tzenum
*) funcctx
->user_fctx
;
4071 /* search for another zone to display */
4074 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4075 tz
= pg_tzenumerate_next(tzenum
);
4076 MemoryContextSwitchTo(oldcontext
);
4080 pg_tzenumerate_end(tzenum
);
4081 funcctx
->user_fctx
= NULL
;
4082 SRF_RETURN_DONE(funcctx
);
4085 /* Convert now() to local time in this zone */
4086 if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
4087 &tzoff
, &tm
, &fsec
, &tzn
, tz
) != 0)
4088 continue; /* ignore if conversion fails */
4090 /* Ignore zic's rather silly "Factory" time zone */
4091 if (tzn
&& strcmp(tzn
, "Local time zone must be set--see zic manual page") == 0)
4094 /* Found a displayable zone */
4098 MemSet(nulls
, 0, sizeof(nulls
));
4100 values
[0] = CStringGetTextDatum(pg_get_timezone_name(tz
));
4101 values
[1] = CStringGetTextDatum(tzn
? tzn
: "");
4103 MemSet(&itm
, 0, sizeof(struct pg_tm
));
4104 itm
.tm_sec
= -tzoff
;
4105 resInterval
= (Interval
*) palloc(sizeof(Interval
));
4106 tm2interval(&itm
, 0, resInterval
);
4107 values
[2] = IntervalPGetDatum(resInterval
);
4109 values
[3] = BoolGetDatum(tm
.tm_isdst
> 0);
4111 tuple
= heap_form_tuple(funcctx
->tuple_desc
, values
, nulls
);
4112 result
= HeapTupleGetDatum(tuple
);
4114 SRF_RETURN_NEXT(funcctx
, result
);