3.4. 日期/时间类型
由 Postgres 提供的日期时间度量有两种基本类型:绝对时钟时间和相对时间区间。两种时间度量都应体现连续性和平滑性。
Postgres
提供两种主要的面向用户的日期时间类型
datetime 和
timespan,以及相关的
SQL92 类型
timestamp、interval、date 和
time。
在未来的版本中,datetime 和
timespan 很可能与
SQL92 类型
timestamp、interval
合并。另外还有一些其他日期时间类型可用,大多出于历史原因。
表 3.7. Postgres 日期/时间类型
| 日期/时间类型 | 存储尺寸 | 建议 | 描述 |
|---|---|---|---|
| abstime | 4字节 | 原始日期和时间 | 范围有限 |
| date | 4字节 | SQL92 类型 | 范围广 |
| datetime | 8字节 | 最佳通用日期和时间 | 范围广、精度高 |
| interval | 12字节 | SQL92 类型 | 等价于 timespan |
| reltime | 4字节 | 原始时间区间 | 范围有限、精度低 |
| time | 4字节 | SQL92 类型 | 范围广 |
| timespan | 12字节 | 最佳通用时间区间 | 范围广、精度高 |
| timestamp | 4字节 | SQL92 类型 | 范围有限 |
timestamp
目前与 datetime
分开实现,不过它们共用输入和输出例程。
表 3.8. Postgres 日期/时间范围
| 日期/时间类型 | 最早 | 最晚 | 精度 |
|---|---|---|---|
| abstime | 1901-12-14 | 2038-01-19 | 1秒 |
| date | 4713 BC | 32767 AD | 1日 |
| datetime | 4713 BC | 1465001 AD | 1微秒到14位 |
| interval | -178000000年 | 178000000年 | 1微秒 |
| reltime | -68年 | +68年 | 1秒 |
| time | 00:00:00.00 | 23:59:59.99 | 1微秒 |
| timespan | -178000000年 | 178000000年 | 1微秒(14位) |
| timestamp | 1901-12-14 | 2038-01-19 | 1秒 |
3.4.1. SQL92 惯例
Postgres 力求在典型用法上与 SQL92 定义兼容。但 SQL92 标准对日期和时间类型及能力有一种奇怪的组合。两个明显的问题是:
尽管
date类型没有关联的时区,time类型却可以或确实有关联的时区。默认时区被指定为相对 GMT/UTC 的固定整数偏移。
现实世界中的时区若不同时关联日期和时间就可能没有意义,因为偏移量在一年中可能随夏令时边界而变化。
为解决这些困难,Postgres 只把时区关联到同时包含日期和时间的日期时间类型,而对只包含日期或只包含时间的类型假定使用本地时间。此外,时区支持源自底层操作系统的时区能力,因此可以处理夏令时和其他预期行为。
在未来的版本中,日期时间类型的数量将会减少:当前
datetime
的实现将变成
timestamp,timespan
变成 interval,而
abstime 和
reltime(可能)被弃用,转而使用
timestamp 和
interval。SQL92
标准中日期时间定义的那些更晦涩的特性不太可能被追求。
3.4.2. 日期/时间样式
输出格式可以设为四种风格之一:ISO-8601、SQL(Ingres)、传统 Postgres 和德式。
表 3.9. Postgres 日期样式
| 样式说明 | 描述 | 示例 |
|---|---|---|
| ISO | ISO-8601 标准 | 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 |
| German | 地区样式 | 17.12.1997 07:37:16.00 PST |
SQL 风格有欧式和非欧式(美式)两种变体,它们决定月跟在日后还是日跟在月前。
表 3.10. Postgres 日期顺序惯例
| 样式说明 | 描述 | 示例 |
|---|---|---|
| European | 地区惯例 | 17/12/1997 15:37:16.00 MET |
| NonEuropean | 地区惯例 | 12/17/1997 07:37:16.00 PST |
| US | 地区惯例 | 12/17/1997 07:37:16.00 PST |
有几种方法可以影响日期/时间类型的外观:
postmaster 启动时后端直接使用的 PGDATESTYLE 环境变量。
会话启动时前端 libpq 使用的 PGDATESTYLE 环境变量。
SET DATESTYLESQL 命令。
对于 Postgres v6.4(及更早版本),默认的日期/时间样式是 "非欧式传统 Postgres"。在未来的版本中,默认值可能变成"ISO"(与 ISO-8601 兼容),它会减轻日期指定的歧义和 Y2K 排序问题。
3.4.3. 历法
Postgres 在所有日期/时间计算中使用儒略日。它们有一个好的特性:能正确预测/计算从公元前 4713 年之后的任何日期到遥远未来的任何日期,其假定是一年的长度为 365.2425 天。
19 世纪之前的日期约定读起来很有意思,但它们并不足够一致,不值得编码进日期/时间处理器。
3.4.4. 时区
对于 1902 到 2038 年之间的日期,Postgres 从底层操作系统获得时区支持(接近 Unix 风格系统的典型日期界限)。在此范围之外,所有日期都假定以协调世界时(UTC)指定和使用。
所有日期和时间在内部都以 UTC(也称为格林尼治标准时间 GMT)存储。时间在发送给客户端前端之前会被转换为数据库服务器上的本地时间,因此默认情况下采用服务器时区。
有几种方法可以影响时区行为:
postmaster 启动时后端直接使用 TZ 环境变量作为默认时区。
在客户端设置的 PGTZ 环境变量由 libpq 用于在连接时向后端发送时区信息。
SQL 命令
SET TIME ZONE设置会话的时区。
如果指定了无效的时区,时区会变成 GMT(至少在大多数系统上如此)。
3.4.5. 日期/时间输入
通用日期时间的输入可采用多种风格,包括 ISO 兼容、SQL 兼容、传统 Postgres 以及日期时间的其他排列组合。在解释可能有歧义的情况下(许多传统日期指定风格很可能如此),Postgres 使用一个风格设置来解决歧义。
大多数日期时间类型共用数据输入代码。对这些类型,输入可以采用多种多样的风格。对于数字日期表示,欧式与美式惯例可能不同,正确的解释要在输入数据之前使用
SET DATESTYLE
命令来获得。注意,风格设置并不妨碍在输入中使用各种风格;它主要用于确定输出风格和解决歧义。
提供了特殊值
current、infinity 和
-infinity。infinity
指定一个晚于任何其他有效时间的时间,而
-infinity
指定一个早于任何其他有效时间的时间。current
表示每当该值出现在计算中时就以当前时间替换。
字符串
now,
today,
yesterday,
tomorrow,
和 epoch
可用于指定时间值。now
表示当前事务时间,它与
current
的区别在于当前时间会被立即替换进去。epoch 表示
Jan 1 00:00:00 1970 GMT。
表 3.11. Postgres 特殊日期/时间常量
| 常量 | 描述 |
|---|---|
| current | 当前事务时间,延迟求值 |
| epoch | 1970-01-01 00:00:00+00(Unix系统时间0) |
| infinity | 晚于其他有效时间 |
| -infinity | 早于其他有效时间 |
| invalid | 非法输入 |
| now | 当前事务时间 |
| today | 今天午夜 |
| tomorrow | 明天午夜 |
| yesterday | 昨天午夜 |
表 3.12. Postgres 日期输入
| 示例 | 描述 |
|---|---|
| January 8, 1999 | 无歧义的文本月份 |
| 1999-01-08 | ISO-8601 |
| 1/8/1999 | 美式;在欧洲模式下读作 8 月 1 日 |
| 8/1/1999 | 欧式;在美式模式下读作 8 月 1 日 |
| 1/18/1999 | 美式;在任何模式下都读作 1 月 18 日 |
| 1999.008 | 年和一年中的日子 |
| 19990108 | ISO-8601 年、月、日 |
| 990108 | ISO-8601 年、月、日 |
| 1999.008 | 年和一年中的日子 |
| 99008 | 年和一年中的日子 |
| January 8, 99 BC | 公元前的 99 年 |
表 3.13. Postgres 月份缩写
| 月份 | 缩写 |
|---|---|
| April | Apr |
| August | Aug |
| December | Dec |
| February | Feb |
| January | Jan |
| July | Jul |
| June | Jun |
| March | Mar |
| November | Nov |
| October | Oct |
| September | Sep, Sept |
注意
出于显而易见的原因,五月(May)没有明确的缩写。
表 3.14. Postgres 星期缩写
| 日 | 缩写 |
|---|---|
| Sunday | Sun |
| Monday | Mon |
| Tuesday | Tue, Tues |
| Wednesday | Wed, Weds |
| Thursday | Thu, Thur, Thurs |
| Friday | Fri |
| Saturday | Sat |
表 3.15. Postgres 时间输入
| 示例 | 描述 |
|---|---|
| 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 |
| z | 和 00:00:00 一样 |
| zulu | 和 00:00:00 一样 |
| allballs | 和 00:00:00 一样 |
表 3.16. Postgres 时区输入
| 时区 | 描述 |
|---|---|
| PST | 太平洋标准时间 |
| -8:00 | PST的ISO-8601偏移 |
| -800 | PST的ISO-8601偏移 |
| -8 | PST的ISO-8601偏移 |
关于 Postgres 可识别的时区细节,参见日期/时间支持 [原文引用目标缺失] [查看原文章节]。
注意
如果设置了编译选项
USE_AUSTRALIAN_RULES,那么
EST 指的是澳大利亚东部标准时间,其偏移为相对
UTC +10:00 小时。
澳大利亚时区及其命名变体占了 Postgres 时区查找表中所有时区的整整四分之一。
3.4.6. datetime
通用日期时间的输入可采用多种风格,包括 ISO 兼容、SQL 兼容、传统 Postgres(见“绝对时间”一节)以及日期时间的其他排列组合。输出风格可以是 ISO 兼容、SQL 兼容或传统 Postgres,默认设置为与 Postgres v6.0 兼容。
datetime 用下列语法指定:
Year-Month-Day [ Hour : Minute : Second ] [AD,BC] [ Timezone ]
YearMonthDay [ Hour : Minute : Second ] [AD,BC] [ Timezone ]
Month Day [ Hour : Minute : Second ] Year [AD,BC] [ Timezone ]
其中
Year(年)为 4013 BC、...、非常大的数
Month(月)为 Jan、Feb、...、Dec 或 1、2、...、12
Day(日)为 1、2、...、31
Hour(时)为 00、02、...、23
Minute(分)为 00、01、...、59
Second(秒)为 00、01、...、59(闰秒为 60)
Timezone(时区)为 3 个字符或相对 GMT 的 ISO 偏移
有效日期从公元前 4013 年 11 月 13 日 00:00:00 GMT 到遥远的未来。时区要么是三个字符(如 "GMT" 或 "PST"),要么是与 GMT 的 ISO 兼容偏移(如太平洋标准时间时为 "-08" 或 "-08:00")。日期在内部以格林尼治标准时间存储。输入和输出例程会在必要时把时间转换为服务器的本地时区。
3.4.7. timespan
通用时间区间的输入可采用多种语法,包括 ISO 兼容、SQL 兼容、传统 Postgres(见“相对时间”一节)以及时间区间的其他排列组合。输出格式可以是 ISO 兼容、SQL 兼容或传统 Postgres,默认设置为与 Postgres 兼容。月和年是“定性”时间区间,与日或小时等其他“定量”时间区间分开存储。在日期运算中,定性时间单位会在相关日期或时间的上下文中实例化。
时间区间用下列语法指定:
Quantity Unit [Quantity Unit...] [Direction]
@ Quantity Unit [Direction]
其中
Quantity(数量)为 ...、-1、0、1、2、...
Unit(单位)为 second、minute、hour、day、week、month、year、
decade、century、millenium,或这些单位的缩写或复数形式。
Direction(方向)为 ago。3.4.8. abstime
绝对时间(abstime)是一种范围有限(±68
年)、精度有限(1
秒)的日期数据类型。可能更宜使用
datetime,因为它覆盖更大的范围且精度更高。
绝对时间用下列语法指定:
Month Day [ Hour : Minute : Second ] Year [ Timezone ]
其中
Month(月)为 Jan、Feb、...、Dec
Day(日)为 1、2、...、31
Hour(时)为 01、02、...、24
Minute(分)为 00、01、...、59
Second(秒)为 00、01、...、59
Year(年)为 1901、1902、...、2038 有效日期从
Dec 13 20:45:53 1901 GMT
到
Jan 19 03:14:04 2038 GMT。
历史注记
从版本 3.0 起,时间的读写不再使用格林尼治标准时间;输入和输出例程默认使用本地时区。
所有允许用于
datetime
的特殊值也同样允许用于“绝对时间”。
3.4.9. reltime
相对时间
reltime 是一种范围有限(±68
年)、精度有限(1
秒)的时间区间数据类型。应优先使用
timespan,因为它覆盖更大的范围、精度更高,而且更重要的是能区分相对单位(月和年)与定量单位(日、小时等)。而
reltime
必须把一个月强制当作恰好
30 天,因此时间运算并不总是按预期工作。例如,把一个
reltime 的
year 加到
abstime 的
today
上,得到的不是从今天起一年的日期,而是从今天起
360 天的日期。
reltime
与其他时间区间类型共用输入和输出例程。timespan
一节对此有更详细的介绍。
3.4.10. timestamp
这是一种目前范围有限的绝对时间,与
abstime
数据类型非常相似。它与其他日期时间类型共用通用输入解析器。在未来的版本中,此类型将吸收
datetime
类型的能力,并向
SQL92 兼容的方向发展。
timestamp 使用与
datetime
相同的语法指定。
3.4.11. interval
interval 是一种 SQL92 数据类型,目前被映射到
timespan Postgres
数据类型。
3.4.12. tinterval
时间范围指定为:
[ 'abstime' 'abstime']
where
abstime 是绝对时间格式中的一个时间。
可以使用诸如
current、infinity 和
-infinity
这样的特殊 abstime 值。