diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/parser/gram.y | 7 | ||||
-rw-r--r-- | src/backend/utils/adt/date.c | 15 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 10 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 20 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.dat | 9 | ||||
-rw-r--r-- | src/test/regress/expected/timestamptz.out | 56 | ||||
-rw-r--r-- | src/test/regress/expected/timetz.out | 42 | ||||
-rw-r--r-- | src/test/regress/sql/timestamptz.sql | 23 | ||||
-rw-r--r-- | src/test/regress/sql/timetz.sql | 17 |
10 files changed, 200 insertions, 1 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index e56cbe77cb7..50ed504e5a0 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -14508,6 +14508,13 @@ a_expr: c_expr { $$ = $1; } COERCE_SQL_SYNTAX, @2); } + | a_expr AT LOCAL %prec AT + { + $$ = (Node *) makeFuncCall(SystemFuncName("timezone"), + list_make1($1), + COERCE_SQL_SYNTAX, + -1); + } /* * These operators must be called out explicitly in order to make use * of bison's automatic operator-precedence handling. All other diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index ae0f24de2c3..c4da10d47a9 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -3125,3 +3125,18 @@ timetz_izone(PG_FUNCTION_ARGS) PG_RETURN_TIMETZADT_P(result); } + +/* timetz_at_local() + * + * Unlike for timestamp[tz]_at_local, the type for timetz does not flip between + * time with/without time zone, so we cannot just call the conversion function. + */ +Datum +timetz_at_local(PG_FUNCTION_ARGS) +{ + Datum time = PG_GETARG_DATUM(0); + const char *tzn = pg_get_timezone_name(session_timezone); + Datum zone = PointerGetDatum(cstring_to_text(tzn)); + + return DirectFunctionCall2(timetz_zone, zone, time); +} diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 442205382e3..ed7f40f053c 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -10347,6 +10347,16 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context) appendStringInfoChar(buf, ')'); return true; + case F_TIMEZONE_TIMESTAMP: + case F_TIMEZONE_TIMESTAMPTZ: + case F_TIMEZONE_TIMETZ: + /* AT LOCAL */ + appendStringInfoChar(buf, '('); + get_rule_expr_paren((Node *) linitial(expr->args), context, false, + (Node *) expr); + appendStringInfoString(buf, " AT LOCAL)"); + return true; + case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL: case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ: case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL: diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 0e50aaec5a5..e172e906142 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -5921,3 +5921,23 @@ generate_series_timestamptz_at_zone(PG_FUNCTION_ARGS) { return generate_series_timestamptz_internal(fcinfo); } + +/* timestamp_at_local() + * timestamptz_at_local() + * + * The regression tests do not like two functions with the same proargs and + * prosrc but different proname, but the grammar for AT LOCAL needs an + * overloaded name to handle both types of timestamp, so we make simple + * wrappers for it. + */ +Datum +timestamp_at_local(PG_FUNCTION_ARGS) +{ + return timestamp_timestamptz(fcinfo); +} + +Datum +timestamptz_at_local(PG_FUNCTION_ARGS) +{ + return timestamptz_timestamp(fcinfo); +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 7d65feaef0f..473687419c9 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202309251 +#define CATALOG_VERSION_NO 202310131 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index f0b7b9cbd8e..72ea4aa8b8c 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -2319,6 +2319,9 @@ { oid => '1159', descr => 'adjust timestamp to new time zone', proname => 'timezone', prorettype => 'timestamp', proargtypes => 'text timestamptz', prosrc => 'timestamptz_zone' }, +{ oid => '9159', descr => 'adjust timestamp to local time zone', + proname => 'timezone', provolatile => 's', prorettype => 'timestamp', + proargtypes => 'timestamptz', prosrc => 'timestamptz_at_local' }, { oid => '1160', descr => 'I/O', proname => 'interval_in', provolatile => 's', prorettype => 'interval', @@ -6095,6 +6098,9 @@ { oid => '2038', descr => 'adjust time with time zone to new zone', proname => 'timezone', prorettype => 'timetz', proargtypes => 'interval timetz', prosrc => 'timetz_izone' }, +{ oid => '9161', descr => 'adjust time to local time zone', + proname => 'timezone', provolatile => 's', prorettype => 'timetz', + proargtypes => 'timetz', prosrc => 'timetz_at_local' }, { oid => '2039', descr => 'hash', proname => 'timestamp_hash', prorettype => 'int4', proargtypes => 'timestamp', prosrc => 'timestamp_hash' }, @@ -6190,6 +6196,9 @@ { oid => '2070', descr => 'adjust timestamp to new time zone', proname => 'timezone', prorettype => 'timestamptz', proargtypes => 'interval timestamp', prosrc => 'timestamp_izone' }, +{ oid => '9160', descr => 'adjust timestamp to local time zone', + proname => 'timezone', provolatile => 's', prorettype => 'timestamptz', + proargtypes => 'timestamp', prosrc => 'timestamp_at_local' }, { oid => '2071', proname => 'date_pl_interval', prorettype => 'timestamp', proargtypes => 'date interval', prosrc => 'date_pl_interval' }, diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 0dd2fe2c82d..2ca2101dd42 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -3136,6 +3136,62 @@ SELECT '2014-10-25 23:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; (1 row) -- +-- Test LOCAL time zone +-- +BEGIN; +SET LOCAL TIME ZONE 'Europe/Paris'; +VALUES (CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE) AT LOCAL); + column1 +-------------------------- + Sat Jul 08 01:38:00 1978 +(1 row) + +VALUES (TIMESTAMP '1978-07-07 19:38' AT LOCAL); + column1 +------------------------------- + Fri Jul 07 19:38:00 1978 CEST +(1 row) + +SET LOCAL TIME ZONE 'Australia/Sydney'; +VALUES (CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE) AT LOCAL); + column1 +-------------------------- + Sat Jul 08 09:38:00 1978 +(1 row) + +VALUES (TIMESTAMP '1978-07-07 19:38' AT LOCAL); + column1 +------------------------------- + Fri Jul 07 19:38:00 1978 AEST +(1 row) + +SET LOCAL TimeZone TO 'UTC'; +CREATE VIEW timestamp_local_view AS + SELECT CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE) AT LOCAL AS ttz_at_local, + timezone(CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE)) AS ttz_func, + TIMESTAMP '1978-07-07 19:38' AT LOCAL AS t_at_local, + timezone(TIMESTAMP '1978-07-07 19:38') AS t_func; +SELECT pg_get_viewdef('timestamp_local_view', true); + pg_get_viewdef +---------------------------------------------------------------------------------------------- + SELECT ('Fri Jul 07 23:38:00 1978 UTC'::timestamp with time zone AT LOCAL) AS ttz_at_local,+ + timezone('Fri Jul 07 23:38:00 1978 UTC'::timestamp with time zone) AS ttz_func, + + ('Fri Jul 07 19:38:00 1978'::timestamp without time zone AT LOCAL) AS t_at_local, + + timezone('Fri Jul 07 19:38:00 1978'::timestamp without time zone) AS t_func; +(1 row) + +\x +TABLE timestamp_local_view; +-[ RECORD 1 ]+----------------------------- +ttz_at_local | Fri Jul 07 23:38:00 1978 +ttz_func | Fri Jul 07 23:38:00 1978 +t_at_local | Fri Jul 07 19:38:00 1978 UTC +t_func | Fri Jul 07 19:38:00 1978 UTC + +\x +DROP VIEW timestamp_local_view; +COMMIT; +-- -- Test that AT TIME ZONE isn't misoptimized when using an index (bug #14504) -- create temp table tmptz (f1 timestamptz primary key); diff --git a/src/test/regress/expected/timetz.out b/src/test/regress/expected/timetz.out index be49588b6d3..7293c3bbb7b 100644 --- a/src/test/regress/expected/timetz.out +++ b/src/test/regress/expected/timetz.out @@ -262,3 +262,45 @@ SELECT date_part('epoch', TIME WITH TIME ZONE '2020-05-26 13:30:25.575401- 63025.575401 (1 row) +-- +-- AT LOCAL with timetz +-- +BEGIN; +SET LOCAL TimeZone TO 'UTC'; +CREATE VIEW timetz_local_view AS + SELECT f1 AS dat, + timezone(f1) AS dat_func, + f1 AT LOCAL AS dat_at_local, + f1 AT TIME ZONE current_setting('TimeZone') AS dat_at_time + FROM TIMETZ_TBL + ORDER BY f1; +SELECT pg_get_viewdef('timetz_local_view', true); + pg_get_viewdef +------------------------------------------------------------------------ + SELECT f1 AS dat, + + timezone(f1) AS dat_func, + + (f1 AT LOCAL) AS dat_at_local, + + (f1 AT TIME ZONE current_setting('TimeZone'::text)) AS dat_at_time+ + FROM timetz_tbl + + ORDER BY f1; +(1 row) + +TABLE timetz_local_view; + dat | dat_func | dat_at_local | dat_at_time +----------------+----------------+----------------+---------------- + 00:01:00-07 | 07:01:00+00 | 07:01:00+00 | 07:01:00+00 + 01:00:00-07 | 08:00:00+00 | 08:00:00+00 | 08:00:00+00 + 02:03:00-07 | 09:03:00+00 | 09:03:00+00 | 09:03:00+00 + 08:08:00-04 | 12:08:00+00 | 12:08:00+00 | 12:08:00+00 + 07:07:00-08 | 15:07:00+00 | 15:07:00+00 | 15:07:00+00 + 11:59:00-07 | 18:59:00+00 | 18:59:00+00 | 18:59:00+00 + 12:00:00-07 | 19:00:00+00 | 19:00:00+00 | 19:00:00+00 + 12:01:00-07 | 19:01:00+00 | 19:01:00+00 | 19:01:00+00 + 15:36:39-04 | 19:36:39+00 | 19:36:39+00 | 19:36:39+00 + 15:36:39-05 | 20:36:39+00 | 20:36:39+00 | 20:36:39+00 + 23:59:00-07 | 06:59:00+00 | 06:59:00+00 | 06:59:00+00 + 23:59:59.99-07 | 06:59:59.99+00 | 06:59:59.99+00 | 06:59:59.99+00 +(12 rows) + +DROP VIEW timetz_local_view; +COMMIT; diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql index 69b36d04202..cdc57bc1606 100644 --- a/src/test/regress/sql/timestamptz.sql +++ b/src/test/regress/sql/timestamptz.sql @@ -612,6 +612,29 @@ SELECT '2014-10-25 22:00:01 UTC'::timestamptz AT TIME ZONE 'MSK'; SELECT '2014-10-25 23:00:00 UTC'::timestamptz AT TIME ZONE 'MSK'; -- +-- Test LOCAL time zone +-- +BEGIN; +SET LOCAL TIME ZONE 'Europe/Paris'; +VALUES (CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE) AT LOCAL); +VALUES (TIMESTAMP '1978-07-07 19:38' AT LOCAL); +SET LOCAL TIME ZONE 'Australia/Sydney'; +VALUES (CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE) AT LOCAL); +VALUES (TIMESTAMP '1978-07-07 19:38' AT LOCAL); +SET LOCAL TimeZone TO 'UTC'; +CREATE VIEW timestamp_local_view AS + SELECT CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE) AT LOCAL AS ttz_at_local, + timezone(CAST('1978-07-07 19:38 America/New_York' AS TIMESTAMP WITH TIME ZONE)) AS ttz_func, + TIMESTAMP '1978-07-07 19:38' AT LOCAL AS t_at_local, + timezone(TIMESTAMP '1978-07-07 19:38') AS t_func; +SELECT pg_get_viewdef('timestamp_local_view', true); +\x +TABLE timestamp_local_view; +\x +DROP VIEW timestamp_local_view; +COMMIT; + +-- -- Test that AT TIME ZONE isn't misoptimized when using an index (bug #14504) -- create temp table tmptz (f1 timestamptz primary key); diff --git a/src/test/regress/sql/timetz.sql b/src/test/regress/sql/timetz.sql index 93c7bb14288..846006640e3 100644 --- a/src/test/regress/sql/timetz.sql +++ b/src/test/regress/sql/timetz.sql @@ -84,3 +84,20 @@ SELECT date_part('microsecond', TIME WITH TIME ZONE '2020-05-26 13:30:25.575401- SELECT date_part('millisecond', TIME WITH TIME ZONE '2020-05-26 13:30:25.575401-04'); SELECT date_part('second', TIME WITH TIME ZONE '2020-05-26 13:30:25.575401-04'); SELECT date_part('epoch', TIME WITH TIME ZONE '2020-05-26 13:30:25.575401-04'); + +-- +-- AT LOCAL with timetz +-- +BEGIN; +SET LOCAL TimeZone TO 'UTC'; +CREATE VIEW timetz_local_view AS + SELECT f1 AS dat, + timezone(f1) AS dat_func, + f1 AT LOCAL AS dat_at_local, + f1 AT TIME ZONE current_setting('TimeZone') AS dat_at_time + FROM TIMETZ_TBL + ORDER BY f1; +SELECT pg_get_viewdef('timetz_local_view', true); +TABLE timetz_local_view; +DROP VIEW timetz_local_view; +COMMIT; |