PowerShell icon indicating copy to clipboard operation
PowerShell copied to clipboard

Possible binding issue with `DateTime` ?

Open santisq opened this issue 1 year ago • 2 comments

Prerequisites

Steps to reproduce

There seem to be a binding issue on parameters typed DateTime on PowerShell functions however no such issue happens on binary cmdlets:

Add-Type @'
using System;
using System.Management.Automation;

[Cmdlet("Test", "DateCs")]
public class TestDateCs : PSCmdlet
{
    [Parameter(Position = 0)]
    public DateTime Date { get; set; }

    protected override void EndProcessing() => WriteObject(Date);
}
'@ -PassThru | Import-Module -Assembly { $_.Assembly }

function Test-DatePwsh {
    [CmdletBinding()]
    param([datetime] $Date)

    $Date
}

& {
    [System.Threading.Thread]::CurrentThread.CurrentCulture = [cultureinfo]::GetCultureInfo('es-AR')
    Test-DatePwsh '28/05/2023 15:05:29' # fails
    Test-DateCs '28/05/2023 15:05:29'   # works????
}

Looking at the traces for both, we can see that for the binary cmdlet the coercion happens instantly:

ParameterBinding Information: 0 : BIND NAMED cmd line args [Test-DateCs]
ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Test-DateCs]
ParameterBinding Information: 0 :     BIND arg [28/05/2023 15:05:29] to parameter [Date]
ParameterBinding Information: 0 :         BIND arg [28/05/2023 15:05:29] to param [Date] SKIPPED
ParameterBinding Information: 0 :     BIND arg [28/05/2023 15:05:29] to parameter [Date]
ParameterBinding Information: 0 :         COERCE arg to [System.DateTime]
ParameterBinding Information: 0 :             Trying to convert argument value from System.String to System.DateTime
ParameterBinding Information: 0 :             CONVERT arg type to param type using LanguagePrimitives.ConvertTo
ParameterBinding Information: 0 :             CONVERT SUCCESSFUL using LanguagePrimitives.ConvertTo: [28/5/2023 15:05:29]
ParameterBinding Information: 0 :         BIND arg [28/5/2023 15:05:29] to param [Date] SUCCESSFUL
ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Test-DateCs]
ParameterBinding Information: 0 : CALLING BeginProcessing
ParameterBinding Information: 0 : CALLING ProcessRecord
ParameterBinding Information: 0 : CALLING EndProcessing

However for the pwsh function, doesn't seem to attempt a coercion to System.DateTime:

& {
    [System.Threading.Thread]::CurrentThread.CurrentCulture = [cultureinfo]::GetCultureInfo('es-AR')
    Trace-Command ParameterBinding { Test-DatePwsh '28/05/2023 15:05:29' } -FilePath trace.txt
    Select-String -Path trace.txt -Pattern 'COERCE arg to \[.+]' | ForEach-Object { $_.Matches.Value }
    Remove-Item trace.txt -EA 0
}

# COERCE arg to [System.Object]
# COERCE arg to [System.Management.Automation.SwitchParameter]
# COERCE arg to [System.String[]]
# COERCE arg to [System.String]
# COERCE arg to [System.Management.Automation.ActionPreference]
# COERCE arg to [System.Version]
# COERCE arg to [System.Version]

Expected behavior

& {
    [System.Threading.Thread]::CurrentThread.CurrentCulture = [cultureinfo]::GetCultureInfo('es-AR')
    Test-DatePwsh '28/05/2023 15:05:29'
}

domingo, 28 de mayo de 2023 15:05:29

Actual behavior

Cannot process argument transformation on parameter 'Date'. Cannot convert value "28/05/2023 15:05:29" to type "System.DateTime". Error: "String '' was not recognized as a valid DateTime."

Error details

Exception             : 
    Type              : System.Management.Automation.ParameterBindingArgumentTransformationException
    Message           : Cannot process argument transformation on parameter 'Date'. Cannot convert value "28/05/2023 15:05:29" to type      
"System.DateTime". Error: "String '' was not recognized as a valid DateTime."
    ParameterName     : Date
    ParameterType     : datetime
    TypeSpecified     : string
    ErrorId           : ParameterArgumentTransformationError
    Line              : 3
    Offset            : 19
    CommandInvocation : 
        MyCommand        : Test-DatePwsh
        ScriptLineNumber : 3
        OffsetInLine     : 5
        HistoryId        : 26
        Line             : Test-DatePwsh '28/05/2023 15:05:29'

        Statement        : Test-DatePwsh '28/05/2023 15:05:29'
        PositionMessage  : At line:3 char:5
                           +     Test-DatePwsh '28/05/2023 15:05:29'
                           +     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        InvocationName   : Test-DatePwsh
        PipelineLength   : 1
        PipelinePosition : 1
        CommandOrigin    : Internal
    ErrorRecord       : 
        Exception             : 
            Type    : System.Management.Automation.ParentContainsErrorRecordException
            Message : Cannot process argument transformation on parameter 'Date'. Cannot convert value "28/05/2023 15:05:29" to type        
"System.DateTime". Error: "String '' was not recognized as a valid DateTime."
            HResult : -2146233087
        CategoryInfo          : InvalidData: (:) [Test-DatePwsh], ParentContainsErrorRecordException
        FullyQualifiedErrorId : ParameterArgumentTransformationError,Test-DatePwsh
        InvocationInfo        : 
            MyCommand        : Test-DatePwsh
            ScriptLineNumber : 3
            OffsetInLine     : 19
            HistoryId        : 26
            Line             : Test-DatePwsh '28/05/2023 15:05:29'

            Statement        : '28/05/2023 15:05:29'
            PositionMessage  : At line:3 char:19
                               +     Test-DatePwsh '28/05/2023 15:05:29'
                               +                   ~~~~~~~~~~~~~~~~~~~~~
            CommandOrigin    : Internal
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 3
                                at <ScriptBlock>, <No file>: line 1
    TargetSite        : 
        Name          : BindPositionalParametersInSet
        DeclaringType : System.Management.Automation.ParameterBinderController, System.Management.Automation, Version=7.4.0.500,
Culture=neutral, PublicKeyToken=31bf3856ad364e35
        MemberType    : Method
        Module        : System.Management.Automation.dll
    Data              : System.Collections.ListDictionaryInternal
    InnerException    : 
        Type           : System.Management.Automation.ArgumentTransformationMetadataException
        ErrorRecord    : 
            Exception             : 
                Type    : System.Management.Automation.ParentContainsErrorRecordException
                Message : Cannot convert value "28/05/2023 15:05:29" to type "System.DateTime". Error: "String '' was not recognized as a   
valid DateTime."
                HResult : -2146233087
            CategoryInfo          : MetadataError: (:) [], ParentContainsErrorRecordException
            FullyQualifiedErrorId : RuntimeException
        TargetSite     : 
            Name          : Transform
            DeclaringType : System.Management.Automation.ArgumentTypeConverterAttribute, System.Management.Automation, Version=7.4.0.500,   
Culture=neutral, PublicKeyToken=31bf3856ad364e35
            MemberType    : Method
            Module        : System.Management.Automation.dll
        Message        : Cannot convert value "28/05/2023 15:05:29" to type "System.DateTime". Error: "String '' was not recognized as a    
valid DateTime."
        InnerException : 
            Type           : System.Management.Automation.PSInvalidCastException
            ErrorRecord    : 
                Exception             : 
                    Type    : System.Management.Automation.ParentContainsErrorRecordException
                    Message : Cannot convert value "28/05/2023 15:05:29" to type "System.DateTime". Error: "String '' was not recognized    
as a valid DateTime."
                    HResult : -2146233087
                CategoryInfo          : InvalidArgument: (:) [], ParentContainsErrorRecordException
                FullyQualifiedErrorId : InvalidCastParseTargetInvocationWithFormatProvider
            TargetSite     : 
                Name          : ConvertWithCulture
                DeclaringType : System.Management.Automation.LanguagePrimitives+ConvertViaParseMethod, System.Management.Automation,        
Version=7.4.0.500, Culture=neutral, PublicKeyToken=31bf3856ad364e35
                MemberType    : Method
                Module        : System.Management.Automation.dll
            Message        : Cannot convert value "28/05/2023 15:05:29" to type "System.DateTime". Error: "String '' was not recognized as  
a valid DateTime."
            InnerException : 
                Type       : System.FormatException
                TargetSite : 
                    Name          : Parse
                    DeclaringType : System.DateTimeParse, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e
                    MemberType    : Method
                    Module        : System.Private.CoreLib.dll
                Message    : String '' was not recognized as a valid DateTime.
                Source     : System.Private.CoreLib
                HResult    : -2146233033
                StackTrace : 
   at System.DateTimeParse.Parse(ReadOnlySpan`1 s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
   at System.DateTime.Parse(String s, IFormatProvider provider)
   at InvokeStub_DateTime.Parse(Object, Span`1)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters,        
CultureInfo culture)
            Source         : System.Management.Automation
            HResult        : -2147467262
            StackTrace     : 
   at System.Management.Automation.LanguagePrimitives.ConvertViaParseMethod.ConvertWithCulture(Object valueToConvert, Type resultType,      
Boolean recursion, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable backupTable)
   at System.Management.Automation.LanguagePrimitives.ConvertTo(Object valueToConvert, Type resultType, Boolean recursion, IFormatProvider  
formatProvider, TypeTable backupTypeTable)
   at System.Management.Automation.ArgumentTypeConverterAttribute.Transform(EngineIntrinsics engineIntrinsics, Object inputData, Boolean    
bindingParameters, Boolean bindingScriptCmdlet)
        Source         : System.Management.Automation
        HResult        : -2146233087
        StackTrace     : 
   at System.Management.Automation.ArgumentTypeConverterAttribute.Transform(EngineIntrinsics engineIntrinsics, Object inputData, Boolean    
bindingParameters, Boolean bindingScriptCmdlet)
   at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter
parameterMetadata, ParameterBindingFlags flags)
    Source            : System.Management.Automation
    HResult           : -2146233087
    StackTrace        : 
   at System.Management.Automation.ParameterBinderController.BindPositionalParametersInSet(UInt32 validParameterSets, Dictionary`2
nextPositionalParameters, CommandParameterInternal argument, ParameterBindingFlags flags, ParameterBindingException& bindingException)      
   at System.Management.Automation.ParameterBinderController.BindPositionalParameters(Collection`1 unboundArguments, UInt32
validParameterSets, UInt32 defaultParameterSet, ParameterBindingException& outgoingBindingException)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
   at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
   at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
--- End of stack trace from previous location ---
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][]
pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
CategoryInfo          : InvalidData: (:) [Test-DatePwsh], ParameterBindingArgumentTransformationException
FullyQualifiedErrorId : ParameterArgumentTransformationError,Test-DatePwsh
InvocationInfo        : 
    MyCommand        : Test-DatePwsh
    ScriptLineNumber : 3
    OffsetInLine     : 19
    HistoryId        : 26
    Line             : Test-DatePwsh '28/05/2023 15:05:29'

    Statement        : '28/05/2023 15:05:29'
    PositionMessage  : At line:3 char:19
                       +     Test-DatePwsh '28/05/2023 15:05:29'
                       +                   ~~~~~~~~~~~~~~~~~~~~~
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 3
                        at <ScriptBlock>, <No file>: line 1

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

santisq avatar Jan 12 '24 00:01 santisq

After a chat with @SeeminglyScience, for PowerShell functions, the issue seem to happen on: https://github.com/PowerShell/PowerShell/blob/e453c006d39a2fa57a903df2d0ea33ddd0cb43dd/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs#L138

I understand changing this line to use CultureInfo.CurrentCulture could cause issues, so this can be closed 😁

santisq avatar Jan 12 '24 01:01 santisq

Given PowerShell's generally culture-invariant behavior, it is the behavior of binary cmdlets that is the anomaly; see:

  • #6989
  • #8129

mklement0 avatar Jan 12 '24 02:01 mklement0

📣 Hey @santisq, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://aka.ms/PSRepoFeedback