Postgres: вычислить дату начала периода, чтобы интервал оставался прежним - PullRequest
0 голосов
/ 25 сентября 2018

Используя 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-09age('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 Тем не менее, я думаю, что это не имеет значения при описанных выше проблемах крайнего случая.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...