From 4c9ffd58e6bc3ff98b7f218e128bb361fa83e45e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 22 Feb 2026 18:25:19 +0000 Subject: [PATCH] Fix #1437: ConvertDateTimeOffset falls back to AsDateTime for timezone-less xs:dateTime values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xs:dateTime values without a timezone suffix (e.g. '2022-04-28T10:09:17') are parsed by ParseISO8601FormattedDateTime as DateTimeKind.Unspecified. AsDateTimeOffset rejects these (returning None) to keep inference strict, causing a runtime exception when the XSD declares the field as xs:dateTime. Fix: In TextRuntime.ConvertDateTimeOffset, fall back to AsDateTime when AsDateTimeOffset returns None. AsDateTime already converts Unspecified to Local, so DateTimeOffset(dt) uses the local timezone offset — matching standard xs:dateTime semantics for timezone-free values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/FSharp.Data.Runtime.Utilities/TextRuntime.fs | 10 +++++++++- tests/FSharp.Data.Tests/XmlProvider.fs | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Data.Runtime.Utilities/TextRuntime.fs b/src/FSharp.Data.Runtime.Utilities/TextRuntime.fs index f6d860dc8..f248981fd 100644 --- a/src/FSharp.Data.Runtime.Utilities/TextRuntime.fs +++ b/src/FSharp.Data.Runtime.Utilities/TextRuntime.fs @@ -71,8 +71,16 @@ type TextRuntime = |> Option.bind (TextConversions.AsDateTime(TextRuntime.GetCulture cultureStr)) static member ConvertDateTimeOffset(cultureStr, text) = + let culture = TextRuntime.GetCulture cultureStr + text - |> Option.bind (TextConversions.AsDateTimeOffset(TextRuntime.GetCulture cultureStr)) + |> Option.bind (fun s -> + match TextConversions.AsDateTimeOffset culture s with + | Some dto -> Some dto + | None -> + // Fall back for xs:dateTime values without timezone (DateTimeKind.Unspecified). + // AsDateTime converts Unspecified to Local, so DateTimeOffset(dt) uses the local offset. + TextConversions.AsDateTime culture s |> Option.map DateTimeOffset) static member ConvertTimeSpan(cultureStr, text) = text diff --git a/tests/FSharp.Data.Tests/XmlProvider.fs b/tests/FSharp.Data.Tests/XmlProvider.fs index 7e34da43a..e4f538d9d 100644 --- a/tests/FSharp.Data.Tests/XmlProvider.fs +++ b/tests/FSharp.Data.Tests/XmlProvider.fs @@ -1213,6 +1213,14 @@ let ``date is formatted properly``() = validXml.XElement.Attribute(XName.Get "date").Value |> should equal "2018-08-29" +[] +let ``xs:dateTime without timezone parses as DateTimeOffset (issue 1437)``() = + // xs:dateTime values without timezone offset (DateTimeKind.Unspecified) should + // still parse successfully as DateTimeOffset, treated as local time. + let xml = """""" + let a = SimpleTypes.Parse(xml) + a.DateTime.DateTime |> should equal (System.DateTime(2022, 4, 28, 10, 9, 17)) + type TimeSpanXML = XmlProvider<"Data/TimeSpans.xml"> []