Используя PostgresQL 9.6, в течение определенного периода времени у меня есть три входных значения:
- "endDate": любая дата, являющаяся датой окончания
- "month":количество месяцев между 0 и ок.30
- «дни»: количество дней от 0 до 29
Задача: Найти дату начала этого периода.Требование: Результат age("end","start")
(*) всегда должен совпадать с интервалом ввода (месяц + дни).Другими словами: сделайте период сохраняемым с помощью только начала и конца без явного сохранения заданного интервала, но следя за тем, чтобы интервал оставался точно таким же.
Моя первая простая попытка была
SELECT "endDate" - ("months" || ' mons ' || "days" ' days')::interval
Однако, это не работает для ввода
- "endDate": 2017-06-29
- "month": 4
- "дни": 19
Начальная дата, рассчитанная этим подходом, будет 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
По сути, идея такова: если число дней в конечной дате больше, чем число дней в интервале, то сначала вычтите дни, а затем месяцы.,В противном случае, делайте, как раньше.
Это работает во многих случаях.Тем не менее, я все еще нашел крайний случай, когда он не:
- "endDate": 2018-03-02
- "months": 0
- "дни ": 28
Независимо от используемого метода: дата начала всегда будет рассчитываться как 2018-02-02
.И если вы сделаете SELECT age_forward('2018-03-02','2018-02-02')
, вы всегда получите 1 mon
, что технически правильно.Однако здесь все не так, поскольку исходный ввод был 28 days
.
(*) Если быть более точным: age_forward.Смотрите мой ответ на мой собственный вопрос здесь https://stackoverflow.com/a/51173709/2710714 Тем не менее, я думаю, что это не имеет значения при описанных выше проблемах крайнего случая.