问题描述
虽然会计用户我们发出帐单31.03
,下一个帐单我们将在30.04
发行,下一个31.05
等(对于月初发行的帐单:05.03
,下一份帐单的日期为05.04
,05.05
等)
在postgresql上执行此操作会导致结果稍有不同:
tucha=> select '2020-03-31'::timestamptz +interval '1mon';
?column?
------------------------
2020-04-30 00:00:00+03
(1 row)
tucha=> select '2020-03-31'::timestamptz +interval '1mon' +interval '1mon';
?column?
------------------------
2020-05-30 00:00:00+03
(1 row)
tucha=> select '2020-03-31'::timestamptz +interval '2mon';
?column?
------------------------
2020-05-31 00:00:00+03
(1 row)
您还可以注意到,根据您添加相同间隔的方式,您将获得不同的结果。
一些用于日期数学的库甚至提供了特殊的参数。例如,优秀的 perl 库DateTime实现了preserve
参数:
在
wrap
模式下,添加会导致新月结束后的天数增加的月份或年份,将延续到下个月。例如,将2月29日加一年将导致3月1日。
如果您将“ end_of_month”模式指定为
limit
,则月末永远不会越过。因此,将2000年2月29日增加一年,将在2001年2月28日产生。如果再加上3年,则将导致2004年2月28日。
如果将“ end_of_month”模式指定为
preserve
,则与limit
的计算相同,不同之处在于,如果原始日期位于月底,新日期也将是。例如,将2000年2月29日增加一个月,则将导致2000年3月31日。
注意:此标志仅在每月的最后几天生效,而其他人完好无损
postgresql是否具有类似的标志或可能是特殊的日期构造函数,如显示的preserve
选项? (可能链接到有关实施欢迎的讨论)
解决方法
您可以简单地将日期截断到一个月初,添加两个月并减去一天:
select date_trunc('month','2020-03-31'::timestamptz) +interval '2 month -1 day'
好处是,无论您给哪个月的开始日期,它总是返回下个月的月底:也就是说,它将返回相同的结果,即3月7日,25日或31日。
这是怎么回事:
- 通过截断日期,您可以获得月份的第一个日期。在我们的例子中是
2020-03-01
。 - 然后我们添加一个月以退还被截断的月份。
- 第二个月是我们的下个月。在我们的例子中是
2020-05-01
- 最后我们减去一天,这就是预期月份的最后一天。在我们的例子中是
2020-04-01
也许此功能可以使您高兴:
CREATE OR REPLACE FUNCTION add_months_preserve_end(d date,i interval)
RETURNS date
LANGUAGE sql IMMUTABLE STRICT AS
$$SELECT
CASE WHEN date_trunc('month',d) + INTERVAL '1 month' = d + INTERVAL '1 day'
AND i = date_trunc('month',i)
THEN (date_trunc('month',d) + i + INTERVAL '1 month -1 day')::date
ELSE (d + i)::date
END$$;
它对待月底的日子的方式有所不同。
,只需创建一些功能:
DROP function first_day_in_month(deet DATE) ;
CREATE function first_day_in_month(deet DATE) RETURNS date
AS
$func$
SELECT date_trunc('mon',$1)::date;
$func$
LANGUAGE sql
;
DROP function last_day_in_month(deet DATE) ;
CREATE function last_day_in_month(deet DATE) RETURNS date
AS
$func$
SELECT (first_day_in_month($1) +interval '1 mon' -interval '1day' )::date;
$func$
LANGUAGE sql
;
SELECT first_day_in_month( CURRENT_DATE );
SELECT last_day_in_month( CURRENT_DATE );
SELECT first_day_in_month( '2020-11-03' );
SELECT last_day_in_month( '2020-11-03' );
输出:
DROP FUNCTION
CREATE FUNCTION
DROP FUNCTION
CREATE FUNCTION
first_day_in_month
--------------------
2020-10-01
(1 row)
last_day_in_month
-------------------
2020-10-31
(1 row)
first_day_in_month
--------------------
2020-11-01
(1 row)
last_day_in_month
-------------------
2020-11-30
(1 row)