しばたテックブログ

気分で書いている技術ブログです。

Get-DateはGet-DateであってNew-Dateではないお話し

最近ブログを書く気力が薄れてしまっているので軽いネタでリハビリします。
小ネタがいくつかたまっているので何とか消化したいですね...

New-Object System.Datetime と Get-Date の違い

先ずはこちらのコードをご覧ください。

PS C:\> $Date1 = New-Object System.Datetime @(2016, 3, 22)
PS C:\> $Date2 = Get-Date -Year 2016 -Month 3 -Day 22

上のコードで取得できる$Date1$Date2は同じ値になると思いますか?

$Date1は年、月、日を指定するコンストラクタでSystem.DateTimeの値を生成します。
$Date2Get-Dateコマンドレットで年、月、日を指定してSystem.DateTimeの値を取得しています。

実際に比較すると結果はFalseを返し二つの値は異なることがわかります。

PS C:\> $Date1 -eq $Date2
False

どちらも本日(2016/03/22)の値を取得していますが、もう少し詳しく見てみると、$Date1は年月日以外は初期値なのですが、$Date2は年月日以外に現在時刻が設定されています。

PS C:\> $Date1.ToString("yyyy/MM/dd HH:mm:ss.fff")
2016/03/22 00:00:00.000
PS C:\> $Date2.ToString("yyyy/MM/dd HH:mm:ss.fff")
2016/03/22 20:01:27.973

Get-DateはGet-DateであってNew-Dateでは無いお話し

Get-Dateの仕様について

私はずっと上記例の$Date1$Date2は同じ値を返すものだと思い込んでいました。
ただ、各パラメーターのヘルプを見てみると確かに、

-Year
表示する年を指定します。1 ~ 9999 の値を入力します。既定値は現在の年です。


-Month
表示する月を指定します。1 ~ 12 の値を入力します。既定値は現在の月です。


-Day
表示されている月の日にちを指定します。1 ~ 31 の値を入力します。既定値は現在の日付です。


-Hour
表示する時を指定します。1 ~ 23 の値を入力します。既定値は現在の時です。


-Minute
表示する分を指定します。1 ~ 59 の値を入力します。既定値は現在の分です。


-Second
表示する秒を指定します。1 ~ 59 の値を入力します。既定値は現在の秒です。


-Millisecond
日付のミリ秒を指定します。0 ~ 999 の値を入力します。既定値は現在のミリ秒数です。

と既定値が「現在の○○です。」になっており、現在時刻の値を設定する様になっていました。

Get-DateはGet-DateであってNew-Dateではないお話し

私見を述べれば何故こんなわけのわからない仕様にしたのだという気持ちでいっぱいです。

PowerShellでは$Date2の様にNew-ObjectDatetime型の値も生成できるので上記の例が同じ値になる方が直感的であり統一性もあると思っています。

中の人たちがどの様な意図でこの仕様にしたのか断定できる資料は無いのですが、推測すると、Get-Dateの概要には、

概要
現在の日付と時刻を取得します。

とあるので、このコマンドレットはあくまでも"現在時刻を取得する"ものであり"時刻を生成するものではない"という事なのでしょう...
不満で仕方ありませんがそういうものだと割り切ることにします。

New-Dateを作ってみた

ただ、何もせずにこの仕様に従うのが悔しかったので極力Get-Dateと互換のあるNew-Dateを作ってみました。
以下の様なコードになります。

新規にDatetime型のオブジェクトを生成するコマンド

こいつを使えば上記の例も、以下の様に同じ値を返す様になります。

PS C:\> $Date1 = New-Object System.Datetime @(2016, 3, 22)
PS C:\> $Date2 = New-Date -Year 2016 -Month 3 -Day 22

PS C:\> $Date1 -eq $Date2
True

実際の運用ではこのファンクションを使うことは無いとは思いますが*1参考までにという事で。

補足 もうひとつのNew-Object System.Datetime と Get-Date の違い

補足として、New-Objectを使った場合とGet-Dateを使ってSystem.Datetime型の値を取得した場合のもう一つの違いについて説明します。

前項のNew-Dateのコード内で軽くネタバレしているのですが、Get-Dateで取得したSystem.Datetime型の値にはDisplayHintNotePropertyが付与されています。
最初の例の結果に対してそれぞれGet-Memberしてやると、

PS C:\> $Date1 | Get-Member -View Extended


   TypeName: System.DateTime

Name     MemberType     Definition
----     ----------     ----------
DateTime ScriptProperty System.Object DateTime {get=if ((& { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq  "...


PS C:\> $Date2 | Get-Member -View Extended


   TypeName: System.DateTime

Name        MemberType     Definition
----        ----------     ----------
DisplayHint NoteProperty   DisplayHintType DisplayHint=DateTime
DateTime    ScriptProperty System.Object DateTime {get=if ((& { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq...

Get-Dateで得た方の値にのみDisplayHintプロパティが付いている事がわかります。

このDisplayHintプロパティはコンソールにSystem.Datetime型の値を表示する際のフォーマットをつかさどり、既定値はDatetimeで日付と時刻がコンソールに表示されます。

このプロパティを変えるとコンソールに表示される内容が変化します。

# DisplayHintプロパティの既定値は DateTime
PS C:\> $Date2.DisplayHint
DateTime

# 既定ではコンソールには日付と時刻が表示される
PS C:\> $Date2

20163220:00:00

# DisplayHintの値を変えるとコンソールに表示される内容も変わる
PS C:\> $Date2.DisplayHint = [Microsoft.PowerShell.Commands.DisplayHintType]::Date
PS C:\> $Date2

2016322

最後に

とりとめのない感じになりましたがとりあえずこんな感じです。
Get-Dateを使う際はちょっとだけ注意してみてください。

*1:作った本人もこんなものをいちいち使う気はないですw