PowerShell のパラメータバインディングは "3 時" を Datetime 型にパースする
いきなりまとめ
PowerShell の関数なりコマンドレットがパラメータを受け取る場合、パラメータバインディングの仕組みで型ごとの変換処理をしており、日付型のパラメータではDateTime.Parse
しているのがわかった。
経緯
先日 ScheduledTask を設定したくだりで初めて知った。
New-ScheduledTaskTrigger (ScheduledTasks) | Microsoft Docs
を見ていて、 -At 3pm
て何これ?と思って日本語も試した。
使い所がわかりかねるが、以下のようなジャパナイズされた入力でも OK!
ただし漢数字、曜日や午前/午後は Parse
できないので無理な。
参照: Convert strings to DateTime | Microsoft Docs
PS> Get-Date 15時
Wednesday, January 16, 2022 03:00:00
PS> Get-Date 6年6月6日
Monday, June 6, 2006 00:00:00
PS> Get-Date 3じ
Get-Date: Cannot bind parameter 'Date'. Cannot convert value "3じ" to type "System.DateTime". Error: "The string '3じ' was not recognized as a valid DateTime. There is an unknown word starting at index '1'."
どういう仕組や。
大まかな予測
コマンドレットや関数に渡す前、引数の型 DateTime
の時点で捏ねくっている様子。
function hiduke() {
param(
[datetime]
$d
)
$d
}
PS> hiduke -d 3時
Wednesday, January 16, 2022 03:00:00
キャストとパースの違いを見る。
PS> [DateTime]::Parse('3時')
Wednesday, January 16, 2022 03:00:00
PS> [DateTime]::Parse('3じ')
MethodInvocationException: Exception calling "Parse" with "1" argument(s): "The string '3じ' was not recognized as a valid DateTime. There is an unknown word starting at index '1'."
PS> [DateTime]'3時'
Wednesday, January 16, 2022 03:00:00
PS> [DateTime]'3じ'
InvalidArgument: Cannot convert value "3じ" to type "System.DateTime". Error: "The string '3じ' was not recognized as a valid DateTime. There is an unknown word starting at index '1'."
同じエラーメッセージ出てるので、 PowerShell が指定の型に評価するのに DateTime.Parse
を呼んでる。
真面目に考えたことなかったが、PowerShell 自体の機能でパラメーターバインディングというらしい。
about Parameters - PowerShell | Microsoft Docs
(今更ながら)また一つ学んでしまったようだな...
パラメーターバインディングの処理内容を知るには Trace-Command
が使える。
PS> Trace-Command -PSHost -Name ParameterBinding {Get-Date 3じ}
DEBUG: 2022-01-30 14:47:05.5488 ParameterBinding Information: 0 : BIND NAMED cmd line args [Get-Date]
DEBUG: 2022-01-30 14:47:05.5491 ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Get-Date]
DEBUG: 2022-01-30 14:47:05.5493 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 14:47:05.5495 ParameterBinding Information: 0 : BIND arg [3じ] to param [Date] SKIPPED
DEBUG: 2022-01-30 14:47:05.5496 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 14:47:05.5497 ParameterBinding Information: 0 : BIND arg [3じ] to param [Date] SKIPPED
DEBUG: 2022-01-30 14:47:05.5499 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 14:47:05.5501 ParameterBinding Information: 0 : BIND arg [3じ] to param [Date] SKIPPED
DEBUG: 2022-01-30 14:47:05.5503 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 14:47:05.5504 ParameterBinding Information: 0 : COERCE arg to [System.DateTime]
DEBUG: 2022-01-30 14:47:05.5506 ParameterBinding Information: 0 : Trying to convert argument value from System.String to System.DateTime
DEBUG: 2022-01-30 14:47:05.5509 ParameterBinding Information: 0 : CONVERT arg type to param type using LanguagePrimitives.ConvertTo
DEBUG: 2022-01-30 14:47:05.5519 ParameterBinding Information: 0 : ERROR: ERROR: COERCE FAILED: arg [3じ] could not be converted to the parameter type [System.DateTime]
(...端折る...)
Get-Date: Cannot bind parameter 'Date'. Cannot convert value "3じ" to type "System.DateTime". Error: "The string '3じ' was not recognized as a valid DateTime. There is an unknown word starting at index '1'."
パラメータバインディング時によしなにしてるのがわかってきたところで、力尽きた。 PowerShell のパラメータバインディングの仕組みに関しては宿題やな。気長に見ていくしかない。
潜る
ここまで来れたので、次は PowerShell のパラメータバインディングのコードに潜り込む。
Trace-Command
の ParameterBinding Information に出てた
"COERCE FAILED: arg .+ could not be converted to the parameter type "
"CONVERT arg type to param type using LanguagePrimitives.ConvertTo"
から、 ParameterBinderBase.cs
にたどり着いた。
例外がスローされたのがどこか LanguagePrimitives.ConvertTo
から先を探るのにはちょっと情報が足りなかったので、Trace-Command
に TypeConversion を足して出力した。
TypeConversion が有用なのがわかったのは、LanguagePrimitives.cs
内の ConvertTo
を追ってたらトレース情報にまんまその名前が出てきたから。
PS> Trace-Command -PSHost -Name ParameterBinding,TypeConversion {Get-Date 3じ}
DEBUG: 2022-01-30 15:36:52.2304 ParameterBinding Information: 0 : BIND NAMED cmd line args [Get-Date]
DEBUG: 2022-01-30 15:36:52.2309 ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Get-Date]
DEBUG: 2022-01-30 15:36:52.2312 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 15:36:52.2314 ParameterBinding Information: 0 : BIND arg [3じ] to param [Date] SKIPPED
DEBUG: 2022-01-30 15:36:52.2315 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 15:36:52.2318 ParameterBinding Information: 0 : BIND arg [3じ] to param [Date] SKIPPED
DEBUG: 2022-01-30 15:36:52.2320 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 15:36:52.2322 ParameterBinding Information: 0 : BIND arg [3じ] to param [Date] SKIPPED
DEBUG: 2022-01-30 15:36:52.2325 ParameterBinding Information: 0 : BIND arg [3じ] to parameter [Date]
DEBUG: 2022-01-30 15:36:52.2326 ParameterBinding Information: 0 : COERCE arg to [System.DateTime]
DEBUG: 2022-01-30 15:36:52.2329 ParameterBinding Information: 0 : Trying to convert argument value from System.String to System.DateTime
DEBUG: 2022-01-30 15:36:52.2331 ParameterBinding Information: 0 : CONVERT arg type to param type using LanguagePrimitives.ConvertTo
DEBUG: 2022-01-30 15:36:52.2336 TypeConversion Information: 0 : Converting "3じ" to "System.DateTime".
DEBUG: 2022-01-30 15:36:52.2346 TypeConversion Information: 0 : Exception calling Parse method with CultureInfo: "The string '3じ' was not recognized as a valid DateTime. There is an unknown word starting at index '1'.".
DEBUG: 2022-01-30 15:36:52.2350 ParameterBinding Information: 0 : ERROR: ERROR: COERCE FAILED: arg [3じ] could not be converted to the parameter type [System.DateTime]
(...端折る...)
Get-Date: Cannot bind parameter 'Date'. Cannot convert value "3じ" to type "System.DateTime". Error: "The string '3じ' was not recognized as a valid DateTime. There is an unknown word starting at index '1'."
ParameterBinding Information のエラー出力までに TypeConversion Information が足されたのがわかる。いい感じじゃないか。
このキーワードを元にコードを探ると、
LanguagePrimitives.cs
内のFigureParseConversion
でパースに使うメソッドをリフレクションで取得しているLanguagePrimitives.cs
内のConvertViaParseMethod
で取得したメソッドを使ったパースが行われてる
というのがわかった。
やっぱり Datetime.Parse
を使ってたんや。あー、スッキリした!
というか PowerShell 使ってる割にちゃんと勉強してないから、せなあかんな。