在一段时间内,使用PostgresQL 9.6,我有三个输入值:
任务是:查找该期间的开始日期。要求是:age("end","start")
(*)的结果必须始终与输入间隔(月+天)相同。换句话说:在不显式保存给定间隔的情况下,仅通过开始和结束即可存储该周期,但要确保该间隔保持完全相同。
我的第一个简单尝试是
SELECT "endDate" - ("months" || ' mons ' || "days" ' days')::interval
但是,这不适用于输入
通过这种方法计算的开始日期将为2017-02-09
。并且age('2017-06-29','2017-02-10')
将返回4 mons 20 days
,这是一天太多了。
可能的原因是减号运算符很可能会首先减去月份,然后如果它降落在“不可能的日期”(如2017-02-29),请转到上一个“可能的”日期(此处为:2017-02) -28),然后减去天数,降落在2017-02-09上-这是错误的。
所以,我想到了这个主意:
WITH prep AS
( SELECT ("months"||' mons')::interval AS moninterval,
("days" || ' days')::interval AS dayinterval )
SELECT
CASE WHEN date_part('day',"endDate") > "days"
THEN (("endDate" - dayinterval) - moninterval)::date
ELSE ("endDate" - ( moninterval + dayinterval) )::date
END AS simstart
FROM prep
基本上,这个想法是:如果结束日期的天数大于间隔中的天数,则先减去天,再减去月。否则,请像以前一样进行操作。
在许多情况下都可以使用。但是,我仍然找到了一个没有的极端情况:
无论使用哪种方法:开始日期将始终以2018-02-02
计算。而且,如果您执行SELECT age_forward('2018-03-02','2018-02-02')
,则将始终以1 mon
结尾-从技术上讲这是正确的。但是,这是错误的,因为原始输入是28 days
。
(*)更准确地说:age_forward。在这里https://stackoverflow.com/a/51173709/2710714中查看我对自己问题的回答。但是,我认为与上述边缘案例问题无关。