Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get \u202F(8239) instead of ' '(32) on DateTime.ToLongTimeString when the currentCulture is en-us with in mcr.microsoft.com/dotnet/aspnet:8.0 #95620

Closed
czd890 opened this issue Dec 5, 2023 · 13 comments

Comments

@czd890
Copy link

czd890 commented Dec 5, 2023

Description

Get \u202F(8239) instead of ' '(32) on DateTime.ToLongTimeString when the currentCulture is en-us with in mcr.microsoft.com/dotnet/aspnet:8.0

on windows 11, it will get ' '(32) when running on NET8.

Reproduction Steps

  • Default Net8 project.
  • Set InvariantGlobalization =false in csproj
System.Globalization.CultureInfo.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
Value = new
{
    dateime_toString = DateTime.UtcNow.ToString(),
    time_toLongTimeString = DateTime.UtcNow.ToLongTimeString(),
    datetime_toString_format = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
    currentCulture = System.Globalization.CultureInfo.CurrentCulture.ToString(),
    currentCulture_LongTimePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern,
    currentCulture_c = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern[^3],
    currentCulture_c_int = (int)System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern[^3],
    space_c = ' ',
    space_c_int = (int)(' ')
}

// output
        "value": {
            "dateime_toString": "12/5/2023 3:53:56\u202FAM",
            "time_toLongTimeString": "3:53:56\u202FAM",
            "datetime_toString_format": "2023-12-05 03:53:56",
            "currentCulture": "en-US",
            "currentCulture_LongTimePattern": "h:mm:ss\u202Ftt",
            "currentCulture_c": "\u202F",
            "currentCulture_c_int": 8239,
            "space_c": " ",
            "space_c_int": 32
        }

Expected behavior

time_toLongTimeString equal to "h:mm:ss tt"

Actual behavior

time_toLongTimeString equal to "h:mm:ss\u202Ftt"

Regression?

Will get ' '(32) in NET6

Known Workarounds

No response

Configuration

No response

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 5, 2023
@ghost
Copy link

ghost commented Dec 5, 2023

Tagging subscribers to this area: @dotnet/area-system-globalization
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

Get \u202F(8239) instead of ' '(32) on DateTime.ToLongTimeString when the currentCulture is en-us with in mcr.microsoft.com/dotnet/aspnet:8.0

Reproduction Steps

  • Default Net8 project.
  • Set InvariantGlobalization =false in csproj
System.Globalization.CultureInfo.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
Value = new
{
    dateime_toString = DateTime.UtcNow.ToString(),
    time_toLongTimeString = DateTime.UtcNow.ToLongTimeString(),
    datetime_toString_format = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
    currentCulture = System.Globalization.CultureInfo.CurrentCulture.ToString(),
    currentCulture_LongTimePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern,
    currentCulture_c = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern[^3],
    currentCulture_c_int = (int)System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern[^3],
    space_c = ' ',
    space_c_int = (int)(' ')
}

// output
        "value": {
            "dateime_toString": "12/5/2023 3:53:56\u202FAM",
            "time_toLongTimeString": "3:53:56\u202FAM",
            "datetime_toString_format": "2023-12-05 03:53:56",
            "currentCulture": "en-US",
            "currentCulture_LongTimePattern": "h:mm:ss\u202Ftt",
            "currentCulture_c": "\u202F",
            "currentCulture_c_int": 8239,
            "space_c": " ",
            "space_c_int": 32
        }

Expected behavior

time_toLongTimeString equal to "h:mm:ss tt"

Actual behavior

time_toLongTimeString equal to "h:mm:ss\u202Ftt"

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response

Author: czd890
Assignees: -
Labels:

area-System.Globalization, untriaged

Milestone: -

@czd890
Copy link
Author

czd890 commented Dec 5, 2023

Similar issue: nodejs/help#4068

@danmoseley
Copy link
Member

From the linked issue (thanks!) ICU intentionally changed this to a non breaking space, which sort of makes sense as one would expect no line break there.

@czd890
Copy link
Author

czd890 commented Dec 5, 2023

Hi @danmoseley How can we roll back this change in NET8? This update will block the upgrade from NET6 to NET8.
and, I did not find any related information on https://learn.microsoft.com/en-us/dotnet/core/compatibility/8.0#globalization

@danmoseley
Copy link
Member

ICU is part of the OS distro, we do not distribute it. The linked issue suggests it's from this update

https://github.com/unicode-org/icu/releases/tag/release-72-1

Could you share how this is breaking your upgrade?

@czd890
Copy link
Author

czd890 commented Dec 5, 2023

The client relies on ' '(32) for parsing data, just like fromJson('{"value":"3:53:56\u202FAM"}').value.split(' '/is 32 instead of 8239/).

@tarekgh
Copy link
Member

tarekgh commented Dec 5, 2023

The client relies on ' '(32) for parsing data, just like fromJson('{"value":"3:53:56\u202FAM"}').value.split(' '/is 32 instead of 8239/).

This is wrong to assume the globalization data will never change. It is consistently changing. The client needs to fix their code. Sooner or later, the client will run into more issues if they don't fix that.

If want to revert using the old ICU version, you can use the app-local-icu feature. Include the following in your project.

  <ItemGroup>
    <PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
    <RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2.0.9" />
  </ItemGroup>

I hope to get the client to fix their code as it is wrong approach. Note, on Windows, users can fully customize the date format to something unexpected too.

@tarekgh tarekgh closed this as completed Dec 5, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Dec 5, 2023
@Clockwork-Muse
Copy link
Contributor

The client needs to fix their code. Sooner or later, the client will run into more issues if they don't fix that.

This isn't entirely a client problem (although they should fix it too).

DateTime.ToLongTimeString when the currentCulture is en-us

@czd890 - If you're passing data between systems, you should be explicitly passing the invariant culture when formatting data, especially date/time.

@czd890
Copy link
Author

czd890 commented Dec 6, 2023

@tarekgh @Clockwork-Muse Thanks!
Yes, I also agree that both server and client should adhere to the new rules of ICU. However, our service is a public Web API with numerous clients, making compatibility and fixing work challenging on the client side. This requires more transitional solutions and time to handle.

Meanwhile, we should explicitly mention this breaking change in the compatibility/8.0#globalization or compatibility/8.0#containers section, highlighting that they behave differently on Windows and Linux. What are your thoughts on this?

😂😢
We have some APIs (that have been around for a long time) currently formatting and passing date/time in a fixed 'en-us'culture.

@Clockwork-Muse
Copy link
Contributor

highlighting that they behave differently on Windows and Linux.

Ah, no, you misunderstand.
You have no guarantee that the formatting will be the same on any platform the next time the OS is updated. Windows or Linux. Linux distros may use different data or update it at different rates. Different machines can and will have different formatting data, even if they have the same OS. Potentially different processes on the same machine can return different data. The updates for this data isn't something we control. Your application can explicitly manage it's own formatting data, but normally you shouldn't do that (your case aside...).

Except in rare cases, changes in the formatting data is not considered a breaking change on our end. This does not meet that bar.

We have some APIs (that have been around for a long time) currently formatting and passing date/time in a fixed 'en-us'culture.

Unless you're manually creating your own culture, no you're not - you're one OS update from it being yanked out from under you.

I'd evaluate whether the InvariantCulture works for you or not (since its behavior is mostly "en-US"-like. If not, create and use an explicitly created/set up culture of your own, and ignore the host-OS data.

@MichalPetryka
Copy link
Contributor

MichalPetryka commented Dec 6, 2023

Or I'd say more correct would be explicitly using a standardized format like RFC 3339 or ISO 8601 when serialising the dates and keeping it fixed in code.

@czd890
Copy link
Author

czd890 commented Dec 6, 2023

@Clockwork-Muse I am planning to do it this way "create and use an explicitly created/set up culture of your own".
@MichalPetryka Yes, but there are always some legacy things that need to be compatible.

@Clockwork-Muse
Copy link
Contributor

@MichalPetryka - Well yes, explicitly ISO would be better on top of that.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants