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 !
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:
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.
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%]