5.5. 日期/时间类型 #
PostgreSQL 支持全套 SQL 日期和时间类型,如表 5.9所示。
表 5.9. 日期/时间类型
| 类型 | 描述 | 存储 | 最早 | 最晚 | 精度 |
|---|---|---|---|---|---|
timestamp [ ( | 日期和时间 | 8字节 | 4713 BC | AD 1465001 | 1微秒 / 14位 |
timestamp [ ( | 日期和时间 | 8字节 | 4713 BC | AD 1465001 | 1微秒 / 14位 |
interval [ ( | 时间间隔 | 12字节 | -178000000年 | 178000000年 | 1微秒 |
date | 仅日期 | 4字节 | 4713 BC | 32767 AD | 1日 |
time [ ( | 仅一天中的时间 | 8字节 | 00:00:00.00 | 23:59:59.99 | 1微秒 |
time [ ( | 仅一天中的时间 | 12字节 | 00:00:00.00+12 | 23:59:59.99-12 | 1微秒 |
time、timestamp 和 interval
都接受一个可选精度值 p,用来指定在秒字段中保留多少位小数。默认情况下,对精度没有显式上界。对于 timestamp 和 interval 类型,p 的允许范围是 0 到 6。
注意
当 timestamp 值以双精度浮点数存储(目前的默认方式)时,精度的有效上限可能小于
6,因为时间戳值存储为相对
2000-01-01 的秒数。对
2000-01-01 前后几年的日期可以达到微秒精度,但对更远的日期精度会降低。当时间戳以八字节整数存储(一种编译期选项)时,全部取值范围内都有微秒精度。
对于 time 类型,采用八字节整数存储时,p 的允许范围是 0 到 6;采用浮点存储时,允许范围是 0 到 10。
时区及时区惯例受政治决策的影响,而不只是地球几何。世界各地的时区在
20 世纪期间变得多少标准化了一些,但仍容易发生任意的变更。PostgreSQL
使用操作系统的底层特性来提供输出时区支持,而这些系统通常只包含
1902 到 2038 年期间的信息(对应传统
Unix 系统时间的全部范围)。timestamp with time zone 和
time with time
zone 只在该年份范围内使用时区信息,并假定范围外的时间位于
UTC。
time with time zone 类型由
SQL 标准定义,但该定义表现出一些使其用途令人怀疑的特性。在大多数情况下,date、time、timestamp
without time zone 和
timestamp with time
zone 的组合应能提供任何应用所需的完整日期/时间功能。
类型abstime和reltime是内部使用的低精度类型。不推荐在应用中使用这些类型;这些内部类型可能在将来的版本中消失。
5.5.1. 日期/时间输入 #
日期和时间输入几乎可以采用任何合理的格式,包括
ISO 8601、SQL
兼容格式、传统
PostgreSQL 格式等。对于某些格式,日期输入中月和日的顺序可能有歧义,因此支持指定所期望的这些字段的顺序。命令
SET DateStyle TO 'US'
或
SET DateStyle TO 'NonEuropean'
指定“月在前”的变体,命令
SET DateStyle TO 'European'
则设置“日在前”的变体。
PostgreSQL 在处理日期/时间方面比 SQL 标准要求的更灵活。关于日期/时间输入的精确解析规则以及可识别的文本字段(包括月份、星期几和时区),参见附录 A。
请记住,任何日期或时间字面值输入都必须像文本字符串一样用单引号括起来。更多信息请参见第 1.1.2.4 节。SQL 要求使用下列语法:
type[ (p) ] 'value'
其中可选精度说明中的
p
是一个对应于秒字段小数位数的整数。可以为
time、timestamp 和
interval types.
5.5.1.1. 日期
表 5.10显示了date类型可能的输入方式。
表 5.10. 日期输入
| 示例 | 描述 |
|---|---|
| January 8, 1999 | 无歧义 |
| 1999-01-08 | ISO-8601 格式,推荐 |
| 1/8/1999 | 美式;在欧洲模式下读作 8 月 1 日 |
| 8/1/1999 | 欧式;在美式模式下读作 8 月 1 日 |
| 1/18/1999 | 美式;在任何模式下都读作 1 月 18 日 |
| 19990108 | ISO-8601 年、月、日 |
| 990108 | ISO-8601 年、月、日 |
| 1999.008 | 年和一年中的日子 |
| 99008 | 年和一年中的日子 |
| J2451187 | 儒略日 |
| January 8, 99 BC | 公元前99年 |
5.5.1.2. 时间
time 类型可以写作
time 或
time without time zone。可选精度
p 应在 0 到 6
之间,默认取输入时间字面量的精度。
表 5.11列出了有效的
time 输入。
表 5.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 |
allballs | 和 00:00:00 一样 |
time with time zone 类型接受所有对
time 类型合法的输入,再加上一个合法的时区,如表 5.12所示。
表 5.12. 带时区的时间输入
| 示例 | 描述 |
|---|---|
| 04:05:06.789-8 | ISO 8601 |
| 04:05:06-08:00 | ISO 8601 |
| 04:05-08:00 | ISO 8601 |
| 040506-08 | ISO 8601 |
更多时区的示例参见表 5.13。
5.5.1.3. 时间戳
时间戳类型是 timestamp [
( 和
p) ] without time zonetimestamp [ (。单独写 p) ] with time
zonetimestamp 等价于
timestamp without time zone。
注意
在 PostgreSQL 7.3
之前,只写
timestamp 等价于
timestamp with time
zone。为符合 SQL
规范,这一点已被改变。
时间戳类型的有效输入由一个日期和一个时间连接而成,其后可跟可选的
AD 或
BC,再后可跟可选的时区。(参见表 5.13。)因此
1999-01-08 04:05:06
和
1999-01-08 04:05:06 -8:00
都是有效的值,它们遵循 ISO 8601 标准。此外,还支持广泛使用的格式
January 8 04:05:06 1999 PST
也被支持。
可选精度
p 应在 0 到 6
之间,默认取输入
timestamp 字面量的精度。
对于 timestamp without time
zone,输入中指定的任何显式时区都会被静默忽略。也就是说,得到的日期/时间值由输入值中显式的日期/时间字段导出,不随时区调整。
对于 timestamp with time
zone,内部存储的值总是
UTC(GMT)。指定了显式时区的输入值会按该时区的相应偏移转换为
UTC。如果输入串中没有给出时区,则假定它位于系统
TimeZone
参数指示的时区,并按
TimeZone 时区的偏移转换为
UTC。
当输出一个 timestamp with time
zone 值时,它总会从
UTC 转换到当前
TimeZone 时区,并显示为该时区的本地时间。若要查看其他时区的时间,可以修改
TimeZone,或者使用
AT TIME ZONE 构造(见第 6.8.3 节)。
timestamp without time
zone 与 timestamp
with time zone 之间的转换通常假定
timestamp without time
zone 值应按
TimeZone
本地时间对待。可以用
AT TIME ZONE
为转换指定不同的时区参照。
表 5.13. 时区输入
| Time Zone | 描述 |
|---|---|
| PST | 太平洋标准时间 |
| -8:00 | PST的ISO-8601偏移 |
| -800 | PST的ISO-8601偏移 |
| -8 | PST的ISO-8601偏移 |
5.5.1.4. 间隔
interval值可以用下列语法书写:
Quantity Unit [Quantity Unit...] [Direction] @ Quantity Unit [Quantity Unit...] [Direction]
其中:Quantity(数量)是一个数字(可带符号);Unit(单位)是 second、minute、hour、day、week、month、year、decade、century、millennium,或者这些单位的缩写或复数形式;Direction(方向)可以是
ago(之前)或为空。艾特符号(@)是可有可无的噪声。不同单位的量会按适当的符号隐式累加。
天、小时、分钟和秒的数量可以不带显式的单位标记来指定。例如,'1 12:59:10'的读法与
'1 day 12 hours 59 min 10 sec'相同。
可选的精度p应当在 0 到 6 之间,默认为输入字面量的精度。
5.5.1.5. 特殊值
下面的函数与 SQL 兼容,可用作相应数据类型的日期或时间值:CURRENT_DATE、CURRENT_TIME、CURRENT_TIMESTAMP。后两个函数接受可选的精度说明。(另见第 6.8.4 节。)
PostgreSQL 还为方便使用支持几种特殊日期/时间输入值,如表 5.14。值
infinity 和
-infinity
在系统内部有特殊的表示并以相同方式显示;其余的只是记法上的简写,读取时会被转换为普通的日期/时间值。
表 5.14. 特殊日期/时间输入
| 输入串 | 描述 | |
|---|---|---|
epoch | 1970-01-01 00:00:00+00(Unix系统时间0) | |
infinity | 晚于所有其他时间戳(类型
date 不可用) | |
-infinity | 早于所有其他时间戳(类型
date 不可用) | |
now | 当前事务时间 | |
today | 今天午夜 | |
tomorrow | 明天午夜 | |
yesterday | 昨天午夜 | |
zulu, allballs, z | 00:00:00.00 GMT |
5.5.2. 日期/时间输出 #
输出格式可以用
SET DateStyle
设为四种风格之一:ISO 8601、SQL(Ingres)、传统
PostgreSQL 和德式。默认是
ISO 格式。(SQL
标准要求使用 ISO
8601 格式。“SQL” 输出格式这一名称是历史的偶然。)表 5.15给出了每种输出风格的示例。date 和
time 类型的输出当然只是所给示例中相应的日期或时间部分。
表 5.15. 日期/时间输出风格
| 样式说明 | 描述 | 示例 |
|---|---|---|
| ISO | ISO 8601/SQL 标准 | 1997-12-17 07:37:16-08 |
| SQL | 传统样式 | 12/17/1997 07:37:16.00 PST |
| PostgreSQL | 原始样式 | Wed Dec 17 07:37:16 1997 PST |
| German | 地区样式 | 17.12.1997 07:37:16.00 PST |
SQL 风格有欧式和非欧式(美式)两种变体,它们决定月跟在日后还是日跟在月后;反之亦然。(关于此设置如何影响输入值的解释,参见第 5.5.1 节。)表 5.16给出了一个示例。
表 5.16. 日期顺序习惯
| 样式说明 | 描述 | 示例 |
|---|---|---|
| European | day/month/year | 17/12/1997 15:37:16.00 MET |
| US | month/day/year | 12/17/1997 07:37:16.00 PST |
interval 的输出与输入格式相似,区别在于像
week 或
century 这样的单位会被转换为年和天。ISO
模式下输出形如
[ Quantity Units [ ... ] ] [ Days ] Hours:Minutes [ ago ]
用户可以用
SET DATESTYLE 命令、postgresql.conf 配置文件中的
datestyle 参数,以及服务器或客户端上的
PGDATESTYLE 环境变量来选择日期/时间风格。格式化函数
to_char(见第 6.7 节)也可作为一种更灵活的日期/时间输出格式化手段。
5.5.3. 时区 #
PostgreSQL 力求在典型用法上与 SQL 标准定义兼容。但 SQL 标准对日期和时间类型和能力方面有一种奇怪的组合。两个明显的问题是:
尽管
date类型没有关联的时区,time类型却可以有关联的时区。现实世界中的时区若不同时关联日期和时间就可能没有意义,因为偏移量在一年中可能随夏令时边界而变化。默认时区被指定为相对 GMT/UTC 的固定整数偏移。在跨 DST 边界进行日期/时间运算时无法适应夏令时。
为解决这些困难,我们建议在使用时区时选用同时包含日期和时间的日期/时间类型。我们建议不要使用
time with
time zone 类型(尽管为了旧应用以及与其他
SQL
实现的兼容,PostgreSQL
支持它)。对于只包含日期或只包含时间的类型,PostgreSQL
假定使用你的本地时区。此外,时区支持源自底层操作系统的时区能力,因此可以处理夏令时和其他预期行为。
对于 1902 到 2038 年之间的日期,PostgreSQL 从底层操作系统获得时区支持(接近 Unix 风格系统的典型日期界限)。在此范围之外,所有日期都假定以协调世界时(UTC)指定和使用。
所有日期和时间在内部都以 UTC(传统上称为格林尼治标准时间 GMT)存储。时间在发送给客户端前端之前会被转换为数据库服务器上的本地时间,因此默认情况下采用服务器时区。
有几种方法可以选择服务器使用的时区:
如果没有指定其他时区,服务器主机上的
TZ环境变量将被服务器用作默认时区。timezone配置参数可以在postgresql.conf中设置。libpq客户端使用
PGTZ环境变量在连接时向服务器发送SET TIME ZONE命令。SQL 命令
SET TIME ZONE设置会话的时区。
注意
如果指定了无效的时区,时区会变成 UTC(至少在大多数系统上如此)。
有关可用时区的列表,请参见附录 A。
5.5.4. 内部实现 #
PostgreSQL在所有日期/时间计算中使用儒略日。这带来一个有用的特性:在假定一年长度为 365.2425 天的前提下,能够正确计算从公元前 4713 年到遥远未来的日期。
19 世纪之前的日期约定读起来很有意思,但它们并不足够一致,不值得写进日期/时间处理器中。