diff --git a/.changeset/floppy-cobras-stick.md b/.changeset/floppy-cobras-stick.md new file mode 100644 index 00000000000..2cde69b188a --- /dev/null +++ b/.changeset/floppy-cobras-stick.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Fix Cron.next and Cron.sequence around timezone boundaries diff --git a/packages/effect/src/internal/dateTime.ts b/packages/effect/src/internal/dateTime.ts index 544ecd86efa..e63f033819d 100644 --- a/packages/effect/src/internal/dateTime.ts +++ b/packages/effect/src/internal/dateTime.ts @@ -256,8 +256,12 @@ export const unsafeMakeZoned = (input: DateTime.DateTime.Input, options?: { } let zone: DateTime.TimeZone if (options?.timeZone === undefined) { - const offset = new Date(self.epochMillis).getTimezoneOffset() * -60 * 1000 - zone = zoneMakeOffset(offset) + const zoneName = Intl.DateTimeFormat().resolvedOptions().timeZone + const parsedZone = zoneFromString(zoneName) + if (Option.isNone(parsedZone)) { + throw new IllegalArgumentException(`Invalid time zone: ${zoneName}`) + } + zone = parsedZone.value } else if (isTimeZone(options?.timeZone)) { zone = options.timeZone } else if (typeof options?.timeZone === "number") { diff --git a/packages/effect/test/Cron.test.ts b/packages/effect/test/Cron.test.ts index 0452ea792b3..c34acdcbf33 100644 --- a/packages/effect/test/Cron.test.ts +++ b/packages/effect/test/Cron.test.ts @@ -173,6 +173,10 @@ describe("Cron", () => { deepStrictEqual(next().pipe(DateTime.formatIsoZoned), b.pipe(DateTime.formatIsoZoned)) deepStrictEqual(next().pipe(DateTime.formatIsoZoned), c.pipe(DateTime.formatIsoZoned)) deepStrictEqual(next().pipe(DateTime.formatIsoZoned), d.pipe(DateTime.formatIsoZoned)) + + const seq = Cron.sequence(Cron.unsafeParse("5 2 * * 6"), new Date("2020-11-01 0:00:01Z")) + deepStrictEqual(seq.next().value, new Date("2020-11-07T07:05:00.000Z")) + deepStrictEqual(seq.next().value, new Date("2020-11-14T07:05:00.000Z")) }) it("handles transition out of daylight savings time", () => {