Проблема SAS DATE – расчет «БЛИЖАЙШЕГО МЕСЯЦА»

StackOverflow https://stackoverflow.com/questions/1042835

  •  22-07-2019
  •  | 
  •  

Вопрос

Мне нужно вычислить цифру, равную «ближайшему» количеству месяцев между двумя датами.Однако стандартная функция SAS (INTCK) не предназначена для учета ДНЯ своих параметров даты (например, код ниже разрешается до 0, когда мне нужно округлить его до 1).

Каков «самый аккуратный» способ решения этой проблемы?

data _null_;
    x="01APR08"d;
    y="28APR08"d;
    z=intck('MONTH',x,y);
    put z= ;
run;

РЕДАКТИРОВАТЬ:ответ на комментарий Мартинса.

Я бы округлил до 0 месяцев - не думаю, что граница имеет значение.Функция, которую я пытаюсь воспроизвести (NEAREST_MONTHS), взята из DCS (приложение Sungard Prophet).Сейчас я жду возможности провести некоторое тестирование внутри самого приложения, чтобы лучше понять, как оно обрабатывает даты (результаты опубликую здесь)).

Файл справки содержит следующее:Категория дата

Описание

Возвращает разницу между двумя датами с точностью до ближайшего числа месяцев.Если вторая дата позже первой, возвращается 0.

Синтаксис

NEAREST_MONTHS(Поздняя_дата, Ранее_дата)

Вернуть тип целого числа

Примеры

Alest_months (date1, date2) возвращает 8, если дата 1 равен 20/3/1997, а дата 2 - 23/7/1996

Alest_months (date1, date2) возвращает 26, если дата 1 равен 20/3/1997, а дата2 - 1/2/1995

Это было полезно?

Решение

Я написал это как функцию, которая, по моему мнению, рассчитывается так же, как приложение DCS.Он использует некоторые функции, новые для SAS версии 9.2, включая непрерывное выравнивание дат.Он также работает вперед или назад во времени (т.е. дает отрицательное целое число, если previous_date находится после Later_date).Я использовал более 15 дней после интервала в качестве предельного значения для округления до следующего месяца, но вы можете изменить это значение, если хотите.

proc fcmp outlib=work.myfuncs.dates;
   function nearest_months(later_date,earlier_date);
        /* Return missing if inputs are missing */
        if (earlier_date eq . ) OR (later_date eq . ) then
            nearest_month=.;
        else do; 
            /* Use 'cont' argument for continuous dates */
            months=intck('MONTH',earlier_date,later_date,'cont');
            if months < 0 then months=months+1;
            days= later_date - intnx('month', earlier_date,months,'same');

            /* Handle negatives (earlier dates) */
            if months < 0 then do;
                if days < -15 then months=months-1;
                nearest_month=months;
                end;
            else do;
                if days > 15 then months + 1;
                nearest_month=months;
                end;
        end;
        return(nearest_month);
   endsub;
run;
options cmplib=work.myfuncs;


data _null_;
x=nearest_months('20Mar1997'd, '23JUL1996'd);
put x=;
x=nearest_months('20Mar1997'd, '01FEB1995'd);
put x=;
run;

Это дает то же самое, что и ваша ссылка:

x=8
x=26

Другие советы

Вы могли бы использовать INTNX чтобы узнать, следует ли округлять в большую или меньшую сторону, например


data _null_;
  format x y date9. z 8.;
  x="01APR08"d;
  y="28APR08"d;
  z=intck('MONTH',x,y);

  * wl is x + z months;
  wl=intnx('MONTH',x,z);

  * wu is x + (z+1) months;
  wu=intnx('MONTH',x,z+1);

  * If y is closer to wu, then adjust z by 1;
  if (abs(y-wu) lt abs(y-wl)) then z = z+1;     

  put x y z=;
run;

Если вы определяете месяц равным 30 дням, вы округлите 15 дней или меньше до 0 месяцев, а 16 дней или больше до 1 месяца.Этого можно достичь следующим образом:

data _null_;
  format x y date9. z 8.;
  x="14FEB09"d;
  y="02MAR09"d;

  z=round(intck('DAY',x,y)/31);
  put x y z=;
run;

Вы также можете использовать подход для подсчета полных месяцев («с первого по последний 1-й») в интервале, а затем сложить оставшиеся дни, чтобы увидеть, составляют ли они в сумме 0, 1 или 2 месяца.Так:

data _null_;
  format x y date9. z 8.;
  x="01FEB09"d;
  y="31MAR09"d;

  if day(x)=1 then do;
     z=intck('MONTH',x,intnx('MONTH',y,0,'BEGINNING'))
         + round((intck('DAY',intnx('MONTH',y,0,'BEGINNING'),y))/31);
  end;
  else do;
     z=intck('MONTH',intnx('MONTH',x,1,'BEGINNING'),intnx('MONTH',y,0,'BEGINNING'))
         + round((intck('DAY',x,intnx('MONTH',x,1,'BEGINNING'))+intck('DAY',intnx('MONTH',y,0,'BEGINNING'),y))/31);
  end;
  put x y z=;
run;

Первый метод проще понять и поддерживать, но второй более точен для больших интервалов (с 01 февраля 2006 г. по 01 февраля 2009 г. — это 36 месяцев, но метод 1 сообщит вам, что это всего лишь 35).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top