↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

9.9. 日期/时间函数和操作符 #

表 9.33 展示了可用于处理日期/时间值的函数,其细节在随后的小节中描述。表 9.32 演示了基本算术操作符(+、* 等)的行为。而与格式化相关的函数,可以参考第 9.8 节。你应当熟悉第 8.5 节中的日期/时间数据类型的背景知识。

此外,表 9.1 中显示的常用比较操作符也适用于日期/时间类型。日期和时间戳(带或不带时区)都是可比较的,而时间(带或不带时区)和间隔只能与相同数据类型的其他值进行比较。将不带时区的时间戳与带时区的时间戳进行比较时,前者的值假定是在 TimeZone 配置参数指定的时区中给出的,并被转换到 UTC,以便与后者的值进行比较(后者在内部已用 UTC 表示)。类似地,日期值会被假定表示 TimeZone 区域中的午夜,当它与时间戳进行比较时。

所有下文描述的接受 time 或 timestamp 输入的函数和操作符实际上都有两种变体:一种接收 time with time zone 或 timestamp with time zone,另外一种接受 time without time zone 或者 timestamp without time zone。为了简化,这些变种没有被独立地展示。此外,+和* 操作符都是可交换的操作符对(例如,date + integer 和 integer + date);我们只显示每一对中的一个。

表 9.32. 日期/时间操作符

操作符

描述

示例

date + integer → date

给日期加上天数

date '2001-09-28' + 7 → 2001-10-05

date + interval → timestamp

为日期添加时间间隔

date '2001-09-28' + interval '1 hour' → 2001-09-28 01:00:00

date + time → timestamp

在日期中添加一天中的时间

date '2001-09-28' + time '03:00' → 2001-09-28 03:00:00

interval + interval → interval

将两个时间间隔相加

interval '1 day' + interval '1 hour' → 1 day 01:00:00

timestamp + interval → timestamp

在时间戳中添加一个时间间隔

timestamp '2001-09-28 01:00' + interval '23 hours' → 2001-09-29 00:00:00

time + interval → time

为时间添加时间间隔

time '01:00' + interval '3 hours' → 04:00:00

- interval → interval

对时间间隔取负

- interval '23 hours' → -23:00:00

date - date → integer

将两个日期相减,得到相隔的天数

date '2001-10-01' - date '2001-09-28' → 3

date - integer → date

从日期中减去天数

date '2001-10-01' - 7 → 2001-09-24

date - interval → timestamp

从日期中减去时间间隔

date '2001-09-28' - interval '1 hour' → 2001-09-27 23:00:00

time - time → interval

将两个时间相减

time '05:00' - time '03:00' → 02:00:00

time - interval → time

从时间中减去时间间隔

time '05:00' - interval '2 hours' → 03:00:00

timestamp - interval → timestamp

从时间戳中减去时间间隔

timestamp '2001-09-28 23:00' - interval '23 hours' → 2001-09-28 00:00:00

interval - interval → interval

将两个时间间隔相减

interval '1 day' - interval '1 hour' → 1 day -01:00:00

timestamp - timestamp → interval

将两个时间戳相减(将 24 小时间隔转换为天数,类似于 justify_hours())

timestamp '2001-09-29 03:00' - timestamp '2001-07-27 12:00' → 63 days 15:00:00

interval * double precision → interval

将时间间隔乘以一个标量

interval '1 second' * 900 → 00:15:00

interval '1 day' * 21 → 21 days

interval '1 hour' * 3.5 → 03:30:00

interval / double precision → interval

将时间间隔除以一个标量

interval '1 hour' / 1.5 → 00:40:00


表 9.33. 日期/时间函数

函数

描述

示例

age ( timestamp, timestamp ) → interval

将两个参数相减,生成一个使用年和月而不是只用日的“符号化”结果

age(timestamp '2001-04-10', timestamp '1957-06-13') → 43 years 9 mons 27 days

age ( timestamp ) → interval

从 current_date(午夜时刻)减去参数

age(timestamp '1957-06-13') → 62 years 6 mons 10 days

clock_timestamp ( ) → timestamp with time zone

当前日期和时间(在语句执行期间变化);参见第 9.9.5 节

clock_timestamp() → 2019-12-23 14:39:53.662522-05

current_date → date

当前日期;参见第 9.9.5 节

current_date → 2019-12-23

current_time → time with time zone

一天中的当前时刻;参见第 9.9.5 节

current_time → 14:39:53.662522-05

current_time ( integer ) → time with time zone

一天中的当前时刻,精度受限;参见第 9.9.5 节

current_time(2) → 14:39:53.66-05

current_timestamp → timestamp with time zone

当前日期和时间(当前事务开始时);参见第 9.9.5 节

current_timestamp → 2019-12-23 14:39:53.662522-05

current_timestamp ( integer ) → timestamp with time zone

当前日期和时间(当前事务开始时),精度受限;参见第 9.9.5 节

current_timestamp(0) → 2019-12-23 14:39:53-05

date_bin ( interval, timestamp, timestamp ) → timestamp

将输入按指定的间隔进行分箱(bin),对齐到指定的原点;参见第 9.9.3 节

date_bin('15 minutes', timestamp '2001-02-16 20:38:40', timestamp '2001-02-16 20:05:00') → 2001-02-16 20:35:00

date_part ( text, timestamp ) → double precision

获取时间戳子字段(等同于 extract);参见第 9.9.1 节

date_part('hour', timestamp '2001-02-16 20:38:40') → 20

date_part ( text, interval ) → double precision

获取时间间隔子字段(等同于 extract);参见第 9.9.1 节

date_part('month', interval '2 years 3 months') → 3

date_trunc ( text, timestamp ) → timestamp

截断到指定的精度;参见第 9.9.2 节

date_trunc('hour', timestamp '2001-02-16 20:38:40') → 2001-02-16 20:00:00

date_trunc ( text, timestamp with time zone, text ) → timestamp with time zone

在规定的时区中截断到指定的精度;参见第 9.9.2 节

date_trunc('day', timestamptz '2001-02-16 20:38:40+00', 'Australia/Sydney') → 2001-02-16 13:00:00+00

date_trunc ( text, interval ) → interval

截断到指定的精度;参见第 9.9.2 节

date_trunc('hour', interval '2 days 3 hours 40 minutes') → 2 days 03:00:00

extract ( field from timestamp ) → numeric

获取时间戳子字段;参见第 9.9.1 节

extract(hour from timestamp '2001-02-16 20:38:40') → 20

extract ( field from interval ) → numeric

获取时间间隔子字段;参见第 9.9.1 节

extract(month from interval '2 years 3 months') → 3

isfinite ( date ) → boolean

测试日期是否有限(不是正负无穷)

isfinite(date '2001-02-16') → true

isfinite ( timestamp ) → boolean

测试时间戳是否有限(不是正负无穷)

isfinite(timestamp 'infinity') → false

isfinite ( interval ) → boolean

测试有限时间间隔(当前总是为真)

isfinite(interval '4 hours') → true

justify_days ( interval ) → interval

调整间隔,将 30 天的时间段转换为月份

justify_days(interval '1 year 65 days') → 1 year 2 mons 5 days

justify_hours ( interval ) → interval

调整间隔,将 24 小时时间段转换为天数

justify_hours(interval '50 hours 10 minutes') → 2 days 02:10:00

justify_interval ( interval ) → interval

使用 justify_days 和 justify_hours 调整时间间隔,并额外调整符号

justify_interval(interval '1 mon -1 hour') → 29 days 23:00:00

localtime → time

一天中的当前时刻;参见第 9.9.5 节

localtime → 14:39:53.662522

localtime ( integer ) → time

一天中的当前时刻,精度受限;参见第 9.9.5 节

localtime(0) → 14:39:53

localtimestamp → timestamp

当前日期和时间(当前事务开始时);参见第 9.9.5 节

localtimestamp → 2019-12-23 14:39:53.662522

localtimestamp ( integer ) → timestamp

当前日期和时间(当前事务开始时),精度受限;参见第 9.9.5 节

localtimestamp(2) → 2019-12-23 14:39:53.66

make_date ( year int, month int, day int ) → date

从年、月和日字段创建日期(负数年份表示 BC)

make_date(2013, 7, 15) → 2013-07-15

make_interval ( [ years int [, months int [, weeks int [, days int [, hours int [, mins int [, secs double precision ]]]]]]] ) → interval

从年、月、周、日、小时、分钟和秒字段创建时间间隔,每个字段默认为 0

make_interval(days => 10) → 10 days

make_time ( hour int, min int, sec double precision ) → time

从小时、分钟和秒字段创建时间

make_time(8, 15, 23.5) → 08:15:23.5

make_timestamp ( year int, month int, day int, hour int, min int, sec double precision ) → timestamp

从年、月、日、小时、分钟和秒字段创建时间戳(负数年份表示 BC)

make_timestamp(2013, 7, 15, 8, 15, 23.5) → 2013-07-15 08:15:23.5

make_timestamptz ( year int, month int, day int, hour int, min int, sec double precision [, timezone text ] ) → timestamp with time zone

从年、月、日、小时、分钟和秒字段并结合时区创建时间戳(负数年份表示 BC)。如果没有指定 timezone,则使用当前时区;示例假设会话时区为 Europe/London

make_timestamptz(2013, 7, 15, 8, 15, 23.5) → 2013-07-15 08:15:23.5+01

make_timestamptz(2013, 7, 15, 8, 15, 23.5, 'America/New_York') → 2013-07-15 13:15:23.5+01

now ( ) → timestamp with time zone

当前日期和时间(当前事务开始时);参见第 9.9.5 节

now() → 2019-12-23 14:39:53.662522-05

statement_timestamp ( ) → timestamp with time zone

当前日期和时间(当前语句开始时);参见第 9.9.5 节

statement_timestamp() → 2019-12-23 14:39:53.662522-05

timeofday ( ) → text

当前的日期和时间(类似 clock_timestamp,但是采用 text 字符串);参见第 9.9.5 节

timeofday() → Mon Dec 23 14:39:53.662522 2019 EST

transaction_timestamp ( ) → timestamp with time zone

当前日期和时间(当前事务开始时);参见第 9.9.5 节

transaction_timestamp() → 2019-12-23 14:39:53.662522-05

to_timestamp ( double precision ) → timestamp with time zone

将 Unix 纪元时间(自 1970-01-01 00:00:00+00 起的秒数)转换为带时区的时间戳

to_timestamp(1284352323) → 2010-09-13 04:32:03+00


除了这些函数以外,还支持 SQL 操作符 OVERLAPS:

(start1, end1) OVERLAPS (start2, end2)
(start1, length1) OVERLAPS (start2, length2)

这个表达式在两个时间段(用它们的端点定义)重叠的时候得到真,当它们不重叠时得到假。端点可以用一对日期、时间或者时间戳来指定;或者是用一个后面跟着一个间隔的日期、时间或时间戳来指定。当一对值被提供时,起点或终点都可以被写在前面,OVERLAPS 会自动地把较早的值作为起点。每一个时间段被认为是表示半开区间 start <= time < end,除非 start 和 end 相等,这种情况下它表示单个时刻。例如这表示两个只有一个共同端点的时间段不重叠。

SELECT (DATE '2001-02-16', DATE '2001-12-21') OVERLAPS
       (DATE '2001-10-30', DATE '2002-10-30');
结果:true
SELECT (DATE '2001-02-16', INTERVAL '100 days') OVERLAPS
       (DATE '2001-10-30', DATE '2002-10-30');
结果:false
SELECT (DATE '2001-10-29', DATE '2001-10-30') OVERLAPS
       (DATE '2001-10-30', DATE '2001-10-31');
结果:false
SELECT (DATE '2001-10-30', DATE '2001-10-30') OVERLAPS
       (DATE '2001-10-30', DATE '2001-10-31');
结果:true

当把一个 interval 值加到某个时间戳上(或从该时间戳中减去一个 interval 值)时,如果该时间戳为 timestamp with time zone 类型,天数部分会相应增加或减少 timestamp with time zone 的日期,变化天数为所指定的天数,而一天中的时刻保持不变。当跨越夏令时变化时(会话时区设为识别夏令时的时区),这意味着 interval '1 day' 不一定等于 interval '24 hours'。例如,当会话时区设置为 America/Denver:

SELECT timestamp with time zone '2005-04-02 12:00:00-07' + interval '1 day';
结果:2005-04-03 12:00:00-06
SELECT timestamp with time zone '2005-04-02 12:00:00-07' + interval '24 hours';
结果:2005-04-03 13:00:00-06

出现这种情况,是因为夏令时变化跳过了一个小时;变化发生的时间为 2005-04-03 02:00:00,所在时区为 America/Denver。

注意,age 返回的 months 字段可能存在歧义,因为不同月份的天数不同。PostgreSQL 在计算不足整月的部分时,会采用两个日期中较早的那个日期所在的月份。例如:age('2004-06-01', '2004-04-30') 使用 4 月得到 1 mon 1 day,而如果使用 5 月则会得到 1 mon 2 days,因为 5 月有 31 天,而 4 月只有 30 天。

日期和时间戳的减法也可能很复杂。一种概念上简单的方法是,先用 EXTRACT(EPOCH FROM ...) 将各值转换为秒数,然后将结果相减;这样得到的是两个值之间的秒数。这种方法会针对每个月的天数、时区变化和夏令时变化进行调整。用“-”操作符将日期或时间戳值相减,会返回两个值之间的天数(每一天为 24 小时)和时/分/秒,也会作相同的调整。age 函数返回年、月、日和时/分/秒,它会逐字段相减,然后调整负值字段。以下查询显示了这些方法的差异。示例结果在 timezone = 'US/Eastern' 设置下产生;所用的两个日期之间发生了夏令时切换:

SELECT EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') -
       EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00');
结果:10537200.000000
SELECT (EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') -
        EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00'))
        / 60 / 60 / 24;
结果:121.9583333333333333
SELECT timestamptz '2013-07-01 12:00:00' - timestamptz '2013-03-01 12:00:00';
结果:121 days 23:00:00
SELECT age(timestamptz '2013-07-01 12:00:00', timestamptz '2013-03-01 12:00:00');
结果:4 mons

9.9.1. EXTRACT, date_part #

EXTRACT(field FROM source)

extract 函数从日期/时间值中检索子字段,例如年份或小时。source 必须是 timestamp、date、time 或 interval 类型的值表达式。(时间戳和时间可以带有或不带有时区。)field 是一个标识符或字符串,用于选择从源值中提取哪个字段。并非每种输入数据类型都适用于所有字段;例如,无法从 date 中提取小于一天的字段,而无法从 time 中提取一天或更长时间的字段。extract 函数返回 numeric 类型的值。

以下是有效的字段名称:

century #

世纪;对于 interval 值,年份字段除以 100

SELECT EXTRACT(CENTURY FROM TIMESTAMP '2000-12-16 12:21:13');
结果:20
SELECT EXTRACT(CENTURY FROM TIMESTAMP '2001-02-16 20:38:40');
结果:21
SELECT EXTRACT(CENTURY FROM DATE '0001-01-01 AD');
结果:1
SELECT EXTRACT(CENTURY FROM DATE '0001-12-31 BC');
结果:-1
SELECT EXTRACT(CENTURY FROM INTERVAL '2001 years');
结果:20
day #

一个月中的第几天(1–31);对于 interval 值,表示天数

SELECT EXTRACT(DAY FROM TIMESTAMP '2001-02-16 20:38:40');
结果:16
SELECT EXTRACT(DAY FROM INTERVAL '40 days 1 minute');
结果:40
decade #

年份字段除以 10

SELECT EXTRACT(DECADE FROM TIMESTAMP '2001-02-16 20:38:40');
结果:200
dow #

一周中的日编号,从星期天(0)到星期六(6)

SELECT EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40');
结果:5

请注意 extract 函数的星期几编号与 to_char(..., 'D') 函数不同。

doy #

一年中的第几天(1–365/366)

SELECT EXTRACT(DOY FROM TIMESTAMP '2001-02-16 20:38:40');
结果:47
epoch #

对于 timestamp with time zone 值,自 1970-01-01 00:00:00 UTC 以来的秒数(早于该时刻的时间戳对应负值);对于 date 和 timestamp 值,自 1970-01-01 00:00:00 以来的名义秒数,不考虑时区或夏令时规则;对于 interval 值,间隔中的总秒数

SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40.12-08');
结果:982384720.120000
SELECT EXTRACT(EPOCH FROM TIMESTAMP '2001-02-16 20:38:40.12');
结果:982355920.120000
SELECT EXTRACT(EPOCH FROM INTERVAL '5 days 3 hours');
结果:442800.000000

您可以使用 to_timestamp 将一个 epoch 值转换回 timestamp with time zone:

SELECT to_timestamp(982384720.12);
结果:2001-02-17 04:38:40.12+00

注意,将 to_timestamp 应用于从 date 或 timestamp 值中提取的 epoch 值可能会产生误导性的结果:结果实际上会假定原始值是以 UTC 时间给出的,这可能并非事实。

hour #

小时字段(时间戳中为 0–23,在间隔中不受限制)

SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 20:38:40');
结果:20
isodow #

一周中的日编号,从星期一(1)到星期日(7)

SELECT EXTRACT(ISODOW FROM TIMESTAMP '2001-02-18 20:38:40');
结果:7

这与 dow 相同,除了星期天。这匹配 ISO 8601 的星期几编号。

isoyear #

日期所在的 ISO 8601 周编号年份

SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-01');
结果:2005
SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-02');
结果:2006

每个 ISO 8601 周编号年都从包含 1 月 4 日的那一周的星期一开始,因此在 1 月初或 12 月末,ISO 年可能与格里高利年不同。更多信息请参见 week 字段。

julian #

与日期或时间戳对应的儒略日。非当地午夜的时间戳会导致小数值。更多信息请参见第 B.7 节。

SELECT EXTRACT(JULIAN FROM DATE '2006-01-01');
结果:2453737
SELECT EXTRACT(JULIAN FROM TIMESTAMP '2006-01-01 12:00');
结果:2453737.50000000000000000000
microseconds #

秒字段,包括小数部分,乘以 1 000 000;注意这包括完整的秒数

SELECT EXTRACT(MICROSECONDS FROM TIME '17:12:28.5');
结果:28500000
millennium #

千年;对于 interval 值,年份字段除以 1000

SELECT EXTRACT(MILLENNIUM FROM TIMESTAMP '2001-02-16 20:38:40');
结果:3
SELECT EXTRACT(MILLENNIUM FROM INTERVAL '2001 years');
结果:2

1900 年至 1999 年的年份属于第二个千年。第三个千年始于 2001 年 1 月 1 日。

milliseconds #

秒字段,包括小数部分,乘以 1000。请注意,这包括完整的秒数。

SELECT EXTRACT(MILLISECONDS FROM TIME '17:12:28.5');
结果:28500.000
minute #

分钟字段(0–59)

SELECT EXTRACT(MINUTE FROM TIMESTAMP '2001-02-16 20:38:40');
结果:38
month #

月份在一年中的编号(1–12);对于 interval 值,月份模 12 的余数(0–11)

SELECT EXTRACT(MONTH FROM TIMESTAMP '2001-02-16 20:38:40');
结果:2
SELECT EXTRACT(MONTH FROM INTERVAL '2 years 3 months');
结果:3
SELECT EXTRACT(MONTH FROM INTERVAL '2 years 13 months');
结果:1
quarter #

日期所在的年份季度(1–4)

SELECT EXTRACT(QUARTER FROM TIMESTAMP '2001-02-16 20:38:40');
结果:1
second #

秒字段,包括任何小数秒

SELECT EXTRACT(SECOND FROM TIMESTAMP '2001-02-16 20:38:40');
结果:40.000000
SELECT EXTRACT(SECOND FROM TIME '17:12:28.5');
结果:28.500000
timezone #

与 UTC 的时区偏移量,以秒为单位。正值对应于 UTC 东部的时区,负值对应于 UTC 西部的时区。(从技术上讲,PostgreSQL 不使用 UTC,因为不处理闰秒。)

timezone_hour #

时区偏移的小时部分

timezone_minute #

时区偏移的分钟部分

week #

一年中按 ISO 8601 周编号体系计算的周序号。根据定义,ISO 周从周一开始,一年的第一周包含该年的 1 月 4 日。换句话说,一年的第一个星期四在该年的第 1 周。

在 ISO 周编号体系中,1 月初的日期可能属于前一年的第 52 周或第 53 周,而 12 月末的日期可能属于下一年的第一周。例如,2005-01-01 属于 2004 年的第 53 周,2006-01-01 属于 2005 年的第 52 周,而 2012-12-31 属于 2013 年的第一周。建议将 isoyear 字段与 week 一起使用,以获得一致的结果。

SELECT EXTRACT(WEEK FROM TIMESTAMP '2001-02-16 20:38:40');
结果:7
year #

年份字段。请记住,没有 0 AD,所以把 BC 年份从 AD 年份中减去时需要小心。

SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40');
结果:2001

当处理 interval 值时,extract 函数会生成与间隔输出函数使用的解释相匹配的字段值。如果从非规范化的间隔表示开始,可能会产生令人惊讶的结果,例如:

SELECT INTERVAL '80 minutes';
结果:01:20:00
SELECT EXTRACT(MINUTES FROM INTERVAL '80 minutes');
结果:20

注意

当输入值为 +/-Infinity 时,extract 对于单调递增的字段(epoch、julian、year、isoyear、decade、century 以及 millennium)返回 +/-Infinity。对于其他字段返回 NULL。PostgreSQL 9.6 之前的版本对所有输入无穷的情况都返回零。

extract 函数主要的用途是做计算性处理。对于用于显示的日期/时间值格式化,参阅第 9.8 节。

date_part 函数仿照传统的 Ingres 实现,后者对应 SQL 标准的 extract 函数:

date_part('field', source)

注意,此处的 field 参数必须是字符串值,而不能是名称。date_part 的有效字段名与 extract 相同。由于历史原因,date_part 函数返回 double precision 类型的值,可能在某些用途中损失精度。建议改用 extract。

SELECT date_part('day', TIMESTAMP '2001-02-16 20:38:40');
结果:16
SELECT date_part('hour', INTERVAL '4 hours 3 minutes');
结果:4

9.9.2. date_trunc #

date_trunc 函数在概念上和用于数字的 trunc 函数类似。

date_trunc(field, source [, time_zone ])

source 是 timestamp、timestamp with time zone 或 interval 类型的值表达式。(date 和 time 类型的值会分别自动转换为 timestamp 或 interval。)field 选择输入值的截断精度。返回值同样为 timestamp、timestamp with time zone 或 interval 类型,其中低于所选精度的所有字段都设为零(日和月则设为一)。

field 的有效值是:

microseconds
milliseconds
second
minute
hour
day
week
month
quarter
year
decade
century
millennium

当输入值为 timestamp with time zone 类型时,截断会以特定时区为准;例如,截断到 day 会得到该时区的午夜。默认情况下,截断以当前的 TimeZone 设置为准,但可以通过可选的 time_zone 参数指定其他时区。时区名称可以用第 8.5.3 节中描述的任意方式指定。

当处理 timestamp without time zone 或 interval 输入时,不能指定时区。这类输入总是按其字面值处理。

示例(假设本地时区是 America/New_York):

SELECT date_trunc('hour', TIMESTAMP '2001-02-16 20:38:40');
结果:2001-02-16 20:00:00
SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40');
结果:2001-01-01 00:00:00
SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40+00');
结果:2001-02-16 00:00:00-05
SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40+00', 'Australia/Sydney');
结果:2001-02-16 08:00:00-05
SELECT date_trunc('hour', INTERVAL '3 days 02:47:33');
结果:3 days 02:00:00

9.9.3. date_bin #

函数 date_bin 将输入时间戳按指定的间隔(stride)进行分箱,对齐到指定的原点。

date_bin(stride, source, origin)

source 是 timestamp 或 timestamp with time zone 类型的值表达式。(类型 date 的值会自动转换为 timestamp。)stride 是 interval 类型的值表达式。返回值同样是 timestamp 或 timestamp with time zone 类型,并且它表示 source 所在分箱的起点。

示例:

SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-01-01');
结果:2020-02-11 15:30:00
SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-01-01 00:02:30');
结果:2020-02-11 15:32:30

在完整单位(1 分钟,1 小时,等等)的情况下,它给出与类似 date_trunc 调用相同的结果,但不同的地方是 date_bin 可以截断到任意间隔。

stride 时间间隔必须大于零,并且不能包含月或更大的单位。

9.9.4. AT TIME ZONE #

AT TIME ZONE 操作符可将不带时区的时间戳转换为带时区的时间戳或反向转换,也可将 time with time zone 值转换到不同的时区。表 9.34 展示了它的各种变体。

表 9.34. AT TIME ZONE 变体

操作符

描述

示例

timestamp without time zone AT TIME ZONE zone → timestamp with time zone

将给定的不带时区时间戳转换为带时区时间戳,假定给定值位于指定时区。

timestamp '2001-02-16 20:38:40' at time zone 'America/Denver' → 2001-02-17 03:38:40+00

timestamp with time zone AT TIME ZONE zone → timestamp without time zone

将给定的带时区时间戳转换为无时区时间戳,表示该时间在指定时区中的本地时间。

timestamp with time zone '2001-02-16 20:38:40-05' at time zone 'America/Denver' → 2001-02-16 18:38:40

time with time zone AT TIME ZONE zone → time with time zone

将给定的带时区时间转换到新的时区。由于没有提供日期,这会使用目标时区当前生效的 UTC 偏移量。

time with time zone '05:34:17-05' at time zone 'UTC' → 10:34:17+00


在这些表达式里,所需的时区 zone 可以指定为文本值(例如 'America/Los_Angeles'),也可以指定为一个间隔值(例如 INTERVAL '-08:00')。在文本情况下,时区名称可以按第 8.5.3 节中描述的任意方式指定。使用间隔值只对与 UTC 存在固定偏移量的时区有意义,因此在实践中并不常见。

示例(假设当前 TimeZone 设置为 America/Los_Angeles):

SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'America/Denver';
结果:2001-02-16 19:38:40-08
SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'America/Denver';
结果:2001-02-16 18:38:40
SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'Asia/Tokyo' AT TIME ZONE 'America/Chicago';
结果:2001-02-16 05:38:40

第一个示例为不带时区的值添加时区,并使用当前 TimeZone 设置显示该值。第二个示例将带时区的时间戳值移到指定时区,并返回不带时区的值。这样就可以存储和显示与当前 TimeZone 设置不同的值。第三个示例将东京时间转换为芝加哥时间。

函数 timezone(zone, timestamp) 等效于符合 SQL 标准的结构 timestamp AT TIME ZONE zone。

9.9.5. 当前日期/时间 #

PostgreSQL 提供了许多返回当前日期和时间的函数。这些 SQL 标准的函数全部都按照当前事务的开始时刻返回值:

CURRENT_DATE
CURRENT_TIME
CURRENT_TIMESTAMP
CURRENT_TIME(precision)
CURRENT_TIMESTAMP(precision)
LOCALTIME
LOCALTIMESTAMP
LOCALTIME(precision)
LOCALTIMESTAMP(precision)

CURRENT_TIME 和 CURRENT_TIMESTAMP 返回带时区的值;LOCALTIME 和 LOCALTIMESTAMP 返回不带时区的值。

CURRENT_TIME、CURRENT_TIMESTAMP、LOCALTIME 和 LOCALTIMESTAMP 可以有选择地接受一个精度参数,该精度会使结果的秒字段舍入到指定的小数位数。如果没有精度参数,结果将给出可用的全部精度。

一些示例:

SELECT CURRENT_TIME;
结果:14:39:53.662522-05
SELECT CURRENT_DATE;
结果:2019-12-23
SELECT CURRENT_TIMESTAMP;
结果:2019-12-23 14:39:53.662522-05
SELECT CURRENT_TIMESTAMP(2);
结果:2019-12-23 14:39:53.66-05
SELECT LOCALTIMESTAMP;
结果:2019-12-23 14:39:53.662522

因为这些函数全部都按照当前事务的开始时刻返回结果,所以它们的值在事务运行的整个期间内都不改变。我们认为这是一个特性:目的是为了允许一个事务在“当前”时间上有一致的概念,这样在同一个事务里的多个修改可以保持同样的时间戳。

注意

其他数据库系统可能会更频繁地推进这些值。

PostgreSQL 还提供了返回当前语句的开始时间以及调用该函数时的实际当前时间的函数。这些非 SQL 标准的函数列表如下:

transaction_timestamp()
statement_timestamp()
clock_timestamp()
timeofday()
now()

transaction_timestamp() 等价于 CURRENT_TIMESTAMP,但是其命名清楚地反映了它的返回值。statement_timestamp() 返回当前语句的开始时刻(更准确地说,是接收到客户端最近一条命令消息的时间)。statement_timestamp() 和 transaction_timestamp() 在一个事务的第一条语句期间返回值相同,但是在随后的语句中却不一定相同。clock_timestamp() 返回真正的当前时间,因此它的值甚至在同一条 SQL 语句中都会变化。timeofday() 是一个有历史原因的 PostgreSQL 函数。和 clock_timestamp() 相似,它也返回真实的当前时间,但是它的结果是一个格式化的 text 串,而不是 timestamp with time zone 值。now() 是 PostgreSQL 中与 transaction_timestamp() 等价的传统函数。

所有日期/时间数据类型也都接受特殊字面值 now 来指定当前日期和时间(同样解释为事务开始时间)。因此,下面三种写法都返回相同的结果:

SELECT CURRENT_TIMESTAMP;
SELECT now();
SELECT TIMESTAMP 'now';  -- 但请参阅下面的提示

提示

当指定以后要计算的值时,不要使用第三种形式,例如在表列的 DEFAULT 子句中。系统将在分析这个常量的时候把 now 转换为一个 timestamp,这样需要默认值时就会得到创建表的时间!而前两种形式要到实际使用默认值的时候才被计算,因为它们是函数调用。因此它们可以给出每次插入行的时刻。(参见第 8.5.1.4 节。)

9.9.6. 延时执行 #

以下函数可用于延迟服务器进程的执行:

pg_sleep ( double precision )
pg_sleep_for ( interval )
pg_sleep_until ( timestamp with time zone )

pg_sleep 使当前会话的进程休眠,直到经过指定的秒数。可以指定带小数部分的秒数作为延迟时间。pg_sleep_for 是一个便捷函数,允许以 interval 指定休眠时间。pg_sleep_until 是在需要指定唤醒时间时使用的便捷函数。例如:

SELECT pg_sleep(1.5);
SELECT pg_sleep_for('5 minutes');
SELECT pg_sleep_until('tomorrow 03:00');

注意

有效的休眠时间间隔精度与平台有关,0.01 秒是常见值。休眠延迟将至少持续指定的时长,也有可能由于服务器负荷而比指定的时间长。特别地,pg_sleep_until 并不保证能刚好在指定的时刻被唤醒,但它不会在比指定时刻早的时候醒来。

警告

请确保在调用 pg_sleep 或者其变体时,你的会话没有持有不必要的锁。否则其它会话可能必须等待你的休眠会话,因而减慢整个系统速度。

报告文档问题

阅读 上游文档. 通过 PostgreSQL 文档反馈表单.