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 *tmask
,
39 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 ':' or '.')
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 if (isdigit((unsigned char) *cp
))
616 APPEND_CHAR(bufp
, bufend
, *cp
++);
617 while (isdigit((unsigned char) *cp
) ||
618 *cp
== ':' || *cp
== '.')
619 APPEND_CHAR(bufp
, bufend
, *cp
++);
622 else if (isalpha((unsigned char) *cp
))
624 ftype
[nf
] = DTK_SPECIAL
;
625 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
626 while (isalpha((unsigned char) *cp
))
627 APPEND_CHAR(bufp
, bufend
, pg_tolower((unsigned char) *cp
++));
629 /* otherwise something wrong... */
631 return DTERR_BAD_FORMAT
;
633 /* ignore other punctuation but use as delimiter */
634 else if (ispunct((unsigned char) *cp
))
639 /* otherwise, something is not right... */
641 return DTERR_BAD_FORMAT
;
643 /* force in a delimiter after each field */
655 * Interpret previously parsed fields for general date and time.
656 * Return 0 if full date, 1 if only time, and negative DTERR code if problems.
657 * (Currently, all callers treat 1 as an error return too.)
659 * External format(s):
660 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
661 * "Fri Feb-7-1997 15:23:27"
662 * "Feb-7-1997 15:23:27"
663 * "2-7-1997 15:23:27"
664 * "1997-2-7 15:23:27"
665 * "1997.038 15:23:27" (day of year 1-366)
666 * Also supports input in compact time:
669 * "20011225T040506.789-07"
671 * Use the system-provided functions to get the current time zone
672 * if not specified in the input string.
674 * If the date is outside the range of pg_time_t (in practice that could only
675 * happen if pg_time_t is just 32 bits), then assume UTC time zone - thomas
679 DecodeDateTime(char **field
, int *ftype
, int nf
,
680 int *dtype
, struct pg_tm
* tm
, fsec_t
*fsec
, int *tzp
)
685 int ptype
= 0; /* "prefix type" for ISO y2001m02d04 format */
690 bool haveTextMonth
= FALSE
;
691 bool is2digits
= FALSE
;
693 pg_tz
*namedTz
= NULL
;
696 * We'll insist on at least all of the date fields, but initialize the
697 * remaining fields in case they are not set later...
704 /* don't know daylight savings time status apriori */
709 for (i
= 0; i
< nf
; i
++)
715 * Integral julian day with attached time zone?
716 * All other forms with JD will be separated into
717 * distinct fields, so we handle just this case here.
719 if (ptype
== DTK_JULIAN
)
725 return DTERR_BAD_FORMAT
;
728 val
= strtoi(field
[i
], &cp
, 10);
730 return DTERR_FIELD_OVERFLOW
;
732 j2date(val
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
733 /* Get the time zone from the end of the string */
734 dterr
= DecodeTimezone(cp
, tzp
);
738 tmask
= DTK_DATE_M
| DTK_TIME_M
| DTK_M(TZ
);
743 * Already have a date? Then this might be a time zone name
744 * with embedded punctuation (e.g. "America/New_York") or a
745 * run-together time with trailing time zone (e.g. hhmmss-zz).
746 * - thomas 2001-12-25
748 * We consider it a time zone if we already have month & day.
749 * This is to allow the form "mmm dd hhmmss tz year", which
750 * we've historically accepted.
752 else if (ptype
!= 0 ||
753 ((fmask
& (DTK_M(MONTH
) | DTK_M(DAY
))) ==
754 (DTK_M(MONTH
) | DTK_M(DAY
))))
756 /* No time zone accepted? Then quit... */
758 return DTERR_BAD_FORMAT
;
760 if (isdigit((unsigned char) *field
[i
]) || ptype
!= 0)
766 /* Sanity check; should not fail this test */
767 if (ptype
!= DTK_TIME
)
768 return DTERR_BAD_FORMAT
;
773 * Starts with a digit but we already have a time
774 * field? Then we are in trouble with a date and time
777 if ((fmask
& DTK_TIME_M
) == DTK_TIME_M
)
778 return DTERR_BAD_FORMAT
;
780 if ((cp
= strchr(field
[i
], '-')) == NULL
)
781 return DTERR_BAD_FORMAT
;
783 /* Get the time zone from the end of the string */
784 dterr
= DecodeTimezone(cp
, tzp
);
790 * Then read the rest of the field as a concatenated
793 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
801 * modify tmask after returning from
802 * DecodeNumberField()
808 namedTz
= pg_tzset(field
[i
]);
812 * We should return an error code instead of
813 * ereport'ing directly, but then there is no way
814 * to report the bad time zone name.
817 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
818 errmsg("time zone \"%s\" not recognized",
821 /* we'll apply the zone setting below */
827 dterr
= DecodeDate(field
[i
], fmask
,
828 &tmask
, &is2digits
, tm
);
835 dterr
= DecodeTime(field
[i
], fmask
, &tmask
, tm
, fsec
);
840 * Check upper limit on hours; other limits checked in
843 /* test for > 24:00:00 */
844 if (tm
->tm_hour
> 24 ||
845 (tm
->tm_hour
== 24 && (tm
->tm_min
> 0 || tm
->tm_sec
> 0)))
846 return DTERR_FIELD_OVERFLOW
;
854 return DTERR_BAD_FORMAT
;
856 dterr
= DecodeTimezone(field
[i
], &tz
);
867 * Was this an "ISO date" with embedded field labels? An
868 * example is "y2001m02d04" - thomas 2001-02-04
876 val
= strtoi(field
[i
], &cp
, 10);
878 return DTERR_FIELD_OVERFLOW
;
881 * only a few kinds are allowed to have an embedded
892 return DTERR_BAD_FORMAT
;
895 else if (*cp
!= '\0')
896 return DTERR_BAD_FORMAT
;
908 * already have a month and hour? then assume
911 if ((fmask
& DTK_M(MONTH
)) != 0 &&
912 (fmask
& DTK_M(HOUR
)) != 0)
915 tmask
= DTK_M(MINUTE
);
920 tmask
= DTK_M(MONTH
);
936 tmask
= DTK_M(MINUTE
);
941 tmask
= DTK_M(SECOND
);
946 frac
= strtod(cp
, &cp
);
948 return DTERR_BAD_FORMAT
;
949 #ifdef HAVE_INT64_TIMESTAMP
950 *fsec
= rint(frac
* 1000000);
954 tmask
= DTK_ALL_SECS_M
;
960 dterr
= DecodeTimezone(field
[i
], tzp
);
967 * previous field was a label for "julian date"?
970 j2date(val
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
971 /* fractional Julian Day? */
976 time
= strtod(cp
, &cp
);
978 return DTERR_BAD_FORMAT
;
981 #ifdef HAVE_INT64_TIMESTAMP
982 dt2time(time
* USECS_PER_DAY
,
983 &tm
->tm_hour
, &tm
->tm_min
,
986 dt2time(time
* SECS_PER_DAY
, &tm
->tm_hour
,
987 &tm
->tm_min
, &tm
->tm_sec
, fsec
);
993 /* previous field was "t" for ISO time */
994 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
995 (fmask
| DTK_DATE_M
),
1000 if (tmask
!= DTK_TIME_M
)
1001 return DTERR_BAD_FORMAT
;
1005 return DTERR_BAD_FORMAT
;
1017 flen
= strlen(field
[i
]);
1018 cp
= strchr(field
[i
], '.');
1020 /* Embedded decimal and no date yet? */
1021 if (cp
!= NULL
&& !(fmask
& DTK_DATE_M
))
1023 dterr
= DecodeDate(field
[i
], fmask
,
1024 &tmask
, &is2digits
, tm
);
1028 /* embedded decimal and several digits before? */
1029 else if (cp
!= NULL
&& flen
- strlen(cp
) > 2)
1032 * Interpret as a concatenated date or time Set the
1033 * type field to allow decoding other fields later.
1034 * Example: 20011223 or 040506
1036 dterr
= DecodeNumberField(flen
, field
[i
], fmask
,
1044 dterr
= DecodeNumberField(flen
, field
[i
], fmask
,
1050 /* otherwise it is a single date/time field... */
1053 dterr
= DecodeNumber(flen
, field
[i
],
1054 haveTextMonth
, fmask
,
1065 type
= DecodeSpecial(i
, field
[i
], &val
);
1066 if (type
== IGNORE_DTF
)
1069 tmask
= DTK_M(type
);
1077 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1078 errmsg("date/time value \"current\" is no longer supported")));
1080 return DTERR_BAD_FORMAT
;
1084 tmask
= (DTK_DATE_M
| DTK_TIME_M
| DTK_M(TZ
));
1086 GetCurrentTimeUsec(tm
, fsec
, tzp
);
1092 GetCurrentDateTime(tm
);
1093 j2date(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) - 1,
1094 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1103 GetCurrentDateTime(tm
);
1112 GetCurrentDateTime(tm
);
1113 j2date(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) + 1,
1114 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1121 tmask
= (DTK_TIME_M
| DTK_M(TZ
));
1139 * already have a (numeric) month? then see if we can
1142 if ((fmask
& DTK_M(MONTH
)) && !haveTextMonth
&&
1143 !(fmask
& DTK_M(DAY
)) && tm
->tm_mon
>= 1 &&
1146 tm
->tm_mday
= tm
->tm_mon
;
1149 haveTextMonth
= TRUE
;
1156 * daylight savings time modifier (solves "MET DST"
1159 tmask
|= DTK_M(DTZ
);
1162 return DTERR_BAD_FORMAT
;
1163 *tzp
+= val
* MINS_PER_HOUR
;
1169 * set mask for TZ here _or_ check for DTZ later when
1170 * getting default timezone
1175 return DTERR_BAD_FORMAT
;
1176 *tzp
= val
* MINS_PER_HOUR
;
1182 return DTERR_BAD_FORMAT
;
1183 *tzp
= val
* MINS_PER_HOUR
;
1209 * This is a filler field "t" indicating that the next
1210 * field is time. Try to verify that this is sensible.
1214 /* No preceding date? Then quit... */
1215 if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
1216 return DTERR_BAD_FORMAT
;
1219 * We will need one of the following fields:
1220 * DTK_NUMBER should be hhmmss.fff
1221 * DTK_TIME should be hh:mm:ss.fff
1222 * DTK_DATE should be hhmmss-zz
1225 (ftype
[i
+ 1] != DTK_NUMBER
&&
1226 ftype
[i
+ 1] != DTK_TIME
&&
1227 ftype
[i
+ 1] != DTK_DATE
))
1228 return DTERR_BAD_FORMAT
;
1236 * Before giving up and declaring error, check to see
1237 * if it is an all-alpha timezone name.
1239 namedTz
= pg_tzset(field
[i
]);
1241 return DTERR_BAD_FORMAT
;
1242 /* we'll apply the zone setting below */
1247 return DTERR_BAD_FORMAT
;
1252 return DTERR_BAD_FORMAT
;
1256 return DTERR_BAD_FORMAT
;
1258 } /* end loop over fields */
1260 /* do final checking/adjustment of Y/M/D fields */
1261 dterr
= ValidateDate(fmask
, is2digits
, bc
, tm
);
1266 if (mer
!= HR24
&& tm
->tm_hour
> 12)
1267 return DTERR_FIELD_OVERFLOW
;
1268 if (mer
== AM
&& tm
->tm_hour
== 12)
1270 else if (mer
== PM
&& tm
->tm_hour
!= 12)
1273 /* do additional checking for full date specs... */
1274 if (*dtype
== DTK_DATE
)
1276 if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
1278 if ((fmask
& DTK_TIME_M
) == DTK_TIME_M
)
1280 return DTERR_BAD_FORMAT
;
1284 * If we had a full timezone spec, compute the offset (we could not do
1285 * it before, because we need the date to resolve DST status).
1287 if (namedTz
!= NULL
)
1289 /* daylight savings time modifier disallowed with full TZ */
1290 if (fmask
& DTK_M(DTZMOD
))
1291 return DTERR_BAD_FORMAT
;
1293 *tzp
= DetermineTimeZoneOffset(tm
, namedTz
);
1296 /* timezone not specified? then find local timezone if possible */
1297 if (tzp
!= NULL
&& !(fmask
& DTK_M(TZ
)))
1300 * daylight savings time modifier but no standard timezone? then
1303 if (fmask
& DTK_M(DTZMOD
))
1304 return DTERR_BAD_FORMAT
;
1306 *tzp
= DetermineTimeZoneOffset(tm
, session_timezone
);
1314 /* DetermineTimeZoneOffset()
1316 * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
1317 * tm_sec fields are set, attempt to determine the applicable time zone
1318 * (ie, regular or daylight-savings time) at that time. Set the struct pg_tm's
1319 * tm_isdst field accordingly, and return the actual timezone offset.
1321 * Note: it might seem that we should use mktime() for this, but bitter
1322 * experience teaches otherwise. This code is much faster than most versions
1323 * of mktime(), anyway.
1326 DetermineTimeZoneOffset(struct pg_tm
* tm
, pg_tz
*tzp
)
1336 long int before_gmtoff
,
1342 if (tzp
== session_timezone
&& HasCTZSet
)
1344 tm
->tm_isdst
= 0; /* for lack of a better idea */
1349 * First, generate the pg_time_t value corresponding to the given
1350 * y/m/d/h/m/s taken as GMT time. If this overflows, punt and decide the
1351 * timezone is GMT. (We only need to worry about overflow on machines
1352 * where pg_time_t is 32 bits.)
1354 if (!IS_VALID_JULIAN(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
))
1356 date
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) - UNIX_EPOCH_JDATE
;
1358 day
= ((pg_time_t
) date
) * SECS_PER_DAY
;
1359 if (day
/ SECS_PER_DAY
!= date
)
1361 sec
= tm
->tm_sec
+ (tm
->tm_min
+ tm
->tm_hour
* MINS_PER_HOUR
) * SECS_PER_MINUTE
;
1363 /* since sec >= 0, overflow could only be from +day to -mytime */
1364 if (mytime
< 0 && day
> 0)
1368 * Find the DST time boundary just before or following the target time. We
1369 * assume that all zones have GMT offsets less than 24 hours, and that DST
1370 * boundaries can't be closer together than 48 hours, so backing up 24
1371 * hours and finding the "next" boundary will work.
1373 prevtime
= mytime
- SECS_PER_DAY
;
1374 if (mytime
< 0 && prevtime
> 0)
1377 res
= pg_next_dst_boundary(&prevtime
,
1378 &before_gmtoff
, &before_isdst
,
1380 &after_gmtoff
, &after_isdst
,
1383 goto overflow
; /* failure? */
1387 /* Non-DST zone, life is simple */
1388 tm
->tm_isdst
= before_isdst
;
1389 return -(int) before_gmtoff
;
1393 * Form the candidate pg_time_t values with local-time adjustment
1395 beforetime
= mytime
- before_gmtoff
;
1396 if ((before_gmtoff
> 0 &&
1397 mytime
< 0 && beforetime
> 0) ||
1398 (before_gmtoff
<= 0 &&
1399 mytime
> 0 && beforetime
< 0))
1401 aftertime
= mytime
- after_gmtoff
;
1402 if ((after_gmtoff
> 0 &&
1403 mytime
< 0 && aftertime
> 0) ||
1404 (after_gmtoff
<= 0 &&
1405 mytime
> 0 && aftertime
< 0))
1409 * If both before or both after the boundary time, we know what to do
1411 if (beforetime
<= boundary
&& aftertime
< boundary
)
1413 tm
->tm_isdst
= before_isdst
;
1414 return -(int) before_gmtoff
;
1416 if (beforetime
> boundary
&& aftertime
>= boundary
)
1418 tm
->tm_isdst
= after_isdst
;
1419 return -(int) after_gmtoff
;
1423 * It's an invalid or ambiguous time due to timezone transition. Prefer
1424 * the standard-time interpretation.
1426 if (after_isdst
== 0)
1428 tm
->tm_isdst
= after_isdst
;
1429 return -(int) after_gmtoff
;
1431 tm
->tm_isdst
= before_isdst
;
1432 return -(int) before_gmtoff
;
1435 /* Given date is out of range, so assume UTC */
1442 * Interpret parsed string as time fields only.
1443 * Returns 0 if successful, DTERR code if bogus input detected.
1445 * Note that support for time zone is here for
1446 * SQL92 TIME WITH TIME ZONE, but it reveals
1447 * bogosity with SQL92 date/time standards, since
1448 * we must infer a time zone from current time.
1449 * - thomas 2000-03-10
1450 * Allow specifying date to get a better time zone,
1451 * if time zones are allowed. - thomas 2001-12-26
1454 DecodeTimeOnly(char **field
, int *ftype
, int nf
,
1455 int *dtype
, struct pg_tm
* tm
, fsec_t
*fsec
, int *tzp
)
1460 int ptype
= 0; /* "prefix type" for ISO h04mm05s06 format */
1464 bool is2digits
= FALSE
;
1467 pg_tz
*namedTz
= NULL
;
1474 /* don't know daylight savings time status apriori */
1480 for (i
= 0; i
< nf
; i
++)
1487 * Time zone not allowed? Then should not accept dates or time
1488 * zones no matter what else!
1491 return DTERR_BAD_FORMAT
;
1493 /* Under limited circumstances, we will accept a date... */
1494 if (i
== 0 && nf
>= 2 &&
1495 (ftype
[nf
- 1] == DTK_DATE
|| ftype
[1] == DTK_TIME
))
1497 dterr
= DecodeDate(field
[i
], fmask
,
1498 &tmask
, &is2digits
, tm
);
1502 /* otherwise, this is a time and/or time zone */
1505 if (isdigit((unsigned char) *field
[i
]))
1510 * Starts with a digit but we already have a time
1511 * field? Then we are in trouble with time already...
1513 if ((fmask
& DTK_TIME_M
) == DTK_TIME_M
)
1514 return DTERR_BAD_FORMAT
;
1517 * Should not get here and fail. Sanity check only...
1519 if ((cp
= strchr(field
[i
], '-')) == NULL
)
1520 return DTERR_BAD_FORMAT
;
1522 /* Get the time zone from the end of the string */
1523 dterr
= DecodeTimezone(cp
, tzp
);
1529 * Then read the rest of the field as a concatenated
1532 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
1533 (fmask
| DTK_DATE_M
),
1544 namedTz
= pg_tzset(field
[i
]);
1548 * We should return an error code instead of
1549 * ereport'ing directly, but then there is no way
1550 * to report the bad time zone name.
1553 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1554 errmsg("time zone \"%s\" not recognized",
1557 /* we'll apply the zone setting below */
1565 dterr
= DecodeTime(field
[i
], (fmask
| DTK_DATE_M
),
1576 return DTERR_BAD_FORMAT
;
1578 dterr
= DecodeTimezone(field
[i
], &tz
);
1589 * Was this an "ISO time" with embedded field labels? An
1590 * example is "h04m05s06" - thomas 2001-02-04
1597 /* Only accept a date under limited circumstances */
1605 return DTERR_BAD_FORMAT
;
1611 val
= strtoi(field
[i
], &cp
, 10);
1612 if (errno
== ERANGE
)
1613 return DTERR_FIELD_OVERFLOW
;
1616 * only a few kinds are allowed to have an embedded
1627 return DTERR_BAD_FORMAT
;
1630 else if (*cp
!= '\0')
1631 return DTERR_BAD_FORMAT
;
1637 tmask
= DTK_M(YEAR
);
1643 * already have a month and hour? then assume
1646 if ((fmask
& DTK_M(MONTH
)) != 0 &&
1647 (fmask
& DTK_M(HOUR
)) != 0)
1650 tmask
= DTK_M(MINUTE
);
1655 tmask
= DTK_M(MONTH
);
1666 tmask
= DTK_M(HOUR
);
1671 tmask
= DTK_M(MINUTE
);
1676 tmask
= DTK_M(SECOND
);
1681 frac
= strtod(cp
, &cp
);
1683 return DTERR_BAD_FORMAT
;
1684 #ifdef HAVE_INT64_TIMESTAMP
1685 *fsec
= rint(frac
* 1000000);
1689 tmask
= DTK_ALL_SECS_M
;
1695 dterr
= DecodeTimezone(field
[i
], tzp
);
1702 * previous field was a label for "julian date"?
1705 j2date(val
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1710 time
= strtod(cp
, &cp
);
1712 return DTERR_BAD_FORMAT
;
1714 tmask
|= DTK_TIME_M
;
1715 #ifdef HAVE_INT64_TIMESTAMP
1716 dt2time(time
* USECS_PER_DAY
,
1717 &tm
->tm_hour
, &tm
->tm_min
, &tm
->tm_sec
, fsec
);
1719 dt2time(time
* SECS_PER_DAY
,
1720 &tm
->tm_hour
, &tm
->tm_min
, &tm
->tm_sec
, fsec
);
1726 /* previous field was "t" for ISO time */
1727 dterr
= DecodeNumberField(strlen(field
[i
]), field
[i
],
1728 (fmask
| DTK_DATE_M
),
1735 if (tmask
!= DTK_TIME_M
)
1736 return DTERR_BAD_FORMAT
;
1740 return DTERR_BAD_FORMAT
;
1752 flen
= strlen(field
[i
]);
1753 cp
= strchr(field
[i
], '.');
1755 /* Embedded decimal? */
1759 * Under limited circumstances, we will accept a
1762 if (i
== 0 && nf
>= 2 && ftype
[nf
- 1] == DTK_DATE
)
1764 dterr
= DecodeDate(field
[i
], fmask
,
1765 &tmask
, &is2digits
, tm
);
1769 /* embedded decimal and several digits before? */
1770 else if (flen
- strlen(cp
) > 2)
1773 * Interpret as a concatenated date or time Set
1774 * the type field to allow decoding other fields
1775 * later. Example: 20011223 or 040506
1777 dterr
= DecodeNumberField(flen
, field
[i
],
1778 (fmask
| DTK_DATE_M
),
1786 return DTERR_BAD_FORMAT
;
1790 dterr
= DecodeNumberField(flen
, field
[i
],
1791 (fmask
| DTK_DATE_M
),
1798 /* otherwise it is a single date/time field... */
1801 dterr
= DecodeNumber(flen
, field
[i
],
1803 (fmask
| DTK_DATE_M
),
1814 type
= DecodeSpecial(i
, field
[i
], &val
);
1815 if (type
== IGNORE_DTF
)
1818 tmask
= DTK_M(type
);
1826 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
1827 errmsg("date/time value \"current\" is no longer supported")));
1828 return DTERR_BAD_FORMAT
;
1834 GetCurrentTimeUsec(tm
, fsec
, NULL
);
1838 tmask
= (DTK_TIME_M
| DTK_M(TZ
));
1847 return DTERR_BAD_FORMAT
;
1855 * daylight savings time modifier (solves "MET DST"
1858 tmask
|= DTK_M(DTZ
);
1861 return DTERR_BAD_FORMAT
;
1862 *tzp
+= val
* MINS_PER_HOUR
;
1868 * set mask for TZ here _or_ check for DTZ later when
1869 * getting default timezone
1874 return DTERR_BAD_FORMAT
;
1875 *tzp
= val
* MINS_PER_HOUR
;
1882 return DTERR_BAD_FORMAT
;
1883 *tzp
= val
* MINS_PER_HOUR
;
1907 * We will need one of the following fields:
1908 * DTK_NUMBER should be hhmmss.fff
1909 * DTK_TIME should be hh:mm:ss.fff
1910 * DTK_DATE should be hhmmss-zz
1913 (ftype
[i
+ 1] != DTK_NUMBER
&&
1914 ftype
[i
+ 1] != DTK_TIME
&&
1915 ftype
[i
+ 1] != DTK_DATE
))
1916 return DTERR_BAD_FORMAT
;
1924 * Before giving up and declaring error, check to see
1925 * if it is an all-alpha timezone name.
1927 namedTz
= pg_tzset(field
[i
]);
1929 return DTERR_BAD_FORMAT
;
1930 /* we'll apply the zone setting below */
1935 return DTERR_BAD_FORMAT
;
1940 return DTERR_BAD_FORMAT
;
1944 return DTERR_BAD_FORMAT
;
1946 } /* end loop over fields */
1948 /* do final checking/adjustment of Y/M/D fields */
1949 dterr
= ValidateDate(fmask
, is2digits
, bc
, tm
);
1954 if (mer
!= HR24
&& tm
->tm_hour
> 12)
1955 return DTERR_FIELD_OVERFLOW
;
1956 if (mer
== AM
&& tm
->tm_hour
== 12)
1958 else if (mer
== PM
&& tm
->tm_hour
!= 12)
1961 if (tm
->tm_hour
< 0 || tm
->tm_min
< 0 || tm
->tm_min
> 59 ||
1962 tm
->tm_sec
< 0 || tm
->tm_sec
> 60 || tm
->tm_hour
> 24 ||
1963 /* test for > 24:00:00 */
1964 #ifdef HAVE_INT64_TIMESTAMP
1965 (tm
->tm_hour
== 24 && (tm
->tm_min
> 0 || tm
->tm_sec
> 0 ||
1966 *fsec
> INT64CONST(0))) ||
1967 *fsec
< INT64CONST(0) || *fsec
>= USECS_PER_SEC
1969 (tm
->tm_hour
== 24 && (tm
->tm_min
> 0 || tm
->tm_sec
> 0 ||
1971 *fsec
< 0 || *fsec
>= 1
1974 return DTERR_FIELD_OVERFLOW
;
1976 if ((fmask
& DTK_TIME_M
) != DTK_TIME_M
)
1977 return DTERR_BAD_FORMAT
;
1980 * If we had a full timezone spec, compute the offset (we could not do it
1981 * before, because we may need the date to resolve DST status).
1983 if (namedTz
!= NULL
)
1987 /* daylight savings time modifier disallowed with full TZ */
1988 if (fmask
& DTK_M(DTZMOD
))
1989 return DTERR_BAD_FORMAT
;
1991 /* if non-DST zone, we do not need to know the date */
1992 if (pg_get_timezone_offset(namedTz
, &gmtoff
))
1994 *tzp
= -(int) gmtoff
;
1998 /* a date has to be specified */
1999 if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
2000 return DTERR_BAD_FORMAT
;
2001 *tzp
= DetermineTimeZoneOffset(tm
, namedTz
);
2005 /* timezone not specified? then find local timezone if possible */
2006 if (tzp
!= NULL
&& !(fmask
& DTK_M(TZ
)))
2012 * daylight savings time modifier but no standard timezone? then error
2014 if (fmask
& DTK_M(DTZMOD
))
2015 return DTERR_BAD_FORMAT
;
2017 if ((fmask
& DTK_DATE_M
) == 0)
2018 GetCurrentDateTime(tmp
);
2021 tmp
->tm_year
= tm
->tm_year
;
2022 tmp
->tm_mon
= tm
->tm_mon
;
2023 tmp
->tm_mday
= tm
->tm_mday
;
2025 tmp
->tm_hour
= tm
->tm_hour
;
2026 tmp
->tm_min
= tm
->tm_min
;
2027 tmp
->tm_sec
= tm
->tm_sec
;
2028 *tzp
= DetermineTimeZoneOffset(tmp
, session_timezone
);
2029 tm
->tm_isdst
= tmp
->tm_isdst
;
2036 * Decode date string which includes delimiters.
2037 * Return 0 if okay, a DTERR code if not.
2039 * str: field to be parsed
2040 * fmask: bitmask for field types already seen
2041 * *tmask: receives bitmask for fields found here
2042 * *is2digits: set to TRUE if we find 2-digit year
2043 * *tm: field values are stored into appropriate members of this struct
2046 DecodeDate(char *str
, int fmask
, int *tmask
, bool *is2digits
,
2054 bool haveTextMonth
= FALSE
;
2058 char *field
[MAXDATEFIELDS
];
2062 /* parse this string... */
2063 while (*str
!= '\0' && nf
< MAXDATEFIELDS
)
2065 /* skip field separators */
2066 while (!isalnum((unsigned char) *str
))
2070 if (isdigit((unsigned char) *str
))
2072 while (isdigit((unsigned char) *str
))
2075 else if (isalpha((unsigned char) *str
))
2077 while (isalpha((unsigned char) *str
))
2081 /* Just get rid of any non-digit, non-alpha characters... */
2087 /* look first for text fields, since that will be unambiguous month */
2088 for (i
= 0; i
< nf
; i
++)
2090 if (isalpha((unsigned char) *field
[i
]))
2092 type
= DecodeSpecial(i
, field
[i
], &val
);
2093 if (type
== IGNORE_DTF
)
2096 dmask
= DTK_M(type
);
2101 haveTextMonth
= TRUE
;
2105 return DTERR_BAD_FORMAT
;
2108 return DTERR_BAD_FORMAT
;
2113 /* mark this field as being completed */
2118 /* now pick up remaining numeric fields */
2119 for (i
= 0; i
< nf
; i
++)
2121 if (field
[i
] == NULL
)
2124 if ((len
= strlen(field
[i
])) <= 0)
2125 return DTERR_BAD_FORMAT
;
2127 dterr
= DecodeNumber(len
, field
[i
], haveTextMonth
, fmask
,
2134 return DTERR_BAD_FORMAT
;
2140 if ((fmask
& ~(DTK_M(DOY
) | DTK_M(TZ
))) != DTK_DATE_M
)
2141 return DTERR_BAD_FORMAT
;
2143 /* validation of the field values must wait until ValidateDate() */
2149 * Check valid year/month/day values, handle BC and DOY cases
2150 * Return 0 if okay, a DTERR code if not.
2153 ValidateDate(int fmask
, bool is2digits
, bool bc
, struct pg_tm
* tm
)
2155 if (fmask
& DTK_M(YEAR
))
2159 /* there is no year zero in AD/BC notation */
2160 if (tm
->tm_year
<= 0)
2161 return DTERR_FIELD_OVERFLOW
;
2162 /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
2163 tm
->tm_year
= -(tm
->tm_year
- 1);
2167 /* allow 2-digit input for 1970-2069 AD; 00 is allowed */
2168 if (tm
->tm_year
< 0) /* just paranoia */
2169 return DTERR_FIELD_OVERFLOW
;
2170 if (tm
->tm_year
< 70)
2171 tm
->tm_year
+= 2000;
2172 else if (tm
->tm_year
< 100)
2173 tm
->tm_year
+= 1900;
2177 /* there is no year zero in AD/BC notation */
2178 if (tm
->tm_year
<= 0)
2179 return DTERR_FIELD_OVERFLOW
;
2183 /* now that we have correct year, decode DOY */
2184 if (fmask
& DTK_M(DOY
))
2186 j2date(date2j(tm
->tm_year
, 1, 1) + tm
->tm_yday
- 1,
2187 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
2190 /* check for valid month */
2191 if (fmask
& DTK_M(MONTH
))
2193 if (tm
->tm_mon
< 1 || tm
->tm_mon
> MONTHS_PER_YEAR
)
2194 return DTERR_MD_FIELD_OVERFLOW
;
2197 /* minimal check for valid day */
2198 if (fmask
& DTK_M(DAY
))
2200 if (tm
->tm_mday
< 1 || tm
->tm_mday
> 31)
2201 return DTERR_MD_FIELD_OVERFLOW
;
2204 if ((fmask
& DTK_DATE_M
) == DTK_DATE_M
)
2207 * Check for valid day of month, now that we know for sure the month
2208 * and year. Note we don't use MD_FIELD_OVERFLOW here, since it seems
2209 * unlikely that "Feb 29" is a YMD-order error.
2211 if (tm
->tm_mday
> day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1])
2212 return DTERR_FIELD_OVERFLOW
;
2220 * Decode time string which includes delimiters.
2221 * Return 0 if okay, a DTERR code if not.
2223 * Only check the lower limit on hours, since this same code can be
2224 * used to represent time spans.
2227 DecodeTime(char *str
, int fmask
, int *tmask
, struct pg_tm
* tm
, fsec_t
*fsec
)
2231 *tmask
= DTK_TIME_M
;
2234 tm
->tm_hour
= strtoi(str
, &cp
, 10);
2235 if (errno
== ERANGE
)
2236 return DTERR_FIELD_OVERFLOW
;
2238 return DTERR_BAD_FORMAT
;
2241 tm
->tm_min
= strtoi(str
, &cp
, 10);
2242 if (errno
== ERANGE
)
2243 return DTERR_FIELD_OVERFLOW
;
2249 else if (*cp
!= ':')
2250 return DTERR_BAD_FORMAT
;
2255 tm
->tm_sec
= strtoi(str
, &cp
, 10);
2256 if (errno
== ERANGE
)
2257 return DTERR_FIELD_OVERFLOW
;
2260 else if (*cp
== '.')
2265 frac
= strtod(str
, &cp
);
2267 return DTERR_BAD_FORMAT
;
2268 #ifdef HAVE_INT64_TIMESTAMP
2269 *fsec
= rint(frac
* 1000000);
2275 return DTERR_BAD_FORMAT
;
2278 /* do a sanity check */
2279 #ifdef HAVE_INT64_TIMESTAMP
2280 if (tm
->tm_hour
< 0 || tm
->tm_min
< 0 || tm
->tm_min
> 59 ||
2281 tm
->tm_sec
< 0 || tm
->tm_sec
> 60 || *fsec
< INT64CONST(0) ||
2282 *fsec
>= USECS_PER_SEC
)
2283 return DTERR_FIELD_OVERFLOW
;
2285 if (tm
->tm_hour
< 0 || tm
->tm_min
< 0 || tm
->tm_min
> 59 ||
2286 tm
->tm_sec
< 0 || tm
->tm_sec
> 60 || *fsec
< 0 || *fsec
>= 1)
2287 return DTERR_FIELD_OVERFLOW
;
2295 * Interpret plain numeric field as a date value in context.
2296 * Return 0 if okay, a DTERR code if not.
2299 DecodeNumber(int flen
, char *str
, bool haveTextMonth
, int fmask
,
2300 int *tmask
, struct pg_tm
* tm
, fsec_t
*fsec
, bool *is2digits
)
2309 val
= strtoi(str
, &cp
, 10);
2310 if (errno
== ERANGE
)
2311 return DTERR_FIELD_OVERFLOW
;
2313 return DTERR_BAD_FORMAT
;
2320 * More than two digits before decimal point? Then could be a date or
2321 * a run-together time: 2001.360 20011225 040506.789
2325 dterr
= DecodeNumberField(flen
, str
,
2326 (fmask
| DTK_DATE_M
),
2334 frac
= strtod(cp
, &cp
);
2336 return DTERR_BAD_FORMAT
;
2337 #ifdef HAVE_INT64_TIMESTAMP
2338 *fsec
= rint(frac
* 1000000);
2343 else if (*cp
!= '\0')
2344 return DTERR_BAD_FORMAT
;
2346 /* Special case for day of year */
2347 if (flen
== 3 && (fmask
& DTK_DATE_M
) == DTK_M(YEAR
) && val
>= 1 &&
2350 *tmask
= (DTK_M(DOY
) | DTK_M(MONTH
) | DTK_M(DAY
));
2352 /* tm_mon and tm_mday can't actually be set yet ... */
2356 /* Switch based on what we have so far */
2357 switch (fmask
& DTK_DATE_M
)
2362 * Nothing so far; make a decision about what we think the input
2363 * is. There used to be lots of heuristics here, but the
2364 * consensus now is to be paranoid. It *must* be either
2365 * YYYY-MM-DD (with a more-than-two-digit year field), or the
2366 * field order defined by DateOrder.
2368 if (flen
>= 3 || DateOrder
== DATEORDER_YMD
)
2370 *tmask
= DTK_M(YEAR
);
2373 else if (DateOrder
== DATEORDER_DMY
)
2375 *tmask
= DTK_M(DAY
);
2380 *tmask
= DTK_M(MONTH
);
2386 /* Must be at second field of YY-MM-DD */
2387 *tmask
= DTK_M(MONTH
);
2391 case (DTK_M(MONTH
)):
2395 * We are at the first numeric field of a date that included a
2396 * textual month name. We want to support the variants
2397 * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous
2398 * inputs. We will also accept MON-DD-YY or DD-MON-YY in
2399 * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.
2401 if (flen
>= 3 || DateOrder
== DATEORDER_YMD
)
2403 *tmask
= DTK_M(YEAR
);
2408 *tmask
= DTK_M(DAY
);
2414 /* Must be at second field of MM-DD-YY */
2415 *tmask
= DTK_M(DAY
);
2420 case (DTK_M(YEAR
) | DTK_M(MONTH
)):
2423 /* Need to accept DD-MON-YYYY even in YMD mode */
2424 if (flen
>= 3 && *is2digits
)
2426 /* Guess that first numeric field is day was wrong */
2427 *tmask
= DTK_M(DAY
); /* YEAR is already set */
2428 tm
->tm_mday
= tm
->tm_year
;
2434 *tmask
= DTK_M(DAY
);
2440 /* Must be at third field of YY-MM-DD */
2441 *tmask
= DTK_M(DAY
);
2447 /* Must be at second field of DD-MM-YY */
2448 *tmask
= DTK_M(MONTH
);
2452 case (DTK_M(MONTH
) | DTK_M(DAY
)):
2453 /* Must be at third field of DD-MM-YY or MM-DD-YY */
2454 *tmask
= DTK_M(YEAR
);
2458 case (DTK_M(YEAR
) | DTK_M(MONTH
) | DTK_M(DAY
)):
2459 /* we have all the date, so it must be a time field */
2460 dterr
= DecodeNumberField(flen
, str
, fmask
,
2468 /* Anything else is bogus input */
2469 return DTERR_BAD_FORMAT
;
2473 * When processing a year field, mark it for adjustment if it's only one
2476 if (*tmask
== DTK_M(YEAR
))
2477 *is2digits
= (flen
<= 2);
2483 /* DecodeNumberField()
2484 * Interpret numeric string as a concatenated date or time field.
2485 * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
2487 * Use the context of previously decoded fields to help with
2488 * the interpretation.
2491 DecodeNumberField(int len
, char *str
, int fmask
,
2492 int *tmask
, struct pg_tm
* tm
, fsec_t
*fsec
, bool *is2digits
)
2497 * Have a decimal point? Then this is a date or something with a seconds
2500 if ((cp
= strchr(str
, '.')) != NULL
)
2504 frac
= strtod(cp
, NULL
);
2505 #ifdef HAVE_INT64_TIMESTAMP
2506 *fsec
= rint(frac
* 1000000);
2513 /* No decimal point and no complete date yet? */
2514 else if ((fmask
& DTK_DATE_M
) != DTK_DATE_M
)
2519 *tmask
= DTK_DATE_M
;
2521 tm
->tm_mday
= atoi(str
+ 6);
2523 tm
->tm_mon
= atoi(str
+ 4);
2525 tm
->tm_year
= atoi(str
+ 0);
2532 *tmask
= DTK_DATE_M
;
2533 tm
->tm_mday
= atoi(str
+ 4);
2535 tm
->tm_mon
= atoi(str
+ 2);
2537 tm
->tm_year
= atoi(str
+ 0);
2544 /* not all time fields are specified? */
2545 if ((fmask
& DTK_TIME_M
) != DTK_TIME_M
)
2550 *tmask
= DTK_TIME_M
;
2551 tm
->tm_sec
= atoi(str
+ 4);
2553 tm
->tm_min
= atoi(str
+ 2);
2555 tm
->tm_hour
= atoi(str
+ 0);
2562 *tmask
= DTK_TIME_M
;
2564 tm
->tm_min
= atoi(str
+ 2);
2566 tm
->tm_hour
= atoi(str
+ 0);
2572 return DTERR_BAD_FORMAT
;
2577 * Interpret string as a numeric timezone.
2579 * Return 0 if okay (and set *tzp), a DTERR code if not okay.
2581 * NB: this must *not* ereport on failure; see commands/variable.c.
2583 * Note: we allow timezone offsets up to 13:59. There are places that
2584 * use +1300 summer time.
2587 DecodeTimezone(char *str
, int *tzp
)
2595 /* leading character must be "+" or "-" */
2596 if (*str
!= '+' && *str
!= '-')
2597 return DTERR_BAD_FORMAT
;
2600 hr
= strtoi(str
+ 1, &cp
, 10);
2601 if (errno
== ERANGE
)
2602 return DTERR_TZDISP_OVERFLOW
;
2604 /* explicit delimiter? */
2608 min
= strtoi(cp
+ 1, &cp
, 10);
2609 if (errno
== ERANGE
)
2610 return DTERR_TZDISP_OVERFLOW
;
2614 sec
= strtoi(cp
+ 1, &cp
, 10);
2615 if (errno
== ERANGE
)
2616 return DTERR_TZDISP_OVERFLOW
;
2619 /* otherwise, might have run things together... */
2620 else if (*cp
== '\0' && strlen(str
) > 3)
2624 /* we could, but don't, support a run-together hhmmss format */
2629 if (hr
< 0 || hr
> 14)
2630 return DTERR_TZDISP_OVERFLOW
;
2631 if (min
< 0 || min
>= 60)
2632 return DTERR_TZDISP_OVERFLOW
;
2633 if (sec
< 0 || sec
>= 60)
2634 return DTERR_TZDISP_OVERFLOW
;
2636 tz
= (hr
* MINS_PER_HOUR
+ min
) * SECS_PER_MINUTE
+ sec
;
2643 return DTERR_BAD_FORMAT
;
2649 * Decode text string using lookup table.
2651 * Implement a cache lookup since it is likely that dates
2652 * will be related in format.
2654 * NB: this must *not* ereport on failure;
2655 * see commands/variable.c.
2658 DecodeSpecial(int field
, char *lowtoken
, int *val
)
2663 tp
= datecache
[field
];
2664 if (tp
== NULL
|| strncmp(lowtoken
, tp
->token
, TOKMAXLEN
) != 0)
2666 tp
= datebsearch(lowtoken
, timezonetktbl
, sztimezonetktbl
);
2668 tp
= datebsearch(lowtoken
, datetktbl
, szdatetktbl
);
2672 type
= UNKNOWN_FIELD
;
2677 datecache
[field
] = tp
;
2698 * Interpret previously parsed fields for general time interval.
2699 * Returns 0 if successful, DTERR code if bogus input detected.
2701 * Allow "date" field DTK_DATE since this could be just
2702 * an unsigned floating point number. - thomas 1997-11-16
2704 * Allow ISO-style time span, with implicit units on number of days
2705 * preceding an hh:mm:ss field. - thomas 1998-04-30
2708 DecodeInterval(char **field
, int *ftype
, int nf
, int *dtype
, struct pg_tm
* tm
, fsec_t
*fsec
)
2710 bool is_before
= FALSE
;
2731 /* read through list backwards to pick up units before values */
2732 for (i
= nf
- 1; i
>= 0; i
--)
2737 dterr
= DecodeTime(field
[i
], fmask
, &tmask
, tm
, fsec
);
2746 * Timezone is a token with a leading sign character and
2747 * otherwise the same as a non-signed time field
2749 Assert(*field
[i
] == '-' || *field
[i
] == '+');
2752 * A single signed number ends up here, but will be rejected
2753 * by DecodeTime(). So, work this out to drop through to
2754 * DTK_NUMBER, which *can* tolerate this.
2757 while (*cp
!= '\0' && *cp
!= ':' && *cp
!= '.')
2760 DecodeTime(field
[i
] + 1, fmask
, &tmask
, tm
, fsec
) == 0)
2762 if (*field
[i
] == '-')
2764 /* flip the sign on all fields */
2765 tm
->tm_hour
= -tm
->tm_hour
;
2766 tm
->tm_min
= -tm
->tm_min
;
2767 tm
->tm_sec
= -tm
->tm_sec
;
2772 * Set the next type to be a day, if units are not
2773 * specified. This handles the case of '1 +02:03' since we
2774 * are reading right to left.
2780 else if (type
== IGNORE_DTF
)
2785 * Got a decimal point? Then assume some sort of
2786 * seconds specification
2790 else if (*cp
== '\0')
2793 * Only a signed integer? Then must assume a
2794 * timezone-like usage
2804 val
= strtoi(field
[i
], &cp
, 10);
2805 if (errno
== ERANGE
)
2806 return DTERR_FIELD_OVERFLOW
;
2808 if (type
== IGNORE_DTF
)
2813 fval
= strtod(cp
, &cp
);
2815 return DTERR_BAD_FORMAT
;
2817 if (*field
[i
] == '-')
2820 else if (*cp
== '\0')
2823 return DTERR_BAD_FORMAT
;
2825 tmask
= 0; /* DTK_M(type); */
2830 #ifdef HAVE_INT64_TIMESTAMP
2831 *fsec
+= val
+ fval
;
2833 *fsec
+= (val
+ fval
) * 1e-6;
2835 tmask
= DTK_M(MICROSECOND
);
2839 #ifdef HAVE_INT64_TIMESTAMP
2840 *fsec
+= (val
+ fval
) * 1000;
2842 *fsec
+= (val
+ fval
) * 1e-3;
2844 tmask
= DTK_M(MILLISECOND
);
2849 #ifdef HAVE_INT64_TIMESTAMP
2850 *fsec
+= fval
* 1000000;
2856 * If any subseconds were specified, consider this
2857 * microsecond and millisecond input as well.
2860 tmask
= DTK_M(SECOND
);
2862 tmask
= DTK_ALL_SECS_M
;
2871 fval
*= SECS_PER_MINUTE
;
2874 #ifdef HAVE_INT64_TIMESTAMP
2875 *fsec
+= (fval
- sec
) * 1000000;
2877 *fsec
+= fval
- sec
;
2880 tmask
= DTK_M(MINUTE
);
2889 fval
*= SECS_PER_HOUR
;
2892 #ifdef HAVE_INT64_TIMESTAMP
2893 *fsec
+= (fval
- sec
) * 1000000;
2895 *fsec
+= fval
- sec
;
2898 tmask
= DTK_M(HOUR
);
2907 fval
*= SECS_PER_DAY
;
2910 #ifdef HAVE_INT64_TIMESTAMP
2911 *fsec
+= (fval
- sec
) * 1000000;
2913 *fsec
+= fval
- sec
;
2916 tmask
= (fmask
& DTK_M(DAY
)) ? 0 : DTK_M(DAY
);
2920 tm
->tm_mday
+= val
* 7;
2926 extra_days
= (int32
) fval
;
2927 tm
->tm_mday
+= extra_days
;
2933 fval
*= SECS_PER_DAY
;
2936 #ifdef HAVE_INT64_TIMESTAMP
2937 *fsec
+= (fval
- sec
) * 1000000;
2939 *fsec
+= fval
- sec
;
2943 tmask
= (fmask
& DTK_M(DAY
)) ? 0 : DTK_M(DAY
);
2952 fval
*= DAYS_PER_MONTH
;
2960 fval
*= SECS_PER_DAY
;
2963 #ifdef HAVE_INT64_TIMESTAMP
2964 *fsec
+= (fval
- sec
) * 1000000;
2966 *fsec
+= fval
- sec
;
2970 tmask
= DTK_M(MONTH
);
2976 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
;
2977 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
2981 tm
->tm_year
+= val
* 10;
2983 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
* 10;
2984 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
2988 tm
->tm_year
+= val
* 100;
2990 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
* 100;
2991 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
2994 case DTK_MILLENNIUM
:
2995 tm
->tm_year
+= val
* 1000;
2997 tm
->tm_mon
+= fval
* MONTHS_PER_YEAR
* 1000;
2998 tmask
= (fmask
& DTK_M(YEAR
)) ? 0 : DTK_M(YEAR
);
3002 return DTERR_BAD_FORMAT
;
3008 type
= DecodeUnits(i
, field
[i
], &val
);
3009 if (type
== IGNORE_DTF
)
3012 tmask
= 0; /* DTK_M(type); */
3025 tmask
= (DTK_DATE_M
|| DTK_TIME_M
);
3030 return DTERR_BAD_FORMAT
;
3035 return DTERR_BAD_FORMAT
;
3039 return DTERR_BAD_FORMAT
;
3047 #ifdef HAVE_INT64_TIMESTAMP
3048 sec
= *fsec
/ USECS_PER_SEC
;
3049 *fsec
-= sec
* USECS_PER_SEC
;
3051 TMODULO(*fsec
, sec
, 1.0);
3059 tm
->tm_sec
= -tm
->tm_sec
;
3060 tm
->tm_min
= -tm
->tm_min
;
3061 tm
->tm_hour
= -tm
->tm_hour
;
3062 tm
->tm_mday
= -tm
->tm_mday
;
3063 tm
->tm_mon
= -tm
->tm_mon
;
3064 tm
->tm_year
= -tm
->tm_year
;
3067 /* ensure that at least one time field has been found */
3069 return DTERR_BAD_FORMAT
;
3076 * Decode text string using lookup table.
3077 * This routine supports time interval decoding
3078 * (hence, it need not recognize timezone names).
3081 DecodeUnits(int field
, char *lowtoken
, int *val
)
3086 tp
= deltacache
[field
];
3087 if (tp
== NULL
|| strncmp(lowtoken
, tp
->token
, TOKMAXLEN
) != 0)
3089 tp
= datebsearch(lowtoken
, deltatktbl
, szdeltatktbl
);
3093 type
= UNKNOWN_FIELD
;
3098 deltacache
[field
] = tp
;
3100 if (type
== TZ
|| type
== DTZ
)
3107 } /* DecodeUnits() */
3110 * Report an error detected by one of the datetime input processing routines.
3112 * dterr is the error code, str is the original input string, datatype is
3113 * the name of the datatype we were trying to accept.
3115 * Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
3116 * DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
3117 * separate SQLSTATE codes, so ...
3120 DateTimeParseError(int dterr
, const char *str
, const char *datatype
)
3124 case DTERR_FIELD_OVERFLOW
:
3126 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW
),
3127 errmsg("date/time field value out of range: \"%s\"",
3130 case DTERR_MD_FIELD_OVERFLOW
:
3131 /* <nanny>same as above, but add hint about DateStyle</nanny> */
3133 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW
),
3134 errmsg("date/time field value out of range: \"%s\"",
3136 errhint("Perhaps you need a different \"datestyle\" setting.")));
3138 case DTERR_INTERVAL_OVERFLOW
:
3140 (errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW
),
3141 errmsg("interval field value out of range: \"%s\"",
3144 case DTERR_TZDISP_OVERFLOW
:
3146 (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE
),
3147 errmsg("time zone displacement out of range: \"%s\"",
3150 case DTERR_BAD_FORMAT
:
3153 (errcode(ERRCODE_INVALID_DATETIME_FORMAT
),
3154 errmsg("invalid input syntax for type %s: \"%s\"",
3161 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
3162 * is WAY faster than the generic bsearch().
3164 static const datetkn
*
3165 datebsearch(const char *key
, const datetkn
*base
, int nel
)
3167 const datetkn
*last
= base
+ nel
- 1,
3171 while (last
>= base
)
3173 position
= base
+ ((last
- base
) >> 1);
3174 result
= key
[0] - position
->token
[0];
3177 result
= strncmp(key
, position
->token
, TOKMAXLEN
);
3182 last
= position
- 1;
3184 base
= position
+ 1;
3190 * Append representation of a numeric timezone offset to str.
3193 EncodeTimezone(char *str
, int tz
, int style
)
3200 min
= sec
/ SECS_PER_MINUTE
;
3201 sec
-= min
* SECS_PER_MINUTE
;
3202 hour
= min
/ MINS_PER_HOUR
;
3203 min
-= hour
* MINS_PER_HOUR
;
3206 /* TZ is negated compared to sign we wish to display ... */
3207 *str
++ = (tz
<= 0 ? '+' : '-');
3210 sprintf(str
, "%02d:%02d:%02d", hour
, min
, sec
);
3211 else if (min
!= 0 || style
== USE_XSD_DATES
)
3212 sprintf(str
, "%02d:%02d", hour
, min
);
3214 sprintf(str
, "%02d", hour
);
3218 * Encode date as local time.
3221 EncodeDateOnly(struct pg_tm
* tm
, int style
, char *str
)
3223 if (tm
->tm_mon
< 1 || tm
->tm_mon
> MONTHS_PER_YEAR
)
3230 /* compatible with ISO date formats */
3231 if (tm
->tm_year
> 0)
3232 sprintf(str
, "%04d-%02d-%02d",
3233 tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3235 sprintf(str
, "%04d-%02d-%02d %s",
3236 -(tm
->tm_year
- 1), tm
->tm_mon
, tm
->tm_mday
, "BC");
3240 /* compatible with Oracle/Ingres date formats */
3241 if (DateOrder
== DATEORDER_DMY
)
3242 sprintf(str
, "%02d/%02d", tm
->tm_mday
, tm
->tm_mon
);
3244 sprintf(str
, "%02d/%02d", tm
->tm_mon
, tm
->tm_mday
);
3245 if (tm
->tm_year
> 0)
3246 sprintf(str
+ 5, "/%04d", tm
->tm_year
);
3248 sprintf(str
+ 5, "/%04d %s", -(tm
->tm_year
- 1), "BC");
3251 case USE_GERMAN_DATES
:
3252 /* German-style date format */
3253 sprintf(str
, "%02d.%02d", tm
->tm_mday
, tm
->tm_mon
);
3254 if (tm
->tm_year
> 0)
3255 sprintf(str
+ 5, ".%04d", tm
->tm_year
);
3257 sprintf(str
+ 5, ".%04d %s", -(tm
->tm_year
- 1), "BC");
3260 case USE_POSTGRES_DATES
:
3262 /* traditional date-only style for Postgres */
3263 if (DateOrder
== DATEORDER_DMY
)
3264 sprintf(str
, "%02d-%02d", tm
->tm_mday
, tm
->tm_mon
);
3266 sprintf(str
, "%02d-%02d", tm
->tm_mon
, tm
->tm_mday
);
3267 if (tm
->tm_year
> 0)
3268 sprintf(str
+ 5, "-%04d", tm
->tm_year
);
3270 sprintf(str
+ 5, "-%04d %s", -(tm
->tm_year
- 1), "BC");
3275 } /* EncodeDateOnly() */
3279 * Encode time fields only.
3282 EncodeTimeOnly(struct pg_tm
* tm
, fsec_t fsec
, int *tzp
, int style
, char *str
)
3284 if (tm
->tm_hour
< 0 || tm
->tm_hour
> HOURS_PER_DAY
)
3287 sprintf(str
, "%02d:%02d", tm
->tm_hour
, tm
->tm_min
);
3290 * Print fractional seconds if any. The fractional field widths here
3291 * should be equal to the larger of MAX_TIME_PRECISION and
3292 * MAX_TIMESTAMP_PRECISION.
3296 #ifdef HAVE_INT64_TIMESTAMP
3297 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3299 sprintf(str
+ strlen(str
), ":%013.10f", tm
->tm_sec
+ fsec
);
3301 TrimTrailingZeros(str
);
3304 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3307 EncodeTimezone(str
, *tzp
, style
);
3310 } /* EncodeTimeOnly() */
3314 * Encode date and time interpreted as local time.
3315 * Support several date styles:
3316 * Postgres - day mon hh:mm:ss yyyy tz
3317 * SQL - mm/dd/yyyy hh:mm:ss.ss tz
3318 * ISO - yyyy-mm-dd hh:mm:ss+/-tz
3319 * German - dd.mm.yyyy hh:mm:ss tz
3320 * XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz
3321 * Variants (affects order of month and day for Postgres and SQL styles):
3323 * European - dd/mm/yyyy
3326 EncodeDateTime(struct pg_tm
* tm
, fsec_t fsec
, int *tzp
, char **tzn
, int style
, char *str
)
3331 * Why are we checking only the month field? Change this to an assert...
3332 * if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR) return -1;
3334 Assert(tm
->tm_mon
>= 1 && tm
->tm_mon
<= MONTHS_PER_YEAR
);
3340 /* Compatible with ISO-8601 date formats */
3342 if (style
== USE_ISO_DATES
)
3343 sprintf(str
, "%04d-%02d-%02d %02d:%02d",
3344 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3345 tm
->tm_mon
, tm
->tm_mday
, tm
->tm_hour
, tm
->tm_min
);
3347 sprintf(str
, "%04d-%02d-%02dT%02d:%02d",
3348 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3349 tm
->tm_mon
, tm
->tm_mday
, tm
->tm_hour
, tm
->tm_min
);
3353 * Print fractional seconds if any. The field widths here should
3354 * be at least equal to MAX_TIMESTAMP_PRECISION.
3356 * In float mode, don't print fractional seconds before 1 AD,
3357 * since it's unlikely there's any precision left ...
3359 #ifdef HAVE_INT64_TIMESTAMP
3362 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3363 TrimTrailingZeros(str
);
3366 if (fsec
!= 0 && tm
->tm_year
> 0)
3368 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3369 TrimTrailingZeros(str
);
3373 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3376 * tzp == NULL indicates that we don't want *any* time zone info
3377 * in the output string. *tzn != NULL indicates that we have alpha
3378 * time zone info available. tm_isdst != -1 indicates that we have
3379 * a valid time zone translation.
3381 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3382 EncodeTimezone(str
, *tzp
, style
);
3384 if (tm
->tm_year
<= 0)
3385 sprintf(str
+ strlen(str
), " BC");
3389 /* Compatible with Oracle/Ingres date formats */
3391 if (DateOrder
== DATEORDER_DMY
)
3392 sprintf(str
, "%02d/%02d", tm
->tm_mday
, tm
->tm_mon
);
3394 sprintf(str
, "%02d/%02d", tm
->tm_mon
, tm
->tm_mday
);
3396 sprintf(str
+ 5, "/%04d %02d:%02d",
3397 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3398 tm
->tm_hour
, tm
->tm_min
);
3401 * Print fractional seconds if any. The field widths here should
3402 * be at least equal to MAX_TIMESTAMP_PRECISION.
3404 * In float mode, don't print fractional seconds before 1 AD,
3405 * since it's unlikely there's any precision left ...
3407 #ifdef HAVE_INT64_TIMESTAMP
3410 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3411 TrimTrailingZeros(str
);
3414 if (fsec
!= 0 && tm
->tm_year
> 0)
3416 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3417 TrimTrailingZeros(str
);
3421 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3423 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3426 sprintf(str
+ strlen(str
), " %.*s", MAXTZLEN
, *tzn
);
3428 EncodeTimezone(str
, *tzp
, style
);
3431 if (tm
->tm_year
<= 0)
3432 sprintf(str
+ strlen(str
), " BC");
3435 case USE_GERMAN_DATES
:
3436 /* German variant on European style */
3438 sprintf(str
, "%02d.%02d", tm
->tm_mday
, tm
->tm_mon
);
3440 sprintf(str
+ 5, ".%04d %02d:%02d",
3441 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1),
3442 tm
->tm_hour
, tm
->tm_min
);
3445 * Print fractional seconds if any. The field widths here should
3446 * be at least equal to MAX_TIMESTAMP_PRECISION.
3448 * In float mode, don't print fractional seconds before 1 AD,
3449 * since it's unlikely there's any precision left ...
3451 #ifdef HAVE_INT64_TIMESTAMP
3454 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3455 TrimTrailingZeros(str
);
3458 if (fsec
!= 0 && tm
->tm_year
> 0)
3460 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3461 TrimTrailingZeros(str
);
3465 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3467 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3470 sprintf(str
+ strlen(str
), " %.*s", MAXTZLEN
, *tzn
);
3472 EncodeTimezone(str
, *tzp
, style
);
3475 if (tm
->tm_year
<= 0)
3476 sprintf(str
+ strlen(str
), " BC");
3479 case USE_POSTGRES_DATES
:
3481 /* Backward-compatible with traditional Postgres abstime dates */
3483 day
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3484 tm
->tm_wday
= j2day(day
);
3486 strncpy(str
, days
[tm
->tm_wday
], 3);
3487 strcpy(str
+ 3, " ");
3489 if (DateOrder
== DATEORDER_DMY
)
3490 sprintf(str
+ 4, "%02d %3s", tm
->tm_mday
, months
[tm
->tm_mon
- 1]);
3492 sprintf(str
+ 4, "%3s %02d", months
[tm
->tm_mon
- 1], tm
->tm_mday
);
3494 sprintf(str
+ 10, " %02d:%02d", tm
->tm_hour
, tm
->tm_min
);
3497 * Print fractional seconds if any. The field widths here should
3498 * be at least equal to MAX_TIMESTAMP_PRECISION.
3500 * In float mode, don't print fractional seconds before 1 AD,
3501 * since it's unlikely there's any precision left ...
3503 #ifdef HAVE_INT64_TIMESTAMP
3506 sprintf(str
+ strlen(str
), ":%02d.%06d", tm
->tm_sec
, fsec
);
3507 TrimTrailingZeros(str
);
3510 if (fsec
!= 0 && tm
->tm_year
> 0)
3512 sprintf(str
+ strlen(str
), ":%09.6f", tm
->tm_sec
+ fsec
);
3513 TrimTrailingZeros(str
);
3517 sprintf(str
+ strlen(str
), ":%02d", tm
->tm_sec
);
3519 sprintf(str
+ strlen(str
), " %04d",
3520 (tm
->tm_year
> 0) ? tm
->tm_year
: -(tm
->tm_year
- 1));
3522 if (tzp
!= NULL
&& tm
->tm_isdst
>= 0)
3525 sprintf(str
+ strlen(str
), " %.*s", MAXTZLEN
, *tzn
);
3529 * We have a time zone, but no string version. Use the
3530 * numeric form, but be sure to include a leading space to
3531 * avoid formatting something which would be rejected by
3532 * the date/time parser later. - thomas 2001-10-19
3534 sprintf(str
+ strlen(str
), " ");
3535 EncodeTimezone(str
, *tzp
, style
);
3539 if (tm
->tm_year
<= 0)
3540 sprintf(str
+ strlen(str
), " BC");
3549 * Interpret time structure as a delta time and convert to string.
3551 * Support "traditional Postgres" and ISO-8601 styles.
3552 * Actually, afaik ISO does not address time interval formatting,
3553 * but this looks similar to the spec for absolute date/time.
3554 * - thomas 1998-04-30
3557 EncodeInterval(struct pg_tm
* tm
, fsec_t fsec
, int style
, char *str
)
3559 bool is_before
= FALSE
;
3560 bool is_nonzero
= FALSE
;
3564 * The sign of year and month are guaranteed to match, since they are
3565 * stored internally as "month". But we'll need to check for is_before and
3566 * is_nonzero when determining the signs of hour/minute/seconds fields.
3570 /* compatible with ISO date formats */
3572 if (tm
->tm_year
!= 0)
3574 sprintf(cp
, "%d year%s",
3575 tm
->tm_year
, (tm
->tm_year
!= 1) ? "s" : "");
3577 is_before
= (tm
->tm_year
< 0);
3581 if (tm
->tm_mon
!= 0)
3583 sprintf(cp
, "%s%s%d mon%s", is_nonzero
? " " : "",
3584 (is_before
&& tm
->tm_mon
> 0) ? "+" : "",
3585 tm
->tm_mon
, (tm
->tm_mon
!= 1) ? "s" : "");
3587 is_before
= (tm
->tm_mon
< 0);
3591 if (tm
->tm_mday
!= 0)
3593 sprintf(cp
, "%s%s%d day%s", is_nonzero
? " " : "",
3594 (is_before
&& tm
->tm_mday
> 0) ? "+" : "",
3595 tm
->tm_mday
, (tm
->tm_mday
!= 1) ? "s" : "");
3597 is_before
= (tm
->tm_mday
< 0);
3601 if (!is_nonzero
|| tm
->tm_hour
!= 0 || tm
->tm_min
!= 0 ||
3602 tm
->tm_sec
!= 0 || fsec
!= 0)
3604 int minus
= (tm
->tm_hour
< 0 || tm
->tm_min
< 0 ||
3605 tm
->tm_sec
< 0 || fsec
< 0);
3607 sprintf(cp
, "%s%s%02d:%02d", is_nonzero
? " " : "",
3608 (minus
? "-" : (is_before
? "+" : "")),
3609 abs(tm
->tm_hour
), abs(tm
->tm_min
));
3611 /* Mark as "non-zero" since the fields are now filled in */
3614 /* need fractional seconds? */
3617 #ifdef HAVE_INT64_TIMESTAMP
3618 sprintf(cp
, ":%02d", abs(tm
->tm_sec
));
3620 sprintf(cp
, ".%06d", Abs(fsec
));
3623 sprintf(cp
, ":%012.9f", fabs(fsec
));
3625 TrimTrailingZeros(cp
);
3630 sprintf(cp
, ":%02d", abs(tm
->tm_sec
));
3636 case USE_POSTGRES_DATES
:
3641 if (tm
->tm_year
!= 0)
3643 int year
= tm
->tm_year
;
3645 if (tm
->tm_year
< 0)
3648 sprintf(cp
, "%d year%s", year
,
3649 (year
!= 1) ? "s" : "");
3651 is_before
= (tm
->tm_year
< 0);
3655 if (tm
->tm_mon
!= 0)
3657 int mon
= tm
->tm_mon
;
3659 if (is_before
|| (!is_nonzero
&& tm
->tm_mon
< 0))
3662 sprintf(cp
, "%s%d mon%s", is_nonzero
? " " : "", mon
,
3663 (mon
!= 1) ? "s" : "");
3666 is_before
= (tm
->tm_mon
< 0);
3670 if (tm
->tm_mday
!= 0)
3672 int day
= tm
->tm_mday
;
3674 if (is_before
|| (!is_nonzero
&& tm
->tm_mday
< 0))
3677 sprintf(cp
, "%s%d day%s", is_nonzero
? " " : "", day
,
3678 (day
!= 1) ? "s" : "");
3681 is_before
= (tm
->tm_mday
< 0);
3684 if (tm
->tm_hour
!= 0)
3686 int hour
= tm
->tm_hour
;
3688 if (is_before
|| (!is_nonzero
&& tm
->tm_hour
< 0))
3691 sprintf(cp
, "%s%d hour%s", is_nonzero
? " " : "", hour
,
3692 (hour
!= 1) ? "s" : "");
3695 is_before
= (tm
->tm_hour
< 0);
3699 if (tm
->tm_min
!= 0)
3701 int min
= tm
->tm_min
;
3703 if (is_before
|| (!is_nonzero
&& tm
->tm_min
< 0))
3706 sprintf(cp
, "%s%d min%s", is_nonzero
? " " : "", min
,
3707 (min
!= 1) ? "s" : "");
3710 is_before
= (tm
->tm_min
< 0);
3714 /* fractional seconds? */
3719 #ifdef HAVE_INT64_TIMESTAMP
3721 if (is_before
|| (!is_nonzero
&& tm
->tm_sec
< 0))
3723 tm
->tm_sec
= -tm
->tm_sec
;
3727 else if (!is_nonzero
&& tm
->tm_sec
== 0 && fsec
< 0)
3732 sprintf(cp
, "%s%d.%02d secs", is_nonzero
? " " : "",
3733 tm
->tm_sec
, ((int) sec
) / 10000);
3738 if (is_before
|| (!is_nonzero
&& fsec
< 0))
3741 sprintf(cp
, "%s%.2f secs", is_nonzero
? " " : "", sec
);
3744 is_before
= (fsec
< 0);
3748 /* otherwise, integer seconds only? */
3749 else if (tm
->tm_sec
!= 0)
3751 int sec
= tm
->tm_sec
;
3753 if (is_before
|| (!is_nonzero
&& tm
->tm_sec
< 0))
3756 sprintf(cp
, "%s%d sec%s", is_nonzero
? " " : "", sec
,
3757 (sec
!= 1) ? "s" : "");
3760 is_before
= (tm
->tm_sec
< 0);
3766 /* identically zero? then put in a unitless zero... */
3773 if (is_before
&& (style
!= USE_ISO_DATES
))
3780 } /* EncodeInterval() */
3784 * We've been burnt by stupid errors in the ordering of the datetkn tables
3785 * once too often. Arrange to check them during postmaster start.
3788 CheckDateTokenTable(const char *tablename
, const datetkn
*base
, int nel
)
3793 for (i
= 1; i
< nel
; i
++)
3795 if (strncmp(base
[i
- 1].token
, base
[i
].token
, TOKMAXLEN
) >= 0)
3797 elog(LOG
, "ordering error in %s table: \"%.*s\" >= \"%.*s\"",
3799 TOKMAXLEN
, base
[i
- 1].token
,
3800 TOKMAXLEN
, base
[i
].token
);
3808 CheckDateTokenTables(void)
3812 Assert(UNIX_EPOCH_JDATE
== date2j(1970, 1, 1));
3813 Assert(POSTGRES_EPOCH_JDATE
== date2j(2000, 1, 1));
3815 ok
&= CheckDateTokenTable("datetktbl", datetktbl
, szdatetktbl
);
3816 ok
&= CheckDateTokenTable("deltatktbl", deltatktbl
, szdeltatktbl
);
3821 * This function gets called during timezone config file load or reload
3822 * to create the final array of timezone tokens. The argument array
3823 * is already sorted in name order. This data is in a temporary memory
3824 * context and must be copied to somewhere permanent.
3827 InstallTimeZoneAbbrevs(tzEntry
*abbrevs
, int n
)
3833 * Copy the data into TopMemoryContext and convert to datetkn format.
3835 newtbl
= (datetkn
*) MemoryContextAlloc(TopMemoryContext
,
3836 n
* sizeof(datetkn
));
3837 for (i
= 0; i
< n
; i
++)
3839 strncpy(newtbl
[i
].token
, abbrevs
[i
].abbrev
, TOKMAXLEN
);
3840 newtbl
[i
].type
= abbrevs
[i
].is_dst
? DTZ
: TZ
;
3841 TOVAL(&newtbl
[i
], abbrevs
[i
].offset
/ 60);
3844 /* Check the ordering, if testing */
3845 Assert(CheckDateTokenTable("timezone offset", newtbl
, n
));
3847 /* Now safe to replace existing table (if any) */
3849 pfree(timezonetktbl
);
3850 timezonetktbl
= newtbl
;
3851 sztimezonetktbl
= n
;
3853 /* clear date cache in case it contains any stale timezone names */
3854 for (i
= 0; i
< MAXDATEFIELDS
; i
++)
3855 datecache
[i
] = NULL
;
3859 * This set-returning function reads all the available time zone abbreviations
3860 * and returns a set of (abbrev, utc_offset, is_dst).
3863 pg_timezone_abbrevs(PG_FUNCTION_ARGS
)
3865 FuncCallContext
*funcctx
;
3871 char buffer
[TOKMAXLEN
+ 1];
3874 Interval
*resInterval
;
3876 /* stuff done only on the first call of the function */
3877 if (SRF_IS_FIRSTCALL())
3880 MemoryContext oldcontext
;
3882 /* create a function context for cross-call persistence */
3883 funcctx
= SRF_FIRSTCALL_INIT();
3886 * switch to memory context appropriate for multiple function calls
3888 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
3890 /* allocate memory for user context */
3891 pindex
= (int *) palloc(sizeof(int));
3893 funcctx
->user_fctx
= (void *) pindex
;
3896 * build tupdesc for result tuples. This must match this function's
3899 tupdesc
= CreateTemplateTupleDesc(3, false);
3900 TupleDescInitEntry(tupdesc
, (AttrNumber
) 1, "abbrev",
3902 TupleDescInitEntry(tupdesc
, (AttrNumber
) 2, "utc_offset",
3903 INTERVALOID
, -1, 0);
3904 TupleDescInitEntry(tupdesc
, (AttrNumber
) 3, "is_dst",
3907 funcctx
->tuple_desc
= BlessTupleDesc(tupdesc
);
3908 MemoryContextSwitchTo(oldcontext
);
3911 /* stuff done on every call of the function */
3912 funcctx
= SRF_PERCALL_SETUP();
3913 pindex
= (int *) funcctx
->user_fctx
;
3915 if (*pindex
>= sztimezonetktbl
)
3916 SRF_RETURN_DONE(funcctx
);
3918 MemSet(nulls
, 0, sizeof(nulls
));
3921 * Convert name to text, using upcasing conversion that is the inverse of
3922 * what ParseDateTime() uses.
3924 strncpy(buffer
, timezonetktbl
[*pindex
].token
, TOKMAXLEN
);
3925 buffer
[TOKMAXLEN
] = '\0'; /* may not be null-terminated */
3926 for (p
= (unsigned char *) buffer
; *p
; p
++)
3927 *p
= pg_toupper(*p
);
3929 values
[0] = CStringGetTextDatum(buffer
);
3931 MemSet(&tm
, 0, sizeof(struct pg_tm
));
3932 tm
.tm_min
= (-1) * FROMVAL(&timezonetktbl
[*pindex
]);
3933 resInterval
= (Interval
*) palloc(sizeof(Interval
));
3934 tm2interval(&tm
, 0, resInterval
);
3935 values
[1] = IntervalPGetDatum(resInterval
);
3937 Assert(timezonetktbl
[*pindex
].type
== DTZ
||
3938 timezonetktbl
[*pindex
].type
== TZ
);
3939 values
[2] = BoolGetDatum(timezonetktbl
[*pindex
].type
== DTZ
);
3943 tuple
= heap_form_tuple(funcctx
->tuple_desc
, values
, nulls
);
3944 result
= HeapTupleGetDatum(tuple
);
3946 SRF_RETURN_NEXT(funcctx
, result
);
3950 * This set-returning function reads all the available full time zones
3951 * and returns a set of (name, abbrev, utc_offset, is_dst).
3954 pg_timezone_names(PG_FUNCTION_ARGS
)
3956 MemoryContext oldcontext
;
3957 FuncCallContext
*funcctx
;
3968 Interval
*resInterval
;
3971 /* stuff done only on the first call of the function */
3972 if (SRF_IS_FIRSTCALL())
3976 /* create a function context for cross-call persistence */
3977 funcctx
= SRF_FIRSTCALL_INIT();
3980 * switch to memory context appropriate for multiple function calls
3982 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
3984 /* initialize timezone scanning code */
3985 tzenum
= pg_tzenumerate_start();
3986 funcctx
->user_fctx
= (void *) tzenum
;
3989 * build tupdesc for result tuples. This must match this function's
3992 tupdesc
= CreateTemplateTupleDesc(4, false);
3993 TupleDescInitEntry(tupdesc
, (AttrNumber
) 1, "name",
3995 TupleDescInitEntry(tupdesc
, (AttrNumber
) 2, "abbrev",
3997 TupleDescInitEntry(tupdesc
, (AttrNumber
) 3, "utc_offset",
3998 INTERVALOID
, -1, 0);
3999 TupleDescInitEntry(tupdesc
, (AttrNumber
) 4, "is_dst",
4002 funcctx
->tuple_desc
= BlessTupleDesc(tupdesc
);
4003 MemoryContextSwitchTo(oldcontext
);
4006 /* stuff done on every call of the function */
4007 funcctx
= SRF_PERCALL_SETUP();
4008 tzenum
= (pg_tzenum
*) funcctx
->user_fctx
;
4010 /* search for another zone to display */
4013 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4014 tz
= pg_tzenumerate_next(tzenum
);
4015 MemoryContextSwitchTo(oldcontext
);
4019 pg_tzenumerate_end(tzenum
);
4020 funcctx
->user_fctx
= NULL
;
4021 SRF_RETURN_DONE(funcctx
);
4024 /* Convert now() to local time in this zone */
4025 if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
4026 &tzoff
, &tm
, &fsec
, &tzn
, tz
) != 0)
4027 continue; /* ignore if conversion fails */
4029 /* Ignore zic's rather silly "Factory" time zone */
4030 if (tzn
&& strcmp(tzn
, "Local time zone must be set--see zic manual page") == 0)
4033 /* Found a displayable zone */
4037 MemSet(nulls
, 0, sizeof(nulls
));
4039 values
[0] = CStringGetTextDatum(pg_get_timezone_name(tz
));
4040 values
[1] = CStringGetTextDatum(tzn
? tzn
: "");
4042 MemSet(&itm
, 0, sizeof(struct pg_tm
));
4043 itm
.tm_sec
= -tzoff
;
4044 resInterval
= (Interval
*) palloc(sizeof(Interval
));
4045 tm2interval(&itm
, 0, resInterval
);
4046 values
[2] = IntervalPGetDatum(resInterval
);
4048 values
[3] = BoolGetDatum(tm
.tm_isdst
> 0);
4050 tuple
= heap_form_tuple(funcctx
->tuple_desc
, values
, nulls
);
4051 result
= HeapTupleGetDatum(tuple
);
4053 SRF_RETURN_NEXT(funcctx
, result
);