user3104363
user3104363

Reputation: 334

Localization affecting the output of Powershell Get-Date command

I am trying to call Powershell Get-Date command within the batch script. Here is my batch script:

@echo off
setlocal EnableDelayedExpansion
set myFormat=dd/MM
set locale_independent_get_date=powershell -NonInteractive -Command "Get-Date -Format %myFormat%"
for /F "tokens=1,2 delims=/" %%i IN ('%locale_independent_get_date%') DO (
    set dd=%%i
    set mm=%%j
)
echo Day:[%dd%]
echo Month:[%mm%]

Because I explicitly specify the format with -Format switch, I was expecting to have the same output with every different locale settings.

But I see that, this is not true. Above Powershell command is not working as I expected with some different settings.

Isn't specifying the format explicitly with -Format enough to ensure locale independence? Clearly it is not, but I am wondering why.

Also, Instead of -Format, I tried -UFormat switch. In this case, command is working as expected in problematic cases too. Here what I did :

set myFormat=%%d/%%m
set locale_independent_get_date=powershell -NonInteractive -Command "Get-Date -UFormat %myFormat%"

But as we have many different possible settings, I cannot try all of them. So, How can I be certain that this command is locale-independent? Is there any documentation ? Or Do you know an easy way to test this code with all possible locale settings ?

Thank you !

Upvotes: 1

Views: 86

Answers (1)

mklement0
mklement0

Reputation: 439822

Escape the / char:

set myFormat=dd\/MM

Doing so ensures that .NET, which PowerShell is built on, uses the / literally in the output string, whereas unescaped use treats / as a placeholder, namely for the culture-appropriate date-component separator; e.g., with the de-DE (German) culture in effect, / turns into . in the output string.

The same applies analogously to the time-component separator, :

(As an alternative to \-escaping, you may use embedded '...' or "..." quoting, e.g. set myFormat=dd'/'MM, but to avoid quote character-escaping headaches when calling from outside PowerShell it is simpler to use the \ escape).

For more information, see the .NET docs:

Caveat:

  • While the above yields Arabic numerals (e.g. 11) and / for all cultures, as intended, the values of these numbers depend on the current culture's calendar - see the bottom section if you need to ensure that the result is always expressed as the components of a Gregorian date.

To provide a succinct demonstration in PowerShell code:

& {

  # Save the culture currently in effect.
  $prevCulture = [cultureinfo]::CurrentCulture

  # Loop over all given cultures.
  $args[0] | ForEach-Object {
    [cultureinfo]::CurrentCulture = $_

    # Contrast escaped and unescaped use.
    [pscustomobject] @{
      Culture = $_
      UnescapedSlash = Get-Date -Format dd/MM
      EscapedSlash = Get-Date -Format dd\/MM # Note the \
    }

  }

  # Restore the previous culture
  [cultureinfo]::CurrentCulture = $prevCulture

} 'en-US', 'de-DE'

The above prints:

Culture UnescapedSlash EscapedSlash
------- -------------- ------------
en-US   26/11          26/11
de-DE   26.11          26/11

Note how, for culture de-DE, the unescaped / in the format string turned into the culture-appropriate . separator, whereas the escaped form was retained verbatim.


Additionally ensuring use of the Gregorian calendar:

As noted, the month and day numbers are expressed in the current culture's calendar, as reflected in [cultureinfo]::CurrentCulture.Calendar.GetType().Name.

Thus, with a culture that uses a calendar other than the Gregorian one in effect, the day and month number can differ (e.g., 26 November 2024 will yield '26/11' in cultures using the Gregorian Calendar, but '24/05' in ar-SA (Saudi Arabian Arabic, which uses the UmAlQuraCalendar calendar).

To ensure use of the Gregorian calendar, temporarily switch to the invariant culture, [cultureinfo]::InvariantCulture, as part of the PowerShell CLI call:

@echo off
set myFormat=dd\/MM
set locale_independent_get_date=powershell -NonInteractive -Command "[cultureinfo]::CurrentCulture=[cultureinfo]::InvariantCulture; Get-Date -Format %myFormat%"
for /F "tokens=1,2 delims=/" %%i IN ('%locale_independent_get_date%') DO (
    set dd=%%i
    set mm=%%j
)
echo Day:[%dd%]
echo Month:[%mm%]

Upvotes: 2

Related Questions