This will produce the correct European formatting on my VM configured for Netherlands, but only in the ISE.
(Get-Date).toString((Get-Culture).DateTimeFormat.ShortDatePattern)
(Get-Date).toString((Get-Culture).DateTimeFormat.ShortTimePattern)
24/01/2025
09:19
When I run it in the console via shortcut it reverts to US standard.
1/24/2025
9:24 AM
Is there something I am missing to ensure correct behavior in the console? Or just one more way that Microsoft's inconsistency makes me pull my hair out?
EDIT: Changed the title to better reflect the fact that the code ONLY works in the deprecated ISE, and fails in the console where one would expect it to work.
EDIT: An update for anyone else who runs into this mess. From the sounds of it I should NOT be seeing this behavior, but I am, which means I need to work around it because I have no idea if a customer machine will have the same problem. So, the solution is
$culture = (Get-ItemPropertyValue 'HKCU:\Control Panel\International' LocaleName)
[cultureinfo]::CurrentCulture = $culture
$startTime.ToString('d')
$startTime.ToString('t')
Where $startTime
is a DateTime
. So easy, and yet it seems to me that I shouldn't even need to concern myself with Culture. .ToString()
should just return the correct format based on OS settings, and if I want to be a $^%# and ignore the user's settings I should have to work harder to force 'EN-US' on someone. /RANT
Preface:
The tl;dr is:
None of the pitfalls discussed below apply to PowerShell (Core) 7, irrespective of its host application; by definition it can not run in the Windows PowerShell ISE, which is but one of the reasons to avoid the latter (see next point).
Let me complement the advice to avoid the ISE in sirtao's answer with the following standard advice:
.NET has both an effective culture ([cultureinfo]::CurrentCulture
) and a UI culture ([cultureinfo]::CurrentUICulture
) per thread that can be set independently of one another and serve distinct purposes:
.CurrentCulture
contains "information includes the names for the culture, the writing system, the calendar used, the sort order of strings, and formatting for dates and numbers."
As such, only .CurrentCulture
is the one that matters in the case at hand.
On Windows, PowerShell's Set-Culture
cmdlet can be used to persistently change the current culture for future sessions.
.CurrentUICulture
is the "culture used by the Resource Manager to look up culture-specific resources at run time."
Set-UICulture
Set-Culture
.While there are many subtle and treacherous differences between the ISE and a regular Windows PowerShell console window / WT (Windows Terminal) tab, there is no appreciable difference with respect to culture handling.[1]
However, there are culture-handling pitfalls that affect both environments:
Interactively setting [cultureinfo]::CurrentCulture
(applies analogously to [cultureinfo]::CurrentUICulture
) takes effect only for the given command submission.
Specifically, after submitting a command that involves changing [cultureinfo]::CurrentCulture
, the original, persistently configured culture is reverted to immediately afterwards.
This questionable behavior has been fixed in PowerShell (Core) 7.
E.g., in Windows PowerShell (whether in the ISE or not):
First, submit:
& { [cultureinfo]::CurrentCulture = 'fr-FR'; [cultureinfo]::CurrentCulture.DisplayName }
, and you'll see that the change did take effect in the context of the command submitted.
Then submit [cultureinfo]::CurrentCulture.DisplayName
alone, and you'll see that the original, persistently configured culture was reverted to (Note: if your persistently configured culture happens to be fr-FR
, substitute a different culture above.)
By contrast, fortunately, in a script (file or block), the change does stay in effect until the script is exited.
While a dynamically changed culture is in effect in Windows PowerShell (whether in the ISE or not), it is not reflected in Get-Culture
's output, only in [cultureinfo]::CurrentCulture
's.
That is, in Windows PowerShell Get-Culture
reports the original, persistently configured culture throughout the entire session, irrespective of in-session changes.
This unexpected behavior has been fixed in PowerShell (Core) 7.
Building on the example above, the following shows this discrepancy:
& { [cultureinfo]::CurrentCulture = 'fr-FR'; [cultureinfo]::CurrentCulture.DisplayName; (Get-Culture).DisplayName }
The upshot for your use case:
Using Get-Culture
isn't robust, as it isn't guaranteed to refer to the culture that is truly in effect if an in-session change was made.
Therefore:
Don't use , use(Get-Culture).DateTimeFormat.ShortDatePattern
[cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern
, for instance.
More simply, you can take advantage of .NET's standard date and time formats in the context of the [datetime]
type's .ToString
method; e.g.:
& {
[cultureinfo]::CurrentCulture = 'nl-NL'
(Get-Date).ToString('d') # 'd' == [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern
(Get-Date).ToString('t') # 't' == [cultureinfo]::CurrentCulture.DateTimeFormat.ShortTimePattern
}
Output:
25-1-2025
11:10
[1] If you have a reproducible case, do tell us.
There is a minor technical difference between these two environments, as iRon points out, but it doesn't seem to have any behavioral impact: in the ISE, Get-Culture
returns an instance of a type that is derived from the expected [cultureinfo]
type, namely [Microsoft.Windows.PowerShell.Gui.Internal.ISECultureInfo]
, which, as noted, can be discovered with (Get-Culture).pstypenames