From 0f2c49eba6efe913b87f07cdc648c5bcabaf3053 Mon Sep 17 00:00:00 2001 From: Alfred Gauthier Date: Mon, 3 Aug 2026 12:01:53 +0200 Subject: [PATCH 1/3] Add DateTime.toEpochSeconds Closes #6148 --- packages/effect/src/DateTime.ts | 22 ++++++++++++++++++++++ packages/effect/src/internal/dateTime.ts | 3 +++ packages/effect/test/DateTime.test.ts | 12 ++++++++++++ 3 files changed, 37 insertions(+) diff --git a/packages/effect/src/DateTime.ts b/packages/effect/src/DateTime.ts index afa1ab1f9c6..d81f7c1b87c 100644 --- a/packages/effect/src/DateTime.ts +++ b/packages/effect/src/DateTime.ts @@ -1600,6 +1600,28 @@ export const zonedOffsetIso: (self: Zoned) => string = Internal.zonedOffsetIso */ export const toEpochMillis: (self: DateTime) => number = Internal.toEpochMillis +/** + * Converts a `DateTime` to the number of seconds since the Unix epoch. + * + * **Details** + * + * This returns the UTC timestamp regardless of any time zone information. + * The result is floored to the nearest second. + * + * **Example** (Reading epoch seconds) + * + * ```ts import.meta.vitest + * import { DateTime } from "effect" + * + * const dt = DateTime.makeUnsafe("2024-01-01T00:00:00Z") + * DateTime.toEpochSeconds(dt) // => 1704067200 + * ``` + * + * @category converting + * @since 4.0.0 + */ +export const toEpochSeconds: (self: DateTime) => number = Internal.toEpochSeconds + /** * Removes the time aspect of a `DateTime`, first adjusting for the time * zone. It will return a `DateTime.Utc` only containing the date. diff --git a/packages/effect/src/internal/dateTime.ts b/packages/effect/src/internal/dateTime.ts index 63a9c2cd8b9..8ee615970ee 100644 --- a/packages/effect/src/internal/dateTime.ts +++ b/packages/effect/src/internal/dateTime.ts @@ -600,6 +600,9 @@ export const zonedOffsetIso = (self: DateTime.Zoned): string => offsetToString(z /** @internal */ export const toEpochMillis = (self: DateTime.DateTime): number => self.epochMilliseconds +/** @internal */ +export const toEpochSeconds = (self: DateTime.DateTime): number => Math.floor(self.epochMilliseconds / 1000) + /** @internal */ export const removeTime = (self: DateTime.DateTime): DateTime.Utc => withDate(self, (date) => { diff --git a/packages/effect/test/DateTime.test.ts b/packages/effect/test/DateTime.test.ts index 36555333037..8249f9c16ff 100644 --- a/packages/effect/test/DateTime.test.ts +++ b/packages/effect/test/DateTime.test.ts @@ -381,6 +381,18 @@ describe("DateTime", () => { }) }) + describe("toEpochSeconds", () => { + it("returns epoch seconds", () => { + const dt = DateTime.makeUnsafe("2024-01-01T00:00:00Z") + strictEqual(DateTime.toEpochSeconds(dt), 1704067200) + }) + + it("floors to nearest second", () => { + const dt = DateTime.makeUnsafe("2024-01-01T00:00:00.999Z") + strictEqual(DateTime.toEpochSeconds(dt), 1704067200) + }) + }) + describe("makeZonedFromString", () => { it.effect("parses an instant with an offset and IANA zone", () => Effect.gen(function*() { From 83d841ce4f4ccea8c1b66fb35c7bd6719b7c01a0 Mon Sep 17 00:00:00 2001 From: Alfred Gauthier Date: Mon, 3 Aug 2026 12:17:47 +0200 Subject: [PATCH 2/3] feat: fromEpochSeconds --- packages/effect/src/DateTime.ts | 16 ++++++++++++++++ packages/effect/src/internal/dateTime.ts | 3 +++ packages/effect/test/DateTime.test.ts | 14 ++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/packages/effect/src/DateTime.ts b/packages/effect/src/DateTime.ts index d81f7c1b87c..c121861a27a 100644 --- a/packages/effect/src/DateTime.ts +++ b/packages/effect/src/DateTime.ts @@ -652,6 +652,22 @@ export const fromDateUnsafe: (date: Date) => Utc = Internal.fromDateUnsafe */ export const makeUnsafe: (input: A) => DateTime.PreserveZone = Internal.makeUnsafe +/** + * Creates a `DateTime.Utc` from the number of seconds since the Unix epoch. + * + * **Example** (Creating from epoch seconds) + * + * ```ts import.meta.vitest + * import { DateTime } from "effect" + * + * DateTime.fromEpochSeconds(1704067200).toJSON() // => "2024-01-01T00:00:00.000Z" + * ``` + * + * @category constructors + * @since 4.0.0 + */ +export const fromEpochSeconds: (seconds: number) => Utc = Internal.fromEpochSeconds + /** * Create a `DateTime.Zoned` using `DateTime.makeUnsafe` and a time zone. * diff --git a/packages/effect/src/internal/dateTime.ts b/packages/effect/src/internal/dateTime.ts index 8ee615970ee..287ae8ef94e 100644 --- a/packages/effect/src/internal/dateTime.ts +++ b/packages/effect/src/internal/dateTime.ts @@ -603,6 +603,9 @@ export const toEpochMillis = (self: DateTime.DateTime): number => self.epochMill /** @internal */ export const toEpochSeconds = (self: DateTime.DateTime): number => Math.floor(self.epochMilliseconds / 1000) +/** @internal */ +export const fromEpochSeconds = (seconds: number): DateTime.Utc => makeUtc(seconds * 1000) + /** @internal */ export const removeTime = (self: DateTime.DateTime): DateTime.Utc => withDate(self, (date) => { diff --git a/packages/effect/test/DateTime.test.ts b/packages/effect/test/DateTime.test.ts index 8249f9c16ff..88d68b0db48 100644 --- a/packages/effect/test/DateTime.test.ts +++ b/packages/effect/test/DateTime.test.ts @@ -393,6 +393,20 @@ describe("DateTime", () => { }) }) + describe("fromEpochSeconds", () => { + it("creates DateTime from epoch seconds", () => { + const dt = DateTime.fromEpochSeconds(1704067200) + strictEqual(dt.toJSON(), "2024-01-01T00:00:00.000Z") + }) + + it("roundtrips with toEpochSeconds", () => { + const original = DateTime.makeUnsafe("2024-06-15T12:30:00Z") + const seconds = DateTime.toEpochSeconds(original) + const restored = DateTime.fromEpochSeconds(seconds) + strictEqual(DateTime.toEpochSeconds(restored), seconds) + }) + }) + describe("makeZonedFromString", () => { it.effect("parses an instant with an offset and IANA zone", () => Effect.gen(function*() { From 9300f2adf1a932a54054c1d18b1900ed2f43aefb Mon Sep 17 00:00:00 2001 From: Tim Smart Date: Tue, 4 Aug 2026 11:45:52 +1200 Subject: [PATCH 3/3] add changeset --- .changeset/bright-clocks-count.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/bright-clocks-count.md diff --git a/.changeset/bright-clocks-count.md b/.changeset/bright-clocks-count.md new file mode 100644 index 00000000000..8f3df8ee01a --- /dev/null +++ b/.changeset/bright-clocks-count.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Add `DateTime.toEpochSeconds` and `DateTime.fromEpochSeconds` for converting date-time values to and from Unix epoch seconds.