Skip to content

Commit

Permalink
use temporal format for zoned strings
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart committed Jul 21, 2024
1 parent cb9f556 commit 162d317
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 14 deletions.
22 changes: 12 additions & 10 deletions packages/effect/src/DateTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,8 @@ export const makeZoned: (
export const make: <A extends DateTime.Input>(input: A) => Option.Option<DateTime.PreserveZone<A>> = Option
.liftThrowable(unsafeMake)

const zonedStringRegex = /^(.{24,35})\[(.+)\]$/

/**
* Create a `DateTime.Zoned` from a string.
*
Expand All @@ -515,14 +517,13 @@ export const make: <A extends DateTime.Input>(input: A) => Option.Option<DateTim
* @category constructors
*/
export const makeZonedFromString = (input: string): Option.Option<Zoned> => {
const parts = input.split(" ")
if (parts.length !== 2) {
return Option.none()
const match = zonedStringRegex.exec(input)
if (match === null) {
const offset = parseOffset(input)
return offset ? makeZoned(input, { timeZone: offset }) : Option.none()
}
return Option.flatMap(
make(parts[0]),
(dt) => Option.map(zoneFromString(parts[1]), (zone) => setZone(dt, zone))
)
const [, isoString, timeZone] = match
return makeZoned(isoString, { timeZone })
}

/**
Expand Down Expand Up @@ -1003,12 +1004,13 @@ export const zonedOffsetIso = (self: Zoned): string => offsetToString(zonedOffse
/**
* Format a `DateTime.Zoned` as a string.
*
* It uses the format: `YYYY-MM-DDTHH:mm:ss.sssZ IANA/TimeZone`.
* It uses the format: `YYYY-MM-DDTHH:mm:ss.sss+HH:MM[Time/Zone]`.
*
* @since 3.6.0
* @category conversions
*/
export const toStringZoned = (self: Zoned): string => `${formatIso(self)} ${zoneToString(self.zone)}`
export const zonedToString = (self: Zoned): string =>
self.zone._tag === "Offset" ? formatIsoOffset(self) : `${formatIsoOffset(self)}[${self.zone.id}]`

/**
* Get the milliseconds since the Unix epoch of a `DateTime`.
Expand Down Expand Up @@ -1910,5 +1912,5 @@ export const formatIso = (self: DateTime): string => toDateUtc(self).toISOString
*/
export const formatIsoOffset = (self: DateTime): string => {
const date = toDateAdjusted(self)
return self._tag === "Utc" ? date.toISOString() : `${date.toISOString().slice(0, 19)}${zonedOffsetIso(self)}`
return self._tag === "Utc" ? date.toISOString() : `${date.toISOString().slice(0, -1)}${zonedOffsetIso(self)}`
}
27 changes: 26 additions & 1 deletion packages/effect/test/DateTime.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DateTime, Duration, Effect, Either, TestClock } from "effect"
import { DateTime, Duration, Effect, Either, Option, TestClock } from "effect"
import { assert, describe, it } from "./utils/extend.js"

const setTo2024NZ = TestClock.setTime(new Date("2023-12-31T11:00:00.000Z").getTime())
Expand Down Expand Up @@ -318,4 +318,29 @@ describe("DateTime", () => {
assert.strictEqual(dt.toJSON(), "2024-01-01T00:00:00.000Z")
})
})

describe("makeZonedFromString", () => {
it.effect("parses time + zone", () =>
Effect.gen(function*() {
const dt = yield* DateTime.makeZonedFromString("2024-07-21T20:12:34.112546348+12:00[Pacific/Auckland]")
assert.strictEqual(dt.toJSON(), "2024-07-21T08:12:34.112Z")
}))

it.effect("only offset", () =>
Effect.gen(function*() {
const dt = yield* DateTime.makeZonedFromString("2024-07-21T20:12:34.112546348+12:00")
assert.strictEqual(dt.zone._tag, "Offset")
assert.strictEqual(dt.toJSON(), "2024-07-21T08:12:34.112Z")
}))

it.effect("roundtrip", () =>
Effect.gen(function*() {
const dt = yield* DateTime.makeZonedFromString("2024-07-21T20:12:34.112546348+12:00[Pacific/Auckland]").pipe(
Option.map(DateTime.zonedToString),
Option.flatMap(DateTime.makeZonedFromString)
)
assert.deepStrictEqual(dt.zone, DateTime.zoneUnsafeMakeNamed("Pacific/Auckland"))
assert.strictEqual(dt.toJSON(), "2024-07-21T08:12:34.112Z")
}))
})
})
2 changes: 1 addition & 1 deletion packages/schema/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6081,7 +6081,7 @@ export class DateTimeZoned extends transformOrFail(
onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)),
onSome: ParseResult.succeed
}),
encode: (dt) => ParseResult.succeed(dateTime.toStringZoned(dt))
encode: (dt) => ParseResult.succeed(dateTime.zonedToString(dt))
}
).annotations({ identifier: "DateTime.Zoned" }) {
static override annotations: (
Expand Down
4 changes: 2 additions & 2 deletions packages/schema/test/Schema/DateTime/DateTime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("DateTime.Zoned", () => {
})

it("decoding", async () => {
await Util.expectDecodeUnknownSuccess(schema, "1970-01-01T00:00:00.000Z Europe/London", dt)
await Util.expectDecodeUnknownSuccess(schema, "1970-01-01T01:00:00.000+01:00[Europe/London]", dt)
await Util.expectDecodeUnknownFailure(
schema,
"1970-01-01T00:00:00.000Z",
Expand All @@ -57,6 +57,6 @@ describe("DateTime.Zoned", () => {
})

it("encoding", async () => {
await Util.expectEncodeSuccess(schema, dt, "1970-01-01T00:00:00.000Z Europe/London")
await Util.expectEncodeSuccess(schema, dt, "1970-01-01T01:00:00.000+01:00[Europe/London]")
})
})

0 comments on commit 162d317

Please sign in to comment.