Skip to content

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 zone8 字节日期和时间,带时区公元前 4713 年公元 294276 年1 微秒
date4 字节日期(无时间)公元前 4713 年公元 5874897 年1 天
时间 [ (p) ] [ 无时区 ]8 字节时间(无日期)00:00:0024:00:001 微秒
时间 [ (p) ] 含时区12 字节时间(无日期),含时区00:00:00+155924:00:00-15591 微秒
间隔 [ 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-08ISO 8601;任何模式下的 1 月 8 日(推荐格式)
1999 年 1 月 8 日在任何 datestyle 输入模式下都不含糊
1/8/1999MDY 模式下为 1 月 8 日;在 DMY 模式下为 8 月 1 日
1/18/1999MDY 模式下为 1 月 18 日;在其他模式下被拒绝
01/02/03MDY 模式下为 2003 年 1 月 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-08YMD 模式下为 1 月 8 日,否则报错
08-01-991 月 8 日,但 YMD 模式下会报错
01-08-991 月 8 日,但 YMD 模式下会报错
19990108ISO 8601;在任何模式下均为 1999 年 1 月 8 日
990108ISO 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.789ISO 8601
04:05:06ISO 8601
04:05ISO 8601
040506ISO 8601
04:05 AM与 04:05 相同;AM 不影响值
04:05 PM与 16:05 相同;输入小时数必须 <= 12
04:05:06.789-8ISO 8601,时区为 UTC 偏移
04:05:06-08:00ISO 8601,时区为 UTC 偏移
04:05-08:00ISO 8601,时区为 UTC 偏移
040506-08ISO 8601,时区为 UTC 偏移
040506+0730ISO 8601,时区为 UTC 偏移的小时数
040506+07:30:00UTC 偏移指定为秒(ISO 8601 中不允许)
04:05:06 PST由缩写指定的时区
2003-04-12 04:05:06 America/New_York由全名指定的时区

表 8.12。时区输入

示例说明
PST缩写(太平洋标准时间)
America/New_York完整时区名称
PST8PDTPOSIX 风格时区规范
-8:00:00PST 的 UTC 偏移量
-8:00PST 的 UTC 偏移量(ISO 8601 扩展格式)
-800PST 的 UTC 偏移量(ISO 8601 基本格式)
-8PST 的 UTC 偏移量(ISO 8601 基本格式)
zuluUTC 的军事缩写
zzulu 的简写(也用于 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'

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

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. 特殊日期/时间输入

输入字符串有效类型说明
epochdatetimestamp1970-01-01 00:00:00+00(Unix 系统时间零)
infinitydatetimestamp晚于所有其他时间戳
-infinitydatetimestamp早于所有其他时间戳
nowdatetimetimestamp当前事务的开始时间
todaydatetimestamp今天的午夜(00:00
tomorrowdatetimestamp明天的午夜(00:00
yesterdaydatetimestamp昨天的午夜(00:00
allballstime00: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(Unixdate格式)或德语。默认格式为ISO格式。(SQL标准要求使用 ISO 8601 格式。““SQL”” 输出格式的名称是一个历史意外。)表 8.14显示了每种输出样式的示例。datetime类型的输出通常仅为日期或时间部分,符合给定的示例。但是,POSTGRES样式以ISO格式输出仅日期值。

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

样式规范说明示例
ISOISO 8601,SQL 标准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
德语区域样式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,DMYday/month/year17/12/1997 15:37:16.00 CET
SQL,MDYmonth/day/year12/17/1997 07:37:16.00 PST
Postgres,DMYday/month/yearWed 17 Dec 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 类型可以。现实世界中的时区如果没有与日期和时间相关联,几乎没有意义,因为偏移量可能会随着夏令时边界而全年变化。

  • 默认时区指定为从 的恒定数字偏移量。因此,在跨 边界进行日期/时间算术时,不可能适应夏令时。

为了解决这些困难,我们建议在使用时区时使用同时包含日期和时间的日期/时间类型。我们建议使用类型time with time zone(尽管PostgreSQL为旧版应用程序和符合SQL标准而支持它)。PostgreSQL假设您仅包含日期或时间的任何类型的本地时区。

所有与时区相关联的日期和时间都以UTC内部存储。在显示给客户端之前,它们会根据TimeZone配置参数指定的区域转换为本地时间。

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

  • 一个完整的时区名称,例如 America/New_York。已识别的时区名称列在 pg_timezone_names 视图中(参见 第 54.32 节)。PostgreSQL 为此使用广泛使用的 IANA 时区数据,因此其他软件也识别相同的时区名称。

  • 时区缩写,例如 PST。这种规范仅定义了与 UTC 的特定偏移量,而完整的时区名称还可以暗示一组夏令时转换规则。已识别的缩写列在 pg_timezone_abbrevs 视图中(参见 第 54.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中设置TimeZone配置参数,也可以在第 20 章中描述的任何其他标准方式中设置。还有一些特殊的方法可以设置它

  • 命令 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*规范允许的最小有效字段“右侧”的字段值将被静默丢弃。例如,编写INTERVAL '1 day 2:03:04' HOUR TO MINUTE将导致丢弃秒字段,但不丢弃天字段。

根据SQL标准,间隔值的所有字段必须具有相同的符号,因此前导负号适用于所有字段;例如,间隔文字'-1 2:03:04'中的负号适用于天和小时/分钟/秒部分。PostgreSQL允许字段具有不同的符号,并且传统上将文本表示中的每个字段视为独立签名,因此在此示例中小时/分钟/秒部分被视为正数。如果IntervalStyle设置为sql_standard,则前导符号被认为适用于所有字段(但仅在没有其他符号出现时)。否则,将使用传统的PostgreSQL解释。为避免歧义,建议为每个字段附加显式符号(如果任何字段为负数)。

字段值可以有小数部分:例如,'1.5 weeks''01:02:03.45'。但是,由于间隔在内部仅存储三个整数单位(月、天、微秒),因此必须将分数单位溢出到较小的单位。大于月份的单位的分数部分将被四舍五入为月份的整数,例如'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-2SQL 标准格式:1 年 2 个月
3 4:05:06SQL 标准格式:3 天 4 小时 5 分钟 6 秒
1 年 2 个月 3 天 4 小时 5 分钟 6 秒传统 Postgres 格式:1 年 2 个月 3 天 4 小时 5 分钟 6 秒
P1Y2M3DT4H5M6SISO 8601 带设计器的格式:与上述含义相同
P0001-02-03T04:05:06ISO 8601 备用格式:与上述含义相同

在内部,interval值存储为月、日和微秒。这样做是因为一个月中的天数是可变的,如果涉及夏令时调整,一天可能有 23 或 25 小时。月和日字段为整数,而微秒字段可以存储小数秒。由于间隔通常从常量字符串或timestamp减法创建,因此这种存储方法在大多数情况下都能正常工作,但可能会导致意外结果

SELECT EXTRACT(hours from '80 minutes'::interval);
 date_part
-----------
         1

SELECT EXTRACT(days from '80 hours'::interval);
 date_part
-----------
         0

函数justify_daysjustify_hours可用于调整超出其正常范围的天数和小时数。

8.5.5. 间隔输出#

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

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

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

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

iso_8601样式的输出与 ISO 8601 标准第 4.4.3.2 节中描述的““带设计器的格式””相匹配。

表 8.18. 间隔输出样式示例

样式规范年-月间隔日-时间间隔混合间隔
sql_standard1-23 4:05:06-1-2 +3 -4:05:06
postgres1 年 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_8601P1Y2MP3DT4H5M6SP-1Y-2M3D​T-4H-5M-6S