File.DateLastModifiedで 嵌まった事。
以下のようなソースコードを書いていて、原因不明のエラーが出て嵌まった。という話。
// このソースコードは正常に動作しない var g_objFileSys = new ActiveXObject("Scripting.FileSystemObject"); function copy_file(src_fname, dst_fname) { try { if ( !g_objFileSys.FileExists(src_fname) ) { WScript.Echo( src_fname + "が存在しません" ); return null; } // フォルダ名指定の場合、ファイル名付加 if ( g_objFileSys.FolderExists(dst_fname) ) dst_fname = g_objFileSys.BuildPath( dst_fname, g_objFileSys.GetFileName(src_fname) ); if ( g_objFileSys.FileExists(dst_fname) ) { var obj_src = g_objFileSys.GetFile(src_fname); var obj_dst = g_objFileSys.GetFile(dst_fname); var date_1 = obj_src.DateLastModified; var date_2 = obj_dst.DateLastModified; var time_diff = date_1.getTime() - date_2.getTime(); //←ここでエラー if ( obj_src.Size == obj_dst.Size && Math.abs(time_diff) <= 2000 ) return dst_fname; } g_objFileSys.CopyFile( src_fname, dst_fname, true ); return dst_fname; } catch(e) { WScript.Echo( e + "(" + (e.number&0xFFFF) + ")" + e.description ); return null; } } copy_file(WScript.Arguments(0), WScript.Arguments(1) );
要するに、ファイルコピーする際にサイズが一緒で、ファイル更新日時が2秒以内ならばコピーしない。ということがしたいわけですが、何故かDate.getTime()の所で「[object Error](5007)オブジェクトを指定してください。」となってしまう。
「もしかして、ヘルプが間違ってて、getTime()というメソッドがないのか?」と思って
var date_1 = new Date(); WScript.Echo(date_1.getTime());
としてみると、エラーにならない←当たり前だ。
違いといえば、デバッガで変数をウォッチすると、エラーになる方は"2011/12/02 13:54:00"。ならない方は"Mon Dec 12 21:47:08 UTC+0900 2011"となんかフォーマットが違う。
「DateLastModifiedプロパティはDateオブジェクトじゃないのかも?」と思って、以下のように修正してみた。
//var date_1 = obj_src.DateLastModified; //var date_2 = obj_dst.DateLastModified; //var time_diff = date_1.getTime() - date_2.getTime(); // ここでエラー var date_1 = Date.parse(obj_src.DateLastModified); var date_2 = Date.parse(obj_dst.DateLastModified); var time_diff = date_1 - date_2; // OK
これだと動く。time_diffに入る値もそれっぽい。
どうもDateLastModifiedは日時を表す特別なオブジェクトみたい*1。DateLastModifiedをDateオブジェクトとして扱いたい場合は
//var date_1 = obj_src.DateLastModified; //var date_2 = obj_dst.DateLastModified; var date_1 = new Date(obj_src.DateLastModified); var date_2 = new Date(obj_dst.DateLastModified); var time_diff = date_1.getTime() - date_2.getTime(); //OK
このようにするといいみたい。
MSDNライブラリ 2001年10月リリースのDateオブジェクトのコンストラクタの説明によると
dateObj = new Date()
dateObj = new Date(dateVal)
dateObj = new Date(year, month, date[, hours[, minutes[, seconds[,ms]]]])
引数
dateObj
必ず指定します。Date オブジェクトを代入する変数名を指定します。
dateVal
必ず指定します。数値で指定する場合は、世界協定時刻 (UTC)での指定する日付と 1970 年 1 月 1 日 0 時 0 分 0 秒との間をミリ秒単位の数値で指定します。文字列で指定する場合は、指定した文字列が parse メソッドでの規則に準じて解析されます。引数 dateVal には、ActiveXR オブジェクトから取得した VT_DATE 値を指定することもできます。
(以下略)
ということで、DateオブジェクトのコンストラクタのdateValとして扱えるということは、DateLastModifiedプロパティは 1970/1/1 0:00:00からのミリ秒単位の数値として振る舞えるみたいなので、、最初のサンプルコードで
//var time_diff = date_1.getTime() - date_2.getTime(); // ここでエラー var time_diff = date_1 - date_2; // OK
と書いておけば嵌まることはなかった。という悲しい事実。
スクリプト言語って型が動的に決定/変換されるから、スクリプトエンジンを信じた方がいいんだろうけど、どうしてもコンパイル時に型が確定している言語に慣れて身からすると、ついついそれを指示したくなってしまう。例えば、
var a,b; a = "25"; b = "5"; WScript.Echo( a - b );
こんなコードは気持ち悪くって吐きそうだから、変数a,bをどうしても文字列として扱わないといけなくて、数値の引き算もしたいなら、引き算の段階で数値型に確実に戻そうと
var a,b; a = "25"; b = "5"; WScript.Echo( a.valueOf() - b.valueOf() );
としてしまいがち。頭を切り換えていった方がいいのかな?
*1:Visual Studio 2010では型は"Date"と表示される。JScriptのDateオブジェクトの型は"Object"となってる。これ以上はどう調べたら良いのか分からない