Support for FF1-FF6 datetime format patterns

SQL Standard 2016 defines FF1-FF9 format patters for fractions of seconds in
jsonpath .datetime() method and CAST (... FORMAT ...) SQL clause.  Parsing
engine of upcoming .datetime() method will be shared with to_date()/
to_timestamp().

This patch implements FF1-FF6 format patterns for upcoming jsonpath .datetime()
method.  to_date()/to_timestamp() functions will also get support of this
format patterns as positive side effect.  FF7-FF9 are not supported due to
lack of precision in our internal timestamp representation.

Extracted from original patch by Nikita Glukhov, Teodor Sigaev, Oleg Bartunov.
Heavily revised by me.

Discussion: https://postgr.es/m/fcc6fc6a-b497-f39a-923d-aa34d0c588e8%402ndQuadrant.com
Discussion: https://postgr.es/m/CAPpHfdsZgYEra_PeCLGNoXOWYx6iU-S3wF8aX0ObQUcZU%2B4XTw%40mail.gmail.com
Author: Nikita Glukhov, Teodor Sigaev, Oleg Bartunov, Alexander Korotkov
Reviewed-by: Anastasia Lubennikova, Peter Eisentraut
This commit is contained in:
Alexander Korotkov 2019-09-16 21:02:14 +03:00
parent d812257809
commit d589f94460
10 changed files with 239 additions and 23 deletions

View File

@ -6150,6 +6150,30 @@ SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
<entry><literal>US</literal></entry>
<entry>microsecond (000000-999999)</entry>
</row>
<row>
<entry><literal>FF1</literal></entry>
<entry>tenth of second (0-9)</entry>
</row>
<row>
<entry><literal>FF2</literal></entry>
<entry>hundredth of second (00-99)</entry>
</row>
<row>
<entry><literal>FF3</literal></entry>
<entry>millisecond (000-999)</entry>
</row>
<row>
<entry><literal>FF4</literal></entry>
<entry>tenth of a millisecond (0000-9999)</entry>
</row>
<row>
<entry><literal>FF5</literal></entry>
<entry>hundredth of a millisecond (00000-99999)</entry>
</row>
<row>
<entry><literal>FF6</literal></entry>
<entry>microsecond (000000-999999)</entry>
</row>
<row>
<entry><literal>SSSS</literal></entry>
<entry>seconds past midnight (0-86399)</entry>

View File

@ -86,6 +86,7 @@
#endif
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/date.h"
@ -434,7 +435,8 @@ typedef struct
clock, /* 12 or 24 hour clock? */
tzsign, /* +1, -1 or 0 if timezone info is absent */
tzh,
tzm;
tzm,
ff; /* fractional precision */
} TmFromChar;
#define ZERO_tmfc(_X) memset(_X, 0, sizeof(TmFromChar))
@ -594,6 +596,12 @@ typedef enum
DCH_Day,
DCH_Dy,
DCH_D,
DCH_FF1,
DCH_FF2,
DCH_FF3,
DCH_FF4,
DCH_FF5,
DCH_FF6,
DCH_FX, /* global suffix */
DCH_HH24,
DCH_HH12,
@ -643,6 +651,12 @@ typedef enum
DCH_dd,
DCH_dy,
DCH_d,
DCH_ff1,
DCH_ff2,
DCH_ff3,
DCH_ff4,
DCH_ff5,
DCH_ff6,
DCH_fx,
DCH_hh24,
DCH_hh12,
@ -743,7 +757,13 @@ static const KeyWord DCH_keywords[] = {
{"Day", 3, DCH_Day, false, FROM_CHAR_DATE_NONE},
{"Dy", 2, DCH_Dy, false, FROM_CHAR_DATE_NONE},
{"D", 1, DCH_D, true, FROM_CHAR_DATE_GREGORIAN},
{"FX", 2, DCH_FX, false, FROM_CHAR_DATE_NONE}, /* F */
{"FF1", 3, DCH_FF1, false, FROM_CHAR_DATE_NONE}, /* F */
{"FF2", 3, DCH_FF2, false, FROM_CHAR_DATE_NONE},
{"FF3", 3, DCH_FF3, false, FROM_CHAR_DATE_NONE},
{"FF4", 3, DCH_FF4, false, FROM_CHAR_DATE_NONE},
{"FF5", 3, DCH_FF5, false, FROM_CHAR_DATE_NONE},
{"FF6", 3, DCH_FF6, false, FROM_CHAR_DATE_NONE},
{"FX", 2, DCH_FX, false, FROM_CHAR_DATE_NONE},
{"HH24", 4, DCH_HH24, true, FROM_CHAR_DATE_NONE}, /* H */
{"HH12", 4, DCH_HH12, true, FROM_CHAR_DATE_NONE},
{"HH", 2, DCH_HH, true, FROM_CHAR_DATE_NONE},
@ -792,7 +812,13 @@ static const KeyWord DCH_keywords[] = {
{"dd", 2, DCH_DD, true, FROM_CHAR_DATE_GREGORIAN},
{"dy", 2, DCH_dy, false, FROM_CHAR_DATE_NONE},
{"d", 1, DCH_D, true, FROM_CHAR_DATE_GREGORIAN},
{"fx", 2, DCH_FX, false, FROM_CHAR_DATE_NONE}, /* f */
{"ff1", 3, DCH_FF1, false, FROM_CHAR_DATE_NONE}, /* f */
{"ff2", 3, DCH_FF2, false, FROM_CHAR_DATE_NONE},
{"ff3", 3, DCH_FF3, false, FROM_CHAR_DATE_NONE},
{"ff4", 3, DCH_FF4, false, FROM_CHAR_DATE_NONE},
{"ff5", 3, DCH_FF5, false, FROM_CHAR_DATE_NONE},
{"ff6", 3, DCH_FF6, false, FROM_CHAR_DATE_NONE},
{"fx", 2, DCH_FX, false, FROM_CHAR_DATE_NONE},
{"hh24", 4, DCH_HH24, true, FROM_CHAR_DATE_NONE}, /* h */
{"hh12", 4, DCH_HH12, true, FROM_CHAR_DATE_NONE},
{"hh", 2, DCH_HH, true, FROM_CHAR_DATE_NONE},
@ -893,10 +919,10 @@ static const int DCH_index[KeyWord_INDEX_SIZE] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, DCH_A_D, DCH_B_C, DCH_CC, DCH_DAY, -1,
DCH_FX, -1, DCH_HH24, DCH_IDDD, DCH_J, -1, -1, DCH_MI, -1, DCH_OF,
DCH_FF1, -1, DCH_HH24, DCH_IDDD, DCH_J, -1, -1, DCH_MI, -1, DCH_OF,
DCH_P_M, DCH_Q, DCH_RM, DCH_SSSS, DCH_TZH, DCH_US, -1, DCH_WW, -1, DCH_Y_YYY,
-1, -1, -1, -1, -1, -1, -1, DCH_a_d, DCH_b_c, DCH_cc,
DCH_day, -1, DCH_fx, -1, DCH_hh24, DCH_iddd, DCH_j, -1, -1, DCH_mi,
DCH_day, -1, DCH_ff1, -1, DCH_hh24, DCH_iddd, DCH_j, -1, -1, DCH_mi,
-1, -1, DCH_p_m, DCH_q, DCH_rm, DCH_ssss, DCH_tz, DCH_us, -1, DCH_ww,
-1, DCH_y_yyy, -1, -1, -1, -1
@ -960,7 +986,6 @@ typedef struct NUMProc
*L_currency_symbol;
} NUMProc;
/* ----------
* Functions
* ----------
@ -993,7 +1018,7 @@ static int from_char_parse_int(int *dest, char **src, FormatNode *node);
static int seq_search(char *name, const char *const *array, int type, int max, int *len);
static int from_char_seq_search(int *dest, char **src, const char *const *array, int type, int max, FormatNode *node);
static void do_to_timestamp(text *date_txt, text *fmt,
struct pg_tm *tm, fsec_t *fsec);
struct pg_tm *tm, fsec_t *fsec, int *fprec);
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);
@ -2518,18 +2543,32 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
#define DCH_to_char_fsec(frac_fmt, frac_val) \
sprintf(s, frac_fmt, (int) (frac_val)); \
if (S_THth(n->suffix)) \
str_numth(s, s, S_TH_TYPE(n->suffix)); \
s += strlen(s);
case DCH_FF1: /* tenth of second */
DCH_to_char_fsec("%01d", in->fsec / 100000);
break;
case DCH_FF2: /* hundredth of second */
DCH_to_char_fsec("%02d", in->fsec / 10000);
break;
case DCH_FF3:
case DCH_MS: /* millisecond */
sprintf(s, "%03d", (int) (in->fsec / INT64CONST(1000)));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
DCH_to_char_fsec("%03d", in->fsec / 1000);
break;
case DCH_FF4: /* tenth of a millisecond */
DCH_to_char_fsec("%04d", in->fsec / 100);
break;
case DCH_FF5: /* hundredth of a millisecond */
DCH_to_char_fsec("%05d", in->fsec / 10);
break;
case DCH_FF6:
case DCH_US: /* microsecond */
sprintf(s, "%06d", (int) in->fsec);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
DCH_to_char_fsec("%06d", in->fsec);
break;
#undef DCH_to_char_fsec
case DCH_SSSS:
sprintf(s, "%d", tm->tm_hour * SECS_PER_HOUR +
tm->tm_min * SECS_PER_MINUTE +
@ -3154,8 +3193,18 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
SKIP_THth(s, n->suffix);
break;
case DCH_FF1:
case DCH_FF2:
case DCH_FF3:
case DCH_FF4:
case DCH_FF5:
case DCH_FF6:
out->ff = n->key->id - DCH_FF1 + 1;
/* fall through */
case DCH_US: /* microsecond */
len = from_char_parse_int_len(&out->us, &s, 6, n);
len = from_char_parse_int_len(&out->us, &s,
n->key->id == DCH_US ? 6 :
out->ff, n);
out->us *= len == 1 ? 100000 :
len == 2 ? 10000 :
@ -3689,8 +3738,9 @@ to_timestamp(PG_FUNCTION_ARGS)
int tz;
struct pg_tm tm;
fsec_t fsec;
int fprec;
do_to_timestamp(date_txt, fmt, &tm, &fsec);
do_to_timestamp(date_txt, fmt, &tm, &fsec, &fprec);
/* Use the specified time zone, if any. */
if (tm.tm_zone)
@ -3708,6 +3758,10 @@ to_timestamp(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
/* Use the specified fractional precision, if any. */
if (fprec)
AdjustTimestampForTypmod(&result, fprec);
PG_RETURN_TIMESTAMP(result);
}
@ -3725,7 +3779,7 @@ to_date(PG_FUNCTION_ARGS)
struct pg_tm tm;
fsec_t fsec;
do_to_timestamp(date_txt, fmt, &tm, &fsec);
do_to_timestamp(date_txt, fmt, &tm, &fsec, NULL);
/* Prevent overflow in Julian-day routines */
if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
@ -3749,8 +3803,8 @@ to_date(PG_FUNCTION_ARGS)
/*
* 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
* and fractional seconds.
* Parse the 'date_txt' according to 'fmt', return results as a struct pg_tm,
* fractional seconds, and fractional precision.
*
* We parse 'fmt' into a list of FormatNodes, which is then passed to
* DCH_from_char to populate a TmFromChar with the parsed contents of
@ -3761,7 +3815,7 @@ to_date(PG_FUNCTION_ARGS)
*/
static void
do_to_timestamp(text *date_txt, text *fmt,
struct pg_tm *tm, fsec_t *fsec)
struct pg_tm *tm, fsec_t *fsec, int *fprec)
{
FormatNode *format;
TmFromChar tmfc;
@ -3817,6 +3871,7 @@ do_to_timestamp(text *date_txt, text *fmt,
DCH_from_char(format, date_str, &tmfc);
pfree(fmt_str);
if (!incache)
pfree(format);
}
@ -3998,6 +4053,8 @@ do_to_timestamp(text *date_txt, text *fmt,
*fsec += tmfc.ms * 1000;
if (tmfc.us)
*fsec += tmfc.us;
if (fprec)
*fprec = tmfc.ff; /* fractional precision, if specified */
/* Range-check date fields according to bit mask computed above */
if (fmask != 0)

View File

@ -70,7 +70,6 @@ typedef struct
static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
static Timestamp dt2local(Timestamp dt, int timezone);
static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
static TimestampTz timestamp2timestamptz(Timestamp timestamp);
static Timestamp timestamptz2timestamp(TimestampTz timestamp);
@ -333,7 +332,7 @@ timestamp_scale(PG_FUNCTION_ARGS)
* AdjustTimestampForTypmod --- round off a timestamp to suit given typmod
* Works for either timestamp or timestamptz.
*/
static void
void
AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
{
static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {

View File

@ -336,4 +336,6 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
extern void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
#endif /* DATETIME_H */

View File

@ -2786,6 +2786,85 @@ 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 i, to_timestamp('2018-11-02 12:34:56', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
i | to_timestamp
---+------------------------------
1 | Fri Nov 02 12:34:56 2018 PDT
2 | Fri Nov 02 12:34:56 2018 PDT
3 | Fri Nov 02 12:34:56 2018 PDT
4 | Fri Nov 02 12:34:56 2018 PDT
5 | Fri Nov 02 12:34:56 2018 PDT
6 | Fri Nov 02 12:34:56 2018 PDT
(6 rows)
SELECT i, to_timestamp('2018-11-02 12:34:56.1', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
i | to_timestamp
---+--------------------------------
1 | Fri Nov 02 12:34:56.1 2018 PDT
2 | Fri Nov 02 12:34:56.1 2018 PDT
3 | Fri Nov 02 12:34:56.1 2018 PDT
4 | Fri Nov 02 12:34:56.1 2018 PDT
5 | Fri Nov 02 12:34:56.1 2018 PDT
6 | Fri Nov 02 12:34:56.1 2018 PDT
(6 rows)
SELECT i, to_timestamp('2018-11-02 12:34:56.12', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
i | to_timestamp
---+---------------------------------
1 | Fri Nov 02 12:34:56.1 2018 PDT
2 | Fri Nov 02 12:34:56.12 2018 PDT
3 | Fri Nov 02 12:34:56.12 2018 PDT
4 | Fri Nov 02 12:34:56.12 2018 PDT
5 | Fri Nov 02 12:34:56.12 2018 PDT
6 | Fri Nov 02 12:34:56.12 2018 PDT
(6 rows)
SELECT i, to_timestamp('2018-11-02 12:34:56.123', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
i | to_timestamp
---+----------------------------------
1 | Fri Nov 02 12:34:56.1 2018 PDT
2 | Fri Nov 02 12:34:56.12 2018 PDT
3 | Fri Nov 02 12:34:56.123 2018 PDT
4 | Fri Nov 02 12:34:56.123 2018 PDT
5 | Fri Nov 02 12:34:56.123 2018 PDT
6 | Fri Nov 02 12:34:56.123 2018 PDT
(6 rows)
SELECT i, to_timestamp('2018-11-02 12:34:56.1234', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
i | to_timestamp
---+-----------------------------------
1 | Fri Nov 02 12:34:56.1 2018 PDT
2 | Fri Nov 02 12:34:56.12 2018 PDT
3 | Fri Nov 02 12:34:56.123 2018 PDT
4 | Fri Nov 02 12:34:56.1234 2018 PDT
5 | Fri Nov 02 12:34:56.1234 2018 PDT
6 | Fri Nov 02 12:34:56.1234 2018 PDT
(6 rows)
SELECT i, to_timestamp('2018-11-02 12:34:56.12345', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
i | to_timestamp
---+------------------------------------
1 | Fri Nov 02 12:34:56.1 2018 PDT
2 | Fri Nov 02 12:34:56.12 2018 PDT
3 | Fri Nov 02 12:34:56.123 2018 PDT
4 | Fri Nov 02 12:34:56.1235 2018 PDT
5 | Fri Nov 02 12:34:56.12345 2018 PDT
6 | Fri Nov 02 12:34:56.12345 2018 PDT
(6 rows)
SELECT i, to_timestamp('2018-11-02 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
i | to_timestamp
---+-------------------------------------
1 | Fri Nov 02 12:34:56.1 2018 PDT
2 | Fri Nov 02 12:34:56.12 2018 PDT
3 | Fri Nov 02 12:34:56.123 2018 PDT
4 | Fri Nov 02 12:34:56.1235 2018 PDT
5 | Fri Nov 02 12:34:56.12346 2018 PDT
6 | Fri Nov 02 12:34:56.123456 2018 PDT
(6 rows)
SELECT i, to_timestamp('2018-11-02 12:34:56.123456789', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
ERROR: date/time field value out of range: "2018-11-02 12:34:56.123456789"
--
-- Check handling of multiple spaces in format and/or input
--

View File

@ -1584,6 +1584,21 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
| 2001 1 1 1 1 1 1
(65 rows)
SELECT '' AS to_char_12, to_char(d, 'FF1 FF2 FF3 FF4 FF5 FF6 ff1 ff2 ff3 ff4 ff5 ff6 MS US')
FROM (VALUES
('2018-11-02 12:34:56'::timestamp),
('2018-11-02 12:34:56.78'),
('2018-11-02 12:34:56.78901'),
('2018-11-02 12:34:56.78901234')
) d(d);
to_char_12 | to_char
------------+--------------------------------------------------------------------
| 0 00 000 0000 00000 000000 0 00 000 0000 00000 000000 000 000000
| 7 78 780 7800 78000 780000 7 78 780 7800 78000 780000 780 780000
| 7 78 789 7890 78901 789010 7 78 789 7890 78901 789010 789 789010
| 7 78 789 7890 78901 789012 7 78 789 7890 78901 789012 789 789012
(4 rows)
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);
make_timestamp

View File

@ -1704,6 +1704,21 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
| 2001 1 1 1 1 1 1
(66 rows)
SELECT '' AS to_char_12, to_char(d, 'FF1 FF2 FF3 FF4 FF5 FF6 ff1 ff2 ff3 ff4 ff5 ff6 MS US')
FROM (VALUES
('2018-11-02 12:34:56'::timestamptz),
('2018-11-02 12:34:56.78'),
('2018-11-02 12:34:56.78901'),
('2018-11-02 12:34:56.78901234')
) d(d);
to_char_12 | to_char
------------+--------------------------------------------------------------------
| 0 00 000 0000 00000 000000 0 00 000 0000 00000 000000 000 000000
| 7 78 780 7800 78000 780000 7 78 780 7800 78000 780000 780 780000
| 7 78 789 7890 78901 789010 7 78 789 7890 78901 789010 789 789010
| 7 78 789 7890 78901 789012 7 78 789 7890 78901 789012 789 789012
(4 rows)
-- Check OF, TZH, TZM with various zone offsets, particularly fractional hours
SET timezone = '00:00';
SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM";

View File

@ -402,6 +402,15 @@ 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 i, to_timestamp('2018-11-02 12:34:56', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.1', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.12', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.123', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.1234', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.12345', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
SELECT i, to_timestamp('2018-11-02 12:34:56.123456789', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i;
--
-- Check handling of multiple spaces in format and/or input
--

View File

@ -224,5 +224,13 @@ SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID')
SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
FROM TIMESTAMP_TBL;
SELECT '' AS to_char_12, to_char(d, 'FF1 FF2 FF3 FF4 FF5 FF6 ff1 ff2 ff3 ff4 ff5 ff6 MS US')
FROM (VALUES
('2018-11-02 12:34:56'::timestamp),
('2018-11-02 12:34:56.78'),
('2018-11-02 12:34:56.78901'),
('2018-11-02 12:34:56.78901234')
) d(d);
-- timestamp numeric fields constructor
SELECT make_timestamp(2014,12,28,6,30,45.887);

View File

@ -248,6 +248,14 @@ SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID')
SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
FROM TIMESTAMPTZ_TBL;
SELECT '' AS to_char_12, to_char(d, 'FF1 FF2 FF3 FF4 FF5 FF6 ff1 ff2 ff3 ff4 ff5 ff6 MS US')
FROM (VALUES
('2018-11-02 12:34:56'::timestamptz),
('2018-11-02 12:34:56.78'),
('2018-11-02 12:34:56.78901'),
('2018-11-02 12:34:56.78901234')
) d(d);
-- Check OF, TZH, TZM with various zone offsets, particularly fractional hours
SET timezone = '00:00';
SELECT to_char(now(), 'OF') as "OF", to_char(now(), 'TZH:TZM') as "TZH:TZM";