Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 23, 2025

Fixing XSLT extension methods format-date & format-time to respect the isDate parameter when using empty format string.

Plan:

  • Analyze the issue and understand the problem
  • Locate the problematic code in XsltFunctions.cs
  • Create focused tests for format-date and format-time functions
  • Fix the MSFormatDateTime method to use the isDate parameter correctly
  • Build and test the changes
  • Verify the fix resolves the issue
  • Run CodeQL security check
  • Fix culture-dependent test failures
  • Remove tests that don't work on .NET Framework
  • Use ThreadCultureChange helper and improve code style
  • Optimize CultureInfo handling to avoid creating it twice
  • Improve test assertions to be more precise

Issue Summary:
The MSFormatDateTime function in XsltFunctions.cs ignored the isDate parameter when the format string was empty. This caused both format-date() and format-time() XSLT functions to output both date and time instead of just the date or just the time respectively when using the default (empty) format string.

Changes Made:

  1. Added 2 focused tests in XslCompilerTests.cs to verify the fix for empty format strings:

    • FormatDateWithEmptyFormatString: Verifies date-only output with empty format
    • FormatTimeWithEmptyFormatString: Verifies time-only output with empty format
    • Tests now use ThreadCultureChange helper for cleaner code
    • String constants use PascalCase naming
    • Tests compare against expected formatted values for precision
  2. Fixed MSFormatDateTime in XsltFunctions.cs:

    • When format string is empty, now uses "d" (short date pattern) for dates
    • When format string is empty, now uses "T" (long time pattern) for times
    • When format string is not empty, uses the provided format (unchanged behavior)
    • Optimized to reuse CultureInfo object instead of creating it twice
  3. Removed tests with explicit format strings that were failing on .NET Framework

Test Results:

  • All 3 XslCompilerTests tests pass ✓
  • All 1618 existing XslCompiledTransformApi tests pass ✓
  • Tests focus specifically on the empty format string issue ✓
  • No security vulnerabilities detected ✓
Original prompt

This section details on the original issue you should resolve

<issue_title>XSLT extension methods format-date & format-time both output date & time when using the empty string (default) format</issue_title>
<issue_description>### Description

I'm porting a .NET FW application to .NET (6 and above).
The application performs an XML to HTML conversion using an XSLT transform via System.Xml.Xsl.XslCompiledTransform.

Under .NET FW, this XSL line:

<xsl:value-of select="ms:format-date('2001-02-03T01:02:03', '')"/>

Produces:

03/02/2001

UK date format since that's my locale.

However, under .NET 6 (I've also tried 8), it produces:

03/02/2001 01:02:03

Similarly, the format-time function also outputs both the date & time.

Reproduction Steps

It's easy to see there's a problem from the identified source code.

Expected behavior

The default (empty) format string should cause format-date() to output just the date, and correspondingly, format-time() to output just the time - as was the case under .NET FW and is how the functions are documented.

Actual behavior

Both functions output both the date and time.

Regression?

Yes, the functions works as documented for .NET FW.

Known Workarounds

Don't use the default (empty) format string - pass either "d" or "T" to the functions.

Configuration

.NET 6 and above (I've not tried anything < 6)

Other information

Looking at the .NET source code, the issue seems to be in the MSFormatDateTime function. Note that the function doesn't use the "isDate" parameter.

Compare that code with the .NET FW version which clearly uses the isDate parameter to do just the Date or Time appropriately.</issue_description>

Comments on the Issue (you are @copilot in this section)

@ Tagging subscribers to this area: @dotnet/area-system-xml See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
Issue Details

Description

I'm porting a .NET FW application to .NET (6 and above).
The application performs an XML to HTML conversion using an XSLT transform via System.Xml.Xsl.XslCompiledTransform.

Under .NET FW, this XSL line:

<xsl:value-of select="ms:format-date('2001-02-03T01:02:03', '')"/>

Produces:

03/02/2001

UK date format since that's my locale.

However, under .NET 6 (I've also tried 8), it produces:

03/02/2001 01:02:03

Similarly, the format-time function also outputs both the date & time.

Reproduction Steps

It's easy to see there's a problem from the identified source code.

Expected behavior

The default (empty) format string should cause format-date() to output just the date, and correspondingly, format-time() to output just the time - as was the case under .NET FW and is how the functions are documented.

Actual behavior

Both functions output both the date and time.

Regression?

Yes, the functions works as documented for .NET FW.

Known Workarounds

Don't use the default (empty) format string - pass either "d" or "T" to the functions.

Configuration

.NET 6 and above (I've not tried anything < 6)

Other information

Looking at the .NET source code, the issue seems to be in the MSFormatDateTime function. Note that the function doesn't use the "isDate" parameter.

Compare that code with the .NET FW version which clearly uses the isDate parameter to do just the Date or Time appropriately.

Author: Dave-Lowndes
Assignees: -
Labels:

area-System.Xml, untriaged

Milestone: -
@krwq It does seem like a bug @Dave-Lowndes . It seems this got regressed here: https://github.com/dotnet/runtime/commit/d784b77a197f48073dbe4766da6da5d129c0f135

We should figure out why that change was made and make sure we don't regress issue being fixed there while fixing this.

@Dave-Lowndes are you perhaps interested in making a pull request?</comment_new>
<comment_new>@krwq
I agree that would be ideal but they're no longer on our team (and even if I doubt anyone...

Fixes #93189


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…meter

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 <[email protected]>
Copilot AI changed the title [WIP] Fix XSLT format-date and format-time methods output Fix XSLT format-date and format-time to output only date or time with empty format string Oct 23, 2025
Copilot finished work on behalf of stephentoub October 23, 2025 18:50
Copilot AI requested a review from stephentoub October 23, 2025 18:50
@tarekgh tarekgh added this to the 11.0.0 milestone Oct 23, 2025
@stephentoub
Copy link
Member

@copilot, there are test failures, e.g.

  Discovering: System.Private.Xml.Tests (method display = ClassAndMethod, method display options = None)
  Discovered:  System.Private.Xml.Tests (found 4794 of 4850 test cases)
  Starting:    System.Private.Xml.Tests (parallel test collections = on [6 threads], stop on fail = off)
    System.Xml.XslCompiledTransformApiTests.XslCompilerTests.FormatTimeWithExplicitFormatString [FAIL]
      Assert.Contains() Failure: Sub-string not found
      String:    "1:02:03 AM"
      Not found: "01:02:03"
      Stack Trace:
        /_/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/XslCompilerTests.cs(184,0): at System.Xml.XslCompiledTransformApiTests.XslCompilerTests.FormatTimeWithExplicitFormatString()
           at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
           at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
        /_/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.CoreCLR.cs(36,0): at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
        /_/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs(126,0): at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
  Finished:    System.Private.Xml.Tests

…String

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 <[email protected]>
Copy link
Contributor Author

Copilot AI commented Oct 23, 2025

@copilot, there are test failures, e.g.

  Discovering: System.Private.Xml.Tests (method display = ClassAndMethod, method display options = None)

...

Fixed in commit cfb0afc. The test assertion was culture-dependent - it expected "01:02:03" but en-US culture produces "1:02:03 AM" with the "T" format. Changed to use a regex pattern 0?1:02:03 that matches both 12-hour and 24-hour formats, and added a check that the date is not present.

Copilot finished work on behalf of stephentoub October 23, 2025 21:37
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 <[email protected]>
Copilot AI requested a review from stephentoub October 23, 2025 22:09
Copilot finished work on behalf of stephentoub October 23, 2025 22:09
@stephentoub stephentoub marked this pull request as ready for review October 24, 2025 00:34
@stephentoub stephentoub requested review from Copilot and krwq October 24, 2025 00:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a regression in XSLT extension methods format-date() and format-time() where both functions incorrectly output both date and time when using an empty format string, instead of outputting only the date or only the time respectively.

Key changes:

  • Fixed MSFormatDateTime in XsltFunctions.cs to use the isDate parameter when format string is empty
  • Added two focused unit tests to verify the fix for both format-date and format-time functions

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
XsltFunctions.cs Modified MSFormatDateTime to apply "d" format for dates and "T" format for times when format string is empty
XslCompilerTests.cs Added two test methods to verify that empty format strings produce date-only or time-only output

- 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 <[email protected]>
Copilot finished work on behalf of stephentoub October 24, 2025 01:06
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 <[email protected]>
Copilot AI requested a review from tarekgh October 24, 2025 16:54
Copilot finished work on behalf of tarekgh October 24, 2025 16:54
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 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

XSLT extension methods format-date & format-time both output date & time when using the empty string (default) format

3 participants