计算postgresql上两个时间戳之间的月份?

Posted

技术标签:

【中文标题】计算postgresql上两个时间戳之间的月份?【英文标题】:Count months between two timestamp on postgresql? 【发布时间】:2012-06-13 10:07:03 【问题描述】:

我想计算两个日期之间的月数。

正在做:

SELECT TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP '2011-04-30 14:38:40';

返回: 0 年 0 月 409 天 20 小时 0 分钟 0.00 秒

等等:

SELECT extract(month from TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP '2011-04-30 14:38:40');

返回 0。

【问题讨论】:

【参考方案1】:

age函数返回区间:

age(timestamp1, timestamp2)

然后我们尝试从区间中提取年份和月份并相应地添加它们:

select extract(year from age(timestamp1, timestamp2)) * 12 +
extract(month from age(timestamp1, timestamp2))

【讨论】:

很好的解决方案谢谢,如果差异超过 20 天,我还添加了这个以增加 1 个月的剩余天数:+ case when extract(days from age(timestamp1, timestamp2)) > 20然后 1 否则 0 结束【参考方案2】:

age 函数给出了一个合理的间隔:

SELECT age(TIMESTAMP '2012-06-13 10:38:40', TIMESTAMP '2011-04-30 14:38:40');

返回1 year 1 mon 12 days 20:00:00,然后您可以轻松地使用EXTRACT 计算月数:

SELECT EXTRACT(YEAR FROM age) * 12 + EXTRACT(MONTH FROM age) AS months_between
FROM age(TIMESTAMP '2012-06-13 10:38:40', TIMESTAMP '2011-04-30 14:38:40') AS t(age);

【讨论】:

巧妙地添加年份和月份:)【参考方案3】:

请注意,当您尝试使用日历月差时,@ram 和 @angelin 投票最多的答案并不准确。

select extract(year from age(timestamp1, timestamp2))*12 + extract(month from age(timestamp1, timestamp2))

例如,如果您尝试这样做:

select extract(year from age('2018-02-02'::date, '2018-03-01'::date))*12 + extract(month from age('2018-02-02'::date , '2018-03-01'::date))

结果将为 0,但从 3 月到 2 月之间的月份,无论日期之间的天数如何,都应为 1。

所以公式应该像下面这样说我们从timestamp1和timestamp2开始:

((year2 - year1)*12) - month1 + month2 = 两个时间戳之间的日历月

在 pg 中将被翻译成:

select ((extract('years' from '2018-03-01 00:00:00'::timestamp)::int -  extract('years' from '2018-02-02 00:00:00'::timestamp)::int) * 12) 
    - extract('month' from '2018-02-02 00:00:00'::timestamp)::int + extract('month' from '2018-03-01 00:00:00'::timestamp)::int;

您可以创建如下函数:

CREATE FUNCTION months_between (t_start timestamp, t_end timestamp)
RETURNS integer
AS $$
select ((extract('years' from $2)::int -  extract('years' from $1)::int) * 12) 
    - extract('month' from $1)::int + extract('month' from $2)::int
$$
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;

【讨论】:

【参考方案4】:

如果您要多次执行此操作,您可以定义以下function:

CREATE FUNCTION months_between (t_start timestamp, t_end timestamp)
    RETURNS integer
    AS $$
        SELECT
            (
                12 * extract('years' from a.i) + extract('months' from a.i)
            )::integer
        from (
            values (justify_interval($2 - $1))
        ) as a (i)
    $$
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT;

这样你就可以了

SELECT months_between('2015-01-01', now());

【讨论】:

您可能需要将now() 转换为时间戳。所以例子是select months_between('2015-01-01', now()::timestamp);【参考方案5】:
SELECT date_part ('year', f) * 12
      + date_part ('month', f)
FROM age ('2015-06-12', '2014-12-01') f

结果:6 个月

【讨论】:

【参考方案6】:

给出两个日期的月差

   SELECT ((extract( year FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract( year FROM TIMESTAMP '2011-04-30 14:38:40' )) *12) + extract(MONTH FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract(MONTH FROM TIMESTAMP '2011-04-30 14:38:40' );

结果:1​​4

必须分别提取两个日期的月份,然后提取两个结果的差异

【讨论】:

我想我们正在路上,但它仍然有问题。你的提议的结果是 14,良好的响应是 13。 select EXTRACT(year FROM age(TIMESTAMP '2012-06-13 10:38:40',TIMESTAMP '2011-04-30 14:38:40'))*12 + EXTRACT(month FROM 年龄(TIMESTAMP '2012-06-13 10:38:40',TIMESTAMP '2011-04-30 14:38:40')); 我已经使用实施的解决方案编辑了您的答案。它应该在同行评审后可见。【参考方案7】:

我曾经有同样的问题,写了这个......它很丑:

postgres=>  SELECT floor((extract(EPOCH FROM TIMESTAMP '2012-06-13 10:38:40' ) - extract(EPOCH FROM TIMESTAMP '2005-04-30 14:38:40' ))/30.43/24/3600);
 floor 
-------
    85
(1 row)

在此解决方案中,“一个月”被定义为 30.43 天,因此它可能会在较短的时间跨度内产生一些意想不到的结果。

【讨论】:

它可以工作,但很奇怪。我看到的另一个问题是您认为一个月是 30 天。从 2 月 3 日到 3 月 4 日为一个月。您的选择将返回 0。从 1 月 3 日到 2 月 2 日没有一个月。您的选择将返回一个。 是的,不幸的是它只是大致正确(但对我来说已经足够好了)。 (对其进行了编辑以使其更清晰)。 它的近似值是因为使用了30,因为月份可能更长或更短 - 时间戳之间的距离越大,这成为一个问题 - 类似的时间戳四舍五入就足够了。 我已将 30 替换为 30.43,因此在较长的时间跨度上会更正确 :-)【参考方案8】:

按年和月提取将按月计算:

select extract(year from age('2016-11-30'::timestamp, '2015-10-15'::timestamp)); --> 1
select extract(month from age('2016-11-30'::timestamp, '2015-10-15'::timestamp)); --> 1
--> Total 13 months

这种方法可以维持几个月的分数(感谢 tobixen 的除数)

select round(('2016-11-30'::date - '2015-10-15'::date)::numeric /30.43, 1); --> 13.5 months

【讨论】:

【参考方案9】:

这是一个 PostgreSQL 函数,其行为与 Oracle MONTHS_BETWEEN 函数完全相同。 它已经过广泛的年份(包括闰年)和超过 700k 的日期组合(包括每个月的结束)的测试。

CREATE OR REPLACE FUNCTION months_between
(   DATE,
    DATE
)
RETURNS float
AS
$$
    SELECT
            (EXTRACT(YEAR FROM $1) - EXTRACT(YEAR FROM $2)) * 12
        +   EXTRACT(MONTH FROM $1) - EXTRACT(MONTH FROM $2)
        +   CASE
                WHEN    EXTRACT(DAY FROM $2) = EXTRACT(DAY FROM LAST_DAY($2))
                    AND EXTRACT(DAY FROM $1) = EXTRACT(DAY FROM LAST_DAY($1))
                THEN
                    0
                ELSE
                    (EXTRACT(DAY FROM $1) - EXTRACT(DAY FROM $2)) / 31
            END
    ;
$$
LANGUAGE SQL
IMMUTABLE STRICT;

此函数需要一个 LAST_DAY 函数(行为与 Oracle 的相同):

CREATE OR REPLACE FUNCTION last_day
(   DATE
)
RETURNS DATE
AS
$$
    SELECT
        (DATE_TRUNC('MONTH', $1) + INTERVAL '1 MONTH' - INTERVAL '1 DAY')::date
    ;
$$
LANGUAGE SQL
IMMUTABLE STRICT;

【讨论】:

【参考方案10】:

试试这个解决方案:

SELECT extract (MONTH FROM age('2014-03-03 00:00:00'::timestamp, 
'2013-02-03 00:00:00'::timestamp)) + 12 * extract (YEAR FROM age('2014-03-03   
00:00:00'::timestamp, '2013-02-03 00:00:00'::timestamp)) as age_in_month;

【讨论】:

【参考方案11】:
SELECT floor(extract(days from TIMESTAMP '2012-06-13 10:38:40' - TIMESTAMP
'2011-04-30 14:38:40')/30.43)::integer as months;

给出一个近似值,但避免重复时间戳。这使用来自tobixen's answer 的提示除以 30.43 代替 30,以便在计算月份时长时间跨度不正确。

【讨论】:

【参考方案12】:

我做了一个这样的函数:

/* similar to ORACLE's MONTHS_BETWEEN */
CREATE OR REPLACE FUNCTION ORACLE_MONTHS_BETWEEN(date_from DATE, date_to DATE)
RETURNS REAL LANGUAGE plpgsql
AS
$$
DECLARE age INTERVAL;
declare rtn real;
BEGIN
    age := age(date_from, date_to);
    rtn := date_part('year', age) * 12 + date_part('month', age) + date_part('day', age)/31::real;
    return rtn;
END;
$$;

Oracle 示例)

SELECT MONTHS_BETWEEN
(TO_DATE('2015-02-02','YYYY-MM-DD'), TO_DATE('2014-12-01','YYYY-MM-DD') ) 
"Months" FROM DUAL;
--result is: 2.03225806451612903225806451612903225806

我的 PostgreSQL 函数示例)

select ORACLE_MONTHS_BETWEEN('2015-02-02'::date, '2014-12-01'::date) Months;
-- result is: 2.032258

根据结果,您可以使用 CEIL()/FLOOR() 进行舍入。

select ceil(2.032258)  --3
select floor(2.032258) --2

【讨论】:

【参考方案13】:

试试;

select extract(month from  age('2012-06-13 10:38:40'::timestamp, '2011-04-30 14:38:40'::timestamp)) as my_months; 

【讨论】:

这个答案是错误的。它返回1,因为extract 只考虑月份,而不考虑你有多少个月,因为涉及的年份不同。

以上是关于计算postgresql上两个时间戳之间的月份?的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL 获取两个日期时间/时间戳之间的随机日期时间/时间戳

两个日期之间的月份 | PostgreSQL

MySQL中日期之间的月份差异

php如何计算两个时间戳之间相差的日时分秒

计算两个时间戳之间的差异并获得 unix 时间戳的差异

数组中时间戳之间的平均间隔