Alojar n8n uno mismo con Docker Compose: guía completa para servidores alemanes
Cómo instalar n8n con Docker Compose: Postgres en lugar de SQLite, .env, volúmenes y actualizaciones paso a paso.
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.
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.
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.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.
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.
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:
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..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..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..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.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.
.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.
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.
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.
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.
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.
Fundador de NordFlux. Siete años de experiencia, desde la web y el SEO hasta la automatización a escala de grupo, hoy de forma pragmática para las pymes y con soberanía de datos alemana.
Certificaciones
Cómo instalar n8n con Docker Compose: Postgres en lugar de SQLite, .env, volúmenes y actualizaciones paso a paso.
De forma predeterminada, n8n se ejecuta en America/New_York en lugar de Europe/Berlin. Así se configura correctamente GENERIC_TIMEZONE y la zona horaria del workflow.
Unas fechas de factura mal formateadas o marcas de tiempo desplazadas por trampas de zona horaria pueden costarle la confianza de clientes o administraciones justo cuando más importa. NordFlux desarrolla y revisa sus workflows de n8n para que la lógica de fecha y hora se mantenga correcta incluso con fuentes de datos internacionales. En una primera conversación revisamos juntos sus expresiones críticas.