Powershell argument completers

Will Pittenger 281 Reputation points
2022-09-03T07:54:40.087+00:00

I wrote the code below as a means of Time Zone lookup and conversion. One of the things I wanted was completion of names based on ID, System Name, Display Name, or Daylight Name. But while the code works, the argument lookup doesn't. What's wrong?

   PowerShell  
   $mapCommonTimeZones =  
   @{  
   	'UTC' = [TimeZoneInfo]::UTC;  
   	'PST' = ([TimeZoneInfo]::FindSystemTimeZoneById('Pacific Standard Time') | Where-Object { $_.DisplayName -match '\(US \& Canada\)' })[0];  
   	'MST' = ([TimeZoneInfo]::FindSystemTimeZoneById('Mountain Standard Time'))[0];  
   	'CST' = ([TimeZoneInfo]::FindSystemTimeZoneById('Central Standard Time') | Where-Object { $_.DisplayName -match '\(US \& Canada\)' })[0];  
   	'EST' = ([TimeZoneInfo]::FindSystemTimeZoneById('Mountain Standard Time'))[0];  
   };  
     
   class TimeZoneCompleter : System.Management.Automation.IArgumentCompleter  
   {  
   	[System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument([string] $strCmdName, [string] $strParamName, [string] $strWordToComplete, [System.Management.Automation.Language.CommandAst] $cmdAst, [System.Collections.IDictionary]$mapFakeBoundParameters)  
   	{  
   		return (([TimeZoneInfo]::GetSystemTimeZones() + $global:mapCommonTimeZones.Values) | Where-Object { $_.Id -like "$strWordToComplete*" -or $_.DisplayName -like "$strWordToComplete*" -or $_.DaylightName -like "$strWordToComplete*" -or $_.StandardName -like "$strWordToComplete*" } | ForEach-Object { Write-Output (new System.Management.Automation.CompletionResult $_.Id) });  
   	}  
   }  
     
   function Get-AllTimeZones  
   {  
   	[OutputType([TimeZoneInfo[]])]  
   	param  
   	(  
   		[Parameter(Position = 1, ValueFromRemainingArguments, ValueFromPipeline)]  
   		[ValidateNotNullOrEmpty()]  
   		[ArgumentCompleter([TimeZoneCompleter])]  
   		[string[]]  
   		$astrPartialTimeZoneInfo  
   	)  
     
   	Write-Output ($PSBoundParameters.ContainsKey('astrPartialTimeZoneInfo') ? ($astrPartialTimeZoneInfo | ForEach-Object { $curInfo = $_; $mapCommonTimeZones.Contains($_) ? $mapCommonTimeZones[$_] : ([System.TimeZoneInfo]::GetSystemTimeZones() | Where-Object { $_.Id -like "$curInfo*" }).Id }) : [System.TimeZoneInfo]::GetSystemTimeZones());  
   }  
     
   function Convert-TimeZones  
   {  
   	[OutputType([datetime])]  
   	param  
   	(  
   		[Parameter(Mandatory, Position = 0)]  
   		[DateTime]  
   		$dateFrom,  
     
   		[Parameter(Mandatory, Position = 1, ParameterSetName = 'TZ_TZ')]  
   		[Parameter(Mandatory, Position = 1, ParameterSetName = 'TZ_STR')]  
   		[ValidateNotNull()]  
   		[System.TimeZoneInfo]  
   		$timezoneFrom,  
     
   		[Parameter(Mandatory, Position = 2, ParameterSetName = 'TZ_TZ')]  
   		[Parameter(Mandatory, Position = 2, ParameterSetName = 'STR_TZ')]  
   		[ValidateScript({ $_ -ne $null -and $_ -ne $timezoneFrom })]  
   		[System.TimeZoneInfo]  
   		$timezoneTo,  
     
   		[Parameter(Mandatory, Position = 1, ParameterSetName = 'STR_STR')]  
   		[Parameter(Mandatory, Position = 1, ParameterSetName = 'STR_TZ')]  
   		[ValidateScript({ $_ -ne $null -and $_ -ne '' -and (Get-AllTimeZones $_).length -ge 0 })]  
   		[ArgumentCompleter([TimeZoneCompleter])]  
   		[string]  
   		$strFromTimeZone,  
     
   		[Parameter(Mandatory, Position = 2, ParameterSetName = 'STR_STR')]  
   		[Parameter(Mandatory, Position = 2, ParameterSetName = 'TZ_STR')]  
   		[ValidateScript({ $_ -ne $null -and $_ -ne '' -and (Get-AllTimeZones $_).length -ge 0 })]  
   		[ArgumentCompleter([TimeZoneCompleter])]  
   		[string]  
   		$strToTimeZone  
   	)  
     
   	begin  
   	{  
   		$timezoneFrom = $PSBoundParameters['timezoneFrom'] ? $timezoneFrom : (Get-TimeZone $strFromTimeZone);  
   		$timezoneTo = $PSBoundParameters['timezoneTo'] ? $timezoneTo : (Get-TimeZone $strToTimeZone);  
   	}  
     
   	process  
   	{  
   		Write-Output ([TimeZoneInfo]::ConvertTime($dateFrom, $timezoneFrom, $timezoneTo));  
   	}  
   }  
Windows Server PowerShell
Windows Server PowerShell
Windows Server: A family of Microsoft server operating systems that support enterprise-level management, data storage, applications, and communications.PowerShell: A family of Microsoft task automation and configuration management frameworks consisting of a command-line shell and associated scripting language.
5,363 questions
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. Rich Matheisen 44,776 Reputation points
    2022-09-03T14:42:02.15+00:00

    Since you're using PowerShell, why not stick to writing the argument completer in PowerShell, too?

    Does this help? about_functions_argument_completion


  2. Will Pittenger 281 Reputation points
    2022-09-03T17:04:09.6+00:00

    Oops. That code relies on an alias being declared above. Sorry for the shortened code. set-alias new new-object


  3. Will Pittenger 281 Reputation points
    2022-09-04T03:00:57.01+00:00

    Fixed it. I had to build a collection and then return it.

       class TimeZoneCompleter : System.Management.Automation.IArgumentCompleter  
       {  
       	[System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument([string] $strCmdName, [string] $strParamName, [string] $strWordToComplete, [System.Management.Automation.Language.CommandAst] $cmdAst, [System.Collections.IDictionary]$mapFakeBoundParameters)  
       	{  
       		[System.Management.Automation.CompletionResult[]]$result = [System.Management.Automation.CompletionResult[]]@();  
       		([TimeZoneInfo]::GetSystemTimeZones() + $global:mapCommonTimeZones.Values) | Where-Object { $_.Id -like "$strWordToComplete*" -or $_.DisplayName -like "$strWordToComplete*" -or $_.DaylightName -like "$strWordToComplete*" -or $_.StandardName -like "$strWordToComplete*" } | ForEach-Object { $result += (new System.Management.Automation.CompletionResult "'$($_.Id)'", ($_.Id), ([Management.Automation.CompletionResultType]::ParameterValue), ($_.DisplayName)) };  
         
       		return $result;  
       	}  
       }  
    
    0 comments No comments