日期和时间数据类型¶
本主题介绍 Snowflake 中支持用于管理日期、时间和时间戳(日期 + 时间组合)的数据类型。此外还介绍了用于操控日期、时间和时间戳的字符串常量支持的格式。
本主题内容:
数据类型¶
DATE¶
Snowflake 支持存储日期(没有时间元素)的单一 DATE 数据类型。
DATE 可接受最常见的日期形式(YYYY-MM-DD
、DD-MON-YYYY
等)。
此外,所有已接受的 TIMESTAMP 值都是日期的有效输入;但是 TIME 信息会被截断。
对于 DATE 和 TIMESTAMP 数据,Snowflake 建议使用 1582 和 9999 之间的年份。Snowflake 可以接受此范围之外的一些年份,但由于 公历限制,应避免使用 1582 年之前的年份。
DATETIME¶
DATETIME 是 TIMESTAMP_NTZ 的别名。
TIME¶
Snowflake 支持以 HH:MI:SS
的形式存储时间的单一 TIME 数据类型。
TIME 支持用于小数秒的可选精度参数,例如 TIME(3)。时间精度的范围为 0(秒)到 9(纳秒)。默认精度为 9。
所有 TIME 值必须介于 00:00:00
和 23:59:59.999999999
之间。TIME 在内部存储“挂钟”时间,对 TIME 值的所有操作都是在完全不考虑时区的前提下执行的。
TIMESTAMP¶
Snowflake 中的 TIMESTAMP 是与一种 TIMESTAMP_* 变体关联的用户指定的别名。在使用 TIMESTAMP 的所有操作中,都会自动使用关联的 TIMESTAMP_* 变体。TIMESTAMP 数据类型从不会存储在表中。
与 TIMESTAMP 关联的 TIMESTAMP_* 变体由 TIMESTAMP_TYPE_MAPPING 会话参数指定。默认为 TIMESTAMP_NTZ。
对于 DATE 和 TIMESTAMP 数据,Snowflake 建议使用 1582 和 9999 之间的年份。Snowflake 可以接受此范围之外的一些年份,但由于 公历限制,应避免使用 1582 年之前的年份。
所有时间戳变体及 TIMESTAMP 别名都支持用于小数秒的可选精度参数,例如 TIMESTAMP(3)。时间戳精度的范围为 0(秒)到 9(纳秒)。默认精度为 9。
TIMESTAMP_LTZ、TIMESTAMP_NTZ、TIMESTAMP_TZ¶
Snowflake 支持三种时间戳变体。
- TIMESTAMP_LTZ:
TIMESTAMP_LTZ 在内部存储具有指定精度的 UTC 时间。但所有操作均在当前会话的时区中执行,由 TIMEZONE 会话参数控制。
TIMESTAMP_LTZ 的别名:
TIMESTAMPLTZ
TIMESTAMP WITH LOCAL TIME ZONE
- TIMESTAMP_NTZ:
TIMESTAMP_NTZ 在内部存储具有指定精度的“挂钟”时间。所有操作的执行均完全不考虑时区。
如果输出格式包含时区,则显示 UTC 指示符 (
Z
)。TIMESTAMP_NTZ 是 TIMESTAMP 的默认值。
TIMESTAMP_NTZ 的别名:
TIMESTAMPNTZ
TIMESTAMP WITHOUT TIME ZONE
- TIMESTAMP_TZ:
TIMESTAMP_TZ 在内部存储 UTC 时间及关联的 时区偏移。如未提供时区,则使用会话时区偏移。所有操作均使用特定于每条记录的时区偏移执行。
TIMESTAMP_TZ 的别名:
TIMESTAMPTZ
TIMESTAMP WITH TIME ZONE
TIMESTAMP_TZ 值根据其 UTC 时间进行比较。例如,不同时区的不同时间如果具有相同的 UTC 时间,则其比较结果为 TRUE。
select '2021-01-01 00:00:00 +0000'::timestamp_tz = '2021-01-01 01:00:00 +0100'::timestamp_tz;
注意
TIMESTAMP_TZ 当前仅存储给定时区的 偏移,而非给定值创建时的实际 时区。在处理夏令时的时候,这一点尤为重要,因为 UTC 不使用夏令时。
例如,如果 TIMEZONE 参数设置为 "America/Los_Angeles"
,则将值转换为给定年份的 1 月的 TIMESTAMP_TZ 会存储 -0800
的时区偏移。如果稍后为该值增加 6 个月,-0800
偏移将保留下来,即使在 7 月份,洛杉矶的该偏移仍为 -0700
。这是因为在创建值后,实际时区信息 ("America/Los_Angeles"
) 不再可用。下面的代码示例展示了此行为:
SELECT '2017-01-01 12:00:00'::TIMESTAMP_TZ; -------------------------------------+ '2017-01-01 12:00:00'::TIMESTAMP_TZ | -------------------------------------+ 2017-01-01 12:00:00 -0800 | -------------------------------------+ SELECT DATEADD(MONTH, 6, '2017-01-01 12:00:00'::TIMESTAMP_TZ); --------------------------------------------------------+ DATEADD(MONTH, 6, '2017-01-01 12:00:00'::TIMESTAMP_TZ) | --------------------------------------------------------+ 2017-07-01 12:00:00 -0800 | --------------------------------------------------------+
时间戳示例¶
使用不同的时间戳创建表:
-- First, use TIMESTAMP (mapped to TIMESTAMP_NTZ) ALTER SESSION SET TIMESTAMP_TYPE_MAPPING = TIMESTAMP_NTZ; CREATE OR REPLACE TABLE ts_test(ts TIMESTAMP); DESC TABLE ts_test; +------+------------------+--------+-------+---------+-------------+------------+-------+------------+---------+ | name | type | kind | null? | default | primary key | unique key | check | expression | comment | |------+------------------+--------+-------+---------+-------------+------------+-------+------------+---------| | TS | TIMESTAMP_NTZ(9) | COLUMN | Y | NULL | N | N | NULL | NULL | NULL | +------+------------------+--------+-------+---------+-------------+------------+-------+------------+---------+ -- Next, explicitly use one of the TIMESTAMP variations (TIMESTAMP_LTZ) CREATE OR REPLACE TABLE ts_test(ts TIMESTAMP_LTZ); DESC TABLE ts_test; +------+------------------+--------+-------+---------+-------------+------------+-------+------------+---------+ | name | type | kind | null? | default | primary key | unique key | check | expression | comment | |------+------------------+--------+-------+---------+-------------+------------+-------+------------+---------| | TS | TIMESTAMP_LTZ(9) | COLUMN | Y | NULL | N | N | NULL | NULL | NULL | +------+------------------+--------+-------+---------+-------------+------------+-------+------------+---------+
为不同时区使用 TIMESTAMP_LTZ:
CREATE OR REPLACE TABLE ts_test(ts timestamp_ltz); ALTER SESSION SET TIMEZONE = 'America/Los_Angeles'; INSERT INTO ts_test values('2014-01-01 16:00:00'); INSERT INTO ts_test values('2014-01-02 16:00:00 +00:00'); -- Note that the time for January 2nd is 08:00 in Los Angeles (which is 16:00 in UTC) SELECT ts, hour(ts) FROM ts_test; +---------------------------------+----------+ | TS | HOUR(TS) | |---------------------------------+----------| | Wed, 01 Jan 2014 16:00:00 -0800 | 16 | | Thu, 02 Jan 2014 08:00:00 -0800 | 8 | +---------------------------------+----------+ -- Next, note that the times change with a different time zone ALTER SESSION SET TIMEZONE = 'America/New_York'; SELECT ts, hour(ts) FROM ts_test; +---------------------------------+----------+ | TS | HOUR(TS) | |---------------------------------+----------| | Wed, 01 Jan 2014 19:00:00 -0500 | 19 | | Thu, 02 Jan 2014 11:00:00 -0500 | 11 | +---------------------------------+----------+
使用 TIMESTAMP_NTZ:
CREATE OR REPLACE TABLE ts_test(ts timestamp_ntz); ALTER SESSION SET TIMEZONE = 'America/Los_Angeles'; INSERT INTO ts_test values('2014-01-01 16:00:00'); INSERT INTO ts_test values('2014-01-02 16:00:00 +00:00'); -- Note that both times from different time zones are converted to the same "wallclock" time SELECT ts, hour(ts) FROM ts_test; +---------------------------+----------+ | TS | HOUR(TS) | |---------------------------+----------| | Wed, 01 Jan 2014 16:00:00 | 16 | | Thu, 02 Jan 2014 16:00:00 | 16 | +---------------------------+----------+ -- Next, note that changing the session time zone does not influence the results ALTER SESSION SET TIMEZONE = 'America/New_York'; SELECT ts, hour(ts) FROM ts_test; +---------------------------+----------+ | TS | HOUR(TS) | |---------------------------+----------| | Wed, 01 Jan 2014 16:00:00 | 16 | | Thu, 02 Jan 2014 16:00:00 | 16 | +---------------------------+----------+
使用 TIMESTAMP_TZ:
CREATE OR REPLACE TABLE ts_test(ts timestamp_tz); ALTER SESSION SET TIMEZONE = 'America/Los_Angeles'; INSERT INTO ts_test values('2014-01-01 16:00:00'); INSERT INTO ts_test values('2014-01-02 16:00:00 +00:00'); -- Note that the January 1st record inherited the session time zone, -- and "America/Los_Angeles" was converted into a numeric time zone offset SELECT ts, hour(ts) FROM ts_test; +---------------------------------+----------+ | TS | HOUR(TS) | |---------------------------------+----------| | Wed, 01 Jan 2014 16:00:00 -0800 | 16 | | Thu, 02 Jan 2014 16:00:00 +0000 | 16 | +---------------------------------+----------+ -- Next, note that changing the session time zone does not influence the results ALTER SESSION SET TIMEZONE = 'America/New_York'; SELECT ts, hour(ts) FROM ts_test; +---------------------------------+----------+ | TS | HOUR(TS) | |---------------------------------+----------| | Wed, 01 Jan 2014 16:00:00 -0800 | 16 | | Thu, 02 Jan 2014 16:00:00 +0000 | 16 | +---------------------------------+----------+
支持的日历¶
Snowflake 为所有日期和时间戳使用公历。公历从 1582 年开始,但可以识别以前的年份,这一点非常重要,因为 Snowflake 不会 为了匹配儒略历而调整 1582 年之前的日期(或涉及 1582 年之前日期的计算)。UUUU
格式元素支持负数年份。
日期和时间格式¶
所有这些数据类型都接受最合理、无歧义的日期、时间或日期 + 时间格式。请参阅 AUTO 检测支持的格式,了解 Snowflake 在 配置为自动检测格式 时可识别的格式。
您也可以 手动指定日期和时间格式。在指定格式时,可使用下表中列出的不区分大小写的元素:
格式元素 |
描述 |
---|---|
|
四位数年份。 |
|
两位数年份,由 TWO_DIGIT_CENTURY_START 会话参数控制,例如,当设置为 |
|
两位数月份(01 = 一月,以此类推)。 |
|
月份名称的全称或缩写。 |
|
月份名称的全称。 |
|
两位数日期(01 至 31)。 |
|
星期的缩写。 |
|
两位数小时(00 至 23)。不能 指定 |
|
两位数小时(01 至 12)。可以指定 |
|
上午 (am)/下午 (pm)。仅可用于 |
|
两位数分钟(00 至 59)。 |
|
两位数秒钟(00 至 59)。 |
|
精度为 0(秒)至 9(纳秒)的小数秒,例如 |
|
时区小时和分钟,从 UTC 开始偏移。可以用 |
|
四位数年份,格式为 ISO (link removed),负数为 BCE 年份。 |
备注
当使用仅日期格式时,关联时间假定为当天的午夜。
格式中双引号之间或上述元素以外的任何内容都会被解析/格式化,但不会被解释。
有关有效范围、位数和最佳实践的更多详细信息,请参阅 有关使用日期、时间和时间戳格式的其他信息。
使用日期和时间格式的示例¶
以下示例使用“FF”指示输出在小数秒字段中应有 9 位数:
CREATE TABLE timestamp_demo_table(t TIMESTAMP, t_tz TIMESTAMP_TZ, t_ntz TIMESTAMP_NTZ, t_ltz TIMESTAMP_LTZ); INSERT INTO timestamp_demo_table (t, t_tz, t_ntz, t_ltz) VALUES ( '2020-03-12 01:02:03.123456789', '2020-03-12 01:02:03.123456789', '2020-03-12 01:02:03.123456789', '2020-03-12 01:02:03.123456789' );ALTER SESSION SET TIMESTAMP_OUTPUT_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'; ALTER SESSION SET TIMESTAMP_TZ_OUTPUT_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'; ALTER SESSION SET TIMESTAMP_NTZ_OUTPUT_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'; ALTER SESSION SET TIMESTAMP_LTZ_OUTPUT_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF';SELECT t, t_tz, t_ntz, t_ltz FROM timestamp_demo_table; +-------------------------------+-------------------------------+-------------------------------+-------------------------------+ | T | T_TZ | T_NTZ | T_LTZ | |-------------------------------+-------------------------------+-------------------------------+-------------------------------| | 2020-03-12 01:02:03.123456789 | 2020-03-12 01:02:03.123456789 | 2020-03-12 01:02:03.123456789 | 2020-03-12 01:02:03.123456789 | +-------------------------------+-------------------------------+-------------------------------+-------------------------------+
日期和时间常量¶
常量*(也称为 *字面量)是指固定的数据值。Snowflake 支持使用字符串常量指定固定的日期、时间或时间戳值。字符串常量必须始终置于分隔符之间。Snowflake 支持使用单引号来分隔字符串常量。
例如:
date '2010-09-14' time '10:03:56' timestamp '2009-09-15 10:59:43'
字符串根据数据类型的输入格式解析为 DATE、TIME 或 TIMESTAMP 值,通过以下参数设置:
- DATE:
- TIME:
- TIMESTAMP:
例如,要将特定日期插入到一个表的某个列中:
CREATE TABLE t1 (d1 DATE); INSERT INTO t1 (d1) VALUES (DATE '2011-10-29');
间隔时间常量¶
您可以使用间隔时间常量在日期、时间或时间戳之间添加或减去特定时间段。间隔时间常量使用 INTERVAL 关键字实现,具有以下语法:
{ + | - } INTERVAL '<integer> [ <date_time_part> ] [ , <integer> [ <date_time_part> ] ... ]'
与所有字符串常量一样,Snowflake 需要用单引号来括起间隔时间常量。
INTERVAL 关键字支持一个以上的整数,以及一个或多个日期或时间部分(可选)。例如:
INTERVAL '1 YEAR'
代表 1 年。INTERVAL '4 years, 5 months, 3 hours'
代表 4 年 5 个月 3 小时。
如果未指定日期或时间部分,则间隔时间表示的是秒(例如,INTERVAL '2 seconds'
与 INTERVAL '2'
相同)。请注意,这与执行日期算术运算的默认时间单位不同。有关更多详细信息,请参阅 :ref:`label-simple_date_arithmetic`(本主题内容)。
有关受支持的日期和时间部分的列表,请参阅 时间间隔支持的日期和时间部分 (本主题内容)。
备注
间隔时间增量的顺序很重要。增量按列出的顺序加减。例如:
INTERVAL '1 year, 1 day'
首先加/减一年,然后加/减一天。INTERVAL '1 day, 1 year'
首先加/减一天,然后加/减一年。
这可能会影响受日历事件(如闰年)影响的计算:
SELECT TO_DATE ('2019-02-28') + INTERVAL '1 day, 1 year'; +---------------------------------------------------+ | TO_DATE ('2019-02-28') + INTERVAL '1 DAY, 1 YEAR' | |---------------------------------------------------| | 2020-03-01 | +---------------------------------------------------+ SELECT TO_DATE ('2019-02-28') + INTERVAL '1 year, 1 day'; +---------------------------------------------------+ | TO_DATE ('2019-02-28') + INTERVAL '1 YEAR, 1 DAY' | |---------------------------------------------------| | 2020-02-29 | +---------------------------------------------------+
INTERVAL 不是数据类型(即您不能将表列定义为 INTERVAL 数据类型)。间隔时间只能用于日期、时间和时间戳算术运算。
间隔时间支持的日期和时间部分¶
INTERVAL 关键字支持将以下日期和时间部分作为实参(不区分大小写):
日期或时间部分 |
缩写/变体 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
间隔时间示例¶
为特定日期加上一个年份间隔时间:
select to_date('2018-04-15') + INTERVAL '1 year'; +-------------------------------------------+ | TO_DATE('2018-04-15') + INTERVAL '1 YEAR' | |-------------------------------------------| | 2019-04-15 | +-------------------------------------------+
为特定时间加上 3 小时 18 分钟的间隔时间:
select to_time('04:15:29') + INTERVAL '3 hours, 18 minutes'; +------------------------------------------------------+ | TO_TIME('04:15:29') + INTERVAL '3 HOURS, 18 MINUTES' | |------------------------------------------------------| | 07:33:29 | +------------------------------------------------------+
在 CURRENT_TIMESTAMP 函数的输出中加上一个复杂的时间间隔:
select current_timestamp + INTERVAL '1 year, 3 quarters, 4 months, 5 weeks, 6 days, 7 minutes, 8 seconds, 1000 milliseconds, 4000000 microseconds, 5000000001 nanoseconds' as complex_interval1; +-------------------------------+ | COMPLEX_INTERVAL | |-------------------------------| | 2020-12-28 08:08:01.325 -0800 | +-------------------------------+
为特定日期加上一个复杂时间间隔(日期/时间部分使用缩写表示法):
select to_date('2025-01-17') + INTERVAL '1 y, 3 q, 4 mm, 5 w, 6 d, 7 h, 9 m, 8 s, 1000 ms, 445343232 us, 898498273498 ns' as complex_interval2; +-------------------------+ | COMPLEX_INTERVAL2 | |-------------------------| | 2027-03-30 07:31:32.841 | +-------------------------+
查询员工信息表,返回过去 2 年 3 个月内录用的员工姓名:
select name, hire_date from employees where hire_date > current_date - INTERVAL '2 y, 3 month';
从名为 tl
的表中筛选出名为 ts
的时间戳列,并为每个返回值加上 4 秒:
select ts + INTERVAL '4 seconds' from t1 where ts > to_timestamp('2014-04-05 01:02:03');
日期的简单算术运算¶
除了使用间隔时间常量为日期、时间和时间戳进行加减之外,Snowflake 还支持以 { + | - } <integer>
的形式对 DATE 值进行天数的基本加减运算,其中 <integer>
指定要加/减的天数。
备注
TIME 和 TIMESTAMP 值尚不支持简单算术运算。
日期算术运算示例¶
为特定日期加 1 天:
select to_date('2018-04-15') + 1; +---------------------------+ | TO_DATE('2018-04-15') + 1 | |---------------------------| | 2018-04-16 | +---------------------------+
为特定日期减 4 天:
select to_date('2018-04-15') - 4; +---------------------------+ | TO_DATE('2018-04-15') - 4 | |---------------------------| | 2018-04-11 | +---------------------------+
查询名为 employees
的表,并返回已离职但受雇时间超过 365 天的人员的姓名:
select name from employees where end_date > start_date + 365;