From 963aa2eae59400e86dfc318b135a2adf6bddaed0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Oct 2025 18:02:01 +0000
Subject: [PATCH 1/8] Initial plan
From 0661cf8974cbd0fd2dffc7eb69df3e2b4cb7d591 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Oct 2025 18:45:13 +0000
Subject: [PATCH 2/8] Fix XSLT format-date and format-time functions to respect
isDate parameter
Fixed MSFormatDateTime to use the isDate parameter when format string is empty.
When format is empty, now uses "d" for date-only and "T" for time-only formatting.
Added comprehensive tests for both functions.
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../System/Xml/Xsl/Runtime/XsltFunctions.cs | 8 +-
.../XslCompilerTests.cs | 140 ++++++++++++++++++
2 files changed, 146 insertions(+), 2 deletions(-)
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
index 7d8613a4d7d3cd..2740e742d046ab 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
@@ -375,8 +375,12 @@ public static string MSFormatDateTime(string dateTime, string format, string lan
}
DateTime dt = xdt.ToZulu();
- // If format is the empty string or not specified, use the default format for the given locale
- return dt.ToString(format.Length != 0 ? format : null, new CultureInfo(locale));
+ // If format is the empty string or not specified, use the default date or time format for the given locale
+ if (format.Length == 0)
+ {
+ format = isDate ? "d" : "T";
+ }
+ return dt.ToString(format, new CultureInfo(locale));
}
catch (ArgumentException)
{ // Operations with DateTime can throw this exception eventualy
diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
index 77230c4d625b1c..fe9422343815a4 100644
--- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
+++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Globalization;
using System.IO;
+using System.Threading;
using System.Xml.Xsl;
using Xunit;
@@ -44,5 +46,143 @@ public void ValueOfInDebugMode()
Assert.Equal("This is my class info", outWriter.ToString());
}
}
+
+ [Fact]
+ public void FormatDateWithEmptyFormatString()
+ {
+ CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
+ try
+ {
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
+
+ string xml = @"";
+ string xsl = @"
+
+
+
+
+";
+
+ using (var outWriter = new StringWriter())
+ {
+ using (var xslStringReader = new StringReader(xsl))
+ using (var xmlStringReader = new StringReader(xml))
+ using (var xslReader = XmlReader.Create(xslStringReader))
+ using (var xmlReader = XmlReader.Create(xmlStringReader))
+ {
+ var transform = new XslCompiledTransform();
+ transform.Load(xslReader);
+ transform.Transform(xmlReader, null, outWriter);
+ }
+
+ string result = outWriter.ToString();
+ Assert.DoesNotContain("01:02:03", result);
+ Assert.Contains("03/02/2001", result);
+ }
+ }
+ finally
+ {
+ Thread.CurrentThread.CurrentCulture = originalCulture;
+ }
+ }
+
+ [Fact]
+ public void FormatTimeWithEmptyFormatString()
+ {
+ CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
+ try
+ {
+ Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
+
+ string xml = @"";
+ string xsl = @"
+
+
+
+
+";
+
+ using (var outWriter = new StringWriter())
+ {
+ using (var xslStringReader = new StringReader(xsl))
+ using (var xmlStringReader = new StringReader(xml))
+ using (var xslReader = XmlReader.Create(xslStringReader))
+ using (var xmlReader = XmlReader.Create(xmlStringReader))
+ {
+ var transform = new XslCompiledTransform();
+ transform.Load(xslReader);
+ transform.Transform(xmlReader, null, outWriter);
+ }
+
+ string result = outWriter.ToString();
+ Assert.DoesNotContain("03/02/2001", result);
+ Assert.Contains("01:02:03", result);
+ }
+ }
+ finally
+ {
+ Thread.CurrentThread.CurrentCulture = originalCulture;
+ }
+ }
+
+ [Fact]
+ public void FormatDateWithExplicitFormatString()
+ {
+ string xml = @"";
+ string xsl = @"
+
+
+
+
+";
+
+ using (var outWriter = new StringWriter())
+ {
+ using (var xslStringReader = new StringReader(xsl))
+ using (var xmlStringReader = new StringReader(xml))
+ using (var xslReader = XmlReader.Create(xslStringReader))
+ using (var xmlReader = XmlReader.Create(xmlStringReader))
+ {
+ var transform = new XslCompiledTransform();
+ transform.Load(xslReader);
+ transform.Transform(xmlReader, null, outWriter);
+ }
+
+ string result = outWriter.ToString();
+ Assert.DoesNotContain("01:02:03", result);
+ }
+ }
+
+ [Fact]
+ public void FormatTimeWithExplicitFormatString()
+ {
+ string xml = @"";
+ string xsl = @"
+
+
+
+
+";
+
+ using (var outWriter = new StringWriter())
+ {
+ using (var xslStringReader = new StringReader(xsl))
+ using (var xmlStringReader = new StringReader(xml))
+ using (var xslReader = XmlReader.Create(xslStringReader))
+ using (var xmlReader = XmlReader.Create(xmlStringReader))
+ {
+ var transform = new XslCompiledTransform();
+ transform.Load(xslReader);
+ transform.Transform(xmlReader, null, outWriter);
+ }
+
+ string result = outWriter.ToString();
+ Assert.Contains("01:02:03", result);
+ }
+ }
}
}
From cfb0afc5a2f5f2c8e6bb9b12c29507a755a361f0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Oct 2025 21:35:44 +0000
Subject: [PATCH 3/8] Fix culture-dependent test assertion for
FormatTimeWithExplicitFormatString
Changed assertion to use regex pattern that matches both 12-hour and 24-hour time formats.
The test was failing in en-US culture where "T" format produces "1:02:03 AM" instead of "01:02:03".
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
index fe9422343815a4..d0f15464556dc3 100644
--- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
+++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
@@ -181,7 +181,8 @@ public void FormatTimeWithExplicitFormatString()
}
string result = outWriter.ToString();
- Assert.Contains("01:02:03", result);
+ Assert.DoesNotContain("2001", result);
+ Assert.Matches(@"0?1:02:03", result);
}
}
}
From 8c56e04fc4978e8f609bf9c741ff71bdb8241516 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 23 Oct 2025 22:08:29 +0000
Subject: [PATCH 4/8] Remove tests with explicit format strings that fail on
.NET Framework
Removed FormatDateWithExplicitFormatString and FormatTimeWithExplicitFormatString tests.
These tests were failing on .NET Framework. The fix is specifically for empty format strings,
so focusing tests on that scenario.
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../XslCompilerTests.cs | 57 -------------------
1 file changed, 57 deletions(-)
diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
index d0f15464556dc3..7a5819a107bcf6 100644
--- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
+++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
@@ -127,63 +127,6 @@ public void FormatTimeWithEmptyFormatString()
}
}
- [Fact]
- public void FormatDateWithExplicitFormatString()
- {
- string xml = @"";
- string xsl = @"
-
-
-
-
-";
-
- using (var outWriter = new StringWriter())
- {
- using (var xslStringReader = new StringReader(xsl))
- using (var xmlStringReader = new StringReader(xml))
- using (var xslReader = XmlReader.Create(xslStringReader))
- using (var xmlReader = XmlReader.Create(xmlStringReader))
- {
- var transform = new XslCompiledTransform();
- transform.Load(xslReader);
- transform.Transform(xmlReader, null, outWriter);
- }
-
- string result = outWriter.ToString();
- Assert.DoesNotContain("01:02:03", result);
- }
- }
- [Fact]
- public void FormatTimeWithExplicitFormatString()
- {
- string xml = @"";
- string xsl = @"
-
-
-
-
-";
-
- using (var outWriter = new StringWriter())
- {
- using (var xslStringReader = new StringReader(xsl))
- using (var xmlStringReader = new StringReader(xml))
- using (var xslReader = XmlReader.Create(xslStringReader))
- using (var xmlReader = XmlReader.Create(xmlStringReader))
- {
- var transform = new XslCompiledTransform();
- transform.Load(xslReader);
- transform.Transform(xmlReader, null, outWriter);
- }
-
- string result = outWriter.ToString();
- Assert.DoesNotContain("2001", result);
- Assert.Matches(@"0?1:02:03", result);
- }
- }
}
}
From 72440c71743e3fc7506768b17052310aae64c237 Mon Sep 17 00:00:00 2001
From: Stephen Toub
Date: Thu, 23 Oct 2025 18:10:29 -0400
Subject: [PATCH 5/8] Update
src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
---
.../src/System/Xml/Xsl/Runtime/XsltFunctions.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
index 2740e742d046ab..4219303af6be91 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
@@ -380,6 +380,7 @@ public static string MSFormatDateTime(string dateTime, string format, string lan
{
format = isDate ? "d" : "T";
}
+
return dt.ToString(format, new CultureInfo(locale));
}
catch (ArgumentException)
From 548882b89eedddc781781ec01795bd42bc5e773e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 01:05:04 +0000
Subject: [PATCH 6/8] Use ThreadCultureChange helper and make strings const
with PascalCase
- Replaced manual try-finally culture management with ThreadCultureChange helper
- Changed xml and xsl strings to const with PascalCase naming (Xml, Xsl)
- Added using System.Tests for ThreadCultureChange
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
---
.../XslCompilerTests.cs | 84 ++++++++-----------
1 file changed, 33 insertions(+), 51 deletions(-)
diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
index 7a5819a107bcf6..c844f3675b156c 100644
--- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
+++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
@@ -3,7 +3,7 @@
using System.Globalization;
using System.IO;
-using System.Threading;
+using System.Tests;
using System.Xml.Xsl;
using Xunit;
@@ -50,13 +50,8 @@ public void ValueOfInDebugMode()
[Fact]
public void FormatDateWithEmptyFormatString()
{
- CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
- try
- {
- Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
-
- string xml = @"";
- string xsl = @"";
+ const string Xsl = @"
@@ -64,39 +59,30 @@ public void FormatDateWithEmptyFormatString()
";
- using (var outWriter = new StringWriter())
+ using (new ThreadCultureChange("en-GB"))
+ using (var outWriter = new StringWriter())
+ {
+ using (var xslStringReader = new StringReader(Xsl))
+ using (var xmlStringReader = new StringReader(Xml))
+ using (var xslReader = XmlReader.Create(xslStringReader))
+ using (var xmlReader = XmlReader.Create(xmlStringReader))
{
- using (var xslStringReader = new StringReader(xsl))
- using (var xmlStringReader = new StringReader(xml))
- using (var xslReader = XmlReader.Create(xslStringReader))
- using (var xmlReader = XmlReader.Create(xmlStringReader))
- {
- var transform = new XslCompiledTransform();
- transform.Load(xslReader);
- transform.Transform(xmlReader, null, outWriter);
- }
-
- string result = outWriter.ToString();
- Assert.DoesNotContain("01:02:03", result);
- Assert.Contains("03/02/2001", result);
+ var transform = new XslCompiledTransform();
+ transform.Load(xslReader);
+ transform.Transform(xmlReader, null, outWriter);
}
- }
- finally
- {
- Thread.CurrentThread.CurrentCulture = originalCulture;
+
+ string result = outWriter.ToString();
+ Assert.DoesNotContain("01:02:03", result);
+ Assert.Contains("03/02/2001", result);
}
}
[Fact]
public void FormatTimeWithEmptyFormatString()
{
- CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
- try
- {
- Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
-
- string xml = @"";
- string xsl = @"";
+ const string Xsl = @"
@@ -104,26 +90,22 @@ public void FormatTimeWithEmptyFormatString()
";
- using (var outWriter = new StringWriter())
+ using (new ThreadCultureChange("en-GB"))
+ using (var outWriter = new StringWriter())
+ {
+ using (var xslStringReader = new StringReader(Xsl))
+ using (var xmlStringReader = new StringReader(Xml))
+ using (var xslReader = XmlReader.Create(xslStringReader))
+ using (var xmlReader = XmlReader.Create(xmlStringReader))
{
- using (var xslStringReader = new StringReader(xsl))
- using (var xmlStringReader = new StringReader(xml))
- using (var xslReader = XmlReader.Create(xslStringReader))
- using (var xmlReader = XmlReader.Create(xmlStringReader))
- {
- var transform = new XslCompiledTransform();
- transform.Load(xslReader);
- transform.Transform(xmlReader, null, outWriter);
- }
-
- string result = outWriter.ToString();
- Assert.DoesNotContain("03/02/2001", result);
- Assert.Contains("01:02:03", result);
+ var transform = new XslCompiledTransform();
+ transform.Load(xslReader);
+ transform.Transform(xmlReader, null, outWriter);
}
- }
- finally
- {
- Thread.CurrentThread.CurrentCulture = originalCulture;
+
+ string result = outWriter.ToString();
+ Assert.DoesNotContain("03/02/2001", result);
+ Assert.Contains("01:02:03", result);
}
}
From bc3213db770d06ddc2ae2eb6c90a65cef3c81a9c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 16:53:41 +0000
Subject: [PATCH 7/8] Optimize MSFormatDateTime to avoid creating CultureInfo
twice
Reuse the CultureInfo object returned by GetCultureInfo instead of creating a new one from the locale name.
Also removed unused locale variable.
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../src/System/Xml/Xsl/Runtime/XsltFunctions.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
index 4219303af6be91..4efa9417fd1d06 100644
--- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
+++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltFunctions.cs
@@ -366,7 +366,7 @@ public static string MSFormatDateTime(string dateTime, string format, string lan
{
try
{
- string locale = GetCultureInfo(lang).Name;
+ CultureInfo ci = GetCultureInfo(lang);
XsdDateTime xdt;
if (!XsdDateTime.TryParse(dateTime, XsdDateTimeFlags.AllXsd | XsdDateTimeFlags.XdrDateTime | XsdDateTimeFlags.XdrTimeNoTz, out xdt))
@@ -381,7 +381,7 @@ public static string MSFormatDateTime(string dateTime, string format, string lan
format = isDate ? "d" : "T";
}
- return dt.ToString(format, new CultureInfo(locale));
+ return dt.ToString(format, ci);
}
catch (ArgumentException)
{ // Operations with DateTime can throw this exception eventualy
From eb9bf083fe7207dc8f45ba8e2ba2f97affedece8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 17:59:06 +0000
Subject: [PATCH 8/8] Improve test assertions to compare against expected
formatted values
Changed FormatDateWithEmptyFormatString and FormatTimeWithEmptyFormatString tests to use Assert.Equal
with the expected formatted date/time values instead of checking for presence/absence of substrings.
This provides more precise and robust test validation.
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../Xslt/XslCompiledTransformApi/XslCompilerTests.cs | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
index c844f3675b156c..0c6cfcd7a6e5c3 100644
--- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
+++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs
@@ -73,8 +73,9 @@ public void FormatDateWithEmptyFormatString()
}
string result = outWriter.ToString();
- Assert.DoesNotContain("01:02:03", result);
- Assert.Contains("03/02/2001", result);
+ DateTime expectedDate = new DateTime(2001, 2, 3, 1, 2, 3, DateTimeKind.Utc);
+ string expectedResult = expectedDate.ToString("d", new CultureInfo("en-GB"));
+ Assert.Equal(expectedResult, result);
}
}
@@ -104,8 +105,9 @@ public void FormatTimeWithEmptyFormatString()
}
string result = outWriter.ToString();
- Assert.DoesNotContain("03/02/2001", result);
- Assert.Contains("01:02:03", result);
+ DateTime expectedTime = new DateTime(2001, 2, 3, 1, 2, 3, DateTimeKind.Utc);
+ string expectedResult = expectedTime.ToString("T", new CultureInfo("en-GB"));
+ Assert.Equal(expectedResult, result);
}
}