Postgresql 中文操作指南

8.5. Date/Time Types #

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

Table 8.9. Date/Time Types

Name

Storage Size

Description

Low Value

High Value

Resolution

timestamp [ (_p ) ] [ without time zone ]_

8 bytes

日期和时间(没有时区)

4713 BC

294276 AD

1 microsecond

timestamp [ (_p ) ] with time zone_

8 bytes

日期和时间,有时区

4713 BC

294276 AD

1 microsecond

date

4 bytes

日期(无时间)

4713 BC

5874897 AD

1 day

time [ (_p ) ] [ without time zone ]_

8 bytes

时间(无日期)

00:00:00

24:00:00

1 microsecond

time [ (_p ) ] with time zone_

12 bytes

时间 (没有日期),带有时区

00:00:00+1559

24:00:00-1559

1 microsecond

interval [ _fields ] [ ( p ) ]_

16 bytes

time interval

-178000000 years

178000000 years

1 microsecond

Note

SQL 标准要求只写 timestamp 等同于 timestamp without time zone,PostgreSQL 遵守该行为。timestamptz 被接受为 timestamp with time zone 的缩写;这是 PostgreSQL 的扩展。

timetimestamp_和 _interval 接受可选的精度值 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 标准定义,但该定义展现出导致有用性存在疑问的属性。在大多数情况下,datetime、_timestamp without time zone_和 _timestamp with time zone_的组合应该可以提供任何应用程序所需的完整日期/时间功能范围。

8.5.1. Date/Time Input #

日期和时间输入被接受为几乎任何合理的格式,包括 ISO 8601、SQL 兼容、传统 POSTGRES 等。对于某些格式,日期输入中日期、月份和年份的顺序是含糊不清的,支持指定这些字段的预期顺序。将 DateStyle 参数设置为 _MDY_以选择月-日-年解释,_DMY_以选择日-月-年解释,或 _YMD_以选择年-月-日解释。

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

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

type [ (p) ] 'value'

其中 p 是可选的精度规范,提供秒字段中的小数位数。可以为 timetimestampinterval 类型指定精度,范围为 0 到 6。如果在常量规范中未指定精度,则默认为字面值精度(但不多于 6 位数字)。

8.5.1.1. Dates #

Table 8.10显示了 _date_类型的可能输入。

Table 8.10. Date Input

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

Julian date

January 8, 99 BC

year 99 BC

8.5.1.2. Times #

时间类型的时区包括 time [ (_p)不含时区_ 和 time [ (_p)含时区_。time_本身等效于 _time without time zone

对这些类型的有效输入包括:一天的具体时间以及可选时区。(见 Table 8.11Table 8.12。)如果为 time without time zone_的输入指定了时区,则会忽略它。您还可以指定日期,但会忽略日期,除非您使用包含夏令时规则的时区名称,例如 _America/New_York。在这种情况下,指定日期是必需的,以便确定适用标准时间还是夏令时。相应的时区偏移量会记录在 _time with time zone_值中,并按储存内容输出;它不会调整为活动时区。

Table 8.11. Time Input

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,带有时区作为 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

时区由全名指定

Table 8.12. Time Zone Input

Example

Description

PST

缩写(太平洋标准时间)

America/New_York

Full time zone name

PST8PDT

POSIX-style time zone specification

-8:00:00

UTC offset for PST

-8:00

PST 的 UTC 偏移(ISO 8601 扩展格式)

-800

PST 的 UTC 偏移(ISO 8601 基本格式)

-8

PST 的 UTC 偏移(ISO 8601 基本格式)

zulu

Military abbreviation for UTC

z

zulu 的简写(也在 ISO 8601 中)

有关如何指定时区的更多信息,请参阅 Section 8.5.3

8.5.1.3. Time Stamps #

时间戳类型的有效输入由日期和时间的连接组成,紧随可选时区,再紧随可选 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 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_结构(见 Section 9.9.4)。

timestamp without time zonetimestamp with time zone 之间的转换通常假设 timestamp without time zone 值应按 timezone 本地时间获取或给出。可使用 AT TIME ZONE 指定转换的不同时区。

8.5.1.4. Special Values #

PostgreSQL 支持几种特殊日期/时间的输入值以方便使用,如 Table 8.13所示。_infinity_和 _-infinity_在系统内有特殊表示,且将保持不变;但其他只是记号缩写,它将在读取时转换为普通日期/时间值。(特别是,_now_和相关字符串在读取时会转换为特定时间值。)所有这些值在作为 SQL 命令中的常量使用时,都需要用单引号引起来。

Table 8.13. Special Date/Time Inputs

Input String

Valid Types

Description

epoch

date, timestamp

1970-01-01 00:00:00+00(Unix 系统时间零)

infinity

date, timestamp

晚于所有其他时间戳

-infinity

date, timestamp

早于所有其他时间戳

now

date, time, timestamp

current transaction’s start time

today

date, timestamp

midnight (00:00) today

tomorrow

date, timestamp

midnight (00:00) tomorrow

yesterday

date, timestamp

midnight (00:00) yesterday

allballs

time

00:00:00.00 UTC

以下 SQL 兼容函数也可用于获取对应数据类型的当前时间值:CURRENT_DATECURRENT_TIMECURRENT_TIMESTAMPLOCALTIMELOCALTIMESTAMP。(见 Section 9.9.5。)请注意,这些是 SQL 函数,并且是 _not_在数据输入字符串中识别的。

Caution

虽然输入字符串 nowtodaytomorrowyesterday 在交互式 SQL 命令中使用很合适,但当命令被保存以稍后再执行时,它们可能会有出人意料的行为,比如在已准备好的语句、视图和函数定义中。该字符串可被转换为特定的时间值,并继续用在它变得过时之后的很长一段时间。在这种情况下,改为使用其中一种 SQL 函数。例如,CURRENT_DATE + 1'tomorrow'::date 更安全。

8.5.2. Date/Time Output #

日期/时间类型的输出格式可以设置为四种格式之一:ISO 8601、SQL(Ingres)、传统的 POSTGRES(Unix 日期格式)或德语。默认格式是 ISO 格式。(SQL 标准要求使用 ISO 8601 格式。 “SQL” 输出格式的名称是一个历史巧合。) Table 8.14示出了每种输出格式的示例。_date_和 _time_类型的输出通常仅是日期部分或时间部分,与给定的示例一致。然而,POSTGRES 样式以 ISO 格式输出仅日期值。

Table 8.14. Date/Time Output Styles

Style Specification

Description

Example

ISO

ISO 8601, SQL standard

1997-12-17 07:37:16-08

SQL

traditional style

12/17/1997 07:37:16.00 PST

Postgres

original style

Wed Dec 17 07:37:16 1997 PST

German

regional style

17.12.1997 07:37:16.00 PST

Note

ISO 8601 规定使用大写字母 T 分隔日期和时间。PostgreSQL 在输入中接受该格式,但在输出中使用空格而不是 T,如上所示。这是为了可读性和与 RFC 3339 及其他一些数据库系统的一致性。

在 SQL 和 POSTGRES 样式中,如果指定了 DMY 字段顺序,则 day 会出现在 month 之前,否则 month 会出现在 day 之前。(请参阅 Section 8.5.1,了解此设置如何影响输入值的解释。) Table 8.15显示示例。

Table 8.15. Date Order Conventions

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

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

日期/时间样式可以选择使用 _SET datestyle_命令、_postgresql.conf_配置文件中的 DateStyle参数或服务器或客户端上的 _PGDATESTYLE_环境变量。

格式化函数 to_char(见 Section 9.8)也可作为一种更灵活的方式来格式化日期/时间输出。

8.5.3. Time Zones #

时区和时区约定受政治决策影响,而不仅仅是地球几何形状。世界各地的时区在20世纪已经得到了一些标准化,但仍然容易受到任意变化的影响,特别是在夏令时规则方面。PostgreSQL 使用广泛使用的 IANA(奥尔森)时区数据库来获取有关历史时区规则的信息。对于未来的时间,假设给定时区的最新已知规则将继续无限期地遵守。

PostgreSQL 努力与 SQL 标准定义保持兼容,以供典型用法。然而,SQL 标准对日期和时间类型和功能有一种奇怪的混合。两个明显的问题是:

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

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

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

简而言之,这就是缩写和全称之间的区别:缩写表示与 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/_下存储的配置文件中获取的(参见 Section B.4)。

TimeZone配置参数可以在文件 _postgresql.conf_中设置,或在 Chapter 20中描述的任何其他标准方式中设置。还有一些特殊的方式来设置它:

8.5.4. Interval Input #

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

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

quantity_为数字(可能带有符号);_unit_为 _microsecondmillisecondsecondminutehourdayweekmonthyeardecadecenturymillennium、或这些单位的缩写或复数;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_开头,并且可能包含引入一天中时间单位的 _TTable 8.16中给出了可用的单位缩写。可以省略单位,并且可以按任何顺序指定单位,但比一天小的单位必须出现在 _T_之后。特别是,_M_的含义取决于它是在 _T_之前还是之后。

Table 8.16. ISO 8601 Interval Unit Abbreviations

Abbreviation

Meaning

Y

Years

M

月份(在日期部分)

W

Weeks

D

Days

H

Hours

M

分钟(在时间部分)

S

Seconds

在备用格式中:

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。只有秒在输出时才会显示为分数。

Table 8.17显示了有效_interval_输入的一些示例。

Table 8.17. Interval Input

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. Interval Output #

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

使用命令_SET intervalstyle_,间隔类型的输出格式可以设置成四种样式之一_sql_standardpostgrespostgres_verbose_或者_iso_8601_。默认是_postgres_样式。 Table 8.18显示了每种输出样式的示例。

如果区间值符合标准的限制(仅限年-月或仅限一天-时间,且不允许正负组件混合),则 sql_standard 格式生成符合 SQL 标准对区间字面值字符串的规范的输出。否则,输出看起来像一个标准的年-月字面值字符串,后接一个日-时字面值字符串,使用明确的符号消歧义混合符号区间。

_postgres_样式的输出匹配 DateStyle参数设置为_ISO_时8.4之前的PostgreSQL发布版本的输出。

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

iso_8601 格式的输出与 ISO 8601 标准的章节 4.4.3.2 中描述的“带指示符的格式”相匹配。

Table 8.18. Interval Output Style Examples

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 年 -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