diff --git a/packages/workflow/src/Extensions/DateExtensions.ts b/packages/workflow/src/Extensions/DateExtensions.ts index 6208c23442..5ad709654a 100644 --- a/packages/workflow/src/Extensions/DateExtensions.ts +++ b/packages/workflow/src/Extensions/DateExtensions.ts @@ -70,57 +70,54 @@ const DATETIMEUNIT_MAP: Record = { ms: 'millisecond', }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function isDateTime(date: any): date is DateTime { - if (date) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return DateTime.isDateTime(date); - } - return false; +function isDateTime(date: unknown): date is DateTime { + return date ? DateTime.isDateTime(date) : false; } -function generateDurationObject(durationValue: number, unit: DurationUnit) { +function generateDurationObject(durationValue: number, unit: DurationUnit): DurationObjectUnits { const convertedUnit = DURATION_MAP[unit] || unit; - return { [`${convertedUnit}`]: durationValue } as DurationObjectUnits; + return { [`${convertedUnit}`]: durationValue }; } -function beginningOf(date: Date | DateTime, extraArgs: DurationUnit[]): Date { - const [unit = 'week'] = extraArgs; +function beginningOf(date: Date | DateTime, extraArgs: DurationUnit[]): Date | DateTime { + const [rawUnit = 'week'] = extraArgs; - if (isDateTime(date)) { - return date.startOf(DATETIMEUNIT_MAP[unit] || unit).toJSDate(); - } - const dateTime = DateTime.fromJSDate(date); - return dateTime.startOf(DATETIMEUNIT_MAP[unit] || unit).toJSDate(); + const unit = DATETIMEUNIT_MAP[rawUnit] || rawUnit; + + if (isDateTime(date)) return date.startOf(unit); + + return DateTime.fromJSDate(date).startOf(unit).toJSDate(); } -function endOfMonth(date: Date | DateTime): Date { - if (isDateTime(date)) { - return date.endOf('month').toJSDate(); - } +function endOfMonth(date: Date | DateTime): Date | DateTime { + if (isDateTime(date)) return date.endOf('month'); + return DateTime.fromJSDate(date).endOf('month').toJSDate(); } -function extract(inputDate: Date | DateTime, extraArgs: DatePart[]): number | Date { - let [part = 'week'] = extraArgs; - let date = inputDate; - if (isDateTime(date)) { - date = date.toJSDate(); - } +function extract(date: Date | DateTime, args: DatePart[]): number { + let [part = 'week'] = args; + if (part === 'yearDayNumber') { + date = isDateTime(date) ? date.toJSDate() : date; + const firstDayOfTheYear = new Date(date.getFullYear(), 0, 0); + const diff = date.getTime() - firstDayOfTheYear.getTime() + (firstDayOfTheYear.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000; + return Math.floor(diff / (1000 * 60 * 60 * 24)); } - if (part === 'week') { - part = 'weekNumber'; - } + if (part === 'week') part = 'weekNumber'; - return DateTime.fromJSDate(date).get((DATETIMEUNIT_MAP[part] as keyof DateTime) || part); + const unit = (DATETIMEUNIT_MAP[part] as keyof DateTime) || part; + + if (isDateTime(date)) return date.get(unit); + + return DateTime.fromJSDate(date).get(unit); } function format(date: Date | DateTime, extraArgs: unknown[]): string { @@ -178,30 +175,46 @@ function isWeekend(date: Date | DateTime): boolean { return WEEKEND_DAYS.includes(weekday); } -function minus(date: Date | DateTime, extraArgs: unknown[]): Date | DateTime { - if (isDateTime(date) && extraArgs.length === 1) { - return date.minus(extraArgs[0] as DurationLike); +function minus( + date: Date | DateTime, + args: [DurationLike] | [number, DurationUnit], +): Date | DateTime { + if (args.length === 1) { + const [arg] = args; + + if (isDateTime(date)) return date.minus(arg); + + return DateTime.fromJSDate(date).minus(arg).toJSDate(); } - const [durationValue = 0, unit = 'minutes'] = extraArgs as [number, DurationUnit]; + const [durationValue = 0, unit = 'minutes'] = args; - if (isDateTime(date)) { - return date.minus(generateDurationObject(durationValue, unit)).toJSDate(); - } - return DateTime.fromJSDate(date).minus(generateDurationObject(durationValue, unit)).toJSDate(); + const duration = generateDurationObject(durationValue, unit); + + if (isDateTime(date)) return date.minus(duration); + + return DateTime.fromJSDate(date).minus(duration).toJSDate(); } -function plus(date: Date | DateTime, extraArgs: unknown[]): Date | DateTime { - if (isDateTime(date) && extraArgs.length === 1) { - return date.plus(extraArgs[0] as DurationLike); +function plus( + date: Date | DateTime, + args: [DurationLike] | [number, DurationUnit], +): Date | DateTime { + if (args.length === 1) { + const [arg] = args; + + if (isDateTime(date)) return date.plus(arg); + + return DateTime.fromJSDate(date).plus(arg).toJSDate(); } - const [durationValue = 0, unit = 'minutes'] = extraArgs as [number, DurationUnit]; + const [durationValue = 0, unit = 'minutes'] = args; - if (isDateTime(date)) { - return date.plus(generateDurationObject(durationValue, unit)).toJSDate(); - } - return DateTime.fromJSDate(date).plus(generateDurationObject(durationValue, unit)).toJSDate(); + const duration = generateDurationObject(durationValue, unit); + + if (isDateTime(date)) return date.plus(duration); + + return DateTime.fromJSDate(date).plus(duration).toJSDate(); } endOfMonth.doc = { diff --git a/packages/workflow/test/ExpressionExtensions/DateExtensions.test.ts b/packages/workflow/test/ExpressionExtensions/DateExtensions.test.ts index 8ddfe0ad53..7c18a0b709 100644 --- a/packages/workflow/test/ExpressionExtensions/DateExtensions.test.ts +++ b/packages/workflow/test/ExpressionExtensions/DateExtensions.test.ts @@ -16,6 +16,10 @@ describe('Data Transformation Functions', () => { test('.beginningOf("week") should work correctly on a date', () => { expect(evaluate('={{ DateTime.local(2023, 1, 20).beginningOf("week") }}')).toEqual( + DateTime.local(2023, 1, 16, { zone: TEST_TIMEZONE }), + ); + + expect(evaluate('={{ new Date(2023, 0, 20).beginningOf("week") }}')).toEqual( DateTime.local(2023, 1, 16, { zone: TEST_TIMEZONE }).toJSDate(), ); }); @@ -49,6 +53,9 @@ describe('Data Transformation Functions', () => { test('.endOfMonth() should work correctly on a date', () => { expect(evaluate('={{ DateTime.local(2023, 1, 16).endOfMonth() }}')).toEqual( + DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: TEST_TIMEZONE }), + ); + expect(evaluate('={{ new Date(2023, 0, 16).endOfMonth() }}')).toEqual( DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: TEST_TIMEZONE }).toJSDate(), ); });