powershelldatetimepowershell-coredatetimeparseexception

DateTime TryParseExact fails when an array of formats is passed in PowerShell


Running the following PowerShell returns false when I'd expect true:

$format = @('yyyy-MM-ddTHH:mm:ssZ', 'yyyy-MM-ddTHH:mm:sszzz', 'yyyy-MM-ddTHH:mm:ss')
$provider = [System.Globalization.CultureInfo]::InvariantCulture
$style = [System.Globalization.DateTimeStyles]::AllowWhiteSpaces -bor [System.Globalization.DateTimeStyles]::AssumeUniversal
$result = [DateTime]::MinValue         
[DateTime]::TryParseExact('2020-01-01T12:01:00Z', $format, $provider, $style , [ref]$result)

# returns false

I've seen the same behaviour with both PowerShell (v5.1.19041.1682) and PWSH (v7.2.4).

If I pass a single value for the Format parameter instead of an array then I get the expected result:

$format = 'yyyy-MM-ddTHH:mm:ssZ'
$provider = [System.Globalization.CultureInfo]::InvariantCulture
$style = [System.Globalization.DateTimeStyles]::AllowWhiteSpaces -bor [System.Globalization.DateTimeStyles]::AssumeUniversal
$result = [DateTime]::MinValue         
[DateTime]::TryParseExact('2020-01-01T12:01:00Z', $format, $provider, $style , [ref]$result)

# returns true

This isn't a bug in .net, as running the same in C# works exactly as one would expect (I've tried running the exact equivalent code as above too):

void Main()
{
    var formats = new []{"yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddTHH:mm:sszzz", "yyyy-MM-ddTHH:mm:ss"};
    var provider = System.Globalization.CultureInfo.InvariantCulture;
    var style = System.Globalization.DateTimeStyles.AllowWhiteSpaces | System.Globalization.DateTimeStyles.AssumeUniversal;
    foreach (var testString in new[]{"", "2020-01-01T12:01:00", "2020-01-01T12:01:00Z", "2020-01-01T12:01:00+01:00", "2020-01-01 12:01:00"}) 
    {
        var result = DateTime.MinValue;
        if (DateTime.TryParseExact(testString, formats, provider, style, out result)) 
        {
            Console.WriteLine($"Parsed '{testString}' as {result}");
        } 
        else 
        {
            Console.WriteLine($"Failed to parse '{testString}'");
        }
    }
}

Output

Failed to parse ''
Parsed '2020-01-01T12:01:00' as 2020-01-01 12:01:00
Parsed '2020-01-01T12:01:00Z' as 2020-01-01 12:01:00
Parsed '2020-01-01T12:01:00+01:00' as 2020-01-01 11:01:00
Failed to parse '2020-01-01 12:01:00'

Solution

  • The issue is down to the Format parameter being seen as an array of objects instead of an array of strings. Include a cast to string array to resolve:

    $format = [string[]]@('yyyy-MM-ddTHH:mm:ssZ', 'yyyy-MM-ddTHH:mm:sszzz', 'yyyy-MM-ddTHH:mm:ss')
    

    More Info

    If we run $format.GetType().FullName on the original version we see that the type is System.Object[]. By casting to [string[]] we change this to System.String[], which then matches the expected signature / works as desired.

    Full Code:

    $format = [string[]]@('yyyy-MM-ddTHH:mm:ssZ', 'yyyy-MM-ddTHH:mm:sszzz', 'yyyy-MM-ddTHH:mm:ss')
    $provider = [System.Globalization.CultureInfo]::InvariantCulture
    $style = [System.Globalization.DateTimeStyles]::AllowWhiteSpaces -bor [System.Globalization.DateTimeStyles]::AssumeUniversal
    $result = [DateTime]::MinValue         
    [DateTime]::TryParseExact('2020-01-01T12:01:00Z', $format, $provider, $style , [ref]$result)