diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index fb55f3258..a027b678f 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [JS/TS] Fix `DateTimeOffset.ToString` with format specifiers `D`, `d`, `T`, `t`, and custom patterns using wrong local timezone instead of the DateTimeOffset's stored offset (fixes #4375) * [Python] Fix object expressions implementing interfaces with `[]` members no longer produce unimplementable abstract Protocol members (fixes #3039) * [Python] Fix `DateTime.TryParse` incorrectly assigning `DateTimeKind.Local` to naive datetime strings (should be `DateTimeKind.Unspecified`) (fixes #3654) diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index 7bdea3b05..76b6e92a0 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +* [JS/TS] Fix `DateTimeOffset.ToString` with format specifiers `D`, `d`, `T`, `t`, and custom patterns using wrong local timezone instead of the DateTimeOffset's stored offset (fixes #4375) * [Python] Fix object expressions implementing interfaces with `[]` members no longer produce unimplementable abstract Protocol members (fixes #3039) * [Python] Fix `DateTime.TryParse` incorrectly assigning `DateTimeKind.Local` to naive datetime strings (should be `DateTimeKind.Unspecified`) (fixes #3654) diff --git a/src/fable-library-ts/Date.ts b/src/fable-library-ts/Date.ts index 2970ba14f..49faa9576 100644 --- a/src/fable-library-ts/Date.ts +++ b/src/fable-library-ts/Date.ts @@ -403,7 +403,11 @@ function dateToISOStringWithOffset(dateWithOffset: Date, offset: number) { } function dateToStringWithOffset(date: IDateTimeOffset, format?: string) { - const d = new Date(date.getTime() + (date.offset ?? 0)); + // Shift by the stored offset so we can read the "wall clock" values via UTC + // methods — using `new Date()` here would cause `day()`, `month()`, etc. to + // fall back to *local* timezone methods, producing wrong results for runners + // in non-UTC timezones (e.g. Australia/Melbourne). See #4375. + const d = DateTime(date.getTime() + (date.offset ?? 0), DateTimeKind.Utc); if (typeof format !== "string") { return d.toISOString().replace(/\.\d+/, "").replace(/[A-Z]|\.\d+/g, " ") + dateOffsetToString((date.offset ?? 0)); } else if (format.length === 1) {