表 8-9
显示了
PostgreSQL
支持的
SQL
中所有日期和时间类型。
这些数据类型上可以进行的操作在
第 9.9 节
中描述。
日期是按照公历计算的,甚至日历之前的年份也介绍了
(参阅
第 B.4 节
获取更多信息)。
表 8-9. 日期/时间类型
名字
|
存储空间
|
描述
|
最低值
|
最高值
|
分辨率
|
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。
对于
time
类型,如果使用了八字节的整数存储,那么
p
允许的范围是从 0 到 6 ,如果使用的是浮点数存储,那么这个范围是 0 到 10 。
interval
类型有一个额外的选项,通过下下面词组之一限制存储的字段值:
MONTH
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. Date Input
例子
|
描述
|
1999-01-08
|
ISO 8601格式(建议格式),任何方式下都是 1999 年 1 月 8 号
|
January 8, 1999
|
在任何
datestyle
输入模式下都无歧义
|
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
。
这些类型的有效输入由当日时间后面跟着可选的时区组成(参阅
表 8-11
和
表 8-12
)。
如果在
time without time zone
类型的输入中声明了时区,那么它会被悄悄地忽略。
同样指定的日期也会被忽略,除非使用了一个包括夏令时规则的时区名,比如
America/New_York
,在这种情况下,
必须指定日期以确定这个时间是标准时间还是夏令时。时区偏移将记录在
time with time zone
中。
表 8-11. 时间输入
例子
|
描述
|
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. 时区输入
例子
|
描述
|
PST
|
太平洋标准时间(Pacific Standard Time)
|
America/New_York
|
完整时区名称
|
PST8PDT
|
POSIX 风格的时区
|
-8:00
|
ISO-8601 与 PST 的偏移
|
-800
|
ISO-8601 与 PST 的偏移
|
-8
|
ISO-8601 与 PST 的偏移
|
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. 特殊日期/时间输入
输入字符串
|
适用类型
|
描述
|
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
。后四个接受一个可选的精度声明(
第 9.9.4 节
)。不过,请注意这些 SQL 函数
不是
被当作数据输入字符串识别的。
时区和时区习惯不仅仅受地球几何形状的影响,还受到政治决定的影响。
到了 19 世纪,全球的时区变得稍微标准化了些,但是还是易于遭受随意的修改,
部分是因为夏时制规则。
PostgreSQL
使用广泛使用的
zoneinfo
(Olson)时区信息数据库有关历史时区的规则。
对于未来的时间,假设对于给定时区最近的规则将在未来继续无期限的遵守。
PostgreSQL
在典型应用中尽可能与
SQL
的定义相兼容。但
SQL
标准在日期/时间类型和功能上有一些奇怪的混淆。
两个显而易见的问题是:
为了克服这些困难,我们建议在使用时区的时候,使用那些同时包含日期和时间的日期/时间类型。
我们建议
不要
使用
time with time zone
类型(尽管
PostgreSQL
出于合理应用以及为了与
SQL
标准兼容的考虑支持这个类型)。
PostgreSQL
假设你用于任何类型的本地时区都只包含日期或时间(而不包含时区)。
在系统内部,所有日期和时间都用全球统一时间
UTC
格式存储,
时间在发给客户前端前由数据库服务器根据
TimeZone
配置参数声明的时区转换成本地时间。
PostgreSQL
允许你用三种方法指定时区:
-
完整的时区名。例如
America/New_York
。所有可以识别的时区名在
pg_timezone_names
视图中列出(参见
第 47.71 节
)。
PostgreSQL
使用广泛使用的
zoneinfo
时区数据,
所以这些时区名在其它软件里也能被轻松的识别。
-
时区缩写。例如
PST
。这种缩写名通常只是定义了相对于 UTC 的偏移量,
而前一种完整的时区名可能还隐含着一组夏时制转换规则。
所有可以识别的时区缩写在
pg_timezone_abbrevs
视图中列出(参见
第 47.70 节
)。你不能设置
TimeZone
或
log_timezone
配置参数为时区缩写,但是你可以在日期/时间输入值中结合
AT TIME ZONE
操作符使用时区缩写。
-
除完整的时区名及其缩写之外,
PostgreSQL
还接受 POSIX 风格的
STD
offset
或
STD
offset
DST
格式的时区,其中的
STD
是时区缩写、
offset
是一个相对于 UTC 的小时偏移量、
DST
是一个可选的夏时制时区缩写
(假定相对于给定的偏移量提前一小时)。例如,如果
EST5EDT
不是一个已识别的时区名,
那么它将等同于美国东部时间。如果存在夏时制时区名是当前时区名,根据
zoneinfo
时区数据库的
posixrules
条目中相同的夏时制事务规则,可以考虑使用这个特性。
在一个
PostgreSQL
标准安装中,
posixrules
与
US/Eastern
相同,因此POSIX格式的时区声明遵循USA夏时制规则。如果需要,可以通过替换
posixrules
文件来调整该习惯。
简言之,这就是完整的时区名与时区缩写之间的差异:时区缩写总是代表一个相对于 UTC 的固定偏移量,
然而大多数完整的时区名隐含着一个本地夏令时规则,因此就有可能有两个相对于 UTC 的不同偏移量。
需要警惕的是,由于没有合理的时区缩写检查,POSIX格式的时区特点能导致静默的伪输入。
例如,使用
SET TIMEZONE TO FOOBAR0
时,实际上系统使用的是一个很特别的UTC缩写。
另一个需要注意的是,在POSIX时区名中,积极的偏移用于
west
格林尼治位置。
在其他地方,
PostgreSQL
遵循ISO-8601规定,
即积极的时区偏移
east
格林威治。
总体而言,
PostgreSQL
8.2 版本以后时区名在所有情况下都是大小写无关的。
而之前的版本在某些情况下是大小写敏感的。
无论是完整的时区名还是时区缩写都不是硬连接进服务器的,它们都是从安装目录下的
.../share/timezone/
和
.../share/timezonesets/
配置文件中获取的(参见
第 B.3 节
)。
可以在
postgresql.conf
文件里设置
TimeZone
配置参数,或者用任何其它在
第 18 章
描述的标准方法。
除此之外,还有好几种特殊方法可以设置它:
间隔类型的输出格式可以用命令
SET intervalstyle
设置为下面四种类型:
sql_standard
,
postgres
,
postgres_verbose
或
iso_8601
。默认是
postgres
格式,
表 8-18
中有每种格式的示例。
sql_standard
格式产生的输出结果符合SQL的间隔字符串标准,
如果间隔值满足标准的限制(无论只有年-月,或只有天-时间,没有积极和消极的构成的混合)。
否则输出类似一个标准年-月文本字符串后跟有一个天-时间文本字符串,
带有添加明确标记的消除歧义混合信号的时间间隔。
当参数
DateStyle
设置为
ISO
时,
postgres
格式的输出与
PostgreSQL
8.4之前的版本一致。
当参数
DateStyle
设置为非-
ISO
,
postgres_verbose
格式的输出与
PostgreSQL
8.4之前的版本一致。
iso_8601
格式的输出与ISO 8601标准4.4.3.2节中的
"format with designators"
一致。
表 8-18. 间隔输出格式示例
格式
|
年-月间隔
|
天-时间间隔
|
混合间隔
|
sql_standard
|
1-2
|
3 4:05:06
|
-1-2 +3 -4:05:06
|
postgres
|
1 年 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_8601
|
P1Y2M
|
P3DT4H5M6S
|
P-1Y-2M3DT-4H-5M-6S
|