mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Add a timezone-specific variant of date_trunc().
date_trunc(field, timestamptz, zone_name) performs truncation using the named time zone as reference, rather than working in the session time zone as is the default behavior. It's equivalent to date_trunc(field, timestamptz at time zone zone_name) at time zone zone_name but it's faster, easier to type, and arguably easier to understand. Vik Fearing and Tom Lane Discussion: https://postgr.es/m/6249ffc4-2b22-4c1b-4e7d-7af84fedd7c6@2ndquadrant.com
This commit is contained in:
@ -3925,14 +3925,15 @@ timestamp_trunc(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_TIMESTAMP(result);
|
||||
}
|
||||
|
||||
/* timestamptz_trunc()
|
||||
* Truncate timestamp to specified units.
|
||||
/*
|
||||
* Common code for timestamptz_trunc() and timestamptz_trunc_zone().
|
||||
*
|
||||
* tzp identifies the zone to truncate with respect to. We assume
|
||||
* infinite timestamps have already been rejected.
|
||||
*/
|
||||
Datum
|
||||
timestamptz_trunc(PG_FUNCTION_ARGS)
|
||||
static TimestampTz
|
||||
timestamptz_trunc_internal(text *units, TimestampTz timestamp, pg_tz *tzp)
|
||||
{
|
||||
text *units = PG_GETARG_TEXT_PP(0);
|
||||
TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
|
||||
TimestampTz result;
|
||||
int tz;
|
||||
int type,
|
||||
@ -3943,9 +3944,6 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
|
||||
struct pg_tm tt,
|
||||
*tm = &tt;
|
||||
|
||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||
PG_RETURN_TIMESTAMPTZ(timestamp);
|
||||
|
||||
lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
|
||||
VARSIZE_ANY_EXHDR(units),
|
||||
false);
|
||||
@ -3954,7 +3952,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
|
||||
|
||||
if (type == UNITS)
|
||||
{
|
||||
if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
|
||||
if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, tzp) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||
errmsg("timestamp out of range")));
|
||||
@ -4055,7 +4053,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
if (redotz)
|
||||
tz = DetermineTimeZoneOffset(tm, session_timezone);
|
||||
tz = DetermineTimeZoneOffset(tm, tzp);
|
||||
|
||||
if (tm2timestamp(tm, fsec, &tz, &result) != 0)
|
||||
ereport(ERROR,
|
||||
@ -4071,6 +4069,83 @@ timestamptz_trunc(PG_FUNCTION_ARGS)
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* timestamptz_trunc()
|
||||
* Truncate timestamptz to specified units in session timezone.
|
||||
*/
|
||||
Datum
|
||||
timestamptz_trunc(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *units = PG_GETARG_TEXT_PP(0);
|
||||
TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
|
||||
TimestampTz result;
|
||||
|
||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||
PG_RETURN_TIMESTAMPTZ(timestamp);
|
||||
|
||||
result = timestamptz_trunc_internal(units, timestamp, session_timezone);
|
||||
|
||||
PG_RETURN_TIMESTAMPTZ(result);
|
||||
}
|
||||
|
||||
/* timestamptz_trunc_zone()
|
||||
* Truncate timestamptz to specified units in specified timezone.
|
||||
*/
|
||||
Datum
|
||||
timestamptz_trunc_zone(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *units = PG_GETARG_TEXT_PP(0);
|
||||
TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
|
||||
text *zone = PG_GETARG_TEXT_PP(2);
|
||||
TimestampTz result;
|
||||
char tzname[TZ_STRLEN_MAX + 1];
|
||||
char *lowzone;
|
||||
int type,
|
||||
val;
|
||||
pg_tz *tzp;
|
||||
|
||||
/*
|
||||
* timestamptz_zone() doesn't look up the zone for infinite inputs, so we
|
||||
* don't do so here either.
|
||||
*/
|
||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||
PG_RETURN_TIMESTAMP(timestamp);
|
||||
|
||||
/*
|
||||
* Look up the requested timezone (see notes in timestamptz_zone()).
|
||||
*/
|
||||
text_to_cstring_buffer(zone, tzname, sizeof(tzname));
|
||||
|
||||
/* DecodeTimezoneAbbrev requires lowercase input */
|
||||
lowzone = downcase_truncate_identifier(tzname,
|
||||
strlen(tzname),
|
||||
false);
|
||||
|
||||
type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
|
||||
|
||||
if (type == TZ || type == DTZ)
|
||||
{
|
||||
/* fixed-offset abbreviation, get a pg_tz descriptor for that */
|
||||
tzp = pg_tzset_offset(-val);
|
||||
}
|
||||
else if (type == DYNTZ)
|
||||
{
|
||||
/* dynamic-offset abbreviation, use its referenced timezone */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* try it as a full zone name */
|
||||
tzp = pg_tzset(tzname);
|
||||
if (!tzp)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("time zone \"%s\" not recognized", tzname)));
|
||||
}
|
||||
|
||||
result = timestamptz_trunc_internal(units, timestamp, tzp);
|
||||
|
||||
PG_RETURN_TIMESTAMPTZ(result);
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201811061
|
||||
#define CATALOG_VERSION_NO 201811141
|
||||
|
||||
#endif
|
||||
|
@ -2280,6 +2280,10 @@
|
||||
descr => 'truncate timestamp with time zone to specified units',
|
||||
proname => 'date_trunc', provolatile => 's', prorettype => 'timestamptz',
|
||||
proargtypes => 'text timestamptz', prosrc => 'timestamptz_trunc' },
|
||||
{ oid => '1284',
|
||||
descr => 'truncate timestamp with time zone to specified units in specified time zone',
|
||||
proname => 'date_trunc', provolatile => 's', prorettype => 'timestamptz',
|
||||
proargtypes => 'text timestamptz text', prosrc => 'timestamptz_trunc_zone' },
|
||||
{ oid => '1218', descr => 'truncate interval to specified units',
|
||||
proname => 'date_trunc', prorettype => 'interval',
|
||||
proargtypes => 'text interval', prosrc => 'interval_trunc' },
|
||||
@ -5825,8 +5829,8 @@
|
||||
prorettype => 'timestamptz', proargtypes => '',
|
||||
prosrc => 'pg_backup_start_time' },
|
||||
{ oid => '3436', descr => 'promote standby server',
|
||||
proname => 'pg_promote', provolatile => 'v',
|
||||
prorettype => 'bool', proargtypes => 'bool int4', proargnames => '{wait,wait_seconds}',
|
||||
proname => 'pg_promote', provolatile => 'v', prorettype => 'bool',
|
||||
proargtypes => 'bool int4', proargnames => '{wait,wait_seconds}',
|
||||
prosrc => 'pg_promote' },
|
||||
{ oid => '2848', descr => 'switch to new wal file',
|
||||
proname => 'pg_switch_wal', provolatile => 'v', prorettype => 'pg_lsn',
|
||||
@ -10007,10 +10011,11 @@
|
||||
proallargtypes => '{text,int8,timestamptz}', proargmodes => '{o,o,o}',
|
||||
proargnames => '{name,size,modification}', prosrc => 'pg_ls_waldir' },
|
||||
{ oid => '5031', descr => 'list of files in the archive_status directory',
|
||||
proname => 'pg_ls_archive_statusdir', procost => '10', prorows => '20', proretset => 't',
|
||||
provolatile => 'v', prorettype => 'record', proargtypes => '',
|
||||
proallargtypes => '{text,int8,timestamptz}', proargmodes => '{o,o,o}',
|
||||
proargnames => '{name,size,modification}', prosrc => 'pg_ls_archive_statusdir' },
|
||||
proname => 'pg_ls_archive_statusdir', procost => '10', prorows => '20',
|
||||
proretset => 't', provolatile => 'v', prorettype => 'record',
|
||||
proargtypes => '', proallargtypes => '{text,int8,timestamptz}',
|
||||
proargmodes => '{o,o,o}', proargnames => '{name,size,modification}',
|
||||
prosrc => 'pg_ls_archive_statusdir' },
|
||||
{ oid => '5029', descr => 'list files in the pgsql_tmp directory',
|
||||
proname => 'pg_ls_tmpdir', procost => '10', prorows => '20', proretset => 't',
|
||||
provolatile => 'v', prorettype => 'record', proargtypes => '',
|
||||
@ -10036,6 +10041,6 @@
|
||||
proallargtypes => '{regclass,regclass,regclass,bool,int4}',
|
||||
proargmodes => '{i,o,o,o,o}',
|
||||
proargnames => '{rootrelid,relid,parentrelid,isleaf,level}',
|
||||
prosrc => 'pg_partition_tree' }
|
||||
prosrc => 'pg_partition_tree' },
|
||||
|
||||
]
|
||||
|
@ -649,6 +649,24 @@ SELECT '' AS date_trunc_week, date_trunc( 'week', timestamp with time zone '2004
|
||||
| Mon Feb 23 00:00:00 2004 PST
|
||||
(1 row)
|
||||
|
||||
SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') as sydney_trunc; -- zone name
|
||||
date_trunc_at_tz | sydney_trunc
|
||||
------------------+------------------------------
|
||||
| Fri Feb 16 05:00:00 2001 PST
|
||||
(1 row)
|
||||
|
||||
SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'GMT') as gmt_trunc; -- fixed-offset abbreviation
|
||||
date_trunc_at_tz | gmt_trunc
|
||||
------------------+------------------------------
|
||||
| Thu Feb 15 16:00:00 2001 PST
|
||||
(1 row)
|
||||
|
||||
SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'VET') as vet_trunc; -- variable-offset abbreviation
|
||||
date_trunc_at_tz | vet_trunc
|
||||
------------------+------------------------------
|
||||
| Thu Feb 15 20:00:00 2001 PST
|
||||
(1 row)
|
||||
|
||||
-- Test casting within a BETWEEN qualifier
|
||||
SELECT '' AS "54", d1 - timestamp with time zone '1997-01-02' AS diff
|
||||
FROM TIMESTAMPTZ_TBL
|
||||
|
@ -193,6 +193,10 @@ SELECT '' AS "54", d1 - timestamp with time zone '1997-01-02' AS diff
|
||||
|
||||
SELECT '' AS date_trunc_week, date_trunc( 'week', timestamp with time zone '2004-02-29 15:44:17.71393' ) AS week_trunc;
|
||||
|
||||
SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'Australia/Sydney') as sydney_trunc; -- zone name
|
||||
SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'GMT') as gmt_trunc; -- fixed-offset abbreviation
|
||||
SELECT '' AS date_trunc_at_tz, date_trunc('day', timestamp with time zone '2001-02-16 20:38:40+00', 'VET') as vet_trunc; -- variable-offset abbreviation
|
||||
|
||||
-- Test casting within a BETWEEN qualifier
|
||||
SELECT '' AS "54", d1 - timestamp with time zone '1997-01-02' AS diff
|
||||
FROM TIMESTAMPTZ_TBL
|
||||
|
Reference in New Issue
Block a user