有没有办法在添加间隔的同时保留一个月的最后一天?

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 函数在一个月的最后一天返回错误值

java如何实现在下个月的某一天自动获取某数据

Javascript / jQuery:如何检查日期是不是是一个月的最后一天

如何获得一个月的最后一天?

在 Postgres 的间隔中使用可变周期

最全的前端javaScript时间处理函数