2024年9月26日:PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不受支持的版本: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

8.5. 日期/时间类型 #

PostgreSQL 支持完整的SQL日期和时间类型,如表 8.9所示。这些数据类型上可用的操作在第 9.9 节中进行了描述。日期根据公历计算,即使在引入该历法之前的年份也是如此(有关更多信息,请参见第 B.6 节)。

表 8.9. 日期/时间类型

名称 存储大小 描述 最小值 最大值 精度
timestamp [ (p) ] [ without time zone ] 8 字节 日期和时间(无时区) 公元前 4713 年 公元 294276 年 1 微秒
timestamp [ (p) ] with time zone 8 字节 日期和时间,含时区 公元前 4713 年 公元 294276 年 1 微秒
date 4 字节 日期(无时间) 公元前 4713 年 公元 5874897 年 1 天
time [ (p) ] [ without time zone ] 8 字节 一天中的时间(无日期) 00:00:00 24:00:00 1 微秒
time [ (p) ] with time zone 12 字节 一天中的时间(无日期),含时区 00:00:00+1559 24:00:00-1559 1 微秒
interval [ fields ] [ (p) ] 16 字节 时间间隔 -178000000 年 178000000 年 1 微秒

注意

SQL 标准要求仅编写 timestamp 等效于 timestamp without time zone,并且 PostgreSQL 遵循此行为。timestamptz 作为 timestamp with time zone 的缩写被接受;这是一个 PostgreSQL 扩展。

timetimestampinterval 接受可选的精度值 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

请注意,如果同时指定了 fieldsp,则 fields 必须包含 SECOND,因为精度仅适用于秒。

time with time zone 类型由 SQL 标准定义,但该定义表现出的特性导致其实用性存疑。在大多数情况下,datetimetimestamp without time zonetimestamp with time zone 的组合应该能够提供任何应用程序所需的完整范围的日期/时间功能。

8.5.1. 日期/时间输入 #

日期和时间输入几乎可以接受任何合理的格式,包括 ISO 8601、SQL兼容的、传统的 POSTGRES 以及其他格式。对于某些格式,日期输入中日、月和年的顺序存在歧义,并且支持指定这些字段的预期顺序。将 DateStyle 参数设置为 MDY 以选择月-日-年解释,DMY 以选择日-月-年解释,或 YMD 以选择年-月-日解释。

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

请记住,任何日期或时间文字输入都需要用单引号括起来,就像文本字符串一样。有关更多信息,请参阅第 4.1.2.7 节SQL需要以下语法

type [ (p) ] 'value'

其中 p 是一个可选的精度规范,用于给出秒字段中的小数位数。可以为 timetimestampinterval 类型指定精度,范围为 0 到 6。如果在常量规范中未指定精度,则默认为文字值的精度(但不超过 6 位)。

8.5.1.1. 日期 #

表 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-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 儒略日
公元前 99 年 1 月 8 日 公元前 99 年

8.5.1.2. 时间 #

一天中的时间类型为 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 与 04:05 相同;AM 不会影响值
下午 04:05 与 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 节

8.5.1.3. 时间戳 #

时间戳类型的有效输入由日期和时间的串联后跟可选的时区,然后后跟可选的 ADBC 组成。(或者,AD/BC 可以出现在时区之前,但这不是首选顺序。)因此

1999-01-08 04:05:06

1999-01-08 04:05:06 -8:00

是有效值,它们遵循ISO8601 标准。此外,还支持以下常见格式:

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'

是一个带时区的时间戳PostgreSQL在确定文字字符串的类型之前永远不会检查其内容,因此会将以上两者都视为不带时区的时间戳。为了确保文字被视为带时区的时间戳,请为其提供正确的显式类型。

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

在被确定为不带时区的时间戳的文字中,PostgreSQL将静默忽略任何时区指示。也就是说,结果值派生自输入值中的日期/时间字段,并且不会根据时区进行调整。

对于带时区的时间戳,内部存储的值始终为 UTC(协调世界时,传统上称为格林威治标准时间,GMT)。具有显式指定时区的输入值将使用该时区的相应偏移量转换为 UTC。如果输入字符串中未声明时区,则假定它位于系统TimeZone参数指示的时区中,并使用timezone时区的偏移量将其转换为 UTC。

当输出带时区的时间戳值时,它始终会从 UTC 转换为当前timezone时区,并在该时区中显示为本地时间。要查看另一个时区中的时间,请更改timezone或使用AT TIME ZONE结构(请参阅第 9.9.4 节)。

不带时区的时间戳带时区的时间戳之间的转换通常假定不带时区的时间戳值应作为timezone本地时间获取或给出。可以使用AT TIME ZONE为转换指定不同的时区。

8.5.1.4. 特殊值 #

PostgreSQL出于方便起见,支持一些特殊的日期/时间输入值,如表 8.13所示。值infinity-infinity在系统内部有特殊的表示方式,并且将保持不变;但其他值只是符号简写,在读取时将转换为普通的日期/时间值。(特别是,now和相关字符串在读取后立即转换为特定的时间值。)所有这些值在用作 SQL 命令中的常量时都需要用单引号括起来。

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

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

以下SQL-兼容函数也可用于获取对应数据类型的当前时间值:CURRENT_DATECURRENT_TIMECURRENT_TIMESTAMPLOCALTIMELOCALTIMESTAMP。(请参阅第 9.9.5 节。)请注意,这些是 SQL 函数,在数据输入字符串中不被识别。

注意

虽然输入字符串nowtodaytomorrowyesterday可以在交互式 SQL 命令中很好地使用,但当命令保存以供稍后执行时,例如在预处理语句、视图和函数定义中,它们可能会产生意外的行为。该字符串可以转换为特定的时间值,该值在变得陈旧很久之后仍将继续使用。在这种情况下,请改用其中一个 SQL 函数。例如,CURRENT_DATE + 1'tomorrow'::date更安全。

8.5.2. 日期/时间输出 #

日期/时间类型的输出格式可以设置为四种样式之一:ISO 8601、SQL(Ingres)、传统的 POSTGRES(Unix date 格式)或德语。默认值为ISO格式。(SQL标准要求使用 ISO 8601 格式。SQL输出格式的名称是一个历史意外。)表 8.14显示了每种输出样式的示例。datetime类型的输出通常仅为日期或时间部分,符合给定的示例。但是,POSTGRES样式以ISO格式输出仅日期的值。

表 8.14. 日期/时间输出样式

样式规范 描述 示例
ISO ISO 8601,SQL 标准 1997-12-17 07:37:16-08
SQL 传统样式 1997 年 12 月 17 日 07:37:16.00 PST
Postgres 原始样式 1997 年 12 月 17 日星期三 07:37:16 PST
德语 区域样式 1997 年 12 月 17 日 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 1997 年 12 月 17 日 15:37:16.00 CET
SQL,MDY month/day/year 1997 年 12 月 17 日 07:37:16.00 PST
Postgres,DMY day/month/year 1997 年 12 月 17 日星期三 07:37:16 PST

ISO样式中,时区始终显示为相对于 UTC 的带符号数字偏移量,对于格林威治以东的时区使用正号。如果偏移量是整数小时,则将显示为hh(仅小时),否则如果它是整数分钟,则显示为hh:mm,否则显示为hh:mm:ss。(在任何现代时区标准中,第三种情况都不可能出现,但当处理早于标准化时区采用之前的时间戳时,它可能会出现。)在其他日期样式中,如果时区在当前时区中普遍使用,则时区将显示为字母缩写。否则,它将显示为 ISO 8601 基本格式(hhhhmm)中的带符号数字偏移量。

用户可以使用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类型(尽管PostgreSQL支持它以用于遗留应用程序以及与SQL标准保持一致)。PostgreSQL假设任何仅包含日期或时间的类型都为您的本地时区。

所有时区感知日期和时间都以内部存储UTC。在显示给客户端之前,它们将转换为TimeZone配置参数指定的时区中的本地时间。

PostgreSQL允许您以三种不同的形式指定时区

  • 完整的时区名称,例如America/New_Yorkpg_timezone_names视图列出了识别的时区名称(请参阅第 52.32 节)。PostgreSQL为此目的使用广泛使用的 IANA 时区数据,因此其他软件也识别相同的时区名称。

  • 时区缩写,例如 PST。这种规范仅仅定义了相对于 UTC 的特定偏移量,与完整的时区名称形成对比,完整的时区名称还可以暗示一组夏令时转换规则。已识别的缩写列在 pg_timezone_abbrevs 视图中(参见 第 52.31 节)。您不能将配置参数 TimeZonelog_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 文件中或在 第 19 章 中描述的任何其他标准方式中设置 TimeZone 配置参数。还有一些特殊的方法来设置它

  • SQL命令 SET TIME ZONE 设置会话的时区。这与 SET TIMEZONE TO 具有更符合 SQL 规范的语法。

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

8.5.4. 区间输入 #

interval 值可以使用以下详细语法编写

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

其中 quantity 是一个数字(可能带符号);unitmicrosecondmillisecondsecondminutehourdayweekmonthyeardecadecenturymillennium 或这些单位的缩写或复数;direction 可以是 ago 或空。@ 符号 (@) 是可选的噪音。不同单位的数量会隐式地加起来,并进行适当的符号计算。ago 会使所有字段取反。如果 IntervalStyle 设置为 postgres_verbose,则此语法也用于区间输出。

可以不带显式单位标记指定天、小时、分钟和秒的数量。例如,'1 12:59:10''1 day 12 hours 59 min 10 sec' 的读取方式相同。此外,可以使用连字符指定年和月的组合;例如,'200-10''200 years 10 months' 的读取方式相同。(这些较短的形式实际上是标准允许的唯一形式,并且在 IntervalStyle 设置为 sql_standard 时用于输出。)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 区间单位缩写

缩写 含义
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 小时。使用其他单位的区间输入字符串会被规范化为这种格式,然后以标准化的方式重建以进行输出,例如

SELECT '2 years 15 months 100 weeks 99 hours 123456789 milliseconds'::interval;
               interval
---------------------------------------
 3 years 3 mons 700 days 133:17:36.789

这里,被理解为“7 天”的周被单独保留,而较小和较大的时间单位被组合并规范化。

输入字段值可以具有小数部分,例如 '1.5 weeks''01:02:03.45'。但是,因为 interval 在内部仅存储整数字段,所以小数值必须转换为较小的单位。大于月的单位的小数部分四舍五入为整数月数,例如 '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“备用格式”:与上述含义相同

8.5.5. 区间输出 #

如前所述,PostgreSQLinterval 值存储为月、天和微秒。对于输出,月字段通过除以 12 转换为年和月。天字段按原样显示。微秒字段转换为小时、分钟、秒和小数秒。因此,月、分钟和秒永远不会显示为超过 0-11、0-59 和 0-59 的范围,而显示的年、天和小时字段可以非常大。(如果需要将较大的天或小时值转换为下一个较高的字段,可以使用 justify_daysjustify_hours 函数。)

可以使用命令 SET intervalstyle 将区间类型的输出格式设置为四种样式之一:sql_standardpostgrespostgres_verboseiso_8601。默认值为 postgres 格式。表 8.18 显示了每种输出样式的示例。

如果区间值满足标准的限制(仅限年-月或日-时间,并且没有正负分量的混合),则 sql_standard 样式会生成符合 SQL 标准对区间文字字符串规范的输出。否则,输出看起来像一个标准的年-月文字字符串后跟一个日-时间文字字符串,并添加显式符号以消除混合符号区间的歧义。

DateStyle 参数设置为 ISO 时,postgres 样式的输出与 8.4 之前版本的 PostgreSQL 的输出匹配。

postgres_verbose 样式的输出与 8.4 之前的 PostgreSQL 版本在 DateStyle 参数设置为非 ISO 输出时的输出匹配。

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 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-2M3D​T-4H-5M-6S

提交更正

如果您在文档中发现任何不正确的内容,与您对特定功能的体验不符,或者需要进一步说明,请使用 此表单 报告文档问题。