Formatos de fecha, su endianness, ISO 8601 y otras reflexiones

Introducción

El propósito de esta publicación es reflexionar en los diferentes formatos de fecha, sus conceptualizaciones, sus ambigüedades, su endianness y finalmente el formato internacional de fecha ISO 8601.

El endianness se refiere al extremo por el que comienza la representación de un número. Por ejemplo, para el número 547 —y considerando que en español escribimos de izquierda a derecha— primero escribimos el 500, después el 40 y después el 7. El 5 tiene más valor relativo que el 7. Puesto que se comienza por el dígito de mayor valor, a esto se le llama big-endian y es común en protocolos de red. El contrario se denomina little-endian y es común en algunos procesadores de Intel. Cuando no se sigue un orden progresivo se le dice middle-endian.

Formatos mexicano y estadounidense

Siendo mexicano me acostumbre desde niño al formato de fecha que se usa aquí, día-mes-año (DMY), que se usa así:

  • Al hablar decimos “[DÍA] de [MES] de [AÑO]”. Ejemplo: 7 de agosto de 2018.
  • Al escribirlo abreviadamente usamos “DD/MM/YYYY”. Ejemplo: 7/8/2018. A veces usamos guiones para separar en lugar de diagonales.
  • Cuando el mes es de un dígito, algunos anteponen un cero: 7/08/2018. Algunos hacen lo mismo con el día, aunque es menos común.
  • Como forma alternativa, a veces ponemos el mes con letra y abreviado: 7/ago/2018.
  • Para acortar el año simplemente escribimos los dos últimos dígitos: 7/8/18.
  • El formato DMY se dice que es little-endian, pues primero se menciona lo que significa menos (el día) y progresivamente se incrementa hasta llegar a lo que significa más: el año.

Sin embargo, como tijuanense, pronto estuve expuesto al formato de fecha estadounidense, mes-día-año (MDY), que se usa así:

  • Al hablar, dicen “[MES] [DÍA](th), [AÑO]”. Ejemplo: August 7th, 2018. Ellos usan el día en forma ordinal.
  • Al escribirlo abreviadamente usan “mm/dd/yyyy”. Ejemplo: 8/7/2018.
  • Ellos no usan la forma alterna con el mes abreviado como nosotros (Aug/7/2018). Resulta más sencillo abreviar la manera larga: Aug 7, 2018 y omitir ordinalizar el día.
  • Para acortar el año, la costumbre es anteponer un apóstrofo (comilla simple): Aug 7, ’18.
  • Este sistema no tiene un endianness progresivo. Primero va una cifra de significancia media (el mes), después la de menor valor (el día) y finalmente la de mayor valor (el año). Esto es, middle-endian.

Hay algunas diferencias interesantes entre estos dos formatos, especialmente en las diferencias de conceptualización de una fecha entre estas dos culturas:

  1. En Estados Unidos, el día se expresa de manera ordinal. En español se usa de manera cardinal. La traducción literal al español de la fecha estadounidense sería: séptimo [día] de agosto de 2018.
  2. Aún, en ambos casos resulta claro que el día pertenece al mes: 7 de agosto y August 7th.
  3. Los meses (al igual que los días de la semana) en inglés estadounidense son sustantivos propios y por lo tanto se escriben con mayúscula. Para los hispanos, son sustantivos comunes:
    • I was born in September.
    • Nací en septiembre.
  4. En ambos casos, cuando se especifica el día de la semana, se antepone.
  5. Si eliminamos el año, nosotros usamos una notación de cantidad menos significativa a más significativa dd < mm mientras que ellos usan una notación inversa: mm > dd.

Ambigüedades en la interpretación

Desafortunadamente, existen situaciones en donde el formato usado no es distinguible: textos traducidos, fechas con contexto insuficiente, formatos internacionales y programas de cómputo.

En el caso de programas de cómputo, no es posible saber si el programa está mostrando la fecha correctamente regionalizada. Por ejemplo, si un programa está en inglés pero instalado sobre un sistema operativo regionalizado al español y la fecha que se muestra es 3/9/2019, tenemos diferentes posibles interpretaciones:

  • Que el programa tiene código para pedir al sistema operativo la correcta expresión de la fecha, en cuyo caso es 3 de septiembre de 2019.
  • Que el programa no tiene código para pedir al sistema operativo la correcta expresión de la fecha, en cuyo caso es 9 de marzo de 2019.
  • Que el programa incluye sus propias rutinas de regionalización de fecha, pudiendo abrirse en otras dos opciones:
    • Que el programa pregunte al sistema operativo la región en cuestión y que el manejo de fecha esté correcto en sus rutinas, en cuyo caso es 3 de septiembre de 2019.
    • Que el programa no pregunte la región al sistema operativo o que su manejo de fechas sea incorrecto, en cuyo caso podría ser 9 de marzo de 2019.
    • Que el programa contenga una opción de configuración para la regionalización de la fecha.
    • Que el formato aceptado por el programa para el ingreso de fechas corresponda con el formato usado para representarla en pantalla.

Cuando el programa en cuestión es un sitio Web, debemos considerar otros factores como:

  • Si el navegador está correctamente configurado para enviar el encabezado Accept-Language y si se incluye la parte del locale.
  • Si la el servicio respeta el valor indicado por Accept-Language o si utiliza la dirección IP para la geoubicación del usuario.
  • Si el usuario puede especificar manualmente una regionalización en su perfil.

Formato japonés

Al explorar diferentes opciones de regionalización en los sistemas operativos conocí otros formatos, como el japonés, donde ellos usan año-mes-día (YMD): Ejemplo: 2018年8月7日. Observaciones:

  • Este formato es big-endian. Se especifican las cantidades de más a menos significativas: yyyy > mm > dd, al revés que nosotros.
  • Para los meses de un solo dígito, no se antepone un cero.
  • Parecería que no estamos acostumbrados a ese orden, sin embargo, es así como expresamos la hora: hh > mm > ss, Decimos que son las 7:32, no que estamos en el segundo 32 del minuto 7.

Formato ISO 8601

Y finalmente, el formato de fecha definido en el estándar ISO 8601 que sigue el mismo orden que el formato japonés pero:

  • el año siempre se indica en cuatro dígitos,
  • el día y el mes se indican invariablemente con dos dígitos.

Entonces, el formato ISO 8601 lleva la forma YYYY-MM-DD: 2018-08-07. Este formato tiene algunas particularidades:

  1. Se especifican las cantidades de más a menos significativas: yyyy > mm > dd, al revés que nosotros. Es big-endian.
  2. Ingresar una fecha en formato YYYY-MM-DD resulta inambiguo pues nadie utiliza YYYY-DD-MM como su formato de fecha corta.
  3. Al escribir de mayor a menor significancia en el mismo orden que nuestra escritura, el formato de fecha presenta una útil peculiaridad: el orden alfabético de un juego de fechas en formato ISO 8601 coincide con su orden cronológico computacional, aún si se especifican con todo y hora del da. Esto no ocurre con los formatos mexicano ni estadounidense.

Ejemplo: si ordenamos alfabéticamente las siguientes fechas: 2018-07-03, 2019-09-13 y 2018-07-04: nos quedan así: 2018-07-03, 2018-07-04, 2019-09-13, que coincide con su orden cronológico. Esto no funciona si usamos el formato mexicano o estadounidense.

Sin embargo, esto sólo funcionará durante años de cuatro dígitos, pues, alfanuméricamente, 10000 < 9999 debido a que el primer 1 es menor que el primer 9. No es ningún secreto que el ISO 8601 padece de un problema de Y10K, pero es lo mejor que tenemos hasta ahora. Y si unos 2000 años antes optan por usar letras en el primer dígito (A001 en lugar de 10,001) podrían extender el uso de ISO 8601 por 20000 años más.

Comentario final

Acostumbrarme a usar ISO 8601 para mis anotaciones personales me resulta sumamente útil y me ahorra problemas. Obviamente, al momento de compartir información, paso las fechas a un formato más conveniente.

Finalmente, no puedo evitar compartir esta caricatura de XKCD, reference al ISO 8601:

Video: Cómo usar Vim, abril de 2018

He subido un nuevo video a YouTube, titulado “Taller práctico: Cómo usar Vim”

Es la grabación de mi participación en el FLISoL 2018 celebrado en Guadalajara, México, en LinuxCabal el pasado sábado 28 de abril de 2018.

Liga al video en YouTube: https://www.youtube.com/watch?v=J7EjB7HiMTw

Video: PostgreSQL – Reunión Gultij, enero de 2017

El pasado sábado 21 de enero de 2017 durante la reunión mensual del Gultij, Grupo de Usuarios de GNU/Linux de Tijuana, México, di una presentación sobre PostgreSQL, un sistema manejador de bases de datos objeto-relacional (ORDBMS).

Liga al video en YouTube: https://www.youtube.com/watch?v=xyq6b9wZ0aw

Esperar hasta terminar la entrada estándar

Si uno teclea líneas a la entrada estándar desde el teclado, ésta se va procesando línea por línea:

$ cat -n
hola
     1	hola
mundo
     2	mundo
maravilloso
     3	maravilloso
^D
$

Así, resulta impráctico marcar la salida usando el ratón para copiarlo al portapapeles o simplemente para ver la salida junta. Como alternativas tenemos:

  • Marcar y copiar línea por línea, acumulando el resultado en un editor antes de copiar todo el texto. Muy impráctico.
  • Redirigir la salida a un archivo, pero aún queda pendiente tener que procesar y borrar el archivo después.
  • Usar tac | tac:
    $ tac | tac | cat -n
    hola
    mundo
    maravilloso
    ^D
         1	hola
         2	mundo
         3	maravilloso
    $
    

tac es una instrucción que concatena archivos y los despliega en reversa (LIFO en lugar de FIFO por línea). Por naturaleza, tac se ve en la obligación de leer la entrada estándar completa antes de poder mandar la primera línea de su salida. Si invertimos la entrada dos veces se obtiene el efecto deseado.

No sé hasta cuántas líneas soporte antes de volverse lento. Aquí con 10,000 líneas funciona aún bastante bien.

¿Alguien conoce alguna mejor manera?

Actualización: Dualbus me recomienda sponge de moreutils. Ejemplo:

$ sponge | cat -n
hola
mundo
maravilloso
^D
     1	hola
     2	mundo
     3	maravilloso
$

Según el manual, sponge “absorbe la entrada estándar y la escribe a un archivo”.

postfix_delivery_search.bash

Este script sirve para buscar en los logs de Postfix 2.9.6 la historia de entrega de los correos que cumplan con el from= y el to= que se indiquen en el primer y segundo argumento del script, respectivamente.

#!/bin/bash

# Configuration variables
REQUIREMENTS="gawk grep"
LOGSOURCES="/var/log/mail.info.1 /var/log/mail.info"

# Check requirements
which $REQUIREMENTS > /dev/null 2>&1 || {
    echo The following required programs were not found:
    for R in $REQUIREMENTS; do
        which "$R" > /dev/null 2>&1 || echo "$R"
    done
    exit 63
}

# Check arguments
[ $# -eq 2 ] || {
    echo Usage: "$0" source_address destination_address
    exit 1
}

# These work for Postfix 2.9.6
function get_filter_from() {
    echo 'BEGIN { FS="[ :]+"} /from=<'$1'>/ && !/NOQUEUE/ { print $8 }'
}

function get_filter_to() {
    echo 'BEGIN { FS="[ :]+"} /to=<'$1'>/ && !/NOQUEUE/ { print $8 }'
}

function awniq() {
    gawk '!a[$0]++'
}

FILTER_FROM=$(get_filter_from "$1")
FILTER_TO=$(get_filter_to "$2")

# For each mail ID found in both from= and to= lines...
for ID in $(grep -f <(gawk "$FILTER_FROM" $LOGSOURCES | awniq) \
  <(gawk "$FILTER_TO" $LOGSOURCES | awniq))
do
    echo ====== $ID ======
    grep -- "$ID" /var/log/{mail.info.1,mail.info}
done