Дата окончания расчета месяца в 4GL
Эта процедура возвращается 31.12.2016 вместо 31.12.2015 и портит отчет. Есть идеи, где все идет не так?
LET date_month = MONTH(p_selection.date_from)
IF date_month = 12 THEN
LET date_month = 1
LET p_selection.date_from = p_selection.date_from + 1 UNITS YEAR
LET date_thru = date_month,"/01/",YEAR(p_selection.date_from)
LET p_selection.date_from = p_selection.date_from - 1 UNITS YEAR
ELSE
LET date_month = date_month + 1
LET date_thru = date_month,"/01/",YEAR(p_selection.date_from)
END IF
LET p_selection.date_thru = date_thru CLIPPED
IF YEAR(p_selection.date_thru) <> YEAR(p_selection.date_from) THEN
LET p_selection.date_thru = p_selection.date_thru + 1 UNITS YEAR
END IF
LET p_selection.date_thru = p_selection.date_thru - 1
3 ответа
Предполагая ввод p_selection.date_from
это 01.12.2015 ...
IF date_month = 12
возвращает TRUE, поэтому date_thru
рассчитывается как 01.01.2016
Но потом второй IF
Заявление также возвращает TRUE, добавив еще один год к p_selection.date_thru
(01.01.2017), перед тем как уменьшить его на один день до 31.12.2016.
Мне кажется, что кто-то несколько раз пытался вычислить дату последнего дня месяца, и требуется тот или иной метод, а не оба. Тот, который появляется первым в вашем коде, особенно сомнителен - есть предположение, что DBDATE
это американский формат, когда он выполняется между DATE и CHAR, что совершенно не нужно.
Гораздо более простым решением было бы просто вычислить:
LET p_selection.date_thru =
MDY(MONTH(p_selection.date_from), 1, YEAR(p_selection.date_from))
+ 1 UNITS MONTH - 1 UNITS DAY
Другими словами, найдите первый день выбранного месяца, добавьте месяц и вычтите день. Простой и надежный, он работает на границе года и в случае високосного дня.
Вот некоторый старый код, который некоторое время не видел свет, но все еще действителен I4GL.
ltmonth.4gl
{
@(#)$Id: ltmonth.4gl,v 1.4 1990/04/05 11:02:05 john Exp $
@(#)Sphinx Informix Tools: General Library
@(#)Find last day of this month
@(#)Author: JL
}
FUNCTION last_of_this_month(edate)
DEFINE
edate DATE { Effective date (frequently TODAY) }
IF edate IS NULL THEN
RETURN edate
END IF
RETURN first_of_next_month(edate) - 1
END FUNCTION {last_of_this_month}
fnmonth.4gl
{
@(#)$Id: fnmonth.4gl,v 1.4 1990/04/05 11:02:03 john Exp $
@(#)Sphinx Informix Tools: General Library
@(#)Find 1st of next month
@(#)Author: JL
}
FUNCTION first_of_next_month(edate)
DEFINE
edate DATE, { Effective date (frequently TODAY) }
mm INTEGER, { Month number }
yy INTEGER { Year }
IF edate IS NULL THEN
RETURN edate
END IF
LET mm = MONTH(edate) + 1
LET yy = YEAR(edate)
IF mm > 12 THEN
LET mm = 1
LET yy = yy + 1
END IF
RETURN MDY(mm, 1, yy)
END FUNCTION {first_of_next_month}
lastthismonth.spl
А вот некоторые SPL, основанные на I4GL выше:
-- @(#)$Id: lastthismonth.spl,v 1.2 2008/07/20 02:54:37 jleffler Exp $
--
-- @(#)Create last_of_this_month Stored Procedure
--
-- @(#)Version: ltmonth.4gl,v 1.4 1990/04/05 11:02:05 john
-- @(#)Sphinx Informix Tools: General Library
-- @(#)Find last day of this month
-- @(#)Author: JL
--
-- Alternative expression:
-- (MDY(MONTH(dateval), 1, YEAR(dateval)) + 1 UNITS MONTH) - 1 UNITS DAY
CREATE PROCEDURE last_of_this_month(edate DATE DEFAULT TODAY)
RETURNING DATE AS last_of_this_month;
IF edate IS NULL THEN
RETURN edate;
END IF
RETURN first_of_next_month(edate) - 1;
END PROCEDURE {last_of_this_month};
firstnextmonth.spl
-- @(#)$Id: firstnextmonth.spl,v 1.1 2008/07/20 02:21:13 jleffler Exp $
--
-- @(#)Create first_of_next_month Stored Procedure
--
-- @(#)Version: fnmonth.4gl,v 1.4 1990/04/05 11:02:03 john
-- @(#)Sphinx Informix Tools: General Library
-- @(#)Find 1st of next month
-- @(#)Author: JL
CREATE PROCEDURE first_of_next_month(edate DATE DEFAULT TODAY)
RETURNING INTEGER AS first_of_next_month;
DEFINE mm INTEGER; { Month number }
DEFINE yy INTEGER; { Year }
IF edate IS NULL THEN
RETURN edate;
END IF
LET mm = MONTH(edate) + 1;
LET yy = YEAR(edate);
IF mm > 12 THEN
LET mm = 1;
LET yy = yy + 1;
END IF
RETURN MDY(mm, 1, yy);
END PROCEDURE {first_of_next_month};
Обратите внимание на альтернативное выражение. Это работает, но это невероятная смесь вычислений DATE и DATETIME.
Я имел
FUNCTION last_of_month(m,y)
DEFINE m,y SMALLINT
RETURN MDY(m,days_in_month(m,y),y)
END FUNCTION
с соответствующей функцией days_in_month() для возврата 28,29,30,31 в зависимости от месяца и года
Для ОП вы должны создавать функцию и повторно использовать ее, а не потенциально повторять логику, как будто вы это делаете.
Что я действительно хотел прокомментировать, так это то, что в Genero мы обсуждали, стоит ли добавлять встроенные функции для выполнения этих типов вычислений. Если у каждого есть свои собственные first_of_month, last_of_month, add_months, тогда думали, что мы должны включить их в язык, а не заставлять всех заново изобретать колесо. В ходе обсуждений я узнал, что МЕСЯЦ + 1 ЕДИНИЦЫ, используемый RET в своем ответе, - это, как он говорит, високосный год. По какой-то причине я думал, что это не так и имел тенденцию избегать этого, и писать в стиле ответа JLefflers. Глядя на другой код библиотеки других клиентов 4GL/Genero, я подозреваю, что другие разработчики думали так же.