↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1
历史版本PostgreSQL 8.3 已于 2013 年 2 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

8.5. 日期/时间类型 #

PostgreSQL支持完整的一组SQL日期和时间类型,如表 8.9所示。这些数据类型上可用的操作见第 9.9 节。

表 8.9. 日期/时间类型

名字 存储大小 描述 低值 高值 精度
timestamp [ (p) ] [ without time zone ] 8字节 日期和时间 4713 BC 5874897 AD 1微秒 / 14位
timestamp [ (p) ] with time zone 8字节 日期和时间,带时区 4713 BC 5874897 AD 1微秒 / 14位
interval [ (p) ] 12字节 时间间隔 -178000000年 178000000年 1微秒 / 14位
date 4字节 仅日期 4713 BC 5874897 AD 1日
time [ (p) ] [ without time zone ] 8字节 仅一天中的时间 00:00:00 24:00:00 1微秒 / 14位
time [ (p) ] with time zone 12字节 仅一天中的时间,带时区 00:00:00+1459 24:00:00-1459 1微秒 / 14位

注意

在 PostgreSQL 7.3 之前的版本中,仅写timestamp等效于timestamp with time zone。为了符合 SQL 标准,这一行为已被改变。

time、timestamp 和 interval 都接受一个可选精度值 p,用来指定在秒字段中 保留多少位小数。默认情况下,对精度没有显式上界。 对于 timestamp 和 interval 类型, p 的允许范围是 0 到 6。

注意

当 timestamp 值存储为双精度浮点数(目前的默认方式)时, 有效精度上界可能小于 6。timestamp 值存储为相对于 2000-01-01 午夜之前或之后的秒数。对于距 2000-01-01 几年之内的 日期可以达到微秒精度,但对更远的日期精度会下降。而当 timestamp 值存储为八字节整数(一个编译期选项)时, 在整个取值范围内都可以得到微秒精度。不过,八字节整数时间戳的 日期范围比上表所列更有限:从公元前 4713 年到公元 294276 年。 同一个编译期选项还决定 time 和 interval 值 存储为浮点数还是八字节整数。在浮点存储的情况下,大的 interval 值会随着间隔长度的增大而降低精度。

对于 time 类型,采用八字节整数存储时, p 的允许范围是 0 到 6;采用浮点存储时, 允许范围是 0 到 10。

time with time zone 类型由 SQL 标准定义,但其定义具有 一些会让人怀疑其实用性的特性。在大多数情况下, date、time、 timestamp without time zone 和 timestamp with time zone 的组合,就足以提供任何应用 所需的完整日期/时间功能。

类型abstime和reltime是内部使用的低精度类型。不推荐在应用中使用这些类型;这些内部类型可能在将来的版本中消失。

8.5.1. 日期/时间输入 #

日期和时间输入几乎接受任何合理的格式,包括 ISO 8601、 与 SQL 兼容的格式、传统 POSTGRES 格式等。对于某些格式, 日期输入中日、月、年的顺序可能存在歧义,因此支持指定这些字段的 预期顺序。将 DateStyle 参数设置为 MDY,表示采用月-日-年解释; 设置为 DMY 表示采用日-月-年解释; 而 YMD 表示采用年-月-日解释。

PostgreSQL 在处理日期/时间输入方面, 比 SQL 标准要求的更灵活。有关日期/时间输入的 精确解析规则,以及可识别的文本字段(包括月份、星期几和时区), 请参见 附录 B。

请记住,任何日期或时间字面值输入都必须像文本字符串一样用单引号 括起来。更多信息请参见 第 4.1.2.5 节。 SQL 要求使用下列语法:

type [ (p) ] 'value'

其中 p 是可选的精度说明,给出秒字段中 保留的小数位数。精度可用于 time、 timestamp 和 interval 类型, 允许的取值范围见上文。如果在常量声明中没有指定 精度,则默认采用该字面值本身的精度。

8.5.1.1. 日期

表 8.10显示了date类型可能的输入方式。

表 8.10. 日期输入

示例 描述
1999-01-08 ISO 8601;任何模式下的1月8日 (推荐格式)
January 8, 1999 在任何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-Jan-08 任何模式下的1月8日
Jan-08-1999 任何模式下的1月8日
08-Jan-1999 任何模式下的1月8日
99-Jan-08 YMD模式中的1月8日,否则错误
08-Jan-99 1月8日,除了在YMD模式中错误
Jan-08-99 1月8日,除了在YMD模式中错误
19990108 ISO 8601;任何模式中的1999年1月8日
990108 ISO 8601;任何模式中的1999年1月8日
1999.008 年和一年中的日子
J2451187 儒略日
January 8, 99 BC 公元前99年

8.5.1.2. 时间

一天中的时间类型包括 time [ (p) ] without time zone 和 time [ (p) ] with time zone。 单独写 time 等效于 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
04:05:06-08:00 ISO 8601
04:05-08:00 ISO 8601
040506-08 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 PST的ISO-8601偏移
-800 PST的ISO-8601偏移
-8 PST的ISO-8601偏移
zulu UTC的军方缩写
z zulu的缩写形式

关于如何指定时区,参见 第 8.5.3 节。

8.5.1.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.3 节)。

在 timestamp without time zone 和 timestamp with time zone 之间转换时,通常假定 timestamp without time zone 值应被解释为,或输出为, timezone 本地时间。要为该转换指定不同的时区, 可以使用 AT TIME ZONE。

8.5.1.4. 间隔

interval值可以用下列语法书写:

[@] quantity unit [quantity unit...] [direction]

其中:quantity是一个数字(可带符号); unit是microsecond、 millisecond、second、 minute、hour、day、 week、month、year、 decade、century、millennium, 或者这些单位的缩写或复数形式; direction可以是ago或为空。 at 符号(@)是可选的噪音。不同单位的数量会带 着适当的符号被隐式地累加起来。

天、小时、分钟和秒的数量可以不带显式的单位标记来指定。 例如,'1 12:59:10'的读法与 '1 day 12 hours 59 min 10 sec'相同。

可选的亚秒精度p应当在 0 到 6 之间, 默认为输入字面量的精度。

在内部,interval值以月、天和秒的形式存储。这样做 是因为一个月的天数不定,而且在涉及夏令时调整时一天可能 有 23 或 25 个小时。由于间隔通常由常量字符串或 timestamp相减创建,这种存储方法在大多数情况下都能 良好工作。可以使用函数justify_days和 justify_hours来调整超出其正常周期的天和小时。

8.5.1.5. 特殊值

为了方便起见,PostgreSQL 支持若干特殊的 日期/时间输入值,如 表 8.13 所示。infinity 和 -infinity 在系统内部有特殊表示,并且输出时会保持不变;其余值则只是记法上的 简写,在读取时会被转换成普通日期/时间值。(特别是, now 及相关字符串在被读取后会立刻转换成某个 特定的时间值。)这些值在 SQL 命令中作为常量使用时,都必须用 单引号括起来。

表 8.13. 特殊日期/时间输入

输入字符串 有效类型 描述
epoch date, timestamp 1970-01-01 00:00:00+00(Unix系统时间0)
infinity timestamp 晚于所有其他时间戳
-infinity timestamp 早于所有其他时间戳
now date, time, timestamp 当前事务的开始时间
today date、timestamp 今天午夜
tomorrow date、timestamp 明天午夜
yesterday date、timestamp 昨天午夜
allballs time 00:00:00.00 UTC

以下与 SQL 兼容的函数也可用于获取相应数据 类型的当前时间值:CURRENT_DATE、 CURRENT_TIME、 CURRENT_TIMESTAMP、LOCALTIME 和 LOCALTIMESTAMP。后四个函数接受可选的亚秒级 精度说明。(参见 第 9.9.4 节。)请注意,这些是 SQL 函数,不会在日期/时间输入字符串中被识别。

8.5.2. 日期/时间输出 #

日期/时间类型的输出格式可以设为四种样式之一:ISO 8601、SQL(Ingres)、传统的 POSTGRES 和 German,这通过命令SET datestyle设置。默认是ISO格式。(SQL标准要求使用 ISO 8601 格式。之所以存在名为“SQL”的输出格式,只是历史原因。)表 8.14展示了各种输出样式的示例。date和time类型的输出当然只包含给定示例中相应的日期部分或时间部分。

表 8.14. 日期/时间输出风格

样式说明 描述 示例
ISO ISO 8601/SQL 标准 1997-12-17 07:37:16-08
SQL 传统样式 12/17/1997 07:37:16.00 PST
POSTGRES original style Wed Dec 17 07:37:16 1997 PST
German 地区样式 17.12.1997 07:37:16.00 PST

SQL和POSTGRES风格中,如果指定了 DMY 字段顺序,“日”将出现在“月”之前,否则“月”出现在“日”之前(有关该设置如何影响输入值的解释,请参考第 8.5.1 节)。表 8.15给出了示例。

表 8.15. 日期顺序习惯

datestyle Setting 输入顺序 输出示例
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

interval的输出与输入格式类似,但诸如 century或week这样的单位会被 转换为年和天,而ago会被转换为适当的符号。 在 ISO 模式下,输出形如:

[ quantity unit [ ... ] ] [ days ] [ hours:minutes:seconds ]

用户可以通过SET datestyle命令、postgresql.conf配置文件中的DateStyle参数,或者服务器端或客户端上的PGDATESTYLE环境变量来选择日期/时间样式。格式化函数to_char(见第 9.8 节)也可用,它是格式化日期/时间输出的一种更灵活的方式。

8.5.3. 时区 #

时区及其约定不仅受地球几何形状影响,也受政治决定影响。世界各地的 时区在 20 世纪逐渐趋于标准化,但仍然容易发生任意变化,尤其是夏令时 规则方面。PostgreSQL目前支持的夏令时 规则覆盖 1902 到 2038 年这段时间(对应传统 Unix 系统时间的完整 范围)。超出该范围的时间将被视为处于所选时区的“标准时间”, 无论它们处于一年中的哪个时段。

PostgreSQL 努力在典型用法上与 SQL 标准定义保持兼容。不过, SQL 标准在日期和时间类型及其能力方面存在一些 奇怪的混搭。两个显而易见的问题是:

  • 尽管 date 类型不能有关联的时区, time 类型却可以。但现实世界中的时区如果不同时关联 日期和时间,几乎没有意义,因为偏移量可能会随着夏令时切换而在 一年中发生变化。

  • 默认时区被指定为相对于 UTC 的一个固定数值 偏移。因此,在跨越 DST 边界做日期/时间 算术时,根本无法适应夏令时变化。

为了克服这些困难,我们建议在使用时区时采用同时包含日期和时间的 日期/时间类型。我们不建议使用 time with time zone 类型(尽管 PostgreSQL 出于兼容旧应用以及遵循 SQL 标准的考虑而支持它)。 PostgreSQL 对于任何只包含日期或时间的 类型,都会假定其使用本地时区。

在系统内部,所有带时区的日期和时间都以 UTC 存储。显示给客户端之前,它们会被转换为由 timezone 配置参数指定的本地时间。

PostgreSQL允许使用三种不同形式来指定时区:

  • 完整时区名称,例如America/New_York。识别到的时区名称列在pg_timezone_names视图中(见第 44.56 节)。PostgreSQL为此使用广泛采用的 zic 时区数据,因此同样的时区名称通常也会被其他软件识别。

  • 时区缩写,例如PST。这种写法仅仅定义一个相对于 UTC 的特定偏移,而完整时区名称则还可能隐含一套夏令时转换日期规则。识别到的缩写列在pg_timezone_abbrevs视图中(见第 44.55 节)。你不能把配置参数timezone或log_timezone设为时区缩写,但可以在日期/时间输入值中以及配合AT TIME ZONE操作符使用缩写。

  • 除时区名称和缩写外,PostgreSQL还接受 POSIX 风格的时区说明,形式为STDoffset或STDoffsetDST,其中STD是区域缩写,offset是以小时计、从 UTC 向西的数字偏移,DST是可选的夏令时区域缩写,假定表示比给定偏移早一小时。例如,如果EST5EDT不是已被识别的区域名称,它也会被接受,并且功能上等价于美国东海岸时间。当存在夏令时区域名称时,假定它按照 zic 时区数据库的 posixrules 条目所用的同一套夏令时转换规则使用。在标准的PostgreSQL安装中,posixrules与US/Eastern相同,因此 POSIX 风格的时区说明遵循美国的夏令时转换规则。如有需要,可以通过替换posixrules文件来调整这一行为。

应当当心,POSIX 风格的时区特性可能导致静默接受伪劣输入,因为对时区缩写的合理性没有任何检查。例如,SET TIMEZONE TO FOOBAR0也能工作,使系统实际上使用一个相当古怪的 UTC 缩写。另一个需要记住的问题是,在 POSIX 时区名中,正偏移用于格林尼治以西的地点;而在其他所有地方,PostgreSQL都遵循 ISO-8601 约定,正的时区偏移位于格林尼治以东。

无论哪种形式,时区名称及其缩写都不区分大小写。(这是对 PostgreSQL 8.2 之前版本的一项改动; 在那些版本中,时区名在某些环境下区分大小写,而在另一些环境下则 不区分。)

时区名称和缩写并不是硬编码在服务器中的;它们来自安装目录下 .../share/timezone/ 和 .../share/timezonesets/ 子目录中的配置文件 (见 第 B.3 节)。

timezone配置参数可以在postgresql.conf文件中设置,也可以通过第 18 章中说明的其他标准方式设置。另外,还有一些特殊的设置方法:

  • 如果没有在postgresql.conf中或以服务器命令行选项的方式指定timezone,服务器会尝试使用TZ环境变量的值作为默认时区。如果TZ没有定义,或者不是PostgreSQL已知的任何时区名称,服务器会通过检查 C 库函数localtime()的行为来确定操作系统的默认时区。默认时区会选取PostgreSQL已知时区中最接近的匹配。(如果未指定log_timezone的默认值,也按这些规则选择。)

  • SQL命令SET TIME ZONE用于设置会话的时区。它是SET TIMEZONE TO的另一种写法,语法上更符合 SQL 规范。

  • libpq客户端使用PGTZ环境变量在连接时向服务器发送SET TIME ZONE命令。

8.5.4. 内部实现 #

PostgreSQL在所有日期/时间计算中使用儒略日。这带来一个有用的特性:在假定一年长度为 365.2425 天的前提下,能够正确计算从公元前 4713 年到遥远未来的日期。

19 世纪之前的日期约定读起来很有意思,但它们并不足够一致,不值得写进日期/时间处理器中。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。