PowerShellとファイルの日付についてちょっとしたこと

PowerShellを使うとファイルやフォルダーの日付を簡単に変更できますが、

(Get-Item c.txt).LastWriteTime = '2012/3/4 5:6:7'

ちゃんと小数点以下まで指定しないと、日付を変更したことがばれます。

Get-Item *.txt | Select-Object Name,{$_.LastWriteTime.ToString('yyyy/MM/dd HH:mm:ss.fffffff')}

f:id:itasuke:20171214160136p:plain

少し前に、証拠データの日付を書き換えた検察官がいて問題になったことがあります。遠隔操作ウイルス事件もそうですが、日本ではあまりフォレンジックとかやらないんでしょうか。

どれくらい昔の日付に設定できる?

ウィキペディア調べるとNTFSはファイルの日付を1601年1月1日 0時0分0秒 (UTC) から100ナノ秒単位で保持しているようです。論理的にはこの日付に設定できるはずですが、実際にやってみるとできません。

(Get-Item c.txt).LastWriteTimeUtc = '1601/1/1'

これはWindowsAPIの仕様で、SetFileTime関数に基点となる日付からきっかり 0 秒後が指定されるとスルーされてしまうのです *1。そういうわけで、0時0分0.0000001秒が、設定できる最も古い日付ということになります。

(Get-Item c.txt).LastWriteTimeUtc = '1601/1/1 0:0:0.0000001'

f:id:itasuke:20171214160127p:plain

ところで、C言語のFILETIME構造体と.NETのDateTime構造体は少し仕様が違います。分解能は同じですが、後者は基点が西暦1年になっています *2。なので、PowerShell側で以下のような日付を作ることは可能ですが、

$dt = [datetime] '0001/1/1'

この日付をNTFSのファイルに指定するとWin32エラーになるのはそういう理由です。

(Get-Item c.txt).LastWriteTimeUtc = $dt
# "LastWriteTimeUtc" の設定中に例外が発生しました:
# "有効な Win32 FileTime ではありません。"

以上です。

*1:そもそもPowerShellはSetFileTime関数を使っているのかという疑問がありますが、少なくとも Process Monitor で見る限り呼び出しているようです。また、FILETIME構造体も日付の仕様はNTFSと同じです。

*2:http://referencesource.microsoft.com/#mscorlib/system/datetime.cs,131 コメントより