支持的版本: 当前 (17) / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 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 时区的午夜。

下面描述的所有接收 timetimestamp 输入的函数和操作符实际上都有两种变体:一种接收 time with time zonetimestamp with time zone,另一种接收 time without time zonetimestamp without time zone。为简洁起见,这些变体未单独显示。此外,+* 操作符成对出现(例如,date + integerinteger + date);我们只显示每对中的一个。

表 9.32. 日期/时间操作符

操作符

描述

示例

date + integerdate

向日期添加天数

date '2001-09-28' + 72001-10-05

date + intervaltimestamp

向日期添加一个间隔

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

date + timetimestamp

向日期添加一天中的时间

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

interval + intervalinterval

添加间隔

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

timestamp + intervaltimestamp

向时间戳添加一个间隔

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

time + intervaltime

向时间添加一个间隔

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

- intervalinterval

取反一个间隔

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

date - dateinteger

减去日期,产生经过的天数

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

date - integerdate

从日期减去天数

date '2001-10-01' - 72001-09-24

date - intervaltimestamp

从日期减去一个间隔

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

time - timeinterval

减去时间

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

time - intervaltime

从时间减去一个间隔

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

timestamp - intervaltimestamp

从时间戳减去一个间隔

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

interval - intervalinterval

减去间隔

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

timestamp - timestampinterval

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

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

interval * double precisioninterval

将间隔乘以标量

interval '1 second' * 90000:15:00

interval '1 day' * 2121 days

interval '1 hour' * 3.503:30:00

interval / double precisioninterval

将间隔除以标量

interval '1 hour' / 1.500: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_datedate

当前日期;参阅第 9.9.5 节

current_date2019-12-23

current_timetime with time zone

当前时间;参阅第 9.9.5 节

current_time14:39:53.662522-05

current_time ( integer ) → time with time zone

当前时间,精度有限;参阅第 9.9.5 节

current_time(2)14:39:53.66-05

current_timestamptimestamp with time zone

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

current_timestamp2019-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_add ( timestamp with time zone, interval [, text ] ) → timestamp with time zone

将一个 interval 加到一个 timestamp with time zone 上,根据第三个参数指定时区计算时间,或如果省略第三个参数则使用当前的 TimeZone 设置来计算一天中的时间以及夏令时调整。带有两个参数的形式等效于 timestamp with time zone + interval 运算符。

date_add('2021-10-31 00:00:00+02'::timestamptz, '1 day'::interval, 'Europe/Warsaw')2021-10-31 23:00:00+00

date_bin ( interval, timestamp, timestamp ) → timestamp

将输入放入与指定原点对齐的指定间隔中;参阅第 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_subtract ( timestamp with time zone, interval [, text ] ) → timestamp with time zone

timestamp with time zone 中减去一个 interval,根据第三个参数指定时区计算时间,或如果省略第三个参数则使用当前的 TimeZone 设置来计算一天中的时间和夏令时调整。带有两个参数的形式等效于 timestamp with time zone - interval 运算符。

date_subtract('2021-11-01 00:00:00+01'::timestamptz, '1 day'::interval, 'Europe/Warsaw')2021-10-30 22:00:00+00

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_daysjustify_hours 调整间隔,并进行额外的符号调整

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

localtimetime

当前时间;参阅第 9.9.5 节

localtime14:39:53.662522

localtime ( integer ) → time

当前时间,精度有限;参阅第 9.9.5 节

localtime(0)14:39:53

localtimestamptimestamp

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

localtimestamp2019-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

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

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

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

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

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

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

从年、月、日、小时、分钟和秒字段创建带时区的时间戳(负数年份表示公元前)。 如果未指定 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)

当两个时间段(由其端点定义)重叠时,此表达式产生 true,不重叠时产生 false。 端点可以指定为日期、时间或时间戳对;或者指定为日期、时间或时间戳后跟一个时间间隔。 当提供一对值时,可以先写入起始值或结束值;OVERLAPS 会自动将这对值中较早的值作为起始值。 除非 startend 相等,在这种情况下它表示该单个时间点,否则每个时间段都视为表示半开区间 start <= time < end。 这意味着例如,只有端点相同的时间段不会重叠。

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

当将 interval 值添加到(或从)timestamptimestamp with time zone 值时,将依次处理 interval 值的月份、天数和微秒字段。 首先,非零月份字段会将时间戳的日期提前或减去指示的月份数,保持月份中的日期相同,除非它会超过新月份的末尾,在这种情况下,将使用该月份的最后一天。(例如,3 月 31 日加上 1 个月变为 4 月 30 日,但 3 月 31 日加上 2 个月变为 5 月 31 日。)然后,天数字段会将时间戳的日期提前或减去指示的天数。在这两个步骤中,本地时间都保持不变。最后,如果存在非零的微秒字段,则会按字面意义添加或减去。当在识别 DST 的时区对 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';
Result: 2005-04-03 12:00:00-06
SELECT timestamp with time zone '2005-04-02 12:00:00-07' + interval '24 hours';
Result: 2005-04-03 13:00:00-06

发生这种情况是因为在时区 America/Denver 中,由于夏令时在 2005-04-03 02:00:00 发生更改而跳过了一个小时。

请注意,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');
Result: 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;
Result: 121.9583333333333333
SELECT timestamptz '2013-07-01 12:00:00' - timestamptz '2013-03-01 12:00:00';
Result: 121 days 23:00:00
SELECT age(timestamptz '2013-07-01 12:00:00', timestamptz '2013-03-01 12:00:00');
Result: 4 mons

9.9.1. EXTRACTdate_part #

EXTRACT(field FROM source)

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

以下是有效的字段名称

century

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

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

月份中的日期 (1–31);对于 interval 值,为天数

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

年份字段除以 10

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

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

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

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

doy

一年中的日期 (1–365/366)

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

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

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

可以使用 to_timestamp 将纪元值转换回 timestamp with time zone

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

请注意,将 to_timestamp 应用于从 datetimestamp 值提取的纪元可能会产生误导性的结果:结果实际上会假设原始值是在 UTC 中给出的,而情况可能并非如此。

hour

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

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

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

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

除了星期日之外,这与 dow 相同。这与ISO8601 的星期几编号相匹配。

isoyear

日期所在的ISO8601 周编号年份

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

每个ISO8601 周编号年份都从包含 1 月 4 日的一周的星期一开始,因此在 1 月初或 12 月末,该ISO年份可能与公历年份不同。有关更多信息,请参见 week 字段。

julian

与日期或时间戳对应的儒略日。不是当地午夜的时间戳将产生一个小数。有关更多信息,请参见 第 B.7 节

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

秒字段(包括小数部分)乘以 1,000,000;请注意,这包括整数秒

SELECT EXTRACT(MICROSECONDS FROM TIME '17:12:28.5');
Result: 28500000
millennium

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

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

1900 年代的年份位于第二个千年。第三个千年始于 2001 年 1 月 1 日。

milliseconds

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

SELECT EXTRACT(MILLISECONDS FROM TIME '17:12:28.5');
Result: 28500.000
minute

分钟字段 (0–59)

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

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

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

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

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

秒字段,包括任何小数秒

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

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

timezone_hour

时区偏移量的小时分量

timezone_minute

时区偏移量的分钟分量

week

一周的编号ISO8601 年的周数。按照定义,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');
Result: 7

年份字段。请记住,没有公元 0 年,因此从公元 AD 年份中减去公元前 BC 年份时应谨慎。

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

当处理 interval 值时,extract 函数生成与 interval 输出函数使用的解释相匹配的字段值。如果从一个非标准化的 interval 表示形式开始,这可能会产生令人惊讶的结果,例如

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

注意

当输入值为 +/-Infinity 时,extract 对于单调递增的字段返回 +/-Infinity(对于 timestamp 输入,包括 epochjulianyearisoyeardecadecenturymillennium;对于 interval 输入,包括 epochhourdayyeardecadecenturymillennium)。对于其他字段,返回 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');
Result: 16
SELECT date_part('hour', INTERVAL '4 hours 3 minutes');
Result: 4

9.9.2. date_trunc #

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

date_trunc(field, source [, time_zone ])

sourcetimestamptimestamp with time zoneinterval 类型的数值表达式。(datetime 类型的值会自动转换为 timestampinterval。)field 选择将输入值截断到的精度。返回值同样是 timestamptimestamp with time zoneinterval 类型,并且它将所有小于所选字段的字段设置为零(对于 day 和 month,则设置为一)。

field 的有效值为

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

当输入值为 timestamp with time zone 类型时,截断是相对于特定时区执行的;例如,截断为 day 会生成该时区的午夜值。默认情况下,截断是相对于当前的TimeZone 设置进行的,但是可以提供可选的 time_zone 参数来指定不同的时区。时区名称可以使用第 8.5.3 节中描述的任何方式指定。

处理 timestamp without time zoneinterval 输入时,无法指定时区。这些始终按字面值处理。

示例(假设本地时区为 America/New_York

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

9.9.3. date_bin #

date_bin 函数将输入时间戳分箱到指定的间隔(即步长)中,并与指定的原点对齐。

date_bin(stride, source, origin)

sourcetimestamptimestamp with time zone 类型的数值表达式。(date 类型的值会自动转换为 timestamp。)strideinterval 类型的数值表达式。返回值同样是 timestamptimestamp with time zone 类型,它标记 source 放置到的箱的开始。

示例

SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-01-01');
Result: 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');
Result: 2020-02-11 15:32:30

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

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

9.9.4. AT TIME ZONE 和 AT LOCAL #

AT TIME ZONE 运算符将不带时区的时间戳转换为带时区的时间戳,反之亦然,并将带时区的 time 值转换为不同的时区。 表 9.34 显示了其变体。

表 9.34. AT TIME ZONEAT LOCAL 变体

操作符

描述

示例

timestamp without time zone AT TIME ZONE zonetimestamp with time zone

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

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

timestamp without time zone AT LOCALtimestamp with time zone

将给定的不带时区的时间戳转换为带有时区的时间戳,时区为会话的 TimeZone 值。

timestamp '2001-02-16 20:38:40' at local2001-02-17 03:38:40+00

timestamp with time zone AT TIME ZONE zonetimestamp 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

timestamp with time zone AT LOCALtimestamp without time zone

将给定的时区的时间戳转换为不带时区的时间戳,其时间将显示为会话的 TimeZone 值所指定的时区的时间。

timestamp with time zone '2001-02-16 20:38:40-05' at local2001-02-16 18:38:40

time with time zone AT TIME ZONE zonetime with time zone

将给定的时区的时间转换为新的时区。由于没有提供日期,因此这将使用命名目标时区当前活动的 UTC 偏移量。

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

time with time zone AT LOCALtime with time zone

将给定的时区的时间转换为新的时区。由于没有提供日期,因此这将使用会话的 TimeZone 值的当前活动 UTC 偏移量。

假设会话的 TimeZone 设置为 UTC

time with time zone '05:34:17-05' at local10:34:17+00


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

语法 AT LOCAL 可以用作 AT TIME ZONE local 的简写,其中 local 是会话的 TimeZone 值。

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

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

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

第五个示例是一个警示故事。由于输入值没有关联的日期,因此转换是使用会话的当前日期进行的。因此,此静态示例可能会根据查看的年份时间显示错误的结果,因为 'America/Los_Angeles' 会遵守夏令时。

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

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

函数 timezone(timestamp) 等效于符合 SQL 标准的结构 timestamp AT LOCAL

函数 timezone(time) 等效于符合 SQL 标准的结构 time AT LOCAL

9.9.5. 当前日期/时间 #

PostgreSQL 提供了许多返回与当前日期和时间相关的值的函数。这些符合 SQL 标准的函数都返回基于当前事务开始时间的值。

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

CURRENT_TIMECURRENT_TIMESTAMP 返回带时区的值;LOCALTIMELOCALTIMESTAMP 返回不带时区的值。

CURRENT_TIMECURRENT_TIMESTAMPLOCALTIMELOCALTIMESTAMP 可以选择接受一个精度参数,这将导致结果中的秒字段被四舍五入到指定的小数位数。如果没有精度参数,则结果将给出所有可用的精度。

一些示例

SELECT CURRENT_TIME;
Result: 14:39:53.662522-05
SELECT CURRENT_DATE;
Result: 2019-12-23
SELECT CURRENT_TIMESTAMP;
Result: 2019-12-23 14:39:53.662522-05
SELECT CURRENT_TIMESTAMP(2);
Result: 2019-12-23 14:39:53.66-05
SELECT LOCALTIMESTAMP;
Result: 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';  -- but see tip below

提示

在指定稍后要计算的值时,例如在表列的 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 是一个方便的函数,允许将休眠时间指定为 intervalpg_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 或其变体时,请确保您的会话持有的锁不超过必要的数量。否则,其他会话可能必须等待您休眠的进程,从而减慢整个系统的速度。

提交更正

如果您在文档中看到任何不正确、与您特定功能的体验不符或需要进一步澄清的内容,请使用此表单报告文档问题。