-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Changing DateTimeFormatInfo.TimeSeparator on some cultures doesn't work because DateTimeFormatInfo's time patterns doesn't use ':' #77037
Comments
Tagging subscribers to this area: @dotnet/area-system-globalization Issue DetailsDescriptionI assume that after switching to ICU, the time separator ( However, the following patterns were also changed to use
This should not have been done: As indicated by the official docs, if the patterns use Note that this seems to be environment-specific. For example, I have only managed to reproduce this in Azure App Service (.NET 6). In particular, I have not managed to reproduce this locally, where I get Reproduction Stepslet ci = CultureInfo.GetCultureInfo("nb-NO")
printfn $"{ci.DateTimeFormat.ShortTimePattern}" Expected behaviorShould print Actual behaviorPrints Regression?No response Known WorkaroundsIf changing the Configuration
Other informationNo response
|
Generally, you shouldn't be doing this, because normally programs should be relying on the data given to them by the OS for these purposes, and not inspecting the results. Is there a reason you're modifying the separator?
If you're running on Windows, you're probably still hitting NLS, and using legacy data. See this page about the switch.
Two more sentences later in that remarks section:
... so having the data that way would presumptively be the "correct" way to do it. That it "works" in the documentation sample is almost certainly due to US formats using let ci = CultureInfo.GetCultureInfo("nb-NO")
printfn $"{dateTimeValue.ToString(ci.DateTimeFormat.ShortTimePattern)}" you should instead by doing something like: let ci = CultureInfo.GetCultureInfo("nb-NO")
let message: FormattableString = $"{dateTimeValue:t}"
printfn message.ToString(ci) (I dunno if F# actually supports that, but that's the idea) |
You are correct; my local machine uses NLS.
Since the 1970's, official rules in Norway mandated Here is an official source in Norwegian (nynorsk) from 2014. Some of it translated:
One concrete problem in our current use-case is that in the context the timestamp is used, using
Sure, I just wanted to highlight that it was the actual value of My general point is that I would expect that setting One argument for this is that the API is more self-explanatory: It's one place to set the time separator. It makes for a more intuitive and less confusing API. I expect (rightly so, I would say) that when I set (I interpret your comment to say that this behavior is incidental, not intended, and that one should expect to have to adjust the time patterns like Another argument is that there is currently a discrepancy here based on the initial value of
TL;DRI can write a test that fetches the I know that NLS and ICU brings some changes, but I can't see how this particular behavior is intended, and certainly not desired, for reasons detailed above. |
The intention is that you write
The documentation for
This assumes that the patterns are using the separator, which isn't actually a guarantee (other than
This is potentially interesting, in that it is likely to cause unwanted results. @tarekgh thoughts here?
You can write the test, but depending on deployment target you shouldn't deploy these changes. More specifically:
|
@cmeeren thanks for your report.
In summary, I don't think we can support your request as it will be a very breaking change and not desirable in all scenarios. By that, I am closing this issue but feel free to send any question we can help with. |
Thank you both for excellent clarifications. TL;DR: I now understand the issue much better, and I believe a single remark added to the
That is not a general truth. I am using this in two server systems. One sends notification emails, that need to include localized values, such as datetimes. The other produces PDF reports that similarly include localized values. Furthermore, our system allows the users to specify a language (among currently just two, in the future not many more), but not more detailed times formats or similar. The formatting is performed using a culture based on the user's configured language. As mentioned in a previous comment, for Norwegian, we'd like to use
That is what I ended up doing (for Norwegian). I ended up making my own wrapper for @tarekgh, thank you for the clarifications. I see now that I have misunderstood the purpose of As to your remark:
To be clear, I never assumed that
It is good enough for me. I am able to format it exactly as I want by overriding the actual time patterns. In hindsight, I also see that the For example, this paragraph could be extended (my insertion in italics):
|
I read that page just now, but I don't understand why I should hit NLS. The page says near the top:
I am using Windows 11 and .NET 6. However, when I try the code posted under the heading Determine if your app is using ICU, I get <ItemGroup>
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
<RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2" />
<RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="false" />
</ItemGroup> I would like to ensure that I use the same settings across environments. Is there a way I can ensure I am using ICU? |
@cmeeren thanks for your feedback, it is helpful.
Type? interopGlobalization = Type.GetType("Interop+Globalization, System.Private.CoreLib");
MethodInfo? methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static);
int version = methodInfo.Invoke(null, null)
Could you tell me what environments you are going to run in? Note, Last 6.0 update (version <ItemGroup>
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
<RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2" />
</ItemGroup> We are working to fix that and hopefully will fix it soon. |
I'll do that. 👍
When run in an empty C# console app, it is <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project> I get the same result if I include the This is also the case if I run it in an F# project. However, strangely enough, when running the code in F# interactive (in Rider 2022.2.3), I get
Azure App Service, Windows server 2016 and newer, and local dev machines with Windows 11 21H2 and newer. |
Could you add the following to the empty C# code project and send the result? It looks like you have something set in your environment. Console.WriteLine($".... UseNls: {typeof(object).Assembly.GetType("System.Globalization.GlobalizationMode")!.GetProperty("UseNls", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)} ....");
Console.WriteLine($".... Invariant: {typeof(object).Assembly.GetType("System.Globalization.GlobalizationMode")!.GetProperty("Invariant", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)} ....");
Console.WriteLine($"OSDescription: {RuntimeInformation.OSDescription}");
Console.WriteLine($"FrameworkDescription: {RuntimeInformation.FrameworkDescription}");
Console.WriteLine($"RuntimeIdentifier: {RuntimeInformation.RuntimeIdentifier}"); |
Thank you for helping me out! Here's the output:
(I have double-checked that I'm using Windows 11 Pro 21H2, build 22000.1098.) |
This means the ICU is used in the app. What makes you think the ICU is not loaded? Do you see different behavior? |
Okay, this is very weird. Consider this code that you previously sent me: Type? interopGlobalization = Type.GetType("Interop+Globalization, System.Private.CoreLib");
MethodInfo? methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static);
Console.WriteLine(methodInfo.Invoke(null, null)); When executed on its own in a blank C# project, it prints However, if I add either of these lines before the code above, it returns typeof(object).Assembly.GetType("System.Globalization.GlobalizationMode")!.GetProperty("UseNls", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null); SortVersion sortVersion = CultureInfo.InvariantCulture.CompareInfo.Version; (The last line above is from Determine if your app is using ICU.) Repro solution (though I'm not sure it helps, if it's related to the environment): |
You are getting CultureInfo ci = CultureInfo.GetCultureInfo("ja-JP"); |
Thanks. It seems it's the opposite, then. My local Win 11 machine is using ICU ( |
Most likely that is because the service is running on |
Thanks for the note. For consistency though, and to avoid future issues where the behavior is different across environments, I will likely stick to app-local ICU going forward. |
In .NET 7.0, we have fixed loading ICU on |
Great, it seems like I can then safely remove app-local ICU after updating to .NET 7. I have no further questions. Thank you so much for all the help! |
Oh, actually, one more thing: If I use .NET 7 preview on Azure App Service now, do you know if I will get the behavior you describe (ICU without having to use app-local)? |
Yes, using the latest preview you should get the behavior you desire. give it a try and let me know if you see anything there. |
Unfortunately, this seemed to have no effect. I published a self-contained app using .NET 7 RC2 without app-local ICU to Azure App Service, and it's not using ICU. |
How did you know that it is not using ICU? Could you send some more info? What OS was used on Azure in your run? |
I'm using Azure App Service on Windows in the Norway East region. Not sure what it's running, or how to find out. Do you know? |
Log the following informaition which can give you the needed info: Console.WriteLine($"OSDescription: {RuntimeInformation.OSDescription}");
Console.WriteLine($"FrameworkDescription: {RuntimeInformation.FrameworkDescription}");
Console.WriteLine($"RuntimeIdentifier: {RuntimeInformation.RuntimeIdentifier}"); |
|
|
Sorry, that was after I reverted back to .NET 6 because it didn't work. I am 99% sure I was actually running .NET 7, but I will report back when I try again. |
Actually though, the issue seems to be that Azure App Service uses Windows Server 2016, as far as I can tell from the version number. Isn't that right? |
You are right. The Windows version is Windows Server 2016. Unfortunetly, you'll need to use ICU app-local for that because this server doesn't carry ICU at all. |
Thanks, glad we clarified that. 🙂 I will continue using app-local ICU. (To avoid #77045 I am deploying self-contained version produced using a locked version with |
Description
After switching to ICU (I assume), the time separator (
DateTimeFormatInfo.TimeSeparator
) for some cultures, such as Norwegian (e.g.nb-NO
), got changed from:
to.
. That's fine.However, it seems like the following patterns were also changed to use
.
, such as:DateTimeFormatInfo.ShortTimePattern
(HH.mm
)DateTimeFormatInfo.LongTimePattern
(HH.mm.ss
)DateTimeFormatInfo.FullDateTimePattern
(HH.mm.ss
)This should not have been done: As indicated by the official docs, if the patterns use
:
, it will be automatically replaced withTimeSeparator
. Therefore, the patterns should use:
.Note that this seems to be environment-specific. For example, I have only managed to reproduce this in Azure App Service (.NET 6). In particular, I have not managed to reproduce this locally, where I get
:
in these patterns even if my regional settings are set to use.
as the time separator.Reproduction Steps
Expected behavior
Should print
HH:mm
.Actual behavior
Prints
HH.mm
(on some environments, as mentioned above).Regression?
No response
Known Workarounds
If changing the
TimeSeparator
, remember to manually update all relevant time patterns to use:
so thatTimeSeparator
is used.Configuration
Other information
No response
The text was updated successfully, but these errors were encountered: