Fecha/hora: Luxon, formatos alemanes, trampas de zona horaria

Luxon en n8n: generar formatos de fecha alemanes con toFormat y setLocale y evitar las trampas típicas de zona horaria en las expresiones.

n8n procesa la fecha y la hora internamente a través de la biblioteca de JavaScript Luxon y no a través del objeto nativo `Date` de JavaScript. Por lo general, esto solo se nota cuando un workflow genera una fecha desviada en una hora, un nombre de mes aparece en inglés en lugar de en alemán, o un comprobante aterriza de repente en el día equivocado a fin de mes. En cuanto entiendes cómo Luxon separa limpiamente zona horaria, idioma y formato, puedes evitar precisamente estos errores de forma específica, y mantienes el control sobre qué fecha aparece realmente en la factura, el CRM o la tabla.

Este artículo se centra deliberadamente en el formateo de fecha y hora en expresiones y en el Date & Time Node, no en la lógica de zona horaria del Schedule Trigger ni de las expresiones Cron. Ese es un tema aparte con sus propias trampas que merece una mirada separada.

Por qué n8n utiliza Luxon en absoluto

El JavaScript puro no conoce ningún concepto integrado para zonas horarias o formatos de idioma que vaya más allá de lo más básico. Luxon llena exactamente ese vacío: cada fecha que n8n genera internamente, por ejemplo mediante `$now` o `$today`, es un objeto Luxon `DateTime` con su propia zona horaria, su propio idioma (locale) y sus propios métodos para calcular, comparar y formatear. Según la documentación oficial sobre el trabajo con fecha y hora en n8n, los valores de fecha se transmiten entre nodos como cadenas de texto y deben volver a analizarse con funciones de Luxon para cálculos o formateo. El `Date()` nativo de JavaScript no respeta la zona horaria configurada en n8n, razón por la cual la documentación recomienda expresamente permanecer con Luxon para todo lo relacionado con fecha y hora.

Formatear una fecha en expresiones

Para el formateo propiamente dicho existen varios caminos en las expresiones, con distinta idoneidad para los requisitos alemanes:

  • `.toFormat(fmt)`: el método nativo de Luxon con el que tú mismo compones un formato mediante tokens fijos, por ejemplo `dd.MM.yyyy`. Funciona en todas partes, también en el Code Node.
  • `.toLocaleString(opts)`: devuelve un formato localizado, es decir, dependiente del idioma. Sin una locale establecida, el resultado sigue el idioma predeterminado de la instancia de n8n, lo que en la práctica a menudo significa inglés.
  • `format()`: una extensión propia de n8n del lenguaje de expresiones para el formateo de fechas que, según la referencia de expresiones DateTime, no está disponible en el Code Node. Ahí recurres en su lugar al `.toFormat()` nativo.
  • Formatos predefinidos en el Date & Time Node: el Node incluye preajustes fijos como `MM/DD/YYYY` o `YYYY-MM-DD`, además de una opción para un formato propio. Según la documentación del Date & Time Node, n8n admite para ello todos los formatos que también conoce Luxon, teniendo en cuenta que los tokens distinguen entre mayúsculas y minúsculas.

Para una fecha alemana puramente numérica, normalmente basta con `.toFormat()` con un patrón de tokens fijo, mientras que `.toLocaleString()` y `.setLocale()` se vuelven importantes en cuanto deben aparecer en alemán nombres de mes o de día de la semana escritos en su forma completa.

Generar correctamente formatos alemanes

Para la escritura alemana clásica `TT.MM.JJJJ` basta con una expresión fija sin dependencia de la locale:

  • `{{$now.toFormat('dd.MM.yyyy')}}` da como resultado, por ejemplo, `20.07.2026`.
  • `{{$now.toFormat('dd.MM.yyyy HH:mm')}}` añade la hora en formato de 24 horas.

En cuanto entran en juego nombres escritos en su forma completa, por ejemplo para una factura o una carta modelo, necesitas además la locale alemana, porque de lo contrario el nombre del mes se muestra en inglés:

  • `{{$now.setLocale('de-DE').toLocaleString({month: 'long', day: 'numeric', year: 'numeric'})}}` produce un resultado como `20. Juli 2026`.
  • `{{$now.setLocale('de-DE').monthLong}}` devuelve directamente el nombre del mes escrito en su forma completa, por ejemplo `Juli`.

Importante: `.setLocale()` cambia exclusivamente el idioma de la salida, no la zona horaria. Debes establecer ambos ajustes por separado, de lo contrario obtienes una palabra alemana para el mes, pero en determinadas circunstancias sigue siendo la hora incorrecta.

Trampas de zona horaria al formatear

La mayoría de los errores no surgen del formato en sí, sino de la zona horaria en la que se formatea. Algunas trampas que aparecen una y otra vez en la práctica:

  • Zona horaria predeterminada de la instancia en lugar de Europe/Berlin: sin configuración explícita, una instancia de n8n autoalojada usa por defecto, según la documentación, `America/New York`, mientras que en n8n Cloud el sistema recae en GMT. `$now` y `$today` se orientan por esta zona horaria de la instancia, salvo que un ajuste propio del workflow o una variable de entorno `GENERIC_TIMEZONE` indiquen otra cosa. Quien pase esto por alto obtiene marcas de tiempo que se desvían varias horas de la hora alemana.
  • Formatear sin `.setZone()` previo: `.toFormat()` y `.toLocaleString()` siempre muestran la hora en la zona horaria que tiene actualmente el objeto `DateTime`, no automáticamente Europe/Berlin. Si una fecha proviene de una API en UTC o en una zona horaria extranjera, primero deberías convertirla con `.setZone('Europe/Berlin')` y solo después formatearla, de lo contrario un comprobante creado a las 23:30 hora alemana termina de repente en el día siguiente dentro del informe.
  • Horario de verano y horario de invierno: Europe/Berlin cambia dos veces al año su desfase respecto a UTC. Si calculas con `.plus({days: n})` a través de un cambio de este tipo y luego formateas la hora, la parte horaria puede desplazarse una hora, aunque el día del calendario sea correcto. Para comparaciones de solo fecha sin hora esto suele ser poco crítico, pero para procesos sensibles al tiempo deberías tenerlo en cuenta.
  • Transmitir `.toISO()` con desfase: la cadena ISO de Luxon contiene por defecto el desfase de zona horaria, por ejemplo `+02:00`. Los sistemas que en su lugar esperan UTC puro o una cadena sin desfase interpretan entonces este valor incorrectamente. Comprueba en el sistema destino qué formato se espera realmente antes de transmitir el resultado sin más.

Ejemplo práctico: mostrar correctamente la fecha de factura

Un caso típico de la práctica: un workflow obtiene una fecha de comprobante desde una API que entrega marcas de tiempo en UTC, y a partir de ella debe generar una fecha de factura alemana. La expresión combinada para esto se ve así:

  • `{{ $json.createdAt.toDateTime().setZone('Europe/Berlin').setLocale('de-DE').toFormat('dd.MM.yyyy') }}`

El orden aquí no es casualidad: primero la marca de tiempo nativa de JavaScript se convierte en un `DateTime` de Luxon, luego se desplaza a la zona horaria correcta, después se fija el idioma y solo al final se formatea. Si intercambias el paso de zona horaria y el de formateo, obtienes una fecha alemana de apariencia correcta, pero posiblemente para el día del calendario equivocado.

Preguntas frecuentes

¿Cuál es la diferencia entre `.toFormat()` y `.toLocaleString()`?

`.toFormat()` sigue un patrón de tokens fijo definido por ti mismo, como `dd.MM.yyyy`, y entrega siempre el mismo formato independientemente de la locale. `.toLocaleString()`, en cambio, se orienta por el idioma establecido y adapta automáticamente tanto el orden como la escritura, lo que marca la diferencia especialmente con nombres de mes o de día de la semana escritos en su forma completa.

¿Por qué aparece el nombre del mes en inglés aunque trabajo en Alemania?

Sin un `.setLocale('de-DE')` explícito, Luxon utiliza el idioma predeterminado de la instancia de n8n, que a menudo está configurado en inglés. La zona horaria de la instancia y su idioma son dos ajustes separados, razón por la cual un `Europe/Berlin` correctamente configurado por sí solo aún no garantiza un nombre de mes en alemán.

¿Tengo que establecer manualmente la zona horaria para cada fecha?

Siempre que una fecha provenga de una fuente externa como una API, una base de datos o un formulario, sí. Estos valores suelen estar en UTC o en una zona horaria extranjera, y solo `$now` o `$today` siguen automáticamente la zona horaria de la instancia o del workflow configurada en n8n.

¿Esto también se aplica al Schedule Trigger o a las expresiones Cron?

No, ahí rigen reglas propias para el momento de ejecución en sí, no para el formateo de valores de fecha en expresiones. Este artículo trata exclusivamente de cómo se formatea correctamente una fecha ya existente dentro de un workflow.

¿Cómo puedo averiguar qué tokens de formato admite Luxon?

Según la documentación del Date & Time Node, n8n admite todos los tokens que también conoce Luxon, teniendo en cuenta que mayúsculas y minúsculas tienen cada una su propio significado. Para una visión completa de todos los tokens disponibles merece la pena echar un vistazo a la referencia de formateo de Luxon enlazada directamente desde la documentación de n8n.

Sobre NordFlux

NordFlux UG (haftungsbeschränkt)

NordFlux crea empleados digitales para las organizaciones: automatizaciones y agentes KI que asumen el trabajo repetitivo. Usted mantiene el control.

Más sobre nosotros
Análisis inicial gratuito

¿Preguntas concretas sobre automatización o IA?

En un análisis inicial gratuito hablamos directamente de su caso. Sin compromiso.