SAS Data de Emissão - cálculo “MAIS PRÓXIMO MÊS”
Pergunta
Eu preciso calcular um número que equivale ao número 'mais próximo' de meses entre duas datas. No entanto, a função SAS padrão (INTCK) não é voltado para considerar a data da sua parâmetros de data (por exemplo, código abaixo resolve 0 quando eu precisar dele para arredondar para 1).
O que é a maneira 'mais puro' de como resolver esse problema?
data _null_;
x="01APR08"d;
y="28APR08"d;
z=intck('MONTH',x,y);
put z= ;
run;
EDIT:. Resposta ao comentário Martins
Eu arredondar para 0 meses - Eu não acho que a fronteira é relevante. A função que eu estou tentando replicar (NEAREST_MONTHS) vem de DCS (aplicação profeta Sungard). Agora estou esperando a chance de realizar alguns testes dentro do próprio aplicativo para entender mais sobre como ele trata datas (vai postar resultados de volta aqui)).
O arquivo de ajuda contém o seguinte: Categoria Data ??p>
Descrição
Retorna a diferença entre duas datas para o próximo número de meses. Se a segunda data for posterior à primeira data, então 0 é retornado.
Sintaxe
NEAREST_MONTHS (Later_Date, Earlier_Date)
Tipo de retorno Integer
Exemplos
NEAREST_MONTHS (date1, date2) Devoluções 8 se date1 é 20/3/1997 e date2 é 23/7/1996
NEAREST_MONTHS (date1, date2) Retorna 26 se date1 é 20/3/1997 e date2 é 1995/01/02
Solução
Eu escrevi isso como uma função que eu acho que calcula, da mesma forma como a aplicação DCS. Ele usa algumas características que são novos para SAS na versão 9.2, incluindo alinhamentos contínuos em datas. Ele também funciona para a frente ou para trás no tempo (isto é, dá um inteiro negativo se earlier_date é depois later_date). Eu usei mais de 15 dias além do intervalo como o corte de volta para o próximo mês, mas você pode ajustar isso se você preferir.
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;
Isto dá o mesmo que sua referência:
x=8
x=26
Outras dicas
Você pode usar INTNX
para ver se a até rodada ou para baixo, por exemplo.
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;
Se você definir um mês para ser 30 dias, você faria redondos 15 dias ou menos até 0 meses e 16 dias ou mais até 1 mês. Isto pode ser conseguido pelo seguinte:
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;
Você também pode tomar a abordagem para contar os meses completos ( "first 1º ao último dia 1º") no intervalo, e depois adicionar-se quaisquer dias restantes para ver se eles resumem a 0, 1 ou 2 meses. Como esta:
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;
O primeiro método é mais fácil de entender e manter, mas o segundo é mais preciso para grandes intervalos (01FEB06 para 01FEB09 é de 36 meses, mas o método 1 irá dizer-lhe que é apenas 35).