本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。
本文介绍了日期/时间类型的定义及相关语法。
日期/时间类型
名字  | 存储尺寸  | 说明  | 最小值  | 最大值  | 解析度  | 
timestamp [ (p) ] [ without time zone ]  | 8字节  | 包括日期和时间(无时区)。  | 4713 BC  | 294276 AD  | 1微秒  | 
timestamp [ (p) ] with time zone  | 8字节  | 包括日期和时间,有时区。  | 4713 BC  | 294276 AD  | 1微秒  | 
date  | 8字节  | 日期和时间。  | 4713 BC  | 5874897 AD  | 1秒  | 
time [ (p) ] [ without time zone ]  | 8字节  | 一天中的时间(无日期)。  | 00:00:00  | 24:00:00  | 1微秒  | 
time [ (p) ] with time zone  | 12字节  | 仅仅是一天中的时间(没有日期),带有时区。  | 00:00:00+1459  | 24:00:00-1459  | 1微秒  | 
INTERVAL DAY TO SECOND [(p)]  | 12字节  | 带精度的表示天到秒的时间间隔。  | -178000000年  | 178000000年  | 1微秒  | 
INTERVAL YEAR TO MONTH  | 6字节  | 表示年到月的时间间隔。  | -178000000 年  | 178000000 年  | 1微秒  | 
SQL 要求只写timestamp等效于timestamp without time zone,并且本数据库鼓励这种行为。timestamptz被接受为timestamp with time zone的一种简写,这是一种本数据库的扩展。
time、timestamp和interval接受一个可选的精度值 p,这个精度值声明在秒域中小数点之后保留的位数。缺省情况下,在精度上没有明确的边界。p允许的范围是从 0 到 6。
interval类型有一个附加选项,它可以通过写下面之一的短语来限制存储的 fields 的集合:
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`的组合就应该能提供任何应用所需的全范围的日期/时间功能。
### 1.  日期/时间输入
日期和时间的输入可以接受几乎任何合理的格式,包括 ISO 8601、SQL-兼容的和其他的形式。 对于一些格式,日期输入里的日、月和年的顺序会让人混淆, 并且支持指定所预期的这些域的顺序。把DateStyle参数设置为`MDY`,就是选择“月-日-年”的解释,设置为`DMY`就是 “日-月-年”,而`YMD`是 “年-月-日”。
本数据库在处理日期/时间输入上比SQL标准要求的更灵活。
请记住任何日期或者时间的文字输入需要由单引号包围,就象一个文本字符串一样。SQL要求下面的语法
```sql
    type [ (p) ] 'value'其中p是一个可选的精度声明,它给出了在秒域中的小数位数目。精度可以被指定给time、timestamp和interval类型,并且可以取从 0 到 6 的值。这允许前文所述的值。如果在一个常数声明中没有指定任何精度,它将默认取文字值的精度(但不能超过 6 位)。
日期
日期输入
例子  | 描述  | 
1999-01-08  | ISO 8601; 任何模式下的1月8日 (推荐格式)  | 
January 8, 1999  | 在任何  | 
1/8/1999  | 
  | 
1/18/1999  | 
  | 
01/02/03  | 
  | 
1999-Jan-08  | 任何模式下的1月8日  | 
Jan-08-1999  | 任何模式下的1月8日  | 
08-Jan-1999  | 任何模式下的1月8日  | 
99-Jan-08  | 
  | 
08-Jan-99  | 1月8日,除了在  | 
Jan-08-99  | 1月8日,除了在  | 
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值中。
时间输入
例子  | 描述  | 
  | ISO 8601  | 
  | ISO 8601  | 
  | ISO 8601  | 
  | ISO 8601  | 
  | 和04:05一样,AM并不影响值  | 
  | 和16:05一样,输入的小时必须为 <= 12  | 
  | ISO 8601  | 
  | ISO 8601  | 
  | ISO 8601  | 
  | ISO 8601  | 
  | 缩写指定的时区  | 
  | 全名指定的时区  | 
时区输入
例子  | 描述  | 
  | 缩写(太平洋标准时间)  | 
  | 完整时区名  | 
  | POSIX风格的时区声明  | 
  | PST的ISO-8601偏移  | 
  | PST的ISO-8601偏移  | 
  | PST的ISO-8601偏移  | 
  | UTC的军方缩写  | 
  | 
  | 
时间戳
时间戳类型的有效输入由一个日期和时间的串接组成,后面跟着一个可选的时区,一个可选的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。本数据库从来不会在确定文字串的类型之前检查其内容,因此会把上面两个都看做是 timestamp without time zone。 因此要保证把上面的文字当作timestamp with time zone看待, 就要给它正确的显式类型:
    TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'如果一个文字已被确定是timestamp without time zone,本数据库将不声不响忽略任何其中指出的时区。 即,结果值是从输入值的日期/时间域衍生出来的,并且没有就时区进行调整。
对于timestamp with time zone,内部存储的值总是 UTC (全球统一时间,以前也叫格林威治时间 GMT)。如果一个输入值有明确的时区声明, 那么它将用该时区合适的偏移量转换成 UTC。如果在输入串里没有时区声明, 那么它就被假设是在系统的 TimeZone 参数里的那个时区,然后使用这个 timezone时区的偏移转换成 UTC。
如果一个timestamp with time zone值被输出,那么它总是从 UTC 转换成当前的timezone时区,并且显示为该时区的本地时间。要看其它时区的时间,要么修改timezone,要么使用AT TIME ZONE构造。
在timestamp without time zone和timestamp with time zone之间的转换通常假设timestamp without time zone值应该以timezone本地时间的形式接受或者写出。为该转换指定一个不同的可以用AT TIME ZONE。
特殊值
为了方便,本数据库支持一些特殊日期/时间输入值。这些值中infinity和-infinity被在系统内部以特殊方式表示并且将被原封不动地显示。但是其他的仅仅只是概念上的速写,当被读到的时候会被转换为正常的日期/时间值(特殊地,now及相关串在被读到时立刻被转换到一个指定的时间值)。在作为常量在 SQL 命令中使用时,所有这些值需要被包括在单引号内。
特殊日期/时间输入
输入串  | 合法类型  | 描述  | 
  | 
  | 1970-01-01 00:00:00+00(Unix系统时间0)  | 
  | 
  | 比任何其他时间戳都晚  | 
  | 
  | 比任何其他时间戳都早  | 
  | 
  | 当前事务的开始时间  | 
  | 
  | 今日午夜 (  | 
  | 
  | 明日午夜 (  | 
  | 
  | 昨日午夜 (  | 
  | 
  | 00:00:00.00 UTC  | 
下列 SQL-兼容的函数可以被用来为相应的数据类型获得当前时间值: CURRENT_DATE、CURRENT_TIME、 CURRENT_TIMESTAMP、LOCALTIME、 LOCALTIMESTAMP。注意这些是 SQL 函数并且在数据输入串中不被识别。
虽然输入字符串 now、today、tomorrow和yesterday都可以在交互式 SQL 命令中使用,但当命令被保存以待稍后执行时,它们可能会有令人惊讶的行为,例如在准备的语句、视图和函数定义中。 字符串可以被转换为特定的时间值,该值在变得过时后长时间继续使用。 在这种上下文中使用某一种 SQL 函数代替。例如, CURRENT_DATE + 1 比 'tomorrow'::date更安全。
日期/时间输出
时间/日期类型的输出格式可以设成四种风格之一: ISO 8601、SQL(Ingres)Unix 的date格式或 German 。缺省是 ISO 格式(ISO 标准要求使用 ISO 8601 格式。ISO 输出格式的名字是历史偶然)。date和time类型的输出通常只有日期或时间部分和例子中一致。不过,本数据库风格输出的是 ISO 格式的只有日期的值。
日期/时间输出风格
风格声明  | 描述  | 例子  | 
  | ISO 8601, SQL标准  | 
  | 
  | 传统风格  | 
  | 
  | 原始风格  | 
  | 
  | 地区风格  | 
  | 
ISO 8601 指定使用大写字母T来分隔日期和时间。本数据库在输入上接受这种格式,但是在输出时它采用一个空格而不是T,如上所示。和一些其他数据库系统一样,这是为了可读性以及与 RFC 3339 的一致性。
SQL 风格中,如果 DMY 域顺序被指定,“日”将出现在“月”之前,否则“月”出现在“日”之前。
日期顺序习惯
  | 输入顺序  | 例子输出  | 
  | 
  | 
  | 
  | 
  | 
  | 
  | 
  | 
  | 
格式化函数to_char也可以作为一个更灵活的方式来格式化日期/时间输出。
时区
时区和时区习惯不仅仅受地球几何形状的影响,还受到政治决定的影响。 到了 19 世纪,全球的时区变得稍微标准化了些,但是还是易于遭受随意的修改,部分是因为夏时制规则。本数据库使用广泛使用的 IANA (Olson) 时区数据库来得到有关历史时区规则的信息。对于未来的时间,我们假设关于一个给定时区的最新已知规则将会一直持续到无穷远的未来。
本数据库努力在典型使用中与 SQL 标准的定义相兼容。但 SQL 标准在日期和时间类型和功能上有一些奇怪的混淆。两个显而易见的问题是:
尽管
date类型与时区没有联系,而time类型却可以有。 然而,现实世界的时区只有在与时间和日期都关联时才有意义, 因为偏移(时差)可能因为实行类似夏时制这样的制度而在一年里有所变化。缺省的时区会指定一个到 UTC 的数字常量偏移(时差)。因此,当跨 DST 边界做日期/时间算术时, 我们根本不可能适应于夏时制时间。
为了克服这些困难,我们建议在使用时区的时候,使用那些同时包含日期和时间的日期/时间类型。我们不建议使用类型 time with time zone (尽管本数据库出于遗留应用以及与 SQL 标准兼容性的考虑支持这个类型)。 本数据库假设你用于任何类型的本地时区都只包含日期或时间。
在系统内部,所有时区相关的日期和时间都用 UTC 存储。它们在被显示给客户端之前会被转换成由 TimeZone 配置参数指定的本地时间。
本数据库允许你使用三种不同形式指定时区:
一个完整的时区名字,例如
America/New_York。能被识别的时区名字被列在pg_timezone_names视图中。本数据库用广泛使用的 IANA 时区数据来实现该目的,因此相同的时区名字也可以在其他软件中被识别。一个时区缩写,例如
PST。这样一种声明仅仅定义了到 UTC 的一个特定偏移,而不像完整时区名那样指出整套夏令时转换规则。 能被识别的缩写被列在pg_timezone_abbrevs视图中。 你不能将配置参数 TimeZone 或 log_timezone 设置成一个时区缩写,但是你可以在日期/时间输入值和AT TIME ZONE操作符中使用时区缩写。除了时区名和缩写,本数据库将接受 POSIX-风格的时区规范。 这个选项通常不优先用于指定时区,但是,如果没有合适的 IANA 时区条目,这可能是必要的。
简而言之,在缩写和全称之间是有不同的:缩写表示从 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。 本数据库会根据在指定的日期它们到底表示什么(或者最近表示什么) 来解释这种缩写。但是,正如上面的EST例子所示,这并不是必须和那一天的本地标准时间相同。
在所有情况下,时区名及其缩写都是大小写不敏感的(这是对本数据库 8.2 之前版本的一个修改,在这些版本中某些环境下时区名是大小写敏感的而在另外一些环境中却是大小写不敏感的)。
时区名和缩写都不是硬写在服务器中的,它们是从存储在安装目录下的.../share/timezone/和.../share/timezonesets/子目录中获取的。
TimeZone 配置参数可以在文件postgresql.conf中被设置。同时也有一些特殊的方法来设置它:
SQL 命令
SET TIME ZONE为会话设置时区。它是SET TIMEZONE TO的另一种拼写,它更加符合 SQL 的语法。libpq客户端使用
PGTZ环境变量来通过连接发送一个SET TIME ZONE命令给服务器。
间隔输入
interval值可以使用下列语法书写:
    [@] quantity unit [quantity unit...] [direction]其中quantity是一个数字(很可能是有符号的); unit是毫秒、 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年10个月'(这些较短的形式事实上是 SQL 标准唯一许可的形式,并且在IntervalStyle被设置为sql_standard时用于输出)。
间隔值也可以被写成 ISO 8601 时间间隔,使用该标准 4.4.X.X 小节的“带标志符的格式”或者 4.4.X.X 小节的“替代格式”。带标志符的格式看起来像这样:
    P quantity unit [ quantity unit ...] [ T [ quantity unit ...]]该串必须以一个P开始,并且可以包括一个引入当日时间单位的T。单位可以被忽略,并且可以以任何顺序指定,但是小于一天的单位必须出现在T之后。特别地,M的含义取决于它出现在T之前还是之后。
ISO 8601 间隔单位缩写
缩写  | 含义  | 
Y  | 年  | 
M  | 月(在日期部分中)  | 
W  | 周  | 
D  | 日  | 
H  | 小时  | 
M  | 分钟(在时间部分中)  | 
S  | 秒  | 
如果使用替代格式:
    P [ years-months-days ] [ T hours:minutes:seconds ]串必须以P开始,并且一个T分隔间隔的日期和时间部分。其值按照类似于 ISO 8601 日期的数字给出。
在用一个域声明书写一个间隔常量时,或者为一个用域声明定义的间隔列赋予一个串时,对于为标记的量的解释依赖于域。例如INTERVAL '1' YEAR被解读成 1 年,而INTERVAL '1'表示 1 秒。同样,域声明允许的最后一个有效域“右边”的域值会被无声地丢弃掉。例如书写INTERVAL '1 day 2:03:04' HOUR TO MINUTE将会导致丢弃秒域,而不是日域。
根据 SQL 标准,一个间隔值的所有域都必须由相同的符号,这样一个领头的负号将会应用到所有域;例如在间隔文字'-1 2:03:04'中的负号会被应用于日、小时、分钟和秒部分。本数据库允许域具有不同的符号,并且在习惯上认为以文本表示的每个域具有独立的符号,因此在这个例子中小时、分钟和秒部分被认为是正值。如果IntervalStyle被设置为sql_standard,则一个领头的符号将被认为是应用于所有域(但是仅当没有额外符号出现)。否则将使用传统的本数据库解释。为了避免混淆,我们推荐在任何域为负值时为每一个域都附加一个显式的符号。
在冗长的输入格式中,以及在更紧凑输入格式的某些域中,域值可以有分数部分;例如'1.5 week'或'01:02:03.45'。这样的输入被转换为合适的月数、日数和秒数用于存储。当这样会导致月和日中的分数时,分数被加到低序域中,使用的转换因子是 1 月=30 日和 1 日=24 小时。例如,'1.5 month'会变成 1 月和 15 日。只有秒总是在输出时被显示为分数。
间隔输入
例子  | 描述  | 
  | SQL标准格式:1年2个月  | 
  | SQL标准格式:3日4小时5分钟6秒  | 
  | 传统Postgres格式:1年2个月3日4小时5分钟6秒钟  | 
  | “带标志符的”ISO 8601 格式:含义同上  | 
  | ISO 8601 的“替代格式”:含义同上  | 
在内部,interval值被存储为 months、days 以及 seconds。之所以这样做是因为一个月中的天数是变化的,并且在涉及到夏令时调整时一天可以有 23 或者 25 个小时。months 以及 days 域是整数,而 seconds 域可以存储分数。因为区间通常是从常量字符串或者timestamp减法创建而来,这种存储方法在大部分情况下都很好,但是也可能导致预料之外的结果:
    SELECT EXTRACT(hours from '80 minutes'::interval);
     date_part
    -----------
             1
    SELECT EXTRACT(days from '80 hours'::interval);
     date_part
    -----------
             0函数justify_days和justify_hours可以用来调整溢出其正常范围之外的 days 和 hours。
间隔输出
间隔类型的输出格式可以被设置为四种风格之一:sql_standard、postgres、postgres_verbose或iso_8601,设置方法使用SET intervalstyle命令。默认值为postgres格式。
如果间隔值符合 SQL 标准的限制(仅年-月或仅日-时间,没有正负值部分的混合),sql_standard风格为间隔文字串产生符合 SQL 标准规范的输出。否则输出将看起来像一个标准的年-月文字串跟着一个日-时间文字串,并且带有显式添加的符号以区分混合符号的间隔。
间隔输出风格例子
风格声明  | 年-月间隔  | 日-时间间隔  | 混合间隔  | 
  | 1-2  | 3 4:05:06  | -1-2 +3 -4:05:06  | 
  | 1 year 2 mons  | 3 days 04:05:06  | -1 year -2 mons +3 days -04:05:06  | 
  | @ 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  | 
  | P1Y2M  | P3DT4H5M6S  | P-1Y-2M3DT-4H-5M-6S  |