pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PostgreSQL支持完整的一组SQL日期和时间类型,如表 8.9所示。这些数据类型上可用的操作见第 9.9 节。
表 8.9. 日期/时间类型
| Name | Storage Size | Description | Low Value | High Value | Resolution |
|---|---|---|---|---|---|
timestamp [ ( |
8字节 | 包括日期和时间(无时区) | 4713 BC | 294276 AD | 1微秒 / 14位 |
timestamp [ ( |
8字节 | 包括日期和时间,有时区 | 4713 BC | 294276 AD | 1微秒 / 14位 |
date |
4字节 | 日期(没有一天中的时间) | 4713 BC | 5874897 AD | 1日 |
time [ ( |
8字节 | 一天中的时间(无日期) | 00:00:00 | 24:00:00 | 1微秒 / 14位 |
time [ ( |
12字节 | 仅一天中的时间,带时区 | 00:00:00+1459 | 24:00:00-1459 | 1微秒 / 14位 |
interval [ |
16字节 | 时间间隔 | -178000000年 | 178000000年 | 1微秒 / 14位 |
SQL 标准要求仅写timestamp时,应等效于timestamp without time zone,而PostgreSQL也遵循这种行为。(7.3 之前的版本将其视为 timestamp with time zone。)
time、timestamp 和 interval 都接受一个可选精度值 p,用来指定在秒字段中 保留多少位小数。默认情况下,对精度没有显式上界。 对于 timestamp 和 interval 类型, p 的允许范围是 0 到 6。
当 timestamp 值存储为八字节整数(目前的默认方式)时, 在整个取值范围内都可以得到微秒精度。而当 timestamp 值改为存储为双精度浮点数(一个已废弃的编译期选项)时,有效精度上界 可能小于 6。timestamp 值存储为相对于 2000-01-01 午夜 之前或之后的秒数。当 timestamp 值采用浮点数实现时, 对于距 2000-01-01 几年之内的日期可以达到微秒精度,但对更远的日期 精度会下降。注意,使用浮点日期时间可以表示比上表所列更大的 timestamp 取值范围:从公元前 4713 年直到公元 5874897 年。
同一个编译期选项还决定 time 和 interval 值 存储为浮点数还是八字节整数。在浮点存储的情况下,大的 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
注意,如果同时指定了 fields 和 p,那么 fields 必须包含 SECOND,因为精度只作用于秒。
time with time zone 类型由 SQL 标准定义,但其定义具有 一些会让人怀疑其实用性的特性。在大多数情况下, date、time、 timestamp without time zone 和 timestamp with time zone 的组合,就足以提供任何应用 所需的完整日期/时间功能。
类型abstime和reltime是内部使用的低精度类型。不推荐在应用中使用这些类型;这些内部类型可能在将来的版本中消失。
日期和时间输入几乎接受任何合理的格式,包括 ISO 8601、 与 SQL 兼容的格式、传统 POSTGRES 格式等。对于某些格式, 日期输入中日、月、年的顺序可能存在歧义,因此支持指定这些字段的 预期顺序。将 DateStyle 参数设置为 MDY,表示采用月-日-年解释; 设置为 DMY 表示采用日-月-年解释; 而 YMD 表示采用年-月-日解释。
PostgreSQL 在处理日期/时间输入方面, 比 SQL 标准要求得更灵活。有关日期/时间输入的 精确解析规则,以及可识别的文本字段(包括月份、星期几和时区), 请参见 附录 B。
请记住,任何日期或时间字面值输入都必须像文本字符串一样用单引号 括起来。更多信息请参见 第 4.1.2.7 节。 SQL 要求使用下列语法:
type[ (p) ] 'value'
其中 p 是可选的精度说明,给出秒字段中 保留的小数位数。精度可用于 time、 timestamp 和 interval 类型, 允许的取值范围见上文。如果在常量声明中没有指定 精度,则默认采用该字面值本身的精度。
表 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年 |
一天中的时间类型包括 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. 时间输入
| 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 节。
时间戳类型的有效输入由一个日期和时间的串接组成,后面跟着一个可选 时区,以及一个可选的 AD 或 BC (另外,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 zone和timestamp 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.3 节)。
在 timestamp without time zone 和 timestamp with time zone 之间转换时,通常假定 timestamp without time zone 值应被解释为,或输出为, timezone 本地时间。要为该转换指定不同的时区, 可以使用 AT TIME ZONE。
为了方便起见,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 |
date、timestamp |
今天午夜 |
tomorrow |
date、timestamp |
明天午夜 |
yesterday |
date、timestamp |
昨天午夜 |
allballs |
time |
00:00:00.00 UTC |
以下与 SQL 兼容的函数也可用于获取相应数据 类型的当前时间值:CURRENT_DATE、 CURRENT_TIME、 CURRENT_TIMESTAMP、LOCALTIME 和 LOCALTIMESTAMP。后四个函数接受可选的亚秒级 精度说明。(参见 第 9.9.4 节。)请注意,这些是 SQL 函数,不会在日期/时间输入字符串中被识别。
日期/时间类型的输出格式可以设为四种样式之一:ISO 8601、SQL(Ingres)、传统的POSTGRES(Unix date格式)或 German。默认是ISO格式。(SQL标准要求使用 ISO 8601 格式。之所以存在名为“SQL”的输出格式,只是历史原因。)表 8.14展示了各种输出样式的示例。date和time类型的输出当然只包含给定示例中相应的日期部分或时间部分。
表 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 节)也可用,它是格式化日期/时间输出的一种更灵活的方式。
时区及其约定不仅受地球几何形状影响,也受政治决定影响。世界各地的 时区在 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 节)。你不能把配置参数timezone或log_timezone设为时区缩写,但可以在日期/时间输入值中以及配合AT TIME ZONE操作符使用缩写。
除时区名称和缩写外,PostgreSQL还接受 POSIX 风格的时区说明,形式为STDoffset或STDoffsetDST,其中STD是区域缩写,offset是以小时计、从 UTC 向西的数字偏移,DST是可选的夏令时区域缩写,假定表示比给定偏移早一小时。例如,如果EST5EDT不是已被识别的区域名称,它也会被接受,并且功能上等价于美国东海岸时间。当存在夏令时区域名称时,假定它按照 IANA 时区数据库posixrules条目所用的同一套夏令时转换规则使用。在标准的PostgreSQL安装中,posixrules与US/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命令。
interval值可以使用下列详细语法书写:
[@]quantityunit[quantityunit...] [direction]
其中quantity是一个数字(可以带有符号); unit是microsecond、 millisecond、second、 minute、hour、day、 week、month、year、 decade、century、millennium 或它们的缩写或复数形式; 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 节的 “替代格式”。带标志符的格式如下:
Pquantityunit[quantityunit...] [ T [quantityunit...]]
字符串必须以 P 开头,并且可以包含一个 T 来引出一天中的时间单位。可用的单位缩写见 表 8.16。单位可以省略, 也可以按任意顺序出现,但小于一天的单位必须出现在 T 之后。特别是,M 的含义 取决于它是在 T 之前还是之后。
表 8.16. ISO 8601 间隔单位缩写
| Abbreviation | Meaning |
|---|---|
| Y | 年 |
| M | 月(在日期部分中) |
| W | 周 |
| D | 日 |
| H | 小时 |
| M | 分钟 (在时间部分中) |
| S | 秒 |
如果使用替代格式:
P [years-months-days] [ Thours: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_days和justify_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 的“替代格式”:含义同上 |
间隔类型的输出格式可以通过 SET intervalstyle 命令设置为以下四种风格之一:sql_standard、 postgres、postgres_verbose 或 iso_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 |
PostgreSQL在所有日期/时间计算中使用儒略日。这带来一个有用的特性:在假定一年长度为 365.2425 天的前提下,能够正确计算从公元前 4713 年到遥远未来的日期。
19 世纪之前的日期约定读起来很有意思,但它们并不足够一致,不值得写进日期/时间处理器中。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。