找出2个日期之间的月数

时间:2014-05-18 22:32:34

标签: sql postgresql

select 
  (age('2012-11-30 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp)),
  (age('2012-12-31 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp)),
  (age('2013-01-31 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp)),
  (age('2013-02-28 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp))

给出了以下内容:

  0 years 0 mons 30 days 0 hours 0 mins 0.00 secs
  0 years 2 mons 0 days 0 hours 0 mins 0.00 secs
  0 years 3 mons 0 days 0 hours 0 mins 0.00 secs
  0 years 3 mons 28 days 0 hours 0 mins 0.00 secs

但我希望有下个月的定义,我该怎么做?

  0 years 1 mons 0 days 0 hours 0 mins 0.00 secs
  0 years 2 mons 0 days 0 hours 0 mins 0.00 secs
  0 years 3 mons 0 days 0 hours 0 mins 0.00 secs
  0 years 4 mons 0 days 0 hours 0 mins 0.00 secs

4 个答案:

答案 0 :(得分:5)

表达式

age('2012-11-30 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp) 

给出30 days。我们期待1 month,因为这两个值都指向一个月的最后几天。如果我们在值上加1天,我们将在下个月的第一天和

age('2012-12-01 00:00:00'::timestamp, '2012-11-01 00:00:00'::timestamp)

将按预期给我们1个月。因此,让我们检查一下是否有两个月的最后一天,在这种情况下,返回接下来几天的年龄间隔。在其他情况下,我们将返回原始值的年龄间隔:

create or replace function age_m (t1 timestamp, t2 timestamp)
returns interval language plpgsql immutable
as $$
declare
    _t1 timestamp = t1+ interval '1 day';
    _t2 timestamp = t2+ interval '1 day';
begin
    if extract(day from _t1) = 1 and extract(day from _t2) = 1 then
        return age(_t1, _t2);
    else
        return age(t1, t2);
    end if;
end $$;

一些例子:

with my_table(date1, date2) as (
values
    ('2012-11-30 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp),
    ('2012-12-31 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp),
    ('2013-01-31 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp),
    ('2013-02-28 00:00:00'::timestamp, '2012-10-31 00:00:00'::timestamp)
)

select *, age(date1, date2), age_m(date1, date2)
from my_table

        date1        |        date2        |      age       | age_m  
---------------------+---------------------+----------------+--------
 2012-11-30 00:00:00 | 2012-10-31 00:00:00 | 30 days        | 1 mon
 2012-12-31 00:00:00 | 2012-10-31 00:00:00 | 2 mons         | 2 mons
 2013-01-31 00:00:00 | 2012-10-31 00:00:00 | 3 mons         | 3 mons
 2013-02-28 00:00:00 | 2012-10-31 00:00:00 | 3 mons 28 days | 4 mons
(4 rows)

答案 1 :(得分:2)

似乎就像你总是使用这个月的最后一天。你想要做的事情与一月的第一天完美无瑕地合作。所以请改用它。您可以随时减去一天以获得上个月的最后一天。

@klin's function is based on that.对于日期(而不是时间戳),简化:

_t1 date = t1 + 1;
_t2 date = t2 + 1;

可以从日期(但不是时间戳)添加/减去整数值。

如果您想添加"一个月" ,请不要只增加月份字段,因为这可能会像您经历的那样失败。并且在年底也有包装。改为添加interval '1 month'

SELECT (mydate + interval '1 month')::date AS mydate_next_month;

由于date + interval的结果是timestamp

,我会重新投稿

这"向下舍入"自动,如果下个月的最后一天是之前原始日期的那一天。请注意,它"向上"在相反的情况下。如果您需要,请按照上述说明操作本月的第一天。

SQL Fiddle.

答案 2 :(得分:0)

这是time rounding function located on PostgreSQL's official wiki的修改版本:

CREATE OR REPLACE FUNCTION interval_round(base_interval INTERVAL, round_interval INTERVAL) RETURNS INTERVAL AS $BODY$
SELECT justify_interval((EXTRACT(epoch FROM $1)::INTEGER + EXTRACT(epoch FROM $2)::INTEGER / 2)
                / EXTRACT(epoch FROM $2)::INTEGER * EXTRACT(epoch FROM $2)::INTEGER * INTERVAL '1 second');
$BODY$ LANGUAGE SQL STABLE;

你可以用另一个间隔调用它来舍入到f.ex。

SELECT interval_round(age('2013-02-28 00:00:00'::timestamp,
                          '2012-10-31 00:00:00'::timestamp), '1 month')

将返回4 mons

答案 3 :(得分:-1)

http://www.postgresql.org/docs/8.4/static/functions-datetime.html开始,您可以交换时间戳的顺序吗?

“请注意,按年龄返回的月份可能会有歧义,因为不同月份的天数不同.PostgreSQL的方法使用计算部分月份的两个日期之前的月份。例如,年龄('2004- 06-01','2004-04-30')使用4月产1个1天,而使用5月将产生1个2天,因为5月有31天,而4月只有30个。“