==========================  时间/日期函数和操作符 ========================== .. container:: sect1 :name: FUNCTIONS-DATETIME .. container:: titlepage .. container:: .. container:: .. rubric:: 时间/日期函数和操作符 :name: 时间日期函数和操作符 :class: title .. container:: toc `EXTRACT, date_part `__ `date_trunc `__ `AT TIME ZONE `__ `当前日期/时间 `__ `延时执行 `__ `表 `__\ 展示了可用于处理日期/时间值的函数,其细节在随后的小节中描述。\ `表 `__\ 演示了基本算术操作符 (\ ``+``\ 、\ ``*``\ 等)的行为。 而与格式化相关的函数,可以参考\ `节 `__\ 。你应该很熟悉\ `节 `__\ 中的日期/时间数据类型的背景知识。 所有下文描述的接受\ ``time``\ 或\ ``timestamp``\ 输入的函数和操作符实际上都有两种变体: 一种接收\ ``time with time zone``\ 或\ ``timestamp with time zone``\ , 另外一种接受\ ``time without time zone``\ 或者 ``timestamp without time zone``\ 。为了简化,这些变种没有被独立地展示。此外,\ ``+``\ 和\ ``*``\ 操作符都是可交换的操作符对(例如,date + integer 和 integer + date);我们只显示其中一个。 .. container:: table :name: OPERATORS-DATETIME-TABLE **表日期/时间操作符** .. container:: table-contents +--------+-----------------------------+-----------------------------+ | 操作符 | 例子 | 结果 | +========+=============================+=============================+ | ``+`` | ``date ' | ``date '2001-10-05'`` | | | 2001-09-28' + integer '7'`` | | +--------+-----------------------------+-----------------------------+ | ``+`` | ``date '2001-0 | ``timest | | | 9-28' + interval '1 hour'`` | amp '2001-09-28 01:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``+`` | ``date '2 | ``timest | | | 001-09-28' + time '03:00'`` | amp '2001-09-28 03:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``+`` | ``interval '1 | ``interval | | | day' + interval '1 hour'`` | '1 day 01:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``+`` | ``timestamp '2001-09-28 01: | ``timest | | | 00' + interval '23 hours'`` | amp '2001-09-29 00:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``+`` | ``time '01 | ``time '04:00:00'`` | | | :00' + interval '3 hours'`` | | +--------+-----------------------------+-----------------------------+ | ``-`` | ``- interval '23 hours'`` | ``interval '-23:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``-`` | ``date '2001-1 | ``integer '3' (days)`` | | | 0-01' - date '2001-09-28'`` | | +--------+-----------------------------+-----------------------------+ | ``-`` | ``date ' | ``date '2001-09-24'`` | | | 2001-10-01' - integer '7'`` | | +--------+-----------------------------+-----------------------------+ | ``-`` | ``date '2001-0 | ``timest | | | 9-28' - interval '1 hour'`` | amp '2001-09-27 23:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``-`` | ``ti | ``interval '02:00:00'`` | | | me '05:00' - time '03:00'`` | | +--------+-----------------------------+-----------------------------+ | ``-`` | ``time '05 | ``time '03:00:00'`` | | | :00' - interval '2 hours'`` | | +--------+-----------------------------+-----------------------------+ | ``-`` | ``timestamp '2001-09-28 23: | ``timest | | | 00' - interval '23 hours'`` | amp '2001-09-28 00:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``-`` | ``interval '1 day' | ``i | | | - interval '1 hour'`` | nterval '1 day -01:00:00'`` | +--------+-----------------------------+-----------------------------+ | ``-`` | ``timesta | ``interval | | | mp '2001-09-29 03:00' - tim | '1 day 15:00:00'`` | | | estamp '2001-09-27 12:00'`` | | +--------+-----------------------------+-----------------------------+ | ``*`` | ``900 * interval | ``interval '00:15:00'`` | | | '1 second'`` | | +--------+-----------------------------+-----------------------------+ | ``*`` | ``21 * interval '1 day'`` | ``interval '21 days'`` | +--------+-----------------------------+-----------------------------+ | ``*`` | ``double precision | ``interval '03:30:00'`` | | | '3.5' * interval '1 hour'`` | | +--------+-----------------------------+-----------------------------+ | ``/`` | ``interval '1 hour'`` | ``interval '00:40:00'`` | | | ``/ double precision '1.5'``| | +--------+-----------------------------+-----------------------------+ .. container:: table :name: FUNCTIONS-DATETIME-TABLE **表 日期/时间函数** .. container:: table-contents +-------------+-------------+-------------+-------------+-------------+ | 函数 | 返回类型 | 描述 | 例子 | 结果 | +=============+=============+=============+=============+=============+ | | ``int | 减去参数 | ``age(times | 43 年 9 | | ``age(t | erval`` | ,生成一个 | tamp '2001- | 月 27 日 | | imestamp, | | 使用年、月 | 04-10', tim | | | t | | (而不是只 | estamp '195 | | | imestamp)`` | | 用日)的“符 | 7-06-13')`` | | | | | 号化”的结果 | | | +-------------+-------------+-------------+-------------+-------------+ | ``age(t | ``int | 从\ | ``age(tim | 43 years | | imestamp``) | erval`` | ``current_d | estamp '195 | 8 months | | | | ate``\ (在 | 7-06-13')`` | 3 days | | | | 午夜)减去 | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time | 当前 |   |   | | ``clock_ti | stamp with | 日期和时间 | | | | mestamp()`` | time zone`` | (在语句执 | | | | | | 行期间变化 | | | | | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``date`` | 当前日 |   |   | | ``cur | | 期;见\ | | | | rent_date`` | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | | 当 |   |   | | ``cur | ``time with | 前时间(一 | | | | rent_time`` | time zone`` | 天中的时间 | | | | | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time | 当前日期和 |   |   | | ``current_ | stamp with | 时间(当前 | | | | timestamp`` | time zone`` | 事务开始时 | | | | | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``double | 获得子域( | ``date_par | ``20`` | | ``date_p | precision`` | 等价于\ ``e | t('hour', t | | | art(text``, | | xtract``\ | imestamp '2 | | | ``t | | );见\ | 001-02-16 2 | | | imestamp``) | | `节 | 0:38:40')`` | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | ``date_p | ``double | 获得子域( | ``date_ | ``3`` | | art(text, | precision`` | 等价于\ ``e | part('month | | | int | | xtract``\ | ', interval | | | erval``) | | );见\ | '2 years 3 | | | | | `节 | months')`` | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``timestam | 截断到指定 | ``date_trun | | | ``date_tr | p`` | 精度;见\ | c('hour', t | ``2001-02-16| | unc(text, | | `节 | imestamp '2 | 20:00:00`` | | t | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | ``date_tr | ``time | 在指 | ``d | ``20 | | unc(text, | stamp with | 定的时区截 | ate_trunc( | 01-02-16 13 | | times | time zone`` | 断到指定的 | 'day', time | :00:00+00`` | | tamp with t | | 精度;参见 | stamptz '20 | | | ime zone, | | `节 `__ | | | +-------------+-------------+-------------+-------------+-------------+ | ``date_tr | ``int | 截断到指定 | ``dat | ``2 days | | unc(text, | erval`` | 精度;见\ | e_trunc('ho | 03:00:00`` | | interva;)`` | | `节 | ur', interv | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``double | 获得子 | ``extract( | ``20`` | | ``extract | precision`` | 域;见\ | hour from t | | | (field fr | | `节 | imestamp '2 | | | om timest | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | ``extract | ``double | 获得子 | ``extra | ``3`` | | field from | precision`` | 域;见\ | ct(month fr | | | interval`` | | `节 | om interval | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``boolean`` | 测试有 | ``isfinit | ``true`` | | ``isfin | | 限日期(不 | e(date '200 | | | ite(date``) | | 是+/-无限) | 1-02-16')`` | | +-------------+-------------+-------------+-------------+-------------+ | | ``boolean`` | 测试有限 | ``is | ``true`` | | ``isfinite(t| | 时间戳(不 | finite(time | | | imestamp``) | | 是+/-无限) | stamp '2 | | | | | | 001-02-16 2 | | | | | | 1:28:30')`` | | +-------------+-------------+-------------+-------------+-------------+ | ``isfinite( | ``boolean`` | 测 | ``isfinite | ``true`` | | interval``) | | 试有限间隔 | (interval ' | | | | | | 4 hours')`` | | +-------------+-------------+-------------+-------------+-------------+ | | | 调整间 | ``j | ``1 m | | ``ju | ``interval``| 隔这样30天 | ustify_days | on 5 days`` | | stify_days( | | 时间周期可 | (interval ' | | | interval)`` | | 以表示为月 | 35 days')`` | | +-------------+-------------+-------------+-------------+-------------+ | | | 调整间隔 | ``jus | ``1 da`` | | ``jus | ``interval``| 这样24小时 | tify_hours( | ``y`` | | tify_hours( | | 时间周期可 | interval '2 | ``03:00:00``| | interval)`` | | 以表示为日 | 7 hours')`` | | +-------------+-------------+-------------+-------------+-------------+ | | | 使用\ ``jus | ``jus | ``29 day | | ``justif | ``interval``| tify_days`` | tify_int | s 23:00:00``| | y_interval( | | \ 和\ ``jus | erval(inter | | | interval)`` | | tify_hours` | val '1 mon | | | | | `\ 调整间隔 | -1 hour')`` | | | | | ,使用额外 | | | | | | 的符号调整 | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time`` | 当 |   |   | | | | 前时间(一 | | | | localtime | | 天中的时间 | | | | | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``t | 当前日期和 |   |   | | ``local | imestamp`` | 时间(当前 | | | | timestamp`` | | 事务的开始 | | | | | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``date`` | 从 | ``ma | ``2 | | ``make_ | | 年、月、日 | ke_date(201 | 013-07-15`` | | date(year`` | | 域创建日期 | 3, 7, 15)`` | | | ``int``, | | | | | | ``month`` | | | | | | ``int``, | | | | | | ``day`` | | | | | | ``int``) | | | | | +-------------+-------------+-------------+-------------+-------------+ | | ``i | 从年 | ``make_ | ``10 days`` | | | nterval`` | 、月、周、 | interval(da | | | ``make_inter| | 日、时、分 | ys => 10)`` | | | val(years`` | | 、秒域创建 | | | | ``int`` | | interval | | | | DEFAULT 0, | | | | | | ``months`` | | | | | | ``int`` | | | | | | DEFAULT 0, | | | | | | ``weeks`` | | | | | | ``int`` | | | | | | DEFAULT 0, | | | | | | ``days`` | | | | | | ``int`` | | | | | | DEFAULT 0, | | | | | | ``hours`` | | | | | | ``int`` | | | | | | DEFAULT 0, | | | | | | ``mins`` | | | | | | ``int`` | | | | | | DEFAULT 0, | | | | | | ``secs`` | | | | | | ``double | | | | | | precision`` | | | | | | DEFAULT | | | | | | 0.0) | | | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time`` | 从 | ``ma | ``0 | | ``make_ | | 时、分、秒 | ke_time(8, | 8:15:23.5`` | | time(hour`` | | 域创建时间 | 15, 23.5)`` | | | ``int``, | | | | | | ``min`` | | | | | | ``int``, | | | | | | ``sec`` | | | | | | ``double p | | | | | | recision``) | | | | | +-------------+-------------+-------------+-------------+-------------+ | | | 从年、 | ``make_time | ``2 | | | ``time | 月、日、时 | stamp(2013, | 013-07-15 0 | | ``make_times| stamp`` | 、分、秒域 | 7, 15, 8, | 8:15:23.5`` | | tamp(year`` | | 创建时间戳 | 15, 23.5)`` | | | ``int``, | | | | | | ``month`` | | | | | | ``int``, | | | | | | ``day`` | | | | | | ``int``, | | | | | | ``hour`` | | | | | | ``int``, | | | | | | ``min`` | | | | | | ``int``, | | | | | | ``sec`` | | | | | | ``double p | | | | | | recision``) | | | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time | 从年、 | | ``2013 | | ``m | stamp with | 月、日、时 | ``make_times| -07-15 08:1 | | ake_timesta | time zone`` | 、分、秒域 | tamptz(2013,| 5:23.5+01`` | | mptz(year`` | | 创建带时区 | 7, 15, 8, | | | ``int``, | | 的时间戳。 | 15, 23.5)`` | | | ``month`` | | 如果没有指 | | | | ``int``, | | 定\ ``time | | | | ``day`` | | zone``\ , | | | | ``int``, | | 则使用 | | | | ``hour`` | | 当前时区。 | | | | ``int``, | | | | | | ``min`` | | | | | | ``int``, | | | | | | ``sec`` | | | | | | ``double p | | | | | | recision,`` | | | | | | ``[ | | | | | | timezone`` | | | | | | ``text])`` | | | | | +-------------+-------------+-------------+-------------+-------------+ | ``now()`` | ``time | 当前日期和 |   |   | | | stamp with | 时间(当前 | | | | | time zone`` | 事务的开始 | | | | | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time | 当前日期和 |   |   | | ``s | stamp with | 时间(当前 | | | | tatement_ti | time zone`` | 事务的开始 | | | | mestamp()`` | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``text`` | 当前日期 |   |   | | ``ti | | 和时间(像 | | | | meofday()`` | | \ ``clock_t | | | | | | imestamp``\ | | | | | | , 但是作为 | | | | | | 一个\ ``tex | | | | | | t``\ 字符串 | | | | | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time | 当前日期和 |   |   | | ``tra | stamp with | 时间(当前 | | | | nsaction_ti | time zone`` | 事务的开始 | | | | mestamp()`` | | );见\ | | | | | | `节 | | | | | | `__ | | | +-------------+-------------+-------------+-------------+-------------+ | | ``time | 把 Unix | ``to_t | ``20 | | | stamp with | 时间(从 | imestamp(12 | 10-09-13 04 | | ``to_timesta| time zone`` | 1970-01-01 | 84352323)`` | :32:03+00`` | | mp(double p | | 00:00:00+00 | | | | recision``) | | 开始的 | | | | | | 秒)转换成 | | | | | | timestamp | | | +-------------+-------------+-------------+-------------+-------------+ 除了这些函数以外,还支持 SQL 操作符\ ``OVERLAPS``\ : .. code:: synopsis (start1, end1) OVERLAPS (start2, end2) (start1, length1) OVERLAPS (start2, length2) 这个表达式在两个时间域(用它们的端点定义)重叠的时候得到真,当它们不重叠时得到假。端点可以用一对日期、时间或者时间戳来指定;或者是用一个后面跟着一个间隔的日期、时间或时间戳来指定。当一对值被提供时,起点或终点都可以被写在前面,\ ``OVERLAPS``\ 会自动地把较早的值作为起点。每一个时间段被认为是表示半开的间隔\ *``start``* ``<=`` *``time``* ``<`` *``end``*\ ,除非\ *``start``*\ 和\ *``end``*\ 相等,这种情况下它表示单个时间实例。例如这表示两个只有一个共同端点的时间段不重叠。 .. code:: screen SELECT (DATE '2001-02-16', DATE '2001-12-21') OVERLAPS (DATE '2001-10-30', DATE '2002-10-30'); 结果:true SELECT (DATE '2001-02-16', INTERVAL '100 days') OVERLAPS (DATE '2001-10-30', DATE '2002-10-30'); 结果:false SELECT (DATE '2001-10-29', DATE '2001-10-30') OVERLAPS (DATE '2001-10-30', DATE '2001-10-31'); 结果:false SELECT (DATE '2001-10-30', DATE '2001-10-30') OVERLAPS (DATE '2001-10-30', DATE '2001-10-31'); 结果:true 当把一个\ ``interval``\ 值添加到\ ``timestamp with time zone``\ 上(或从中减去)时, days 部分会按照指定的天数增加或减少\ ``timestamp with time zone``\ 的日期。 对于横跨夏令时的变化(当会话的时区被设置为可识别DST的时区时),这意味着\ ``interval '1 day'``\ 并 不一定等于\ ``interval '24 hours'``\ 。例如,当会话的时区设置为\ ``CST7CDT``\ 时,\ ``timestamp with time zone '2005-04-02 12:00-07' + interval '1 day'``\ 的结果是\ ``timestamp with time zone '2005-04-03 12:00-06'``\ ,而将\ ``interval '24 hours'``\ 增加到相同的初始\ ``timestamp with time zone``\ 的结果 则是\ ``timestamp with time zone '2005-04-03 13:00-06'``\ , 因为\ ``CST7CDT``\ 时区在\ ``2005-04-03 02:00``\ 有一个夏令时变更。 注意\ ``age``\ 返回的\ ``月数``\ 域可能有歧义,因为不同的月份有不同的天数。 PostgreSQL的方法是当计算部分月数时,采用两个日期中较早的月。例如:\ ``age('2004-06-01', '2004-04-30')``\ 使用4月份得到\ ``1 mon 1 day``\ ,而用5月分时会得到\ ``1 mon 2 days``\ ,因为5月有31天,而4月只有30天。 日期和时间戳的减法也可能会很复杂。执行减法的一种概念上很简单的方法是,使用 ``EXTRACT(EPOCH FROM ...)``\ 把每个值都转换成秒数,然后执行减法, 这样会得到两个值之间的\ *秒*\ 数。这种方法将会适应每个月中天数、 时区改变和夏令时调整。使用“\ ``-``\ ”操作符的日期或时间 戳减法会返回值之间的天数(24小时)以及时/分/秒,也会做同样的调整。 ``age``\ 函数会返回年、月、日以及时/分/秒,执行按域的减法,然后对 负值域进行调整。下面的查询展示了这些方法的不同。例子中的结果由 ``timezone = 'US/Eastern'``\ 产生,这使得两个使用的日期之间存在着夏令 时的变化: .. code:: screen SELECT EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') - EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00'); Result: 10537200 SELECT (EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') - EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00')) / 60 / 60 / 24; Result: 121.958333333333 SELECT timestamptz '2013-07-01 12:00:00' - timestamptz '2013-03-01 12:00:00'; Result: 121 days 23:00:00 SELECT age(timestamptz '2013-07-01 12:00:00', timestamptz '2013-03-01 12:00:00'); Result: 4 mons .. container:: sect2 :name: FUNCTIONS-DATETIME-EXTRACT .. container:: titlepage .. container:: .. container:: .. rubric:: 9.9.1. ``EXTRACT``, ``date_part`` :name: extract-date_part :class: title .. code:: synopsis EXTRACT(field FROM source) ``extract``\ 函数从日期/时间值中抽取子域,例如年或者小时等。\ *``source``*\ 必须是一个类型 ``timestamp``\ 、\ ``time``\ 或\ ``interval``\ 的值表达式(类型为\ ``date``\ 的表达式将被造型为 ``timestamp``\ ,并且因此也可以被同样使用)。\ *``field``*\ 是一个标识符或者字符串,它指定从源值中抽取的域。\ ``extract``\ 函数返回类型为\ ``double precision``\ 的值。 下列值是有效的域名字∶ .. container:: variablelist ``century`` 世纪 .. code:: screen SELECT EXTRACT(CENTURY FROM TIMESTAMP '2000-12-16 12:21:13'); 结果:20 SELECT EXTRACT(CENTURY FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:21 第一个世纪从 0001-01-01 00:00:00 AD 开始, 尽管那时候人们还不知道这是第一个世纪。这个定义适用于所有使用格里高利历法的国家。其中没有 0 世纪,我们直接从公元前 1 世纪到公元 1 世纪。 如果你认为这个不合理,那么请把抱怨发给:罗马圣彼得教堂,梵蒂冈,教皇收。 ``day`` 对于\ ``timestamp``\ 值,是(月份)里的日域(1-31);对于\ ``interval``\ 值,是日数 .. code:: screen SELECT EXTRACT(DAY FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:16 SELECT EXTRACT(DAY FROM INTERVAL '40 days 1 minute'); 结果:40 ``decade`` 年份域除以10 .. code:: screen SELECT EXTRACT(DECADE FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:200 ``dow`` 一周中的日,从周日(\ ``0``\ )到周六(\ ``6``\ ) .. code:: screen SELECT EXTRACT(DOW FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:5 请注意,\ ``extract``\ 的一周中的日和\ ``to_char(..., 'D')``\ 函数不同。 ``doy`` 一年的第几天(1 -365/366) .. code:: screen SELECT EXTRACT(DOY FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:47 ``epoch`` 对于\ ``timestamp with time zone``\ 值, 是自 1970-01-01 00:00:00 UTC 以来的秒数(结果可能是负数); 对于\ ``date`` and ``timestamp``\ 值,是自本地时间 1970-01-01 00:00:00 以来的描述;对于\ ``interval``\ 值,它是时间间隔的总秒数。 .. code:: screen SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40.12-08'); 结果:982384720.12 SELECT EXTRACT(EPOCH FROM INTERVAL '5 days 3 hours'); 结果:442800 不能用\ ``to_timestamp``\ 把一个 epoch 值转换回成时间戳: .. code:: screen SELECT to_timestamp(982384720.12); Result: 2001-02-17 04:38:40.12+00 ``hour`` 小时域(0 - 23) .. code:: screen SELECT EXTRACT(HOUR FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:20 ``isodow`` 一周中的日,从周一(\ ``1``\ )到周日(\ ``7``\ ) .. code:: screen SELECT EXTRACT(ISODOW FROM TIMESTAMP '2001-02-18 20:38:40'); 结果:7 除了周日,这和\ ``dow``\ 相同。这符合ISO 8601 中一周中的日的编号。 ``isoyear`` 日期所落在的ISO 8601 周编号的年(不适用于间隔) .. code:: screen SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-01'); 结果:2005 SELECT EXTRACT(ISOYEAR FROM DATE '2006-01-02'); 结果:2006 每一个ISO 8601 周编号的年都开始于包含1月4日的那一周的周一,在早的1月或迟的12月中ISO年可能和格里高利年不同。更多信息见\ ``week``\ 域。 这个域不能用于 PostgreSQL 8.3之前的版本。 ``microseconds`` 秒域,包括小数部分,乘以 1,000,000。请注意它包括全部的秒 .. code:: screen SELECT EXTRACT(MICROSECONDS FROM TIME '17:12:28.5'); 结果:28500000 ``millennium`` 千年 .. code:: screen SELECT EXTRACT(MILLENNIUM FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:3 19xx的年份在第二个千年里。第三个千年从 2001 年 1 月 1 日开始。 ``milliseconds`` 秒域,包括小数部分,乘以 1000。请注意它包括完整的秒。 .. code:: screen SELECT EXTRACT(MILLISECONDS FROM TIME '17:12:28.5'); 结果:28500 ``minute`` 分钟域(0 - 59) .. code:: screen SELECT EXTRACT(MINUTE FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:38 ``month`` 对于\ ``timestamp``\ 值,它是一年里的月份数(1 - 12); 对于\ ``interval``\ 值,它是月的数目,然后对 12 取模(0 - 11) .. code:: screen SELECT EXTRACT(MONTH FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:2 SELECT EXTRACT(MONTH FROM INTERVAL '2 years 3 months'); 结果:3 SELECT EXTRACT(MONTH FROM INTERVAL '2 years 13 months'); 结果:1 ``quarter`` 该天所在的该年的季度(1 - 4) .. code:: screen SELECT EXTRACT(QUARTER FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:1 ``second`` 秒域,包括小数部分(0 - 59\ `[7] <#ftn.id-1.5.8.14.12.5.11.16.2.1.1>`__\ ) .. code:: screen SELECT EXTRACT(SECOND FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:40 SELECT EXTRACT(SECOND FROM TIME '17:12:28.5'); 结果:28.5 ``timezone`` 与 UTC 的时区偏移,以秒记。正数对应 UTC 东边的时区,负数对应 UTC 西边的时区(从技术上来看,PostgreSQL不使用 UTC,因为其中不处理闰秒)。 ``timezone_hour`` 时区偏移的小时部分。 ``timezone_minute`` 时区偏移的分钟部分。 ``week`` 该天在所在的ISO 8601 周编号的年份里是第几周。根据定义, 一年的第一周包含该年的 1月 4 日并且 ISO 周从星期一开始。换句话说,一年的第一个星期四在第一周。 在 ISO 周编号系统中,早的 1 月的日期可能位于前一年的第五十二或者第五十三周,而迟的 12 月的日期可能位于下一年的第一周。例如, ``2005-01-01``\ 位于 2004 年的第五十三周,并且\ ``2006-01-01``\ 位于 2005 年的第五十二周,而\ ``2012-12-31``\ 位于 2013 年的第一周。我们推荐把\ ``isoyear``\ 域和\ ``week``\ 一起使用来得到一致的结果。 .. code:: screen SELECT EXTRACT(WEEK FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:7 ``year`` 年份域。要记住这里没有\ ``0 AD``\ ,所以从\ ``AD``\ 年里抽取\ ``BC``\ 年应该小心处理。 .. code:: screen SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40'); 结果:2001 .. note:: .. rubric:: 注意 :name: 注意 :class: title 当输入值为 +/-Infinity 时,\ ``extract``\ 对于单调增的域(\ ``epoch``\ 、\ ``julian``\ 、\ ``year``\ 、\ ``isoyear``\ 、\ ``decade``\ 、\ ``century``\ 以及\ ``millennium``\ )返回 +/-Infinity。对于其他域返回 NULL。OushuDB 6.0 之前的版本对所有输入无穷的情况都返回零。 ``extract``\ 函数主要的用途是做计算性处理。对于用于显示的日期/时间值格式化,参阅\ `节 `__\ 。 在传统的Ingres上建模的\ ``date_part``\ 函数等价于SQL标准函数\ ``extract``\ : .. code:: synopsis date_part('field', source) 请注意这里的\ *``field``*\ 参数必须是一个串值,而不是一个名字。有效的\ ``date_part``\ 域名 和\ ``extract``\ 相同。 .. code:: screen SELECT date_part('day', TIMESTAMP '2001-02-16 20:38:40'); 结果:16 SELECT date_part('hour', INTERVAL '4 hours 3 minutes'); 结果:4 .. container:: sect2 :name: FUNCTIONS-DATETIME-TRUNC .. container:: titlepage .. container:: .. container:: .. rubric:: 9.9.2. ``date_trunc`` :name: date_trunc :class: title ``date_trunc``\ 函数在概念上和用于数字的\ ``trunc``\ 函数类似。 .. code:: synopsis date_trunc(field, source [, time_zone ]) ``source``\ 是类型\ ``timestamp``\ 或\ ``interval``\ 的值表达式(类型\ ``date``\ 和 ``time``\ 的值都分别被自动转换成\ ``timestamp``, ``timestamp with time zone``,或者\ ``interval``\ )。\ ``field``\ 选择对输入值选用什么样的精度进行截断。返回的值是\ ``timestamp``, ``timestamp with time zone``,类型或者所有小于选定的 精度的域都设置为零(或者一,对于日期和月份)的\ ``interval``\ 。 ``field``\ 的有效值是∶ +------------------+ | ``microseconds`` | +------------------+ | ``milliseconds`` | +------------------+ | ``second`` | +------------------+ | ``minute`` | +------------------+ | ``hour`` | +------------------+ | ``day`` | +------------------+ | ``week`` | +------------------+ | ``month`` | +------------------+ | ``quarter`` | +------------------+ | ``year`` | +------------------+ | ``decade`` | +------------------+ | ``century`` | +------------------+ | ``millennium`` | +------------------+ 当输入值的类型为\ ``timestamp with time zone``\ 时。截断是针对特定时区进行的。 例如,截断为\ ``day``\ ,产生的值是 是该区域的午夜。 默认情况下,截断是在以下方面进行的 到当前的\ `TimeZone `__\ 设置,但在当前的 可以提供可选的\ ``time_zone``\ 参数。以指定不同的时区。 可以指定时区名称 `节 `__\ 中描述的任何一种方式。 当处理\ ``timestamp without time zone`` 或\ ``interval``\ 输入时,不能指定时区。 这些总是按表面值来处理。 示例 (假设当地时区为 ``America/New_York``): .. code:: screen SELECT date_trunc('hour', TIMESTAMP '2001-02-16 20:38:40'); 结果:2001-02-16 20:00:00 SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40'); 结果:2001-01-01 00:00:00 SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40+00'); Result: 2001-02-16 00:00:00-05 SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40+00', 'Australia/Sydney'); Result: 2001-02-16 08:00:00-05 SELECT date_trunc('hour', INTERVAL '3 days 02:47:33'); Result: 3 days 02:00:00 .. container:: sect2 :name: FUNCTIONS-DATETIME-ZONECONVERT .. container:: titlepage .. container:: .. container:: .. rubric:: 9.9.3. ``AT TIME ZONE`` :name: at-time-zone :class: title ``AT TIME ZONE``\ 把时间戳\ *without time zone*\ 转换成时间戳\ *with time zone*\ 或者反过来,并且把\ *time*\ 值转换成不同的时区。\ `表 `__\ 展示了它的变体。 .. container:: table :name: FUNCTIONS-DATETIME-ZONECONVERT-TABLE **表. AT TIME ZONE 变体** .. container:: table-contents +----------------------+----------------------+----------------------+ | 表达式 | 返回类型 | 描述 | +======================+======================+======================+ | ``timestamp | ``timest | 把给定的\ *不带时 | | without time zone`` | amp with time zone`` | 区*\ 的时间戳当作位 | | AT TIME ZONE | | 于指定时区的时间对待 | | ``zone`` | | | +----------------------+----------------------+----------------------+ | ``timest | ``timestamp | 把给定的\ *带时区 | | amp with time zone`` | without time zone`` | *\ 的时间戳转换到新 | | AT TIME ZONE | | 的时区,不带时区指定 | | ``zone`` | | | +----------------------+----------------------+----------------------+ | ``t | ``t | 把给定的\ *带时区* | | ime with time zone`` | ime with time zone`` | \ 的时间转换到新时区 | | AT TIME ZONE | | | | ``zone`` | | | +----------------------+----------------------+----------------------+ 在这些表达式里,我们需要的时区\ *``zone``*\ 可以指定为文本串(例如,\ ``'America/Los_Angeles'``\ )或者一个间隔 (例如,\ ``INTERVAL '-08:00'``\ )。 在文本情况下,可用的时区名字可以用\ `第 8.5.3 节 `__\ 中描述的任何方式指定。 例子(假设本地时区是\ ``America/Los_Angeles``\ ): .. code:: screen SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'America/Denver'; Result: 2001-02-16 19:38:40-08 SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'America/Denver'; Result: 2001-02-16 18:38:40 SELECT TIMESTAMP '2001-02-16 20:38:40-05' AT TIME ZONE 'Asia/Tokyo' AT TIME ZONE 'America/Chicago'; Result: 2001-02-16 05:38:40 第一个例子给缺少时区的值加上了时区,并且显示了使用当前\ ``TimeZone``\ 设置的值。第二个例子把带有时区值的时间戳移动到指定的时区,并且返回不带时区的值。这允许存储和显示不同于当前\ ``TimeZone``\ 设置的值。第三个例子把东京时间转换成芝加哥时间。把\ *time*\ 值转换成其他时区会使用当前活跃的时区规则,因为没有提供日期。 函数\ ``timezone``\ (*``zone``*, *``timestamp``*)等效于 SQL 兼容的结构\ ``timestamp`` AT TIME ZONE *``zone``*\ 。 .. container:: sect2 :name: FUNCTIONS-DATETIME-CURRENT .. container:: titlepage .. container:: .. container:: .. rubric:: 9.9.4. 当前日期/时间 :name: 当前日期时间 :class: title PostgreSQL提供了许多返回当前日期和时间的函数。这些 SQL 标准的函数全部都按照当前事务的开始时刻返回值: .. code:: synopsis CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_TIME(precision) CURRENT_TIMESTAMP(precision) LOCALTIME LOCALTIMESTAMP LOCALTIME(precision) LOCALTIMESTAMP(precision) ``CURRENT_TIME``\ 和\ ``CURRENT_TIMESTAMP``\ 传递带有时区的值;\ ``LOCALTIME``\ 和\ ``LOCALTIMESTAMP``\ 传递的值不带时区。 ``CURRENT_TIME``\ 、\ ``CURRENT_TIMESTAMP``\ 、\ ``LOCALTIME``\ 和 ``LOCALTIMESTAMP``\ 可以有选择地接受一个精度参数, 该精度导致结果的秒域被园整为指定小数位。如果没有精度参数,结果将被给予所能得到的全部精度。 一些例子: .. code:: screen SELECT CURRENT_TIME; 结果:14:39:53.662522-05 SELECT CURRENT_DATE; 结果:2001-12-23 SELECT CURRENT_TIMESTAMP; 结果:2001-12-23 14:39:53.662522-05 SELECT CURRENT_TIMESTAMP(2); 结果:2001-12-23 14:39:53.66-05 SELECT LOCALTIMESTAMP; 结果:2001-12-23 14:39:53.662522 因为这些函数全部都按照当前事务的开始时刻返回结果,所以它们的值在事务运行的整个期间内都不改变。 我们认为这是一个特性:目的是为了允许一个事务在“当前”时间上有一致的概念, 这样在同一个事务里的多个修改可以保持同样的时间戳。 .. note:: .. rubric:: 注意 :name: 注意-1 :class: title 许多其它数据库系统可能会更频繁地推进这些值。 PostgreSQL同样也提供了返回当前语句开始时间的函数, 它们会返回函数被调用时的真实当前时间。这些非 SQL 标准的函数列表如下: .. code:: synopsis transaction_timestamp() statement_timestamp() clock_timestamp() timeofday() now() ``transaction_timestamp()``\ 等价于\ ``CURRENT_TIMESTAMP``\ ,但是其命名清楚地反映了它的返回值。\ ``statement_timestamp()``\ 返回当前语句的开始时刻(更准确的说是收到 客户端最后一条命令的时间)。\ ``statement_timestamp()``\ 和\ ``transaction_timestamp()``\ 在一个事务的第一条命令期间返回值相同,但是在随后的命令中却不一定相同。 ``clock_timestamp()``\ 返回真正的当前时间,因此它的值甚至在同一条 SQL 命令中都会变化。\ ``timeofday()``\ 是一个有历史原因的PostgreSQL函数。和\ ``clock_timestamp()``\ 相似,\ ``timeofday()``\ 也返回真实的当前时间,但是它的结果是一个格式化的\ ``text``\ 串,而不是\ ``timestamp with time zone``\ 值。\ ``now()``\ 是PostgreSQL的一个传统,等效于\ ``transaction_timestamp()``\ 。 所有日期/时间类型还接受特殊的文字值\ ``now``\ ,用于指定当前的日期和时间(重申,被解释为当前事务的开始时刻)。 因此,下面三个都返回相同的结果: .. code:: programlisting SELECT CURRENT_TIMESTAMP; SELECT now(); SELECT TIMESTAMP 'now'; -- 对于和 DEFAULT 一起使用是不正确的 .. tip:: .. rubric:: 提示 :name: 提示 :class: title 在创建表期间指定一个\ ``DEFAULT``\ 子句时,你不会希望使用第三种形式。系统将在分析这个常量的时候把\ ``now``\ 转换为一个\ ``timestamp``\ , 这样需要默认值时就会得到创建表的时间!而前两种形式要到实际使用缺省值的时候才被计算, 因为它们是函数调用。因此它们可以给出每次插入行的时刻。 .. container:: sect2 :name: FUNCTIONS-DATETIME-DELAY .. container:: titlepage .. container:: .. container:: .. rubric:: 9.9.5. 延时执行 :name: 延时执行 :class: title 下面的这些函数可以用于让服务器进程延时执行: .. code:: synopsis pg_sleep(seconds) pg_sleep_for(interval) pg_sleep_until(timestamp with time zone) ``pg_sleep``\ 让当前的会话进程休眠\ *``seconds``* 秒以后再执行。\ *``seconds``*\ 是一个\ ``double precision`` 类型的值,所以可以指定带小数的秒数。\ ``pg_sleep_for``\ 是针对用 ``interval``\ 指定的较长休眠时间的函数。\ ``pg_sleep_until`` 则可以用来休眠到一个指定的时刻唤醒。例如: .. code:: programlisting SELECT pg_sleep(1.5); SELECT pg_sleep_for('5 minutes'); SELECT pg_sleep_until('tomorrow 03:00'); .. note:: .. rubric:: 注意 :name: 注意-2 :class: title 有效的休眠时间间隔精度是平台相关的,通常 0.01 秒是通用值。休眠延迟将至少持续指 定的时长, 也有可能由于服务器负荷而比指定的时间长。特别地, ``pg_sleep_until``\ 并不保证能刚好在指定的时刻被唤醒,但它不会 在比指定时刻早的时候醒来。 .. warning:: .. rubric:: 警告 :name: 警告 :class: title 请确保在调用\ ``pg_sleep``\ 或者其变体时,你的会话没有持有不必要 的锁。否则其它会话可能必须等待你的休眠会话,因而减慢整个系统速度。 .. container:: footnotes -------------- .. container:: footnote :name: ftn.id-1.5.8.14.12.5.11.16.2.1.1 `[7] <#id-1.5.8.14.12.5.11.16.2.1.1>`__ 如果操作系统实现了闰秒,则为60