选择 打开 改范围 完整检索页

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
历史版本PostgreSQL 9.0 已于 2015 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

8.5. 日期/时间类型 #

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

表 8.9. 日期/时间类型

Name Storage Size Description Low Value High Value Resolution
timestamp [ (p) ] [ without time zone ] 8字节 包括日期和时间(无时区) 4713 BC 294276 AD 1微秒 / 14位
timestamp [ (p) ] with time zone 8字节 包括日期和时间,有时区 4713 BC 294276 AD 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位
interval [ fields ] [ (p) ] 16字节 时间间隔 -178000000年 178000000年 1微秒 / 14位

注意

SQL 标准要求仅写timestamp时,应等效于timestamp without time zone,而PostgreSQL也遵循这种行为。(7.3 之前的版本将其视为 timestamp with time zone。)

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

注意

timestamp 值存储为八字节整数(目前的默认方式)时, 在整个取值范围内都可以得到微秒精度。而当 timestamp 值改为存储为双精度浮点数(一个已废弃的编译期选项)时,有效精度上界 可能小于 6。timestamp 值存储为相对于 2000-01-01 午夜 之前或之后的秒数。当 timestamp 值采用浮点数实现时, 对于距 2000-01-01 几年之内的日期可以达到微秒精度,但对更远的日期 精度会下降。注意,使用浮点日期时间可以表示比上表所列更大的 timestamp 取值范围:从公元前 4713 年直到公元 5874897 年。

同一个编译期选项还决定 timeinterval 值 存储为浮点数还是八字节整数。在浮点存储的情况下,大的 interval 值会随着间隔长度的增大而降低精度。

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

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

注意,如果同时指定了 fieldsp,那么 fields 必须包含 SECOND,因为精度只作用于秒。

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

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

8.5.1. 日期/时间输入 #

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

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

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

type [ (p) ] 'value'

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

8.5.1.1. 日期

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

表 8.10. 日期输入

Example Description
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 zonetime [ (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. 时间输入

Example Description
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. 时区输入

Example Description
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. 时间戳

时间戳类型的有效输入由一个日期和时间的串接组成,后面跟着一个可选 时区,以及一个可选的 ADBC (另外,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 zonetimestamp 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 zonePostgreSQL在确定字符串字面量的类型之前,从不检查其内容,因此会把上述两者都视为timestamp without time zone。为确保字面量被视为timestamp with time zone,应为它显式指定正确类型:

TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'

若字面量已经被确定为timestamp without time zonePostgreSQL会静默忽略任何时区标记。也就是说,所得值来自输入值中的日期/时间字段,不会根据时区调整。

对于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 zonetimestamp with time zone 之间转换时,通常假定 timestamp without time zone 值应被解释为,或输出为, timezone 本地时间。要为该转换指定不同的时区, 可以使用 AT TIME ZONE

8.5.1.4. 特殊值

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

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

Input String Valid Types Description
epoch date, timestamp 1970-01-01 00:00:00+00(Unix系统时间0)
infinity date, timestamp 晚于所有其他时间戳
-infinity date, timestamp 早于所有其他时间戳
now date, time, timestamp 当前事务的开始时间
today datetimestamp 今天午夜
tomorrow datetimestamp 明天午夜
yesterday datetimestamp 昨天午夜
allballs time 00:00:00.00 UTC

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

8.5.2. 日期/时间输出 #

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

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

Style Specification Description Example
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 Input Ordering Example Output
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

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

8.5.3. 时区 #

时区及其约定不仅受地球几何形状影响,也受政治决定影响。世界各地的 时区在 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视图中(见第 45.60 节)。PostgreSQL为此使用广泛采用的 IANA 时区数据,因此同样的时区名称通常也会被其他软件识别。

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

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

简而言之,这就是缩写与完整名称之间的区别:缩写表示一个特定的 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例子一样,这并不一定等同于该日期的当地民用时间。

应当当心,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. 间隔输入 #

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

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

其中quantity是一个数字(可以带有符号); unitmicrosecondmillisecondsecondminutehourdayweekmonthyeardecadecenturymillennium 或它们的缩写或复数形式; 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 节的 替代格式。带标志符的格式如下:

P quantity unit [ quantity unit ...] [ T [ quantity unit ...]]

字符串必须以 P 开头,并且可以包含一个 T 来引出一天中的时间单位。可用的单位缩写见 表 8.16。单位可以省略, 也可以按任意顺序出现,但小于一天的单位必须出现在 T 之后。特别是,M 的含义 取决于它是在 T 之前还是之后。

表 8.16. ISO 8601 间隔单位缩写

Abbreviation Meaning
Y
M 月(在日期部分中)
W
D
H 小时
M 分钟 (在时间部分中)
S

如果使用替代格式:

P [ years-months-days ] [ T hours: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 解释。为了避免混淆,我们建议 只要有任何字段为负值,就为每个字段都显式写出符号。

在内部,interval值按月、日和秒存储。之所以这样做,是因为一个月的天数并不固定,而且在涉及夏令时调整时,一天可能有 23 或 25 小时。月和日字段是整数,而秒字段可以存储小数。由于 interval 通常由常量字符串或timestamp相减创建,这种存储方法在大多数情况下都能良好工作。函数justify_daysjustify_hours可用于调整超出正常范围的日和小时。

在冗长输入格式中,以及在一些更紧凑输入格式的某些字段中,字段值可以带小数部分;例如'1.5 week''01:02:03.45'。这样的输入会被转换为适当数量的月、日和秒来存储。当转换结果出现小数的月数或天数时,小数部分会按 1 月 = 30 天、1 天 = 24 小时的换算系数计入更低阶的字段。例如,'1.5 month'会变成 1 个月零 15 天。输出时,只有秒会显示小数部分。

表 8.17展示了一些有效interval输入的示例。

表 8.17. 间隔输入

Example Description
1-2 SQL标准格式:1年2个月
3 4:05:06 SQL标准格式:3日4小时5分钟6秒
1 year 2 months 3 days 4 hours 5 minutes 6 seconds 传统Postgres格式:1年2个月3日4小时5分钟6秒钟
P1Y2M3DT4H5M6S 带标志符的ISO 8601 格式:含义同上
P0001-02-03T04:05:06 ISO 8601 的替代格式:含义同上

8.5.5. 间隔输出 #

间隔类型的输出格式可以通过 SET intervalstyle 命令设置为以下四种风格之一:sql_standardpostgrespostgres_verboseiso_8601。默认值是 postgres 格式。 表 8.18 展示了每种输出风格的 示例。

如果间隔值满足 SQL 标准的限制条件(仅有年-月或仅有日-时间,且 不混合正负分量),sql_standard 风格会生成符合 SQL 标准的间隔字面值输出。否则,输出将表现为一个标准的年-月字面值 串后跟一个日-时间字面值串,并显式加上符号,以消除正负混合间隔的 歧义。

postgres 样式的输出与 PostgreSQL 8.4 之前版本在 DateStyle 参数设为 ISO 时的输出一致。

postgres_verbose 样式的输出与 PostgreSQL 8.4 之前版本在 DateStyle 参数设为非 ISO 输出时的结果一致。

iso_8601 风格的输出符合 ISO 8601 标准 4.4.3.2 节描述的带标志符的格式

表 8.18. 间隔输出风格示例

Style Specification Year-Month Interval Day-Time Interval Mixed Interval
sql_standard 1-2 3 4:05:06 -1-2 +3 -4:05:06
postgres 1 year 2 mons 3 days 04:05:06 -1 year -2 mons +3 days -04:05:06
postgres_verbose @ 1 year 2 mons @ 3 days 4 hours 5 mins 6 secs @ 1 year 2 mons -3 days 4 hours 5 mins 6 secs ago
iso_8601 P1Y2M P3DT4H5M6S P-1Y-2M3DT-4H-5M-6S

8.5.6. 内部实现 #

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

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

提交更正

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