From b0f6c6f0648a89ab4565274ed37cad7225cbc6da Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:32:44 +0200 Subject: [PATCH] fix(core): Use the configured timezone in task runner (#12032) --- docker/images/n8n/n8n-task-runners.json | 1 + .../src/config/base-runner-config.ts | 3 ++ .../__tests__/js-task-runner.test.ts | 39 ++++++++++++++++++- packages/@n8n/task-runner/src/start.ts | 6 ++- .../__tests__/task-runner-process.test.ts | 1 + .../cli/src/runners/task-runner-process.ts | 1 + 6 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docker/images/n8n/n8n-task-runners.json b/docker/images/n8n/n8n-task-runners.json index 10cf338731283..a37c59fccbfc8 100644 --- a/docker/images/n8n/n8n-task-runners.json +++ b/docker/images/n8n/n8n-task-runners.json @@ -7,6 +7,7 @@ "args": ["/usr/local/lib/node_modules/n8n/node_modules/@n8n/task-runner/dist/start.js"], "allowed-env": [ "PATH", + "GENERIC_TIMEZONE", "N8N_RUNNERS_GRANT_TOKEN", "N8N_RUNNERS_N8N_URI", "N8N_RUNNERS_MAX_PAYLOAD", diff --git a/packages/@n8n/task-runner/src/config/base-runner-config.ts b/packages/@n8n/task-runner/src/config/base-runner-config.ts index 723d3279bf1a3..d70f7e2ee87fc 100644 --- a/packages/@n8n/task-runner/src/config/base-runner-config.ts +++ b/packages/@n8n/task-runner/src/config/base-runner-config.ts @@ -34,6 +34,9 @@ export class BaseRunnerConfig { @Env('N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT') idleTimeout: number = 0; + @Env('GENERIC_TIMEZONE') + timezone: string = 'America/New_York'; + @Nested healthcheckServer!: HealthcheckServerConfig; } diff --git a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts index 05e0b91f77d37..a99e8b9f071f6 100644 --- a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts +++ b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts @@ -1,5 +1,5 @@ import { DateTime } from 'luxon'; -import type { CodeExecutionMode, IDataObject } from 'n8n-workflow'; +import { setGlobalState, type CodeExecutionMode, type IDataObject } from 'n8n-workflow'; import fs from 'node:fs'; import { builtinModules } from 'node:module'; @@ -326,6 +326,43 @@ describe('JsTaskRunner', () => { }); }); + describe('timezone', () => { + it('should use the specified timezone in the workflow', async () => { + const taskData = newDataRequestResponse(inputItems.map(wrapIntoJson), {}); + taskData.workflow.settings = { + timezone: 'Europe/Helsinki', + }; + + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: 'return { val: $now.toSeconds() }', + nodeMode: 'runOnceForAllItems', + }), + taskData, + }); + + const helsinkiTimeNow = DateTime.now().setZone('Europe/Helsinki').toSeconds(); + expect(outcome.result[0].json.val).toBeCloseTo(helsinkiTimeNow, 1); + }); + + it('should use the default timezone', async () => { + setGlobalState({ + defaultTimezone: 'Europe/Helsinki', + }); + + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: 'return { val: $now.toSeconds() }', + nodeMode: 'runOnceForAllItems', + }), + taskData: newDataRequestResponse(inputItems.map(wrapIntoJson), {}), + }); + + const helsinkiTimeNow = DateTime.now().setZone('Europe/Helsinki').toSeconds(); + expect(outcome.result[0].json.val).toBeCloseTo(helsinkiTimeNow, 1); + }); + }); + it('should allow access to Node.js Buffers', async () => { const outcomeAll = await execTaskWithParams({ task: newTaskWithSettings({ diff --git a/packages/@n8n/task-runner/src/start.ts b/packages/@n8n/task-runner/src/start.ts index 005cbfc840e8d..f68779f38d135 100644 --- a/packages/@n8n/task-runner/src/start.ts +++ b/packages/@n8n/task-runner/src/start.ts @@ -1,4 +1,4 @@ -import { ensureError } from 'n8n-workflow'; +import { ensureError, setGlobalState } from 'n8n-workflow'; import Container from 'typedi'; import { MainConfig } from './config/main-config'; @@ -44,6 +44,10 @@ function createSignalHandler(signal: string) { void (async function start() { const config = Container.get(MainConfig); + setGlobalState({ + defaultTimezone: config.baseRunnerConfig.timezone, + }); + if (config.sentryConfig.sentryDsn) { const { ErrorReporter } = await import('@/error-reporter'); errorReporter = new ErrorReporter(config.sentryConfig); diff --git a/packages/cli/src/runners/__tests__/task-runner-process.test.ts b/packages/cli/src/runners/__tests__/task-runner-process.test.ts index 447a57d3c7491..85dbaa6930884 100644 --- a/packages/cli/src/runners/__tests__/task-runner-process.test.ts +++ b/packages/cli/src/runners/__tests__/task-runner-process.test.ts @@ -76,6 +76,7 @@ describe('TaskRunnerProcess', () => { 'N8N_VERSION', 'ENVIRONMENT', 'DEPLOYMENT_NAME', + 'GENERIC_TIMEZONE', ])('should propagate %s from env as is', async (envVar) => { jest.spyOn(authService, 'createGrantToken').mockResolvedValue('grantToken'); process.env[envVar] = 'custom value'; diff --git a/packages/cli/src/runners/task-runner-process.ts b/packages/cli/src/runners/task-runner-process.ts index 01e351c0e409d..e33157a286b5e 100644 --- a/packages/cli/src/runners/task-runner-process.ts +++ b/packages/cli/src/runners/task-runner-process.ts @@ -54,6 +54,7 @@ export class TaskRunnerProcess extends TypedEmitter { private readonly passthroughEnvVars = [ 'PATH', + 'GENERIC_TIMEZONE', 'NODE_FUNCTION_ALLOW_BUILTIN', 'NODE_FUNCTION_ALLOW_EXTERNAL', 'N8N_SENTRY_DSN',