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

向timestamp with time zone添加interval,根据第三个参数指定的时区或当前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_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

根据年、月、日字段创建日期(负数年表示公元前)

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 自动将这对值中较早的值作为开始。每个时间段都被视为表示半开区间 start <= time < end,除非 start 和 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

当向timestamp或timestamp with time zone值添加(或从中减去)interval值时,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') 使用四月得到 1 mon 1 day,而使用五月会得到 1 mon 2 days,因为五月有31天,而四月只有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. 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');
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 以来的秒数(在此之前的为负数);对于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');
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 应用于从 date 或 timestamp 值中提取的纪元可能会产生误导性结果:结果将有效假设原始值已在 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

该ISO日期所属的 8601 周编号年份

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

传递给 -c 的每个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);对于interval值,为月份字段除以3加1

SELECT EXTRACT(QUARTER FROM TIMESTAMP '2001-02-16 20:38:40');
Result: 1
SELECT EXTRACT(QUARTER FROM INTERVAL '1 year 6 months');
Result: 3
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 周编号系统中,一月初的日期可能属于上一年的第 52 或 53 周,而十二月末的日期可能属于下一年的第一周。例如,2005-01-01 属于 2004 年的第 53 周,2006-01-01 属于 2005 年的第 52 周,而 2012-12-31 属于 2013 年的第一周。建议将 isoyear 字段与 week 字段一起使用以获得一致的结果。

对于interval值,周字段就是整数天数除以7。

SELECT EXTRACT(WEEK FROM TIMESTAMP '2001-02-16 20:38:40');
Result: 7
SELECT EXTRACT(WEEK FROM INTERVAL '13 days 24 hours');
Result: 1
year

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

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

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

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

注意

当输入值为 +/-Infinity 时,extract 对单调递增的字段(timestamp 输入的epoch、julian、year、isoyear、decade、century和millennium;interval 输入的epoch、hour、day、year、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');
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 ])

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');
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)

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');
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 with time zone值转换为不同的时区。表 9.34 显示了其变体。

表 9.34. AT TIME ZONE 和 AT LOCAL 变体

运算符

描述

示例

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 without time zone AT LOCAL → timestamp with time zone

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

timestamp '2001-02-16 20:38:40' at local → 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

timestamp with time zone AT LOCAL → timestamp without time zone

将给定的带时区的时间戳转换为不带时区的时间戳,如同使用会话的TimeZone值作为时区的时间一样。

timestamp with time zone '2001-02-16 20:38:40-05' at local → 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

time with time zone AT LOCAL → time with time zone

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

假设会话的TimeZone设置为UTC

time with time zone '05:34:17-05' at local → 10: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 TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE '+05';
Result: 2001-02-16 20:38:40
SELECT TIME WITH TIME ZONE '20:38:40-05' AT LOCAL;
Result: 17:38:40

第一个例子为缺少时区的值添加了一个时区,并使用当前TimeZone设置显示该值。第二个例子将带时区的时间戳值移到指定的时区,并返回不带时区的值。这允许存储和显示不同于当前TimeZone设置的值。第三个例子将东京时间转换为芝加哥时间。第四个例子将带时区的时间戳值移到当前由TimeZone设置指定的时区,并返回不带时区的值。第五个例子演示了POSIX风格时区规范中的符号与ISO-8601日期时间文字中的符号具有相反的含义,如第 8.5.3 节和附录 B所述。

第六个例子是一个警示。由于输入值没有关联日期,因此使用会话的当前日期进行转换。因此,这个静态示例可能会根据查看的时间显示错误的结果,因为'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_TIME 和 CURRENT_TIMESTAMP 返回带时区的值;LOCALTIME 和 LOCALTIMESTAMP 返回不带时区的值。

CURRENT_TIME、CURRENT_TIMESTAMP、LOCALTIME 和 LOCALTIMESTAMP 可选地接受一个精度参数,该参数导致结果在秒字段中四舍五入到该小数位数。如果没有精度参数,结果将以完整可用精度给出。

一些例子

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是一个方便函数,允许将休眠时间指定为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或其变体时,请确保您的会话持有的锁不多于必要。否则,其他会话可能不得不等待您休眠的进程,从而减慢整个系统。