Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

8.5 日期/时间类型 #

8.5.1. 日期/时间输入
8.5.2. 日期/时间输出
8.5.3. 时区
8.5.4. 间隔输入
8.5.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 微秒

注意

timestamp 的 SQL 标准要求仅仅编写等同于 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,因为精度仅适用于秒。

SQL 标准定义了 time with time zone 类型,但该定义显示出导致可疑实用性的属性。在大多数情况下,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 年 2 月 2 日;在 DMY 模式下的 2003 年 2 月 1 日;在 YMD 模式下的 2001 年 2 月 3 日
1999-01-08 任何模式下的 1 月 8 日
01-08-1999 任何模式下的 1 月 8 日
08-01-1999 任何模式下的 1 月 8 日
99-01-08 YMD 模式下的 1 月 8 日,否则出错
08-01-99 1 月 8 日,除了在 YMD 模式下出错
08-01-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 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,时区为 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带时区的timestamp 文字。因此,根据该标准,

TIMESTAMP '2004-10-19 10:23:54'

无时区的timestamp,而

TIMESTAMP '2004-10-19 10:23:54+02'

带时区的timestampPostgreSQL 在确定其类型之前从不检查文字字符串的内容,因此会将以上内容视为 无时区的timestamp。要确保文字被视为 带时区的timestamp,请给它正确的显式类型

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

对于已确定为 无时区的timestamp 的文字,PostgreSQL 将默默忽略任何时区指示。也就是说,结果值派生自输入值中的日期/时间字段,并且未针对时区进行调整。

对于 带时区的timestamp,内部存储的值始终为 UTC(世界协调时间,传统上称为格林尼治标准时间,GMT)。将指定了显式时区的输入值使用该时区的适当偏移量转换为 UTC。如果输入字符串中未指定时区,则假定它位于系统 TimeZone 参数指示的时区中,并且使用代码 timezone 时区偏移量转换为 UTC。

当输出 带时区的timestamp 值时,它总是从 UTC 转换为当前 timezone 时区,并作为该时区的当地时间显示。要查看其他时区中的时间,请更改 timezone 或使用 AT TIME ZONE 构造(参见 章节 9.9.4)。

无时区的timestamp带时区的timestamp 之间的转换通常假定 无时区的timestamp 值应获取或提供为 timezone 当地时间。可以使用 AT TIME ZONE 为转换指定不同的时区。

8.5.1.4. 特殊的标量 #

PostgreSQL 为方便起见,支持多种特殊日期/时间输入值,如 表 8.13 所示。值 无穷大-无穷大 在系统内有特殊表示形式,并且将保持不变地显示;但其他值只是带有简短标记的常规表示,在读取时将转换为普通的日期/时间值。(尤其是, 现在 及相关字符串一经读取,便转换为特定时间值。)将这些值用作 SQL 命令中的常量时,所有这些值均需要用单引号引起来。

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

输入字符串 有效类型 描述
epoch 日期时间戳 1970-01-01 00:00:00+00(Unix 系统时间零)
无穷大 日期时间戳间隔 晚于其他所有时间戳
-无穷大 日期时间戳间隔 早于所有其他时间戳
现在 日期时间时间戳 当前事务的开始时间
今天 日期时间戳 今天午夜(00:00
明天 日期时间戳 明天午夜(00:00
昨天 日期时间戳 昨天午夜(00:00
allballs 时间 00:00:00.00 UTC

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

警告

虽然输入字符串 现在今天明天昨天 在交互式 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 传统样式 12/17/1997 07:37:16.00 PST
Postgres 原始样式 星期三 12 月 17 日 07:37:16 1997 PST
德语 区域样式 17.12.1997 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 // 17/12/1997 15:37:16.00 CET
SQL,MDY // 12/17/1997 07:37:16.00 PST
Postgres,DMY // 星期三 12 月 17 日 07:37:16 1997 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 with time zone(尽管 PostgreSQL 为旧版本应用程序或为遵守SQL标准提供对它的支持)。PostgreSQL 对任何仅包含日期或时间的类型假设您的时区是本地时区。

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

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

  • 完整的时区名称,例如 America/New_York。可识别的时区名称在 pg_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 节)。

TimeZone 配置参数可在 postgresql.conf 文件中设置,或使用 第 19 章 中描述的任何其他标准方式进行设置。还有一些特殊方法可用于对其进行设置

  • SQL命令 SET TIME ZONE 为会话设置时区。这是 SET TIMEZONE TO 的另一种拼写方式,具有更兼容 SQL 规范的语法。

  • 环境变量 PGTZlibpq 客户端用于在连接时向服务器发送 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 间隔单位缩写

缩写 含义
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 规范允许的最小重大域“右侧”to the right 的域值将被静默丢弃。例如,编写 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。Interval 输入

示例 描述
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。Interval 输出 #

如前所述,PostgreSQLinterval 值存储为月、日和微秒。对于输出,月字段将除以 12 转化为年和月。日字段将按原样显示。微秒字段将转换为小时、分钟、秒和分数秒。因此,月、分和秒将永远不会超过 0–11、0–59 和 0–59 的范围,而显示的年、日和小时字段可能会很大。(justify_daysjustify_hours 函数可用于将较大日或小时值转置到下一个较高字段,如果认为有必要的话)。

Interval 类型输出格式可以设置成使用命令 SET intervalstyle,其中四种样式之一为 sql_standardpostgrespostgres_verboseiso_8601。默认是 postgres 格式。表 8.18 展示了每种输出样式的示例。

如果间隔值满足标准的限制(只能为年-月或日-时,并且不能混合正负分量),则 sql_standard 样式将生成符合 SQL 标准的间隔文本字符串规范的输出。否则,输出看起来像一个标准的年-月文本字符串,后面跟着一个日-时文本字符串,带有明确的符号以消除混合符号间隔的歧义。

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

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

iso_8601 样式的输出与 ISO 8601 标准第 4.4.3.2 部分中所述的“format with designators”相匹配。

表 8.18. 间隔输出样式示例

样式规范 年-月间隔 日-时间间隔 混合间隔
sql_standard 1-2 3 4:05:06 -1-2 +3 -4:05:06
postgres 1 年 2 个月 3 天 04:05:06 -1 年 -2 个月 +3 天 -04:05:06
postgres_verbose @ 1 年 2 个月 @ 3 天 4 小时 5 分钟 6 秒 @ 1 年 2 个月 -3 天 4 小时 5 分钟 6 秒前
iso_8601 P1Y2M P3DT4H5M6S P-1Y-2M3D​T-4H-5M-6S