有没有办法在添加间隔的同时保留一个月的最后一天?
Posted
技术标签:
【中文标题】有没有办法在添加间隔的同时保留一个月的最后一天?【英文标题】:Is there a way to preserve last day of month while adding interval? 【发布时间】:2020-10-28 14:07:24 【问题描述】:当会计用户我们发出账单31.03
,下一个账单我们将在30.04
,下一个31.05
等(对于在月初发出的账单:05.03
,下一个账单的日期将是@987654326 @、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 日。如果再添加三年,将导致 2004 年 2 月 28 日。
如果将“end_of_month”模式指定为
preserve
,则与limit
进行相同的计算,但如果原始日期在月末,则新日期也会是。例如,在 2000 年 2 月 29 日加上一个月将得到 2000 年 3 月 31 日。
注意:此标志仅在一个月的最后几天生效,其他日期保持不变
postgresql 是否有类似的标志或可能是特殊的日期构造函数,如显示的preserve
选项? (可能链接到有关实施的讨论欢迎)
【问题讨论】:
这只是优先级和关联性的问题。添加一些()
来查看。
【参考方案1】:
或许这个功能可以让你开心:
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$$;
它以不同的方式对待月末的日子。
【讨论】:
【参考方案2】:您可以简单地将日期截断到月初,加上两个月并减去一天:
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
【讨论】:
不起作用:select date_trunc('month', '2020-02-01'::timestamptz) +interval '2 month -1 day'
。在这里我期待2020-04-01
。在这里我的意思是,如果我发布01
,那么下一个发布日期也应该是01
下个月的下一天。但它在下个月底返回的效果总是很有趣。它是如何工作的?
@EugenKonkov:这个要求实际上并没有出现在问题中......
我同意你的观点,但是当使用提到的库时,我们可以在任何一天使用。它将被保留。请关注:result in days beyond the end of the new month will roll over into the following month
。所以我说的参数只在最后几天生效,其他的保持不变。
感谢您的通知,我稍微清理一下问题。【参考方案3】:
只需创建一些函数:
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)
【讨论】:
以上是关于有没有办法在添加间隔的同时保留一个月的最后一天?的主要内容,如果未能解决你的问题,请参考以下文章
如果添加 12 小时(43200 秒),Qt 5.5 QDateTime::addSec 函数在一个月的最后一天返回错误值