PostgreSQL 支持 SQL 日期和时间类型的全套类型,如 表 8.9 所示。这些数据类型可用的操作在 第 9.9 节 中进行了描述。日期按照格里高利历进行计算,即使是在该历法引入之前的年份(有关详细信息,请参阅 第 B.6 节)。
表 8.9. 日期/时间类型
名称 | 存储大小 | 说明 | 低值 | 高值 | 分辨率 |
---|---|---|---|---|---|
timestamp [ ( |
8 字节 | 日期和时间(无时区) | 公元前 4713 年 | 公元 294276 年 | 1 微秒 |
timestamp [ ( |
8 字节 | 日期和时间,带时区 | 公元前 4713 年 | 公元 294276 年 | 1 微秒 |
date |
4 字节 | 日期(无时间) | 公元前 4713 年 | 公元 5874897 年 | 1 天 |
时间 [ ( |
8 字节 | 时间(无日期) | 00:00:00 | 24:00:00 | 1 微秒 |
时间 [ ( |
12 字节 | 时间(无日期),含时区 | 00:00:00+1559 | 24:00:00-1559 | 1 微秒 |
间隔 [ |
16 字节 | 时间间隔 | -178000000 年 | 178000000 年 | 1 微秒 |
SQL 标准要求仅编写 timestamp
等效于 timestamp without time zone
,PostgreSQL 遵循此行为。 timestamptz
被接受为 timestamp with time zone
的缩写;这是 PostgreSQL 扩展。
time
、timestamp
和 interval
接受可选精度值 p
,该值指定秒字段中保留的小数位数。默认情况下,精度没有明确的界限。p
的允许范围为 0 到 6。
interval
类型有一个附加选项,即通过编写以下短语之一来限制存储字段的集合
YEAR MONTH DAY HOUR MINUTE SECOND YEAR TO MONTH DAY TO HOUR DAY TO MINUTE DAY TO SECOND HOUR TO MINUTE HOUR TO SECOND MINUTE TO SECOND
请注意,如果同时指定 fields
和 p
,则 fields
必须包括 SECOND
,因为精度仅适用于秒。
类型 time with time zone
由 SQL 标准定义,但该定义表现出导致可疑实用性的属性。在大多数情况下,date
、time
、timestamp without time zone
和 timestamp with time zone
的组合应该提供任何应用程序所需的完整日期/时间功能。
几乎所有合理的格式都可以接受日期和时间输入,包括 ISO 8601、SQL 兼容、传统的 POSTGRES 等。对于某些格式,日期输入中日期、月份和年份的顺序是模棱两可的,并且支持指定这些字段的预期顺序。将 DateStyle 参数设置为 MDY
以选择月-日-年解释,设置为 DMY
以选择日-月-年解释,或设置为 YMD
以选择年-月-日解释。
PostgreSQL 在处理日期/时间输入方面比 SQL 标准要求的更灵活。有关日期/时间输入的确切解析规则以及包括月份、星期几和时区在内的已识别文本字段,请参见 附录 B。
请记住,任何日期或时间文字输入都需要用单引号括起来,就像文本字符串一样。有关更多信息,请参阅 第 4.1.2.7 节。 SQL 要求以下语法
type
[ (p
) ] 'value
'
其中 p
是一个可选精度规范,给出秒字段中的小数位数。精度可以为 time
、timestamp
和 interval
类型指定,并且可以从 0 到 6。如果在常量规范中未指定精度,则默认为文字值的精度(但不得超过 6 位数字)。
表 8.10 显示了 date
类型的某些可能输入。
表 8.10. 日期输入
示例 | 说明 |
---|---|
1999-01-08 | ISO 8601;任何模式下的 1 月 8 日(推荐格式) |
1999 年 1 月 8 日 | 在任何 datestyle 输入模式下都不含糊 |
1/8/1999 | 在 MDY 模式下为 1 月 8 日;在 DMY 模式下为 8 月 1 日 |
1/18/1999 | 在 MDY 模式下为 1 月 18 日;在其他模式下被拒绝 |
01/02/03 | 在 MDY 模式下为 2003 年 1 月 2 日;在 DMY 模式下为 2003 年 2 月 1 日;在 YMD 模式下为 2001 年 2 月 3 日 |
1999-01-08 | 在任何模式下均为 1 月 8 日 |
01-08-1999 | 在任何模式下均为 1 月 8 日 |
08-01-1999 | 在任何模式下均为 1 月 8 日 |
99-01-08 | 在 YMD 模式下为 1 月 8 日,否则报错 |
08-01-99 | 1 月 8 日,但 YMD 模式下会报错 |
01-08-99 | 1 月 8 日,但 YMD 模式下会报错 |
19990108 | ISO 8601;在任何模式下均为 1999 年 1 月 8 日 |
990108 | ISO 8601;在任何模式下均为 1999 年 1 月 8 日 |
1999.008 | 年份和一年中的天数 |
J2451187 | 儒略日 |
公元前 99 年 1 月 8 日 | 公元前 99 年 |
时间类型为 time [ (
和 p
) ] without time zonetime [ (
。单独的 p
) ] with time zonetime
等同于 time without time zone
。
这些类型的有效输入包括一天中的时间,后跟一个可选时区。(请参见 表 8.11 和 表 8.12。)如果在 time without time zone
的输入中指定了时区,则会自动忽略它。您还可以指定日期,但它将被忽略,但当您使用涉及夏令时规则的时区名称(例如 America/New_York
)时除外。在这种情况下,需要指定日期以确定适用标准时间还是夏令时。适当的时区偏移记录在 time with time zone
值中,并按存储方式输出;不会调整为活动时区。
表 8.11. 时间输入
示例 | 说明 |
---|---|
04:05:06.789 |
ISO 8601 |
04:05:06 |
ISO 8601 |
04:05 |
ISO 8601 |
040506 |
ISO 8601 |
04:05 AM |
与 04:05 相同;AM 不影响值 |
04:05 PM |
与 16:05 相同;输入小时数必须 <= 12 |
04:05:06.789-8 |
ISO 8601,时区为 UTC 偏移 |
04:05:06-08:00 |
ISO 8601,时区为 UTC 偏移 |
04:05-08:00 |
ISO 8601,时区为 UTC 偏移 |
040506-08 |
ISO 8601,时区为 UTC 偏移 |
040506+0730 |
ISO 8601,时区为 UTC 偏移的小时数 |
040506+07:30:00 |
UTC 偏移指定为秒(ISO 8601 中不允许) |
04:05:06 PST |
由缩写指定的时区 |
2003-04-12 04:05:06 America/New_York |
由全名指定的时区 |
表 8.12. 时区输入
示例 | 说明 |
---|---|
PST |
缩写(太平洋标准时间) |
America/New_York |
完整时区名称 |
PST8PDT |
POSIX 风格时区规范 |
-8:00:00 |
PST 的 UTC 偏移量 |
-8:00 |
PST 的 UTC 偏移量(ISO 8601 扩展格式) |
-800 |
PST 的 UTC 偏移量(ISO 8601 基本格式) |
-8 |
PST 的 UTC 偏移量(ISO 8601 基本格式) |
zulu |
UTC 的军事缩写 |
z |
zulu 的简写(也用于 ISO 8601 中) |
请参阅第 8.5.3 节以获取有关如何指定时区的更多信息。
时间戳类型的有效输入由日期和时间连接而成,后跟一个可选时区,再后跟一个可选的 AD
或 BC
。(或者,AD
/BC
可以出现在时区之前,但这并不是首选顺序。)因此
1999-01-08 04:05:06
和
1999-01-08 04:05:06 -8:00
是有效值,它们遵循ISO 8601 标准。此外,还支持常见格式
January 8 04:05:06 1999 PST
。
SQL 标准通过时间后的 “+” 或 “-” 符号和时区偏移量来区分 timestamp without time zone
和 timestamp with time zone
字面量。因此,根据该标准,
TIMESTAMP '2004-10-19 10:23:54'
是 timestamp without time zone
,而
TIMESTAMP '2004-10-19 10:23:54+02'
是 timestamp with time zone
。在确定其类型之前,PostgreSQL 绝不会检查字面量字符串的内容,因此会将上述两者都视为 timestamp without time zone
。要确保将字面量视为 timestamp with time zone
,请为其指定正确的显式类型
TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'
在已确定为 timestamp without time zone
的字面量中,PostgreSQL 将静默忽略任何时区指示。也就是说,结果值是从输入值中的日期/时间字段派生的,并且不会针对时区进行调整。
对于 timestamp with time zone
,内部存储的值始终为 UTC(世界协调时间,传统上称为格林尼治标准时间,GMT)。具有指定显式时区的输入值将使用该时区的适当偏移量转换为 UTC。如果输入字符串中未指定时区,则假定它位于系统TimeZone 参数指示的时区中,并使用 timezone
时区的偏移量转换为 UTC。
输出 timestamp with time zone
值时,它总是从 UTC 转换为当前 timezone
时区,并以该时区的本地时间显示。要查看另一个时区的时间,请更改 timezone
或使用 AT TIME ZONE
结构(请参阅 第 9.9.4 节)。
timestamp without time zone
和 timestamp with time zone
之间的转换通常假设 timestamp without time zone
值应采用或给出为 timezone
本地时间。可以使用 AT TIME ZONE
为转换指定不同的时区。
PostgreSQL 支持多种特殊日期/时间输入值以方便使用,如 表 8.13 所示。值 infinity
和 -infinity
在系统内部以特殊方式表示,并且将保持不变地显示;但其他值只是简单的符号缩写,在读取时将转换为普通日期/时间值。(特别是,now
和相关字符串在读取时将转换为特定时间值。)所有这些值在作为 SQL 命令中的常量使用时需要用单引号引起来。
表 8.13. 特殊日期/时间输入
输入字符串 | 有效类型 | 说明 |
---|---|---|
epoch |
date 、timestamp |
1970-01-01 00:00:00+00(Unix 系统时间零) |
infinity |
date 、timestamp |
晚于所有其他时间戳 |
-infinity |
date 、timestamp |
早于所有其他时间戳 |
now |
date 、time 、timestamp |
当前事务的开始时间 |
today |
date 、timestamp |
今天的午夜(00:00 ) |
tomorrow |
date 、timestamp |
明天的午夜(00:00 ) |
yesterday |
date 、timestamp |
昨天的午夜(00:00 ) |
allballs |
time |
00:00:00.00 UTC |
以下 SQL 兼容函数也可用于获取相应数据类型的当前时间值:CURRENT_DATE
、CURRENT_TIME
、CURRENT_TIMESTAMP
、LOCALTIME
、LOCALTIMESTAMP
。(请参阅 第 9.9.5 节。)请注意,这些是 SQL 函数,在数据输入字符串中 不 识别。
虽然输入字符串 now
、today
、tomorrow
和 yesterday
在交互式 SQL 命令中使用时很好,但在将命令保存为稍后执行时,例如在预处理语句、视图和函数定义中,它们可能会产生令人惊讶的行为。该字符串可以转换为特定时间值,并且在它变得陈旧后很长时间内继续使用。在这种情况下,请改用其中一个 SQL 函数。例如,CURRENT_DATE + 1
比 'tomorrow'::date
更安全。
日期/时间类型的输出格式可以设置为四种样式之一:ISO 8601、SQL(Ingres)、传统 POSTGRES(Unix date 格式)或德语。默认格式为 ISO 格式。(SQL 标准要求使用 ISO 8601 格式。““SQL”” 输出格式的名称是一个历史意外。)表 8.14 显示了每种输出样式的示例。date
和 time
类型的输出通常仅为日期或时间部分,符合给定的示例。但是,POSTGRES 样式以 ISO 格式输出仅日期值。
表 8.14. 日期/时间输出样式
样式规范 | 说明 | 示例 |
---|---|---|
ISO |
ISO 8601,SQL 标准 | 1997-12-17 07:37:16-08 |
SQL |
传统样式 | 12/17/1997 07:37:16.00 PST |
Postgres |
原始样式 | Wed Dec 17 07:37:16 1997 PST |
德语 |
区域样式 | 17.12.1997 07:37:16.00 PST |
ISO 8601 规定使用大写字母 T
来分隔日期和时间。PostgreSQL 在输入时接受该格式,但在输出时它使用空格而不是 T
,如上所示。这是为了便于阅读,并且与 RFC 3339 以及其他一些数据库系统保持一致。
在 SQL 和 POSTGRES 样式中,如果指定了 DMY 字段顺序,则日出现在月之前;否则,月出现在日之前。(请参阅 第 8.5.1 节,了解此设置如何影响输入值的解释。)表 8.15 显示了示例。
表 8.15. 日期顺序约定
datestyle 设置 |
输入顺序 | 示例输出 |
---|---|---|
SQL,DMY |
day /month /year |
17/12/1997 15:37:16.00 CET |
SQL,MDY |
month /day /year |
12/17/1997 07:37:16.00 PST |
Postgres,DMY |
day /month /year |
Wed 17 Dec 07:37:16 1997 PST |
在 ISO 样式中,时区始终显示为与 UTC 的带符号数字偏移量,对于格林尼治以东的时区使用正号。如果偏移量是整数小时数,则显示为 hh
(仅小时);否则,如果偏移量是整数分钟数,则显示为 hh
:mm
;否则,显示为 hh
:mm
:ss
。(第三种情况在任何现代时区标准中都不可能出现,但它可能出现在处理早于采用标准化时区的的时间戳时。)在其他日期样式中,如果时区在当前时区中常用,则时区显示为字母缩写。否则,它以 ISO 8601 基本格式(hh
或 hhmm
)显示为带符号数字偏移量。
用户可以使用 SET datestyle
命令、postgresql.conf
配置文件中的 DateStyle 参数或服务器或客户端上的 PGDATESTYLE
环境变量来选择日期/时间样式。
格式化函数 to_char
(参见 第 9.8 节)也可作为一种更灵活的方式来格式化日期/时间输出。
时区和时区惯例受政治决策影响,而不仅仅是地球几何形状。世界各地的时区在 20 世纪得到一定程度的标准化,但仍然容易发生任意更改,尤其是在夏令时规则方面。PostgreSQL 使用广泛使用的 IANA(Olson)时区数据库来获取有关历史时区规则的信息。对于未来的时间,假设给定时区的最新已知规则将在无限远的未来继续被遵守。
PostgreSQL 努力与 SQL 标准定义兼容,以供典型使用。但是,SQL 标准对日期和时间类型和功能有着奇怪的混合。两个明显的问题是
虽然 date
类型不能有相关的时区,但 time
类型可以。现实世界中的时区如果没有与日期和时间相关联,几乎没有意义,因为偏移量可能会随着夏令时边界而全年变化。
默认时区指定为从 UTC 的恒定数字偏移量。因此,在跨 DST 边界进行日期/时间算术时,不可能适应夏令时。
为了解决这些困难,我们建议在使用时区时使用同时包含日期和时间的日期/时间类型。我们不建议使用类型 time with time zone
(尽管 PostgreSQL 为旧版应用程序和符合 SQL 标准而支持它)。PostgreSQL 假设您仅包含日期或时间的任何类型的本地时区。
所有与时区相关联的日期和时间都以 UTC 内部存储。在显示给客户端之前,它们会根据 TimeZone 配置参数指定的区域转换为本地时间。
PostgreSQL 允许您以三种不同的形式指定时区
一个完整的时区名称,例如 America/New_York
。已识别的时区名称列在 pg_timezone_names
视图中(参见 第 54.32 节)。PostgreSQL 为此使用广泛使用的 IANA 时区数据,因此其他软件也识别相同的时区名称。
时区缩写,例如 PST
。这种规范仅定义了与 UTC 的特定偏移量,而完整的时区名称还可以暗示一组夏令时转换规则。已识别的缩写列在 pg_timezone_abbrevs
视图中(参见 第 54.31 节)。您无法将配置参数 TimeZone 或 log_timezone 设置为时区缩写,但您可以在日期/时间输入值中和使用 AT TIME ZONE
运算符时使用缩写。
除了时区名称和缩写之外,PostgreSQL 还将接受 POSIX 样式时区规范,如 第 B.5 节 中所述。此选项通常不如使用命名时区好,但如果没有任何合适的 IANA 时区条目,则可能需要此选项。
简而言之,缩写和全名之间的区别在于:缩写表示与 UTC 的特定偏移量,而许多全名暗示了当地的夏令时规则,因此可能有两种可能的 UTC 偏移量。例如,2014-06-04 12:00 America/New_York
表示纽约的当地中午时间,对于这个特定日期,它是美国东部夏令时 (UTC-4)。因此,2014-06-04 12:00 EDT
指定了同一时间点。但 2014-06-04 12:00 EST
指定了美国东部标准时间 (UTC-5),无论该日期是否名义上实施了夏令时。
复杂之处在于,一些司法管辖区在不同的时间使用相同的时区缩写来表示不同的 UTC 偏移量;例如,在莫斯科,MSK
在某些年份表示 UTC+3,而在其他年份表示 UTC+4。 PostgreSQL 根据它们在指定日期的含义(或最近的含义)来解释此类缩写;但是,与上面的 EST
示例一样,这不一定与该日期的当地民用时间相同。
在所有情况下,时区名称和缩写都以不区分大小写的方式识别。(这是对 8.2 之前的 PostgreSQL 版本的更改,在某些情况下区分大小写,而在其他情况下不区分大小写。)
时区名称和缩写都没有硬编码到服务器中;它们是从安装目录的 .../share/timezone/
和 .../share/timezonesets/
下存储的配置文件中获取的(请参见 第 B.4 节)。
可以在文件 postgresql.conf
中设置 TimeZone 配置参数,也可以在 第 20 章 中描述的任何其他标准方式中设置。还有一些特殊的方法可以设置它
SQL 命令 SET TIME ZONE
为会话设置时区。这是 SET TIMEZONE TO
的替代拼写,具有更兼容 SQL 规范的语法。
环境变量 PGTZ
由 libpq 客户端使用,以便在连接时向服务器发送 SET TIME ZONE
命令。
interval
值可以使用以下详细语法编写
[@]quantity
unit
[quantity
unit
...] [direction
]
其中 quantity
是一个数字(可能带符号);unit
是 microsecond
、millisecond
、second
、minute
、hour
、day
、week
、month
、year
、decade
、century
、millennium
或这些单位的缩写或复数;direction
可以是 ago
或空。at 符号 (@
) 是可选噪音。不同单位的数量会隐式相加,并进行适当的符号计算。 ago
会对所有字段取反。如果将 IntervalStyle 设置为 postgres_verbose
,此语法还用于间隔输出。
天、小时、分钟和秒的数量可以在没有明确单位标记的情况下指定。例如,'1 12:59:10'
与 '1 day 12 hours 59 min 10 sec'
的读法相同。此外,可以使用连字符指定年和月的组合;例如,'200-10'
与 '200 years 10 months'
的读法相同。(实际上,这些较短的格式是 SQL 标准允许的唯一格式,并且在将 IntervalStyle
设置为 sql_standard
时用于输出。)
间隔值还可以使用 ISO 8601 时间间隔编写,可以使用该标准的第 4.4.3.2 节中的 “带指示符的格式” 或第 4.4.3.3 节中的 “备用格式”。带指示符的格式如下所示
Pquantity
unit
[quantity
unit
...] [ T [quantity
unit
...]]
字符串必须以 P
开头,并且可以包含 T
,它会引入时间单位。可在 表 8.16 中找到可用的单位缩写。可以省略单位,并且可以按任何顺序指定单位,但小于一天的单位必须出现在 T
之后。特别是,M
的含义取决于它是在 T
之前还是之后。
表 8.16. ISO 8601 间隔单位缩写
缩写 | 含义 |
---|---|
Y | 年 |
M | 月份(在日期部分) |
W | 周 |
D | 天 |
H | 小时 |
M | 分钟(在时间部分) |
S | 秒 |
在备用格式中
P [years
-months
-days
] [ Thours
:minutes
:seconds
]
字符串必须以 P
开头,并且 T
分隔间隔的日期和时间部分。值以类似于 ISO 8601 日期的方式给出数字。
使用 fields
规范编写间隔常量,或将字符串分配给使用 fields
规范定义的间隔列时,未标记数量的解释取决于 fields
。例如,INTERVAL '1' YEAR
被解读为 1 年,而 INTERVAL '1'
表示 1 秒。此外,fields
规范允许的最小有效字段“右侧”的字段值将被静默丢弃。例如,编写 INTERVAL '1 day 2:03:04' HOUR TO MINUTE
将导致丢弃秒字段,但不丢弃天字段。
根据 SQL 标准,间隔值的所有字段必须具有相同的符号,因此前导负号适用于所有字段;例如,间隔文字 '-1 2:03:04'
中的负号适用于天和小时/分钟/秒部分。PostgreSQL 允许字段具有不同的符号,并且传统上将文本表示中的每个字段视为独立签名,因此在此示例中小时/分钟/秒部分被视为正数。如果 IntervalStyle
设置为 sql_standard
,则前导符号被认为适用于所有字段(但仅在没有其他符号出现时)。否则,将使用传统的 PostgreSQL 解释。为避免歧义,建议为每个字段附加显式符号(如果任何字段为负数)。
字段值可以有小数部分:例如,'1.5 weeks'
或 '01:02:03.45'
。但是,由于间隔在内部仅存储三个整数单位(月、天、微秒),因此必须将分数单位溢出到较小的单位。大于月份的单位的分数部分将被四舍五入为月份的整数,例如 '1.5 years'
将变为 '1 year 6 mons'
。周和天的分数部分将被计算为天的整数和微秒,假设每月 30 天,每天 24 小时,例如,'1.75 months'
将变为 1 mon 22 days 12:00:00
。只有秒会在输出中显示为分数。
表 8.17 显示了一些有效的 interval
输入示例。
表 8.17。间隔输入
示例 | 说明 |
---|---|
1-2 |
SQL 标准格式:1 年 2 个月 |
3 4:05:06 |
SQL 标准格式:3 天 4 小时 5 分钟 6 秒 |
1 年 2 个月 3 天 4 小时 5 分钟 6 秒 |
传统 Postgres 格式:1 年 2 个月 3 天 4 小时 5 分钟 6 秒 |
P1Y2M3DT4H5M6S |
ISO 8601 “带设计器的格式”:与上述含义相同 |
P0001-02-03T04:05:06 |
ISO 8601 “备用格式”:与上述含义相同 |
在内部,interval
值存储为月、日和微秒。这样做是因为一个月中的天数是可变的,如果涉及夏令时调整,一天可能有 23 或 25 小时。月和日字段为整数,而微秒字段可以存储小数秒。由于间隔通常从常量字符串或timestamp
减法创建,因此这种存储方法在大多数情况下都能正常工作,但可能会导致意外结果
SELECT EXTRACT(hours from '80 minutes'::interval); date_part ----------- 1 SELECT EXTRACT(days from '80 hours'::interval); date_part ----------- 0
函数 justify_days
和 justify_hours
可用于调整超出其正常范围的天数和小时数。
间隔类型的输出格式可以设置为四种样式之一 sql_standard
、postgres
、postgres_verbose
或 iso_8601
,使用命令 SET intervalstyle
。默认值为 postgres
格式。表 8.18 显示了每种输出样式的示例。
如果间隔值符合标准的限制(仅年-月或仅日-时,没有正负分量的混合),sql_standard
样式会生成符合 SQL 标准间隔文字字符串规范的输出。否则,输出看起来像一个标准的年-月文字字符串,后跟一个日-时文字字符串,并添加显式符号以消除符号混合间隔的歧义。
当 DateStyle 参数设置为 ISO
时,postgres
样式的输出与 8.4 之前的 PostgreSQL 版本的输出相匹配。
当 DateStyle
参数设置为非 ISO
输出时,postgres_verbose
样式的输出与 8.4 之前的 PostgreSQL 版本的输出相匹配。
iso_8601
样式的输出与 ISO 8601 标准第 4.4.3.2 节中描述的““带设计器的格式””相匹配。
表 8.18. 间隔输出样式示例
样式规范 | 年-月间隔 | 日-时间间隔 | 混合间隔 |
---|---|---|---|
sql_standard |
1-2 | 3 4:05:06 | -1-2 +3 -4:05:06 |
postgres |
1 年 2 个月 | 3 天 04:05:06 | -1 年 -2 个月 +3 天 -04:05:06 |
postgres_verbose |
@ 1 年 2 个月 | @ 3 天 4 小时 5 分钟 6 秒 | @ 1 年 2 个月 -3 天 4 小时 5 分钟 6 秒前 |
iso_8601 |
P1Y2M | P3DT4H5M6S | P-1Y-2M3DT-4H-5M-6S |