ManzDev
Perfil de Manz Dashboard de actividad
HTML5 Etiquetas HTML
CSS CSS vanilla
PostCSS Transformador CSS
Javascript Javascript ES2020+
NPM Node Package Manager
WebComponents Componentes web
Terminal Terminal de GNU/Linux
VueJS Framework VueJS 3
Automatizadores Empaquetadores Javascript
Dibujando con CSS ¡Dibujos hechos con sólo CSS!

Formatear fechas relativas con Intl

El objeto Intl de localización proporciona un interesante mecanismo llamado RelativeTimeFormat que permite manejar y tratar fechas relativas en Javascript de forma nativa, sin necesidad de utilizar librerías externas, incluso añadiendo capacidad de diferentes idiomas.

Objeto Descripción
Intl.RelativeTimeFormat Crea un objeto de formato relativo con las preferencias de la región indicada.

Una fecha relativa es el tiempo que se calcula respecto a otra fecha, habitualmente con textos como «Hace 10 minutos», «Hace 2 horas», «Dentro de 2 días», «Mañana», «Ayer», etc... Funciona de forma muy similar a Intl.DateTimeFormat, y podemos crear un objeto con una cierta configuración y luego utilizar los métodos .format() o .formatToParts():

Método Descripción
.format(value, unit) Formatea valores en las unidades indicadas con la configuración regional.
.formatToPart(value, unit) Idem, pero devuelve un de con las partes.
.resolvedOptions() Devuelve las opciones de región definidas en la instancia.

Ten en cuenta que podemos utilizar el método .resolvedOptions() para obtener las opciones por defecto que se han utilizado al generar la instancia de la configuración de formato relativo.

El objeto RelativeTimeFormat

Comencemos viendo un ejemplo de .format(), donde formateamos varios valores teniendo en cuenta las diferentes unidades:

new Intl.RelativeTimeFormat("es-ES").format(1, "day");       // "dentro de 1 día"
new Intl.RelativeTimeFormat("es-ES").format(30, "minute");   // "dentro de 30 minutos"
new Intl.RelativeTimeFormat("es-ES").format(30, "minutes");  // "dentro de 30 minutos"
new Intl.RelativeTimeFormat("es-ES").format(-5, "month");    // "hace 5 meses"
new Intl.RelativeTimeFormat("es-ES").format(-1, "day");      // "hace 1 día"

Observa que en el segundo y tercer caso, estamos usando tanto minute como minutes. Funciona para ambos, por lo que las unidades pueden indicarse tanto en singular como plural.

Opciones de personalización

En el ejemplo anterior, hemos utilizado sólo el parámetro es-ES para inicializar la instancia de RelativeTimeFormat, pero es posible indicar un segundo parámetro de opciones para personalizar la configuración regional:

Opción Descripción
localeMatcher Indica el algoritmo para determinar la localización: lookup o best fit (por defecto).
numeric Indica si usa números: always (siempre, por defecto) o auto (usa otros si puede).
style Estilo del formato: long (por defecto), short, narrow.

Por ejemplo, utilicemos el parámetro numeric a auto, para que en lugar de utilizar siempre el formato numérico pueda cambiarlo por otra frase o palabra cuando sea posible:

const rtf = new Intl.RelativeTimeFormat("es-ES", { numeric: "auto" });
rtf.format(1, "day");       // "mañana"
rtf.format(2, "day");       // "pasado mañana"
rtf.format(0, "day");       // "hoy"
rtf.format(-1, "day");      // "ayer"
rtf.format(-2, "day");      // "anteayer"
rtf.format(-1, "month");     // "el mes pasado"
rtf.format(1, "month");     // "el próximo mes"

Formatear fechas relativas

Ahora que sabemos como funciona, vamos a intentar aplicarlo a un ejemplo más parecido a la realidad, combinándolo con el objeto Date de Javascript. Imaginemos una situación en la que tenemos la fecha de un artículo, postDate, y la fecha del día actual al cargar la página (imaginemos que estamos en el 10/Feb/2022).

Calculamos la diferencia entre ambas fechas (nos lo devuelve en milisegundos), por lo que dividimos para pasarlo a la unidad buscada, 1000 para los milisegundos, 60 para los segundos, 60 para los minutos y finalmente 24 para las horas. Nos quedamos con los días:

const postDate = new Date(2022, 0, 31);   // 31/Ene/2022
const currentDate = new Date();           // Fecha actual: 10/Feb/2022
const time = Number.parseInt((postDate - currentDate) / 1000 / 60 / 60 / 24);

const rtf = new Intl.RelativeTimeFormat("es-ES", { numeric: "auto" })
const date = rtf.format(time, "day");
"hace 10 días"

Con este sistema en mente, no nos costaría mucho crear una función que haga las conversiones de fechas y con RelativeTimeFormat te devuelva el texto con la localización que queramos (o indiquemos).

Formatear partes de una fecha

Igual que hemos trabajado con .format(), podemos hacerlo con .formatToPart(). La diferencia es que el primero nos devuelve un con la fecha relativa formateada y el último nos devuelve un de con las diferentes partes de la fecha formateada, por si queremos trabajar con ella. El formato sería el siguiente:

const rtf = new Intl.RelativeTimeFormat("es-ES", { numeric: "auto" });

rtf.format(60, "hour");
// Devuelve un string
"dentro de 60 horas"

rtf.formatToParts(60, "hour");
// Devuelve un array de objetos
[
  { type: "literal", value: "dentro de " },
  { type: "literal", value: "60", unit: "hour" },
  { type: "literal", value: " horas" }
]

Esto hace que sea posible trabajar con estos datos para hacer personalizaciones posteriores, donde podríamos acceder o buscar campos especificos y quedarnos con los valores que necesitemos.

Manz
Publicado por Manz

Docente, divulgador informático y Streamer de código. Amante del CSS y de la plataforma web en general. Autor de Emezeta.com tiempo atrás. Ha trabajado como profesor en la Universidad de La Laguna y es director del curso de Programación web FullStack y FrontEnd de EOI en Tenerife. En sus ratos libres, busca GIF de gatos en Internet.