PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

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

9.9.1. EXTRACT、date_part
9.9.2. date_trunc
9.9.3. date_bin
9.9.4. AT TIME ZONE 和 AT LOCAL
9.9.5. 当前日期/时间
9.9.6. 延迟执行

Table 9.33 列出了可用于日期/时间数据处理的函数,详细信息见后续各小节。Table 9.32 演示了基础算术操作符(+、* 等)的行为。关于格式化函数,请参阅 Section 9.8。您应当预先熟悉 Section 8.5 中关于日期/时间类型的背景知识。

此外,Table 9.1 中展示的常规比较操作符同样适用于日期/时间类型。日期与时间戳(无论是否带时区)均可相互比较,而时间(带或不带时区)和时间间隔(interval)仅能与同数据类型的其他值进行比较。当将不带时区的时间戳与带时区的时间戳进行比较时,前者被假定为处于由 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);我们仅展示其中的一种形式。

Table 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


Table 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

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

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

current_date → date

当前日期;参见 Section 9.9.5

current_date → 2019-12-23

current_time → time with time zone

当前一天中的时间; 参见 Section 9.9.5

current_time → 14:39:53.662522-05

current_time ( integer ) → time with time zone

当前一天中的时间, 具有受限的精度; 参见 Section 9.9.5

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

current_timestamp → timestamp with time zone

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

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

current_timestamp ( integer ) → timestamp with time zone

当前日期和时间 (当前事务的开始), 具有受限的精度; 参见 Section 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

把输入分箱到与指定原点对齐的指定 interval 中; 参见 Section 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); 参见 Section 9.9.1

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

date_part ( text, interval ) → double precision

获取 interval 子字段 (等同于 extract); 参见 Section 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

截断到指定的精度; 参见 Section 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

在指定的时区中截断到指定的精度; 参见 Section 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

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

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

extract ( field from timestamp ) → numeric

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

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

extract ( field from interval ) → numeric

获取 interval 子字段; 参见 Section 9.9.1

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

isfinite ( date ) → boolean

测试是否为有限日期 (非 +/-infinity)

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

isfinite ( timestamp ) → boolean

测试是否为有限时间戳 (非 +/-infinity)

isfinite(timestamp 'infinity') → false

isfinite ( interval ) → boolean

测试是否为有限 interval (非 +/-infinity)

isfinite(interval '4 hours') → true

justify_days ( interval ) → interval

调整 interval, 把 30 天的时间周期转换为月

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

justify_hours ( interval ) → interval

调整 interval, 把 24 小时的时间周期转换为天

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

justify_interval ( interval ) → interval

使用 justify_days 和 justify_hours 调整 interval, 并做额外的符号调整

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

localtime → time

当前一天中的时间; 参见 Section 9.9.5

localtime → 14:39:53.662522

localtime ( integer ) → time

当前一天中的时间, 具有受限的精度; 参见 Section 9.9.5

localtime(0) → 14:39:53

localtimestamp → timestamp

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

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

localtimestamp ( integer ) → timestamp

当前日期和时间 (当前事务的开始), 具有受限的精度; 参见 Section 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

从年、月、周、日、小时、分钟和秒字段创建 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

从年、月、日、小时、分钟和秒字段创建 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

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

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

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

statement_timestamp ( ) → timestamp with time zone

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

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

timeofday ( ) → text

当前日期和时间 (类似于 clock_timestamp, 但以一个 text 字符串的形式); 参见 Section 9.9.5

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

transaction_timestamp ( ) → timestamp with time zone

当前日期和时间 (当前事务的开始); 参见 Section 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)

当两个时间段 (由它们的端点定义) 重叠时, 这个表达式得出真, 不重叠时得出假。 端点可以指定为日期、时间或时间戳的对; 或者指定为一个日期、时间或时间戳后跟一个 interval。 当提供了一对值时, 可以先写开始也可以先写结束; 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

把一个 interval 值加到一个 timestamp 或 timestamp with time zone 值上 (或从其中减去), 该 interval 值的月、日和微秒字段会被依次处理。 首先, 一个非零的月字段会把时间戳的日期推进或回退指定的月数, 保持月中的日不变, 除非它会超出新月份的末尾, 在那种情况下会使用该月的最后一天。 (例如, 3 月 31 日加 1 个月变成 4 月 30 日, 但 3 月 31 日加 2 个月变成 5 月 31 日。) 然后日字段会把时间戳的日期推进或回退指定的天数。 在这两个步骤中, 本地一天中的时间保持不变。 最后, 如果有一个非零的微秒字段, 它会被按字面加上或减去。 当在一个识别夏令时的时区中对一个 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. 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 值, 为该 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 把一个 epoch 值转换回 timestamp with time zone:

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

注意, 把 to_timestamp 应用到从一个 date 或 timestamp 值提取出的 epoch 上可能产生误导性的结果: 结果实际上会假定原始值是以 UTC 给出的, 但情况可能并非如此。

hour

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

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 相同。 这与 ISO 8601 的星期几编号相匹配。

isoyear

该日期所处的 ISO 8601 周编号年

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

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

julian

与该日期或时间戳相对应的 儒略日。 不是本地午夜的时间戳会产生一个小数值。 更多信息请参见 Section 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

The minutes field (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

该年中 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 一起使用以获得一致的结果。

对于 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 函数产生的字段值与 interval 输出函数所用的解释相匹配。 如果从一个未规范化的 interval 表示开始, 这可能产生令人意外的结果, 例如:

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

Note

当输入值为 +/-Infinity 时, 对于单调递增的字段 (对于 timestamp 输入, 为 epoch、 julian、 year、 isoyear、 decade、 century 和 millennium; 对于 interval 输入, 为 epoch、 hour、 day、 year、 decade、 century 和 millennium), extract 返回 +/-Infinity。 对于其他字段, 则返回 NULL。 PostgreSQL 9.6 之前的版本对无限输入的所有情形都返回零。

extract 函数主要预期用于计算处理。 用于显示目的的日期/时间值格式化, 请参见 Section 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 类型, 并且所有比所选字段更不重要的字段都被设置为零 (对于日和月则为 1)。

field 的有效值是:

microseconds(微秒)
milliseconds(毫秒)
second(秒)
minute(分)
hour(时)
day(日)
week(周)
month(月)
quarter(季度)
year(年)
decade(十年)
century(世纪)
millennium(千年)

当输入值是 timestamp with time zone 类型时, 截断会相对于一个特定的时区执行; 例如, 截断到 day 会产生该时区中一个午夜时刻的值。 默认情况下, 截断相对于当前的 TimeZone 设置执行, 但可以提供可选的 time_zone 参数来指定一个不同的时区。 时区名可以用 Section 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 把输入的时间戳 “分箱”到与一个指定原点对齐的指定 interval (即 步长) 中。

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 可以截断到任意的 interval。

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

9.9.4. AT TIME ZONE 和 AT LOCAL #

AT TIME ZONE 操作符把 不带时区的时间戳转换为 带时区的时间戳 (以及反向转换), 并把 time with time zone 值转换到不同的时区。 Table 9.34 展示了它的变体。

Table 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 指定 (例如 INTERVAL '-08:00')。 在文本的情形下, 时区名可以用 Section 8.5.3 中描述的任何方式指定。 interval 的情形只对具有固定 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 日期时间字面量中的符号具有相反的含义, 如 Section 8.5.3 和 Appendix 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 可以选择性地接受一个精度参数, 它会让结果在秒字段中被舍入到那么多的小数位。 在没有精度参数的情况下, 结果会以完整的可用精度给出。

Some examples:

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

由于这些函数返回的是当前事务的开始时间, 它们的值在事务期间不会改变。 这被认为是一个特性: 其意图是允许单个事务对 “当前”时间有一致的概念, 使同一事务中的多次修改都带有相同的时间戳。

Note

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

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() 是与 transaction_timestamp() 等价的传统的 PostgreSQL 函数。

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

SELECT CURRENT_TIMESTAMP;
SELECT now();
SELECT TIMESTAMP 'now';  -- but see tip below

Tip

在指定一个要在以后求值的值时 (例如在表列的一个 DEFAULT 子句中), 不要使用第三种形式。 系统会在常量被解析时立刻把 now 转换成一个 timestamp, 因此当需要默认值时, 用到的将是表创建时的时间! 前两种形式要到默认值被使用时才会被求值, 因为它们是函数调用。 因此它们会给出默认为行插入时刻的期望行为。 (另请参见 Section 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');

Note

睡眠间隔的有效分辨率取决于平台; 0.01 秒是一个常见的值。 睡眠延迟至少会与指定的一样长。 它可能更长, 取决于服务器负载等因素。 特别是, pg_sleep_until 并不保证恰好在指定时刻醒来, 但它不会提前醒来。

Warning

在调用 pg_sleep 或其变体时, 请确保你的会话没有持有超过必要数量的锁。 否则其他会话可能不得不等待你的睡眠进程, 从而拖慢整个系统。