0

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?

lit
  • 14,456
  • 10
  • 65
  • 119
  • It appears that the PowerShell team is a bit en-us oriented. Being ISO-8601 oriented might be better. – lit Jun 13 '23 at 17:53

1 Answers1

1

.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);
}
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • It might be helpful it the datetime format in `[cultureinfo]::InvariantCulture` was ISO-8601. – lit Jun 13 '23 at 18:08
  • 1
    Nicely done; I assume that `Get-ScriptBlockDisassembly` is from the third-party [`ScriptBlockDisassembler` module](https://github.com/SeeminglyScience/ScriptBlockDisassembler). @lit, to get ISO 8601 format, you can use `(Get-Date).ToString('o')` for instance, and this format is also recognized by the invariant culture, and therefore in _casts_; e.g. `[datetime] (Get-Date).ToString('o')` – mklement0 Jun 13 '23 at 19:03
  • @lit, for a (hopefully) comprehensive overview of when string operations in PowerShell are culture-sensitive and when not, see [this answer](https://stackoverflow.com/a/37603732/45375). – mklement0 Jun 13 '23 at 19:05