日期/时间类型#

下表显示了 OushuDB 支持的SQL中所有日期和时间类型。 这些数据类型上可以进行的操作在 日期/时间函数和操作符 中描述。

表.日期/时间类型

名字

存储空间

描述

最低值

最高值

分辨率

timestamp [ (p) ] [ without time zone ]

8 字节

日期和时间(无时区)

4713 BC

294276 AD

1 毫秒 / 14 位

timestamp [ (p) ] with time zone

8 字节

日期和时间,有时区

4713 BC

294276 AD

1 毫秒 / 14 位

date

4 字节

只用于日期

4713 BC

5874897 AD

1 天

time [ (p) ] [ without time zone ]

8 字节

只用于一日内时间

00:00:00

24:00:00

1 毫秒 / 14 位

time [ (p) ] with time zone

12 字节

只用于一日内时间,带时区

00:00:00+1459

24:00:00-1459

1 毫秒 / 14 位

interval [ fields ] [ (p) ]

12 字节

时间间隔

-178000000 年

178000000 年

1 毫秒 / 14 位

time, timestamp和interval接受一个可选的精度值 p以指明秒域中小数部分的位数。没有明确的缺省精度, p的范围对timestamp和interval类型是从0到6。

备注

如果timestamp 数值是以双精度浮点数(目前缺省)的方式存储的, 那么有效精度会小于 6。timestamp值是以 2000-01-01 午夜之前或之后的秒数存储的。 当timestamp值用浮点数实现时,微秒的精度是为那些在 2000-01-01 前后几年的日期实现的, 对于那些远一些的日子,精度会下降。如果timestamp数值是以8字节整数(一个编译选项)的方式存储的, 那么微秒的精度就可以在数值的全部范围内都可以获得。同一个编译选项也决定time和interval值是保存成浮点数还是八字节整数。在以浮点数存储的时候,随着时间间隔的增加,大的 interval数值的精度会降低。

对于time类型,如果使用了八字节的整数存储,那么p 允许的范围是从 0 到 6 ,如果使用的是浮点数存储,那么这个范围是 0 到 10 。

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就按照”年-月-日”解析。

请记住任何日期或者时间的文本输入需要由单引号包围,就像一个文本字符串一样。 参考 其他类型的常量 获取更多信息。 SQL要求使用下面的语法

type [ (p) ] 'value'

可选的精度声明中的p是一个整数,表示在秒域中小数部分的位数, 我们可以对time,timestamp,interval类型声明精度。 允许的精度在上面已经说明。如果在常量声明中没有声明精度,缺省是文本值的精度。

日期#

下表显示了date类型可能的输入方式。

表.Date Input

例子

描述

January 8, 1999

在任何datestyle输入模式下都无歧义

1999-01-08

ISO 8601格式(建议格式),任何方式下都是 1999 年 1 月 8 号

1/8/1999

有歧义,在MDY下是一月八号;在DMY模式下是八月一日

1/18/1999

MDY模式下是一月十八日,其它模式下被拒绝

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

一月八日,除了在YMD模式下是错误的之外

Jan-08-99

一月八日,除了在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 zone 和time [ (p) ] with time zone。 只写time等效于time without time zone。

这些类型的有效输入由当日时间后面跟着可选的时区组成(参阅下表)。 如果在time without time zone类型的输入 同样指定的日期也会被忽略,除非使用了一个包括夏令时规则的时区名,比如 America/New_York,在这种情况下, 必须指定日期以确定这个时间是标准时间还是夏令时。时区偏移将记录在 time with time zone中。

表.时间输入

例子

描述

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

用名字声明的时区

表.时区输入

例子

描述

PST

太平洋标准时间(Pacific Standard T

America/New_York

完整时区名称

PST8PDT

POSIX 风格的时区

-8:00

ISO-8601 与 PST 的偏移

-800

ISO-8601 与 PST 的偏移

-8

ISO-8601 与 PST 的偏移

zulu

军方对 UTC 的缩写

z

zulu的缩写

时间戳#

时间戳类型的有效输入由一个日期和时间的连接组成,后面跟着一个可选的时区, 一个可选的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。OushuDB 从来不会在确定文本的类型之前检查文本内容,因此会把上面两个都看做是 timestamp without time zone。因此要保证把上面的第二个当作 timestamp with time zone看待,就要给它明确的类型:

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

如果一个文本已被确定是timestamp without time zone,OushuDB 将悄悄忽略任何文本中指出的时区。因此,生成的日期/时间值是从输入值的日期/时间字段衍生出来的, 并且没有就时区进行调整。

对于timestamp with time zone,内部存储的数值总是 UTC(全球统一时间, 以前也叫格林威治时间GMT)。如果一个输入值有明确的时区声明, 那么它将用该时区合适的偏移量转换成 UTC 。如果在输入字符串里没有时区声明, 那么它就假设是在系统的TimeZone参数里的那个时区, 然后使用这个timezone时区转换成 UTC 。

当输出timestamp with time zone时,它始终从UTC转换为当前时区,并显示为该区域中的本地时间。要查看其他时区的时间,请更改时区或使用AT TIME ZONE结构(请参阅 AT TIME ZONE

在timestamp without time zone和timestamp with time zone 之间的转换通常假设timestamp without time zone数值应该以 timezone本地时间的形式接受或者写出。其它的时区可以用 AT TIME ZONE的方式为转换声明。

间隔#

间隔值可以用下面的语法写:

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

这里quantity是一个数字(可能已标记);unit 可以是microsecond,millisecond,second, minute,hour,day, week,month,year, decade,century,millennium 或这些单位的缩写或复数。direction可以是ago或为空。 @标记是可选的。不同的单位的数量被隐式地添加适当的计算符号。

可以在没有明确单位标记的情况下声明天,小时,分钟和秒。例如,’1 12:59:10’ 等同于’1 day 12 hours 59 min 10 sec’。

可选的秒的精确度p应该取0-6,而且其缺省值与输入文本的精确度一致。

内部,interval值被存储为月,日,秒的格式, 这是因为月中包含天数不同,并且如果进行了夏令时调整,那么一天可以有23或25小时。 当秒字段可以存储分数时,月和天字段可以是整数型。 由于时间间隔通常是由常量字符串或timestamp减法来定义的, 这种存储方法在大多数情况下很有效。justify_days和justify_hours 函数可用于调整溢出正常范围值的天和小时。

特殊值#

OushuDB 为方便起见支持在下表里面显示的几个特殊输入值。 值infinity和-infinity是特别在系统内部表示的, 并且将按照同样的方式显示;但是其它的都只是符号缩写, 在读取的时候将被转换成普通的日期/时间值。特别是now 和相关的字符串在读取的时候就被转换成对应的数值。 所有这些值在 SQL 命令里当作普通常量对待时,都需要包围在单引号里面。

表.特殊日期/时间输入

输入字符串

适用类型

描述

epoch

date, timestamp

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

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。后四个接受一个可选的精度声明( 当前日期/时间 )。不过,请注意这些 SQL 函数不是 被当作数据输入字符串识别的。

日期/时间输出#

日期/时间类型的输出格式可以通过SET datestyle 命令设成 ISO 8601(默认)、SQL(Ingres)、 传统的POSTGRES(Unix date格式)或German四种风格之一。缺省的是ISO 格式。(SQL标准要求使用 ISO 8601 格式。) 下表显示了每种输出风格的例子。 date和time类型的输出当然只是给出的例子里面的日期和时间部分。

表. 日期/时间输出风格

风格

描述

例子

ISO

ISO 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

German

地区风格

17.12.1997 07:37:16.00 PST

如果声明了 DMY 顺序,那么在SQL和 POSTGRES 风格里, 日期在月份之前出现,否则月份出现在日期之前(参阅 日期/时间输入 看看这个设置如何影响对输入值的解释)。下表显示了一个例子。

表. 日期顺序习惯

datestyle设置

输入顺序

输出样例

SQL, DMY

日/月/年

17/12/1997 15:37:16.00 CET

SQL, MDY

月/日/年

12/17/1997 07:37:16.00 PST

Postgres, DMY

日/月/年

Wed 17 Dec 07:37:16 1997 PST

间隔输出格式看起来像输入格式,除了century或week单位被转换成了years和days,而且ago被转换成一个合适的标记。在ISO 模式中,输入看起来像:

[ quantity unit [ ... ] ] [ days ] [ hours:minutes:seconds ]

用户可以用SET datestyle命令选取日期/时间的风格, 也可以在配置文件中的DateStyle参数中设置,或者在服务器或客户端的 PGDATESTYLE环境变量中设置。也可以用格式化函数to_char(参见 数据类型格式化参数 ) 来更灵活地控制时间/日期地输出。

时区#

时区和时区习惯不仅仅受地球几何形状的影响,还受到政治决定的影响。 到了 19 世纪,全球的时区变得稍微标准化了些,但是还是易于遭受随意的修改, 尤其是因为夏时制规则。OushuDB 支持1902年到2038年间的夏时令规则(与传统Unix 系统时间的全范围一致)。不属于这个范围的时间被看作选定时区的“标准时间”,无论它落入一年中的哪个部分。

OushuDB 在典型应用中尽可能与SQL 的定义相兼容。但SQL标准在日期/时间类型和功能上有一些奇怪的混淆。 两个显而易见的问题是:

  • date类型与时区没有联系,而time类型却有。 然而,现实世界的时区只有在与时间和日期都关联时才有意义, 因为时间偏移量(时差)可能因为实行类似夏时制这样的制度而在一年里有所变化。

  • 缺省的时区用一个数字常量表示与UTC的偏移(时差)。因此, 当跨DST(夏时制)界限做日期/时间算术时, 我们根本不可能把夏时制这样的因素计算进去。

为了克服这些困难,我们建议在使用时区的时候,使用那些同时包含日期和时间的日期/时间类型。 我们建议不要使用time with time zone类型(尽管 OushuDB 出于合理应用以及为了与SQL 标准兼容的考虑支持这个类型)。OushuDB 假设你用于任何类型的本地时区都只包含日期或时间(而不包含时区)。

在系统内部,所有时区已知的日期和时间都用全球统一时间UTC格式存储, 时间在发给客户前端前由数据库服务器根据TimeZone 配置参数声明的时区转换成本地时间。

OushuDB 允许你用三种方法指定时区:

  • 完整的时区名。例如Asia/Shanghai。所有可以识别的时区名在 pg_timezone_names视图中列出。OushuDB 使用广泛使用的zic 时区数据, 所以这些时区名在其它软件里也能被轻松的识别。

  • 时区缩写。例如PST。这种缩写名通常只是定义了相对于 UTC 的偏移量, 不同于完整的时区名可能还隐含着一组夏时制转换规则。 所有可以识别的时区缩写在pg_timezone_abbrevs视图中列出。你不能设置 TimeZone或log_timezone 配置参数为时区缩写,但是你可以在日期/时间输入值中结合AT TIME ZONE 操作符使用时区缩写。

  • 除完整的时区名及其缩写之外,OushuDB 还接受 POSIX 风格的 STDoffset或STDoffsetDST 格式的时区,其中的STD是时区缩写、offset 是一个相对于 UTC 的小时偏移量、DST是一个可选的夏时制时区缩写,假定相对于给定的偏移量提前一小时。例如,如果EST5EDT不是一个已识别的时区名, 那么它将等同于美国东部时间。如果存在夏时制时区名是当前时区名,根据zic 时区数据库的 posixrules条目中相同的夏时制事务规则,可以考虑使用这个特性。 在一个OushuDB 标准安装中,posixrules与US/Eastern 相同,因此POSIX格式的时区声明遵循USA夏时制规则。如果需要,可以通过替换posixrules 文件来调整该习惯。

这就是完整的时区名与时区缩写之间的差异:时区缩写总是代表一个相对于 UTC 的固定偏移量, 然而大多数完整的时区名隐含着一个本地夏令时规则,因此就有可能有两个相对于 UTC 的不同偏移量。

需要警惕的是,由于没有合理的时区缩写检查,POSIX格式的时区特点能导致静默的伪输入。 例如,使用SET TIMEZONE TO FOOBAR0时,实际上系统使用的是一个很特别的UTC缩写。时区名是大小写不敏感的。

可以在postgresql.conf文件里设置timezone 配置参数, 除此之外,还有好几种特殊方法可以设置它:

  • 如果在postgresql.conf中没有声明时区或时区没有作为服务器命令行的一个选择,服务器会尝试使用TZ 环境变量作为缺省的时区。如果TZ 没有被定义或没有任何其他对 OushuDB 已知的时区,服务器会试图通过检查C语言库函数localtime() 得到服务器操作系统的缺省值。该缺省值被选作在OushuDB 中所有已知的时区中最易匹配的。

  • 使用SQL命令SET TIME ZONE为会话设置时区, 这是SET TIMEZONE TO的一个可选的拼写方式,更加兼容标准。

  • 如果在客户端设置了PGTZ环境变量,那么libpq 在连接时将使用这个环境变量给后端发送一个SET TIME ZONE命令。

内部#

OushuDB 使用 Julian 日期进行所有的日期/时间计算。假设一年的长度是 365.2425 天。这个方法可以很精确地预计/计算从 4713 BC(公元前 4713 年)到很久的未来中任意一天的日期。

19 世纪以前的日期传统(历法)只在一些趣味读物上出现,因此在日期/时间控制器中并没有考虑。