1

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 !

14
  • In PowerShell the -Format string should be quoted, yours is not. Also, why bother using a forward slash in the string, then delimit it out anyhow? Also, don't enable delayed variable expansion, if you aren't needing it. Commented Nov 26, 2024 at 16:56
  • Example with singlequoting: For /F "Tokens=1-2" %%G In ('%SystemRoot%\Sysem32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NonInteractive -Command "Get-Date -Format 'dd MM'" 2^>NUL') Do Set "dd=%%G" & Set "MM=%%H". Alternatively with doublequoting: For /F "Tokens=1-2" %%G In ('%SystemRoot%\Sysem32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NonInteractive -Command "Get-Date -Format \"dd MM\"" 2^>NUL') Do Set "dd=%%G" & Set "MM=%%H". Commented Nov 26, 2024 at 17:10
  • @Compo, while quoting the -Format argument never hurts, it isn't necessary here: Get-Date -Format dd/MM works just fine (though not culture-invariantly). Fair point about not really needing a separator in this particular use case, but future readers may have use cases where the / is needed in the result string. Commented Nov 26, 2024 at 18:25
  • I'd assume that the space is not treated as a placeholder for some culture-appropriate date component. So using the examples I've provided above do solve the reported problem with the submitted code, (because the separator in the -Format string for the Get-Date PowerShell command, was never required at all by the OP). Commented Nov 26, 2024 at 23:03
  • @Compo, you're conflating two unrelated aspects, which is confusing: your claim that -Format (always) requires a quoted argument isn't true as such, as I've demonstrated. Using a space as the separator inside the argument indeed would require quoting. These considerations are solely related to PowerShell's parsing in argument-parsing mode, not to the specific issue at hand. Commented Nov 28, 2024 at 1:51

1 Answer 1

2

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%]
Sign up to request clarification or add additional context in comments.

4 Comments

@user3104363, the solution in this answer isn't locale-dependent (which was the point). Also, the use of standard formats such as the short date format doesn't come into play here. Please see my update, which demonstrates the effectiveness of the solution (paste into a PowerShell script). Do you get different results?
So, Powershell is using .Net under the hood ? If so, as you explain, I need to follow the rules you explain. I am getting the same results when I try to execute the Powershell code. But, when I try to modify my batch script as you explained : set myFormat=dd\/MM set LOCALE_INDEPENDENT_GET_DATE_CMD=powershell -NonInteractive -Command "Get-Date -Format %DATE_FORMAT%" for /F "tokens=1,2 delims=\/" %%i IN ('%LOCALE_INDEPENDENT_GET_DATE_CMD%') DO ( set dd=%%i set mm=%%j ) I am still getting unexpected output with Format: English (United States), Short Time: yyyy-MM-dd
Issue is solved. I was trying to test with more and more different settings. No issue found. Thanks a lot for helping me Just to clarify, please confirm my understanding, the issue was originally related with using invalid delimiters. Using a valid delimiter fixed the issue. (I am currently ignoring different calendar types, only talking about Gregorian calendar.) @mklement0
Glad to hear it, @user3104363; my pleasure. No, the issue wasn't that the separator was invalid, it' s just that if / is unescaped, it acts as a placeholder rather than as a literal. That is, it then stands for whatever separator is customary in the current culture (see the de-DE example in the answer).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.