Fechas relativas con Intl

Fechas relativas del tipo «Hace X minutos...»


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.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev