powershellpowershell-7.3

Why does implicit date casting to [string] not use current regional settings?


PowerShell does an implicit conversion of a [datetime] to [string] in order to do this concatenation.

Why does using ToString() use the current regional settings while not using it produces a different result?

PS C:\> 'now ' + (Get-Date).AddDays(-3)
now 06/10/2023 12:32:28
PS C:\> 'now ' + (Get-Date).AddDays(-3).ToString()
now 2023-06-10 12:32:47

PS C:\> $PSVersionTable.PSVersion.ToString()
7.3.4

Related question: Why is Powershell Write-Output Date Format not the System Setting?


Solution

  • .ToString() will call the DateTime.ToString() instance method which uses the current culture, where as casting will use whatever the PowerShell team decided to use for IFormatProvider.

    I believe the code in charge of this is LanguagePrimitives.ConvertTo, which by the looks of it, they decided to use InvariantCulture: LanguagePrimitives.cs#L1758C2-L1766.

    # Both produce the same DateTime format string
    [System.Management.Automation.LanguagePrimitives]::ConvertTo(
        (Get-Date).AddDays(-3),
        [string])
    
    (Get-Date).AddDays(-3).ToString([cultureinfo]::InvariantCulture)
    

    Type casting is actually handled by PSConvertBinder and they're also using InvariantCulture there: Binders.cs#L3765-L3795. ScriptBlockDisassembler Module makes it really easy to understand what internal classes are being used:

    PS ..\pwsh> { [string] [datetime]::Now } | Get-ScriptBlockDisassembly -Minimal
    
    // ScriptBlock.EndBlock
    try
    {
        funcContext._outputPipe.Add(
            Fake.Dynamic<Func<CallSite, DateTime, string>>(PSConvertBinder.Get(typeof(string)))(DateTime.Now));
    }
    catch (FlowControlException)
    {
        throw;
    }
    catch (Exception exception)
    {
        ExceptionHandlingOps.CheckActionPreference(funcContext, exception);
    }