INTERVAL(时间间隔)表示可以添加到 DATE、TIMESTAMP、TIMESTAMPTZ 或 TIME 值,或从中减去的时间段。
| 名称 | 描述 |
|---|---|
INTERVAL |
时间段 |
可以通过提供数值和单位来构建 INTERVAL。非 months(月)、days(天)或 microseconds(微秒)的单位会被转换为这三个基础单位中较小单位的等效值。
SELECT
INTERVAL 1 YEAR, -- single unit using YEAR keyword; stored as 12 months
INTERVAL (random() * 10) YEAR, -- parentheses necessary for variable amounts;
-- stored as integer number of months
INTERVAL '1 month 1 day', -- string type necessary for multiple units; stored as (1 month, 1 day)
'16 months'::INTERVAL, -- string cast supported; stored as 16 months
'48:00:00'::INTERVAL, -- HH::MM::SS string supported; stored as (48 * 60 * 60 * 1e6 microseconds)
;
警告:当与单位关键字一起使用时,十进制值会被截断为整数(除非单位是
SECONDS或MILLISECONDS)。SELECT INTERVAL '1.5' YEARS; -- Returns 12 months; equivalent to `to_years(CAST(trunc(1.5) AS INTEGER))`如需更高精度,请将单位包含在字符串中或使用更细粒度的单位;例如
INTERVAL '1.5 years'或INTERVAL 18 MONTHS。
之所以需要三个独立的基础单位,是因为一个月不对应固定的天数(二月比三月天数少),而一天也不对应固定的微秒数(由于夏令时,一天可能是 25 小时或 23 小时)。这种组件拆分方式使得 INTERVAL 类适合于在日期上添加或减去特定的时间单位。例如,我们可以使用以下 SQL 查询生成一个包含每月第一天的表:
SELECT DATE '2000-01-01' + INTERVAL (i) MONTH
FROM range(12) t(i);
当 INTERVAL 通过 datepart 函数解构时,months 组件会被进一步拆分为年和月,microseconds 组件会被拆分为小时、分钟和微秒。days 组件不会被拆分为其他单位。为了演示这一点,以下查询通过对这三个基础单位的随机量求和来生成一个名为 period 的 INTERVAL。然后,它从 period 中提取上述六个部分,将它们重新相加,并确认结果始终等于原始的 period。
SELECT
period = list_reduce(
[INTERVAL (datepart(part, period) || part) FOR part IN
['year', 'month', 'day', 'hour', 'minute', 'microsecond']
],
(i1, i2) -> i1 + i2
) -- always true
FROM (
VALUES (
INTERVAL (random() * 123_456_789_123) MICROSECONDS
+ INTERVAL (random() * 12_345) DAYS
+ INTERVAL (random() * 12_345) MONTHS
)
) _(period);
警告:microseconds 组件仅被拆分为小时、分钟和微秒,而不是小时、分钟、秒和微秒。
下表描述了 datepart 如何根据三个基础单位,在公式中提取这些部分。
| 部分 | 公式 |
|---|---|
年份 |
#months // 12 |
month |
#months % 12 |
day |
#days |
hour |
#microseconds // (60 * 60 * 1_000_000) |
minute |
(#microseconds // (60 * 1_000_000)) % 60 |
microsecond |
#microseconds % (60 * 1_000_000) |
此外,datepart 还可用于从 INTERVAL 中提取世纪、年代、季度、秒和毫秒。但是,在重新组合原始 INTERVAL 时不需要这些部分。事实上,如果前面的查询额外提取了这些部分中的任何一个,那么提取出的部分之和通常会大于原始的 period。
| 部分 | 公式 |
|---|---|
century |
datepart('year', interval) // 100 |
decade |
datepart('year', interval) // 10 |
quarter |
datepart('month', interval) // 3 + 1 |
second |
datepart('microsecond', interval) // 1_000_000 |
millisecond |
datepart('microsecond', interval) // 1_000 |
所有单位均使用从 0 开始的索引,但季度除外,季度使用从 1 开始的索引。
例如
SELECT
datepart('decade', INTERVAL 12 YEARS), -- returns 1
datepart('year', INTERVAL 12 YEARS), -- returns 12
datepart('second', INTERVAL 1_234 MILLISECONDS), -- returns 1
datepart('microsecond', INTERVAL 1_234 MILLISECONDS), -- returns 1_234_000
;
时间戳、日期和间隔的算术运算
可以使用 + 和 - 运算符将 INTERVAL 添加到 TIMESTAMP、TIMESTAMPTZ、DATE 和 TIME,或从中减去。
SELECT
DATE '2000-01-01' + INTERVAL 1 YEAR,
TIMESTAMP '2000-01-01 01:33:30' - INTERVAL '1 month 13 hours',
TIME '02:00:00' - INTERVAL '3 days 23 hours', -- wraps; equals TIME '03:00:00'
;
即使
INTERVAL没有微秒组件,将INTERVAL添加到DATE也会返回一个TIMESTAMP。结果与在添加INTERVAL之前将DATE转换为TIMESTAMP(将时间组件设置为00:00:00)的效果相同。
相反,从另一个 TIMESTAMP 或 TIMESTAMPTZ 中减去一个 TIMESTAMP 或 TIMESTAMPTZ 会创建一个 INTERVAL,描述两个时间戳之间的差异,且仅包含 days 和 microseconds 组件。例如:
SELECT
TIMESTAMP '2000-02-06 12:00:00' - TIMESTAMP '2000-01-01 11:00:00', -- 36 days 1 hour
TIMESTAMP '2000-02-01' + (TIMESTAMP '2000-02-01' - TIMESTAMP '2000-01-01'), -- '2000-03-03', NOT '2000-03-01'
;
从另一个 DATE 中减去一个 DATE 不会创建 INTERVAL,而是返回两个日期之间的天数作为整数值。
警告:提取两个
TIMESTAMP之间INTERVAL差值的一部分,并不等同于计算由datediff函数计算出的两个TIMESTAMP之间对应单位的分区边界数。SELECT datediff('day', TIMESTAMP '2020-01-01 01:00:00', TIMESTAMP '2020-01-02 00:00:00'), -- 1 datepart('day', TIMESTAMP '2020-01-02 00:00:00' - TIMESTAMP '2020-01-01 01:00:00'), -- 0 ;
相等性和比较
仅对于相等性和顺序比较,INTERVAL 中的总微秒数是通过将“天”基础单位转换为 24 * 60 * 60 * 1e6 微秒,并将“月”基础单位转换为 30 天(即 30 * 24 * 60 * 60 * 1e6 微秒)来计算的。
因此,即使 INTERVAL 在功能上不同,它们也可能被比较为相等,并且当 INTERVAL 被添加到日期或时间戳时,其顺序并不总是能够保持。
例如
INTERVAL 30 DAYS = INTERVAL 1 MONTH- 但是
DATE '2020-01-01' + INTERVAL 30 DAYS != DATE '2020-01-01' + INTERVAL 1 MONTH。
以及
INTERVAL '30 days 12 hours' > INTERVAL 1 MONTH- 但是
DATE '2020-01-01' + INTERVAL '30 days 12 hours' < DATE '2020-01-01' + INTERVAL 1 MONTH。
函数
请参阅 日期部分函数页面,获取可与 INTERVAL 一起使用的日期部分列表。
请参阅 间隔运算符页面,获取可对间隔进行操作的函数。