SAS: Operaciones con fechas

Trabajar con fechas y con formatos de fechas es una de las cosas a las que más cuesta acostumbrarse a aquellos que comienzan a trabajar con SAS. Aquí quiero recoger un pequeño resumen de las funciones de fecha más habituales en SAS, cómo funcionan y cuales son las diferencias entre ellas. Aquí veremos solo funciones de fecha, no entraré en las funciones fecha-hora.

Lo primero que hay que saber de las fechas en SAS es que, como en muchos otros lenguajes de programación, en realidad se representan internamente por un número y la fecha que vemos es una máscara o formato de representación de ese número. Al ser números se pueden realizar operaciones aritméticas con ellas directamente sin problema, por lo que el día de ayer se puede calcular como date()-1.

La fecha 0 es en SAS el 1 de enero de 1960. En otros lenguajes la fecha 0 puede ser otra. El 31 de diciembre de 1959 tiene el valor -1.

Para expresar una fecha en SAS podemos hacerlo por un número o por la notación '1jan2021'd. La fecha actual se obtiene con date().

  • datedate()
    Da la fecha del sistema, o sea, la fecha de la máquina en la que corre SAS. El resultado, sin aplicar ningún formato, será un número.

    Ejemplo Resultado
    date() 22240
    put(date(),date9.) 21NOV2020
  • datepartdatepart([datetime])
    Convierte un valor tipo fecha-hora en una fecha.

    Ejemplo Resultado
    datepart(datetime()) 22240
    put(datepart('15apr2020:00:00:00'dt),date9.) 15APR2020
  • intnxintnx([intervalo], [fecha], [incremento], [alineamiento])
    La función intnx() calcula la fecha resultante de aplicar un incremento (positivo o negativo) a la «fecha» indicada. El «incremento» representa el número de unidades del tipo definido en el parámetro «intervalo» que hay que añadir a esa fecha. Este «incremento» puede ser positivo o negativo. El «alineamiento» puede tomar los valores ‘B’ inicio del periodo; ‘E’ fin del periodo; ‘M’ medio del periodo y ‘S’ mismo día del periodo, y significa que si vamos a sumar x meses a una fecha si indicamos «B», la fecha resultante será el primer día del mes después de sumar x meses a la fecha dada. Lo vemos mejor en unos ejemplos:

    Ejemplo Resultado
    intnx('month','31aug2020'd,1) 1SEP2020
    intnx('month','31aug2020'd,1,'S') 30SEP2020
    intnx('month','18jan2020'd,-1,'E') 31DEC2019
    intnx('year','5jun1999'd,0,'B') 01JAN1999
    intnx('week','18apr2020'd,1,'S') 25APR2020
    intnx('qtr','18apr2020'd,0,'E') 30JUN2020
  • intckintck([intervalo], [fecha-inicio], [fecha-fin], [método])
    La función intck() calcula la diferencia entre las dos fechas indicándolo en función de la unidad temporal expresada en el parámetro intervalo. El ‘método’ sirve para indicar cómo se va a realizar el cálculo: puede tomar los valores ‘CONTINUOUS’ o ‘C’ para calcular el número de aniversarios sucedidos antes de la fecha-fin, o ‘DISCRETE’ o ‘D’ que toma el tiempo como una variable discreta y cuenta cuantas unidades hay entre las dos fechas proporcionadas. Esto último se ve mejor en el siguiente ejemplo donde se observa que cuando comienza un nuevo periodo temporal natural (en este caso estoy contando años) se incrementa la cuenta que ofrece el método ‘DISCRETE’.

    Ejemplo Resultado
    intck('year','1jul2000'd,'1jan2001'd,'D'); 1
    intck('year','1jul2000'd,'31dec2001'd,'D') 1
    intck('year','1jul2000'd,'1jan2002'd,'D') 2
    intck('year','1jul2000'd,'1jan2002'd,'C') 1
    yrdif('1jul2000'd,'1jan2002'd) 1
  • DAY(), MONTH(), YEAR()
    Estas funciones dan como resultado un número que representa, respectivamente, el día el mes y el año de la fecha que se indica entre paréntesis.

    Ejemplo Resultado
    day(date()) 21
    month('25may2020'd) 5
    year('25may2020'd) 2020
  • WEEK(), WEEKDAY()
    Estas funciones devuelven, respectivamente, el número que ocupa esa fecha dentro de un año (entre 1 y 53); y el número del día de la semana desde 1-domingo hasta 7-sábado.

    Ejemplo Resultado
    week('21nov2020'd) 46
    weekdate('21nov2020'd) /*sábado*/ 7
    weekdate('22nov2020'd) /*domingo*/ 1
  • QTR()QTR([fecha])
    Da como resultado un entero que indica el trimestre del año (de 1 a 4) en que se encuentra la fecha.

    Ejemplo Resultado
    QTR('21nov2020'd) 4
  • YRDIFYRDIF([fecha-inicio],[fecha-fin])
    YRDIF da la diferencia exacta en años entre dos fechas. El resultado es un número con decimales que representa exactamente la diferencia en años. Se puede añadir un tercer parámetro que hace que se consideren años de meses de 30 días y 360 días por año y otras opciones.

    Ejemplo Resultado
    yrdif('1jul2000'd,'1jan2002'd) 1.504109589

Ejecutar un programa SAS pasando parámetros

La entrada de hoy es la solución a un problema muy fácil: se trata de planificar un programa sas pasándole algún parámetros desde línea de comandos, en este caso, la solución aplica a Unix/Linux.

Para ello, debemos crear un fichero de script (.sh) que contenga la llamada y los parámetros a enviar:

sh [ruta_SAS]/Lev1/SASApp/sas.sh -set fecha 20201111 -set hora 17:28 -sysin [ruta_programa]/programa.sas -log [ruta_log]/fichero.log

La llamada a sas.sh ejecuta sas.exe con las opciones que estén configuradas para el usuario. El parámetro set contiene una dupla clave valor con el nombre de la variable a enviar al programa en ejecución y su valor. Se pueden concatenar varios parámetros set. En el ejemplo se pasa la fecha y la hora. Finalmente, los parámetros sysin y log indican la ruta y fichero del programa sas y del log que se debe generar, respectivamente.

Una vez hecho esto, solo queda recoger en el programa los valores que hemos enviado. Esto podemos hacerlo de la siguiente forma:

%let fecha=%sysget(fecha);
%let hora=%sysget(hora);

Con ello, podemos utilizar los parámetros que hemos enviado como simples macrovariables: &fecha y &hora.

Unix: Extensión de un fichero Unix

En esta ocasión vamos a ver cómo podemos hacer con un script de Unix una tarea que en realidad también podemos hacer con SAS. Se trata de renombrar ficheros *.zip a *.zip.old, en este caso modificando su extensión. Para ello, debemos poder extraer las dos partes del nombre de un fichero: el nombre y la extensión.

FICHERO="${i%%.*}"
EXTENSION=$([[ "$i" = *.* ]] && echo "${i#.}" || echo "${i##.}")

Si queremos procesar todos los ficheros zip de un directorio para renombrarlos recursivamente a zip.old (por ejemplo) podemos usar el siguiente script. Lo primero que hace es listar los ficheros que tienen zip en su nombre (o extensión). Luego revisa, para cada uno de ellos, cual es exactamente su extensión, y si finalmente tienen extensión zip los renombra (con mv) a *.zip.old:

for i in $(ls -1 | grep .zip);
do FICHERO="${i%%.*}";
EXTENSION=$([[ "$i" = *.* ]] && echo "${i#.}" || echo "${i##.}");
if [ "$EXTENSION" == "zip" ];
then mv $FICHERO.zip $FICHERO.zip.old;
fi;
done

Este script puede ser llamado también desde SAS para hacer esa misma tarea con el comando X:

x"for i in $(ls -1 | grep .zip); do FICHERO="${i%%.*}"; EXTENSION=$([[ "$i" = *.* ]] && echo "${i#.}" || echo "${i##.}"); if [ "$EXTENSION" == "zip" ]; then mv $FICHERO.zip $FICHERO.zip.old; fi; done";