Angular 1.x y las fechas

Trabajar con fechas en cualquier sistema siempre ha sido uno de los puntos más conflictivos y propensos a fallos. Entender la gestión que las diferentes plataformas hacen de las fechas locales, las fechas UTC, las zonas horarias, los formatos de serialización, … no es trivial. Panificar cómo debe almacenar las fechas nuestro sistema y cómo las debe representar para que sean leidas y modificadas por el usuario, exige un mínimo de panificación.

En las aplicaciones web en general, tenemos que tener en cuenta las características del objeto Date en javascript (por ejemplo que los meses empiezan en 0!), el comportamiento de la serialización y deserialización a JSON para que viaje por la red, y el comportamiento de la capa de serialización-deserialización de nuestra tecnología de backend.

Una de las caracteríasticas que tiene el objeto Date, es que va a representar siempre la fecha en la zona horaria configurada en el navegador (normalmente la del sistema), y que por lo tanto, para representar horas UTC vamos a tener que hacer algo de “magia” una de las librerías que facilitan enormemente manipular fechas en javascript es moment, pero no nos va a hacer falta siempre.

Angular viene con dos pequeñas joyas, poco conocidas, para poder configurar como vamos a bindear las fechas.

ng-model con fechas

Cuando bindeamos un input[date] con ng-model, la fecha que vamos a recibir en el modelo va a ser siempre la fecha local. Es decir, que si yo selecciono el día 12/03/2016 en el input y, en mi caso estoy en ese momento en la zona horaria GMT + 1, la fecha que obtendré es 12/03/2016 00:00:00 GMT+1. El problema es que esa fecha, serializada a JSON (por lo tanto UTC) se representará como 11/03/2016T23:00:00Z.

Evidentemente, si lo que estoy bindeando, por ejemplo, es un filtro de fecha que voy a enviar al servidor, no obtendré los resultados esperados, ya que mi intención es filtrar por la fecha 12/03/2016 y en realizad estoy enviando el 11/03/2016.

Afortunadamente, desde la versión 1.3 de angular, en la directiva ng-model-options hay una configuración para configurar la zona horaria.

<input type="date" ng-model-options="{timezone: 'UTC'}" ng-model="$dates.dateTimezone">

Con esta configuración, con el ejemplo anterior, cuando yo seleccione el 12/03/2016  en el input, obtendré el 12/03/2016 01:00:00 GMT+1, que cuando lo serializamos a JSON, se representa como 12/03/2016T00:00:00Z.

Por lo tanto, ya enviaremos la fecha correcta del filtro al servidor, y sin utilizar ninguna librería externa.

filter date

Esta misma configuración la tenemos disponibles en el filtro date. Este filtro permite recibir como segundo parámetro la zona horaria en la que queremos que se visualice la fecha.

<div>{{ $dates.asDate | date:'yyyy-MM-dd HH:mm:ss.sss':'UTC' }}</div>

Con lo cual, nos resultará muy sencillo mostrar al usuario fechas en formato UTC, otra vez sin necesidad de usar librerías externas.

Además otra cuestión muy interesante con el filtro date es que soporta objetos Date, strings y number. Por lo tanto, si recibimos la fecha serializada en JSON en el modelo, y sólo queremos mostrarla (no editarla), podemos bindearla tal cual sin necesidad de convertirla a un objeto Date. Si necesitamos editarla, sí que tendremos que hacer esa conversión, porque ng-model sobre un input[date] sí que necesita bindear sobre una propiedad de tipo Date.

Os dejo un plunk con algunas pruebas sobre estos temas para jugar.

Hasta la próxima!