日期/时间类型
本页目录
日期/时间类型#
下表显示了 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 世纪以前的日期传统(历法)只在一些趣味读物上出现,因此在日期/时间控制器中并没有考虑。