From 3734c89cf64514489831b5339d722c89b300cc54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 16 Jan 2024 18:28:19 +0100 Subject: [PATCH] fix(core): Ensure waiting executions account for workflow timezone (#8340) --- packages/core/package.json | 1 + packages/core/src/NodeExecuteFunctions.ts | 3 +- packages/core/src/utils.ts | 5 ++++ packages/core/test/utils.test.ts | 35 +++++++++++++++++++++++ pnpm-lock.yaml | 8 ++++++ 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/utils.ts create mode 100644 packages/core/test/utils.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 2c336663fc..5039d4cf79 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -57,6 +57,7 @@ "file-type": "16.5.4", "form-data": "4.0.0", "lodash": "4.17.21", + "luxon": "^3.4.4", "mime-types": "2.1.35", "n8n-workflow": "workspace:*", "oauth-1.0a": "2.2.6", diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index f539b30a94..746d30eede 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -151,6 +151,7 @@ import Container from 'typedi'; import type { BinaryData } from './BinaryData/types'; import merge from 'lodash/merge'; import { InstanceSettings } from './InstanceSettings'; +import { toUtcDate } from './utils'; axios.defaults.timeout = 300000; // Prevent axios from adding x-form-www-urlencoded headers by default @@ -3489,7 +3490,7 @@ export function getExecuteFunctions( binaryToBuffer: async (body: Buffer | Readable) => Container.get(BinaryDataService).toBuffer(body), async putExecutionToWait(waitTill: Date): Promise { - runExecutionData.waitTill = waitTill; + runExecutionData.waitTill = toUtcDate(waitTill, getTimezone(workflow)); if (additionalData.setExecutionStatus) { additionalData.setExecutionStatus('waiting'); } diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts new file mode 100644 index 0000000000..991044ad41 --- /dev/null +++ b/packages/core/src/utils.ts @@ -0,0 +1,5 @@ +import { DateTime } from 'luxon'; + +export function toUtcDate(datetime: Date, tz: string) { + return DateTime.fromISO(datetime.toISOString().slice(0, -1), { zone: tz }).toUTC().toJSDate(); +} diff --git a/packages/core/test/utils.test.ts b/packages/core/test/utils.test.ts new file mode 100644 index 0000000000..a155b72e1c --- /dev/null +++ b/packages/core/test/utils.test.ts @@ -0,0 +1,35 @@ +import { toUtcDate } from '@/utils'; + +describe('utils', () => { + describe('toUtcDate()', () => { + test('should convert to UTC date by adding', () => { + const originalDate = new Date('2020-01-01T00:00:00.000Z'); + const timezone = 'America/New_York'; // +5 to reach Z + + const utcDate = toUtcDate(originalDate, timezone); + + expect(utcDate).toBeInstanceOf(Date); + expect(utcDate.toISOString()).toBe('2020-01-01T05:00:00.000Z'); + }); + + test('should convert to UTC date by subtracting', () => { + const originalDate = new Date('2020-01-01T00:00:00.000Z'); + const timezone = 'Europe/Paris'; // -1 to reach Z + + const utcDate = toUtcDate(originalDate, timezone); + + expect(utcDate).toBeInstanceOf(Date); + expect(utcDate.toISOString()).toBe('2019-12-31T23:00:00.000Z'); + }); + + test('should convert to UTC date when already UTC', () => { + const originalDate = new Date('2020-01-01T00:00:00.000Z'); + const timezone = 'UTC'; // already at Z + + const utcDate = toUtcDate(originalDate, timezone); + + expect(utcDate).toBeInstanceOf(Date); + expect(utcDate.toISOString()).toBe('2020-01-01T00:00:00.000Z'); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccd855c9a5..f418fe6ba0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -767,6 +767,9 @@ importers: lodash: specifier: 4.17.21 version: 4.17.21 + luxon: + specifier: ^3.4.4 + version: 3.4.4 mime-types: specifier: 2.1.35 version: 2.1.35 @@ -19831,6 +19834,11 @@ packages: engines: {node: '>=12'} dev: false + /luxon@3.4.4: + resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + engines: {node: '>=12'} + dev: false + /lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true