diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4f7195c508..6788ba8ef4 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -8131,13 +8131,11 @@ SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}'); TZ - upper case time-zone abbreviation - (only supported in to_char) + upper case time-zone abbreviation tz - lower case time-zone abbreviation - (only supported in to_char) + lower case time-zone abbreviation TZH @@ -8149,8 +8147,8 @@ SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}'); OF - time-zone offset from UTC - (only supported in to_char) + time-zone offset from UTC (HH + or HH:MM) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 17b0248bf7..cccabb0c2a 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -3246,6 +3246,82 @@ DecodeTimezoneNameToTz(const char *tzname) return result; } +/* DecodeTimezoneAbbrevPrefix() + * Interpret prefix of string as a timezone abbreviation, if possible. + * + * This has roughly the same functionality as DecodeTimezoneAbbrev(), + * but the API is adapted to the needs of formatting.c. Notably, + * we will match the longest possible prefix of the given string + * rather than insisting on a complete match, and downcasing is applied + * here rather than in the caller. + * + * Returns the length of the timezone abbreviation, or -1 if not recognized. + * On success, sets *offset to the GMT offset for the abbreviation if it + * is a fixed-offset abbreviation, or sets *tz to the pg_tz struct for + * a dynamic abbreviation. + */ +int +DecodeTimezoneAbbrevPrefix(const char *str, int *offset, pg_tz **tz) +{ + char lowtoken[TOKMAXLEN + 1]; + int len; + + *offset = 0; /* avoid uninitialized vars on failure */ + *tz = NULL; + + if (!zoneabbrevtbl) + return -1; /* no abbrevs known, so fail immediately */ + + /* Downcase as much of the string as we could need */ + for (len = 0; len < TOKMAXLEN; len++) + { + if (*str == '\0' || !isalpha((unsigned char) *str)) + break; + lowtoken[len] = pg_tolower((unsigned char) *str++); + } + lowtoken[len] = '\0'; + + /* + * We could avoid doing repeated binary searches if we cared to duplicate + * datebsearch here, but it's not clear that such an optimization would be + * worth the trouble. In common cases there's probably not anything after + * the zone abbrev anyway. So just search with successively truncated + * strings. + */ + while (len > 0) + { + const datetkn *tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs, + zoneabbrevtbl->numabbrevs); + + if (tp != NULL) + { + if (tp->type == DYNTZ) + { + DateTimeErrorExtra extra; + pg_tz *tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, + &extra); + + if (tzp != NULL) + { + /* Caller must resolve the abbrev's current meaning */ + *tz = tzp; + return len; + } + } + else + { + /* Fixed-offset zone abbrev, so it's easy */ + *offset = tp->value; + return len; + } + } + lowtoken[--len] = '\0'; + } + + /* Did not find a match */ + return -1; +} + /* ClearPgItmIn * diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 83e1f1265c..829aaa8d0e 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -418,14 +418,24 @@ typedef struct us, yysz, /* is it YY or YYYY ? */ clock, /* 12 or 24 hour clock? */ - tzsign, /* +1, -1 or 0 if timezone info is absent */ + tzsign, /* +1, -1, or 0 if no TZH/TZM fields */ tzh, tzm, ff; /* fractional precision */ + bool has_tz; /* was there a TZ field? */ + int gmtoffset; /* GMT offset of fixed-offset zone abbrev */ + pg_tz *tzp; /* pg_tz for dynamic abbrev */ + char *abbrev; /* dynamic abbrev */ } TmFromChar; #define ZERO_tmfc(_X) memset(_X, 0, sizeof(TmFromChar)) +struct fmt_tz /* do_to_timestamp's timezone info output */ +{ + bool has_tz; /* was there any TZ/TZH/TZM field? */ + int gmtoffset; /* GMT offset in seconds */ +}; + /* ---------- * Debug * ---------- @@ -1058,8 +1068,8 @@ static bool from_char_seq_search(int *dest, const char **src, char **localized_array, Oid collid, FormatNode *node, Node *escontext); static bool do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std, - struct pg_tm *tm, fsec_t *fsec, int *fprec, - uint32 *flags, Node *escontext); + struct pg_tm *tm, fsec_t *fsec, struct fmt_tz *tz, + int *fprec, uint32 *flags, Node *escontext); static char *fill_str(char *str, int c, int max); static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree); static char *int_to_roman(int number); @@ -3444,7 +3454,7 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out, case DCH_FF5: case DCH_FF6: out->ff = n->key->id - DCH_FF1 + 1; - /* fall through */ + /* FALLTHROUGH */ case DCH_US: /* microsecond */ len = from_char_parse_int_len(&out->us, &s, n->key->id == DCH_US ? 6 : @@ -3467,11 +3477,63 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out, break; case DCH_tz: case DCH_TZ: + { + int tzlen; + + tzlen = DecodeTimezoneAbbrevPrefix(s, + &out->gmtoffset, + &out->tzp); + if (tzlen > 0) + { + out->has_tz = true; + /* we only need the zone abbrev for DYNTZ case */ + if (out->tzp) + out->abbrev = pnstrdup(s, tzlen); + out->tzsign = 0; /* drop any earlier TZH/TZM info */ + s += tzlen; + break; + } + else if (isalpha((unsigned char) *s)) + { + /* + * It doesn't match any abbreviation, but it starts + * with a letter. OF format certainly won't succeed; + * assume it's a misspelled abbreviation and complain + * accordingly. + */ + ereturn(escontext,, + (errcode(ERRCODE_INVALID_DATETIME_FORMAT), + errmsg("invalid value \"%s\" for \"%s\"", + s, n->key->name), + errdetail("Time zone abbreviation is not recognized."))); + } + /* otherwise parse it like OF */ + } + /* FALLTHROUGH */ case DCH_OF: - ereturn(escontext,, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("formatting field \"%s\" is only supported in to_char", - n->key->name))); + /* OF is equivalent to TZH or TZH:TZM */ + /* see TZH comments below */ + if (*s == '+' || *s == '-' || *s == ' ') + { + out->tzsign = *s == '-' ? -1 : +1; + s++; + } + else + { + if (extra_skip > 0 && *(s - 1) == '-') + out->tzsign = -1; + else + out->tzsign = +1; + } + if (from_char_parse_int_len(&out->tzh, &s, 2, n, escontext) < 0) + return; + if (*s == ':') + { + s++; + if (from_char_parse_int_len(&out->tzm, &s, 2, n, + escontext) < 0) + return; + } break; case DCH_TZH: @@ -4167,22 +4229,16 @@ to_timestamp(PG_FUNCTION_ARGS) Timestamp result; int tz; struct pg_tm tm; + struct fmt_tz ftz; fsec_t fsec; int fprec; do_to_timestamp(date_txt, fmt, collid, false, - &tm, &fsec, &fprec, NULL, NULL); + &tm, &fsec, &ftz, &fprec, NULL, NULL); /* Use the specified time zone, if any. */ - if (tm.tm_zone) - { - DateTimeErrorExtra extra; - int dterr = DecodeTimezone(tm.tm_zone, &tz); - - if (dterr) - DateTimeParseError(dterr, &extra, text_to_cstring(date_txt), - "timestamptz", NULL); - } + if (ftz.has_tz) + tz = ftz.gmtoffset; else tz = DetermineTimeZoneOffset(&tm, session_timezone); @@ -4211,10 +4267,11 @@ to_date(PG_FUNCTION_ARGS) Oid collid = PG_GET_COLLATION(); DateADT result; struct pg_tm tm; + struct fmt_tz ftz; fsec_t fsec; do_to_timestamp(date_txt, fmt, collid, false, - &tm, &fsec, NULL, NULL, NULL); + &tm, &fsec, &ftz, NULL, NULL, NULL); /* Prevent overflow in Julian-day routines */ if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday)) @@ -4256,12 +4313,13 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, Node *escontext) { struct pg_tm tm; + struct fmt_tz ftz; fsec_t fsec; int fprec; uint32 flags; if (!do_to_timestamp(date_txt, fmt, collid, strict, - &tm, &fsec, &fprec, &flags, escontext)) + &tm, &fsec, &ftz, &fprec, &flags, escontext)) return (Datum) 0; *typmod = fprec ? fprec : -1; /* fractional part precision */ @@ -4274,18 +4332,9 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, { TimestampTz result; - if (tm.tm_zone) + if (ftz.has_tz) { - DateTimeErrorExtra extra; - int dterr = DecodeTimezone(tm.tm_zone, tz); - - if (dterr) - { - DateTimeParseError(dterr, &extra, - text_to_cstring(date_txt), - "timestamptz", escontext); - return (Datum) 0; - } + *tz = ftz.gmtoffset; } else { @@ -4366,18 +4415,9 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, { TimeTzADT *result = palloc(sizeof(TimeTzADT)); - if (tm.tm_zone) + if (ftz.has_tz) { - DateTimeErrorExtra extra; - int dterr = DecodeTimezone(tm.tm_zone, tz); - - if (dterr) - { - DateTimeParseError(dterr, &extra, - text_to_cstring(date_txt), - "timetz", escontext); - return (Datum) 0; - } + *tz = ftz.gmtoffset; } else { @@ -4430,7 +4470,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, * do_to_timestamp: shared code for to_timestamp and to_date * * Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm, - * fractional seconds, and fractional precision. + * fractional seconds, struct fmt_tz, and fractional precision. * * 'collid' identifies the collation to use, if needed. * 'std' specifies standard parsing mode. @@ -4447,12 +4487,12 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, * 'date_txt'. * * The TmFromChar is then analysed and converted into the final results in - * struct 'tm', 'fsec', and 'fprec'. + * struct 'tm', 'fsec', struct 'tz', and 'fprec'. */ static bool do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std, - struct pg_tm *tm, fsec_t *fsec, int *fprec, - uint32 *flags, Node *escontext) + struct pg_tm *tm, fsec_t *fsec, struct fmt_tz *tz, + int *fprec, uint32 *flags, Node *escontext) { FormatNode *format = NULL; TmFromChar tmfc; @@ -4469,6 +4509,7 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std, ZERO_tmfc(&tmfc); ZERO_tm(tm); *fsec = 0; + tz->has_tz = false; if (fprec) *fprec = 0; if (flags) @@ -4744,11 +4785,14 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std, goto fail; } - /* Save parsed time-zone into tm->tm_zone if it was specified */ + /* + * If timezone info was present, reduce it to a GMT offset. (We cannot do + * this until we've filled all of the tm struct, since the zone's offset + * might be time-varying.) + */ if (tmfc.tzsign) { - char *tz; - + /* TZH and/or TZM fields */ if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR || tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR) { @@ -4757,10 +4801,27 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std, goto fail; } - tz = psprintf("%c%02d:%02d", - tmfc.tzsign > 0 ? '+' : '-', tmfc.tzh, tmfc.tzm); - - tm->tm_zone = tz; + tz->has_tz = true; + tz->gmtoffset = (tmfc.tzh * MINS_PER_HOUR + tmfc.tzm) * SECS_PER_MINUTE; + /* note we are flipping the sign convention here */ + if (tmfc.tzsign > 0) + tz->gmtoffset = -tz->gmtoffset; + } + else if (tmfc.has_tz) + { + /* TZ field */ + tz->has_tz = true; + if (tmfc.tzp == NULL) + { + /* fixed-offset abbreviation; flip the sign convention */ + tz->gmtoffset = -tmfc.gmtoffset; + } + else + { + /* dynamic-offset abbreviation, resolve using specified time */ + tz->gmtoffset = DetermineTimeZoneAbbrevOffset(tm, tmfc.abbrev, + tmfc.tzp); + } } DEBUG_TM(tm); diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 02a700292b..22f598cd35 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -2296,20 +2296,14 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp, static const char *fmt_str[] = { "yyyy-mm-dd", /* date */ - "HH24:MI:SS.USTZH:TZM", /* timetz */ - "HH24:MI:SS.USTZH", - "HH24:MI:SSTZH:TZM", - "HH24:MI:SSTZH", + "HH24:MI:SS.USTZ", /* timetz */ + "HH24:MI:SSTZ", "HH24:MI:SS.US", /* time without tz */ "HH24:MI:SS", - "yyyy-mm-dd HH24:MI:SS.USTZH:TZM", /* timestamptz */ - "yyyy-mm-dd HH24:MI:SS.USTZH", - "yyyy-mm-dd HH24:MI:SSTZH:TZM", - "yyyy-mm-dd HH24:MI:SSTZH", - "yyyy-mm-dd\"T\"HH24:MI:SS.USTZH:TZM", - "yyyy-mm-dd\"T\"HH24:MI:SS.USTZH", - "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM", - "yyyy-mm-dd\"T\"HH24:MI:SSTZH", + "yyyy-mm-dd HH24:MI:SS.USTZ", /* timestamptz */ + "yyyy-mm-dd HH24:MI:SSTZ", + "yyyy-mm-dd\"T\"HH24:MI:SS.USTZ", + "yyyy-mm-dd\"T\"HH24:MI:SSTZ", "yyyy-mm-dd HH24:MI:SS.US", /* timestamp without tz */ "yyyy-mm-dd HH24:MI:SS", "yyyy-mm-dd\"T\"HH24:MI:SS.US", diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 460c75cfdd..e4ac2b8e7f 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -348,6 +348,9 @@ extern int DecodeUnits(int field, const char *lowtoken, int *val); extern int DecodeTimezoneName(const char *tzname, int *offset, pg_tz **tz); extern pg_tz *DecodeTimezoneNameToTz(const char *tzname); +extern int DecodeTimezoneAbbrevPrefix(const char *str, + int *offset, pg_tz **tz); + extern int j2day(int date); extern struct Node *TemporalSimplify(int32 max_precis, struct Node *node); diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out index cfb4b205e4..0bdb76695a 100644 --- a/src/test/regress/expected/horology.out +++ b/src/test/regress/expected/horology.out @@ -3140,8 +3140,66 @@ SELECT to_timestamp('2011-12-18 11:38 20', 'YYYY-MM-DD HH12:MI TZM'); Sun Dec 18 03:18:00 2011 PST (1 row) -SELECT to_timestamp('2011-12-18 11:38 PST', 'YYYY-MM-DD HH12:MI TZ'); -- NYI -ERROR: formatting field "TZ" is only supported in to_char +SELECT to_timestamp('2011-12-18 11:38 EST', 'YYYY-MM-DD HH12:MI TZ'); + to_timestamp +------------------------------ + Sun Dec 18 08:38:00 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI TZ'); + to_timestamp +------------------------------ + Sun Dec 18 08:38:00 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38 +01:30', 'YYYY-MM-DD HH12:MI TZ'); + to_timestamp +------------------------------ + Sun Dec 18 02:08:00 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38 MSK', 'YYYY-MM-DD HH12:MI TZ'); -- dyntz + to_timestamp +------------------------------ + Sat Dec 17 23:38:00 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38ESTFOO24', 'YYYY-MM-DD HH12:MITZFOOSS'); + to_timestamp +------------------------------ + Sun Dec 18 08:38:24 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38-05FOO24', 'YYYY-MM-DD HH12:MITZFOOSS'); + to_timestamp +------------------------------ + Sun Dec 18 08:38:24 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38 JUNK', 'YYYY-MM-DD HH12:MI TZ'); -- error +ERROR: invalid value "JUNK" for "TZ" +DETAIL: Time zone abbreviation is not recognized. +SELECT to_timestamp('2011-12-18 11:38 ...', 'YYYY-MM-DD HH12:MI TZ'); -- error +ERROR: invalid value ".." for "TZ" +DETAIL: Value must be an integer. +SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI OF'); + to_timestamp +------------------------------ + Sun Dec 18 08:38:00 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38 +01:30', 'YYYY-MM-DD HH12:MI OF'); + to_timestamp +------------------------------ + Sun Dec 18 02:08:00 2011 PST +(1 row) + +SELECT to_timestamp('2011-12-18 11:38 +xyz', 'YYYY-MM-DD HH12:MI OF'); -- error +ERROR: invalid value "xy" for "OF" +DETAIL: Value must be an integer. +SELECT to_timestamp('2011-12-18 11:38 +01:xyz', 'YYYY-MM-DD HH12:MI OF'); -- error +ERROR: invalid value "xy" for "OF" +DETAIL: Value must be an integer. SELECT to_timestamp('2018-11-02 12:34:56.025', 'YYYY-MM-DD HH24:MI:SS.MS'); to_timestamp ---------------------------------- @@ -3557,6 +3615,19 @@ SELECT to_date('0000-02-01','YYYY-MM-DD'); -- allowed, though it shouldn't be 02-01-0001 BC (1 row) +-- to_char's TZ format code produces zone abbrev if known +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); + to_char +------------------------- + 2012-12-12 12:00:00 PST +(1 row) + +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS tz'); + to_char +------------------------- + 2012-12-12 12:00:00 pst +(1 row) + -- -- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) -- @@ -3598,4 +3669,11 @@ SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD SSSSS'); 2012-12-12 43200 (1 row) +SET TIME ZONE '+2'; +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); + to_char +------------------------- + 2012-12-12 12:00:00 +02 +(1 row) + RESET TIME ZONE; diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out index e758d729f4..eea2af30c8 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -3228,6 +3228,18 @@ select jsonb_path_query('"2017-03-10T12:34:56.789+3:10"', '$.datetime()'); select jsonb_path_query('"2017-03-10t12:34:56.789+3:10"', '$.datetime()'); ERROR: datetime format is not recognized: "2017-03-10t12:34:56.789+3:10" HINT: Use a datetime template argument to specify the input data format. +select jsonb_path_query('"2017-03-10T12:34:56.789EST"', '$.datetime()'); + jsonb_path_query +--------------------------------- + "2017-03-10T12:34:56.789-05:00" +(1 row) + +select jsonb_path_query('"2017-03-10T12:34:56.789Z"', '$.datetime()'); + jsonb_path_query +--------------------------------- + "2017-03-10T12:34:56.789+00:00" +(1 row) + select jsonb_path_query('"12:34:56"', '$.datetime().type()'); jsonb_path_query -------------------------- diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql index 252bce4b1c..3b9573e954 100644 --- a/src/test/regress/sql/horology.sql +++ b/src/test/regress/sql/horology.sql @@ -501,7 +501,19 @@ SELECT to_timestamp('2011-12-18 11:38 +05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); SELECT to_timestamp('2011-12-18 11:38 -05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); SELECT to_timestamp('2011-12-18 11:38 20', 'YYYY-MM-DD HH12:MI TZM'); -SELECT to_timestamp('2011-12-18 11:38 PST', 'YYYY-MM-DD HH12:MI TZ'); -- NYI +SELECT to_timestamp('2011-12-18 11:38 EST', 'YYYY-MM-DD HH12:MI TZ'); +SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI TZ'); +SELECT to_timestamp('2011-12-18 11:38 +01:30', 'YYYY-MM-DD HH12:MI TZ'); +SELECT to_timestamp('2011-12-18 11:38 MSK', 'YYYY-MM-DD HH12:MI TZ'); -- dyntz +SELECT to_timestamp('2011-12-18 11:38ESTFOO24', 'YYYY-MM-DD HH12:MITZFOOSS'); +SELECT to_timestamp('2011-12-18 11:38-05FOO24', 'YYYY-MM-DD HH12:MITZFOOSS'); +SELECT to_timestamp('2011-12-18 11:38 JUNK', 'YYYY-MM-DD HH12:MI TZ'); -- error +SELECT to_timestamp('2011-12-18 11:38 ...', 'YYYY-MM-DD HH12:MI TZ'); -- error + +SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI OF'); +SELECT to_timestamp('2011-12-18 11:38 +01:30', 'YYYY-MM-DD HH12:MI OF'); +SELECT to_timestamp('2011-12-18 11:38 +xyz', 'YYYY-MM-DD HH12:MI OF'); -- error +SELECT to_timestamp('2011-12-18 11:38 +01:xyz', 'YYYY-MM-DD HH12:MI OF'); -- error SELECT to_timestamp('2018-11-02 12:34:56.025', 'YYYY-MM-DD HH24:MI:SS.MS'); @@ -616,6 +628,10 @@ SELECT to_date('2016 366', 'YYYY DDD'); -- ok SELECT to_date('2016 367', 'YYYY DDD'); SELECT to_date('0000-02-01','YYYY-MM-DD'); -- allowed, though it shouldn't be +-- to_char's TZ format code produces zone abbrev if known +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS tz'); + -- -- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) -- @@ -632,4 +648,8 @@ SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD SSSS'); SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD SSSSS'); +SET TIME ZONE '+2'; + +SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); + RESET TIME ZONE; diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql index 418eeac5ec..6406a2ea3b 100644 --- a/src/test/regress/sql/jsonb_jsonpath.sql +++ b/src/test/regress/sql/jsonb_jsonpath.sql @@ -755,6 +755,8 @@ select jsonb_path_query('"2017-03-10t12:34:56+3:10"', '$.datetime()'); select jsonb_path_query('"2017-03-10 12:34:56.789+3:10"', '$.datetime()'); select jsonb_path_query('"2017-03-10T12:34:56.789+3:10"', '$.datetime()'); select jsonb_path_query('"2017-03-10t12:34:56.789+3:10"', '$.datetime()'); +select jsonb_path_query('"2017-03-10T12:34:56.789EST"', '$.datetime()'); +select jsonb_path_query('"2017-03-10T12:34:56.789Z"', '$.datetime()'); select jsonb_path_query('"12:34:56"', '$.datetime().type()'); select jsonb_path_query('"12:34:56"', '$.datetime()'); select jsonb_path_query('"12:34:56+3"', '$.datetime().type()');