【.NET】VB.NETのDateDiff()が正常に動作しないことへの対処法
VB.NETでは、2つの日付(Date)間の期間を数値で取得するDateDiff()メソッドが用意されています。
DateDiff()は、VB6やVBAにもありましたが、VB.NET(2002~2008)の場合は、おかしな挙動を取るところがあり、注意が必要です。
以下のコードは、DateDiff()を使用して二つの日付の間が何年であるかを表示するプログラムです。
'VB.NETのサンプル Dim a As Date = #1/1/1985# '1985年1月1日(昭和60年1月1日) Dim b As Date = #1/1/2000# '2000年1月1日(平成12年1月1日) Dim diffYear As Long diffYear = DateDiff(DateInterval.Year, a, b) MsgBox("差は、" & diffYear.ToString() & " 年です。")
OSの日付表示設定が西暦の場合
実行結果です。
2000年 – 1985年 = 15年は正しい結果です。正常に動作しています。
問題は、コントロールパネルの地域と言語のオプションで日付の形式を和暦にしている場合に発生します。
OSの日付表示設定が和暦の場合
【コントロール パネル】-【地域と言語のオプション】の【地域オプション】タブ内で、【カスタマイズ】ボタンをクリックします。
【日付】タブを開き、【カレンダーの種類】を和暦に設定し、【OK】ボタンをクリックしてダイアログを閉じます。
この状態で、先ほどのプログラムを実行すると、以下のようになります。
-48年? DateDiff()で正常な値を取得できません。
どうやら、2000年と1985年の間隔を求めるときに 平成12年 – 昭和60年 = -48年 というような計算をしてしまっているようなのです。
いくらOSの日付の設定が和暦であろうとも、この計算は仕様としては不自然ですし、通常の処理として上記のような結果を求める意味がありません。
不具合だと思われますが、現段階ではOSの設定を和暦にしている人用に対処用のコードを書く必要があります。
とりあえずの対処法
DateDiff()の不自然な結果は、現在のスレッドのカルチャ(Threading.Thread.CurrentThread.CurrentCulture)に依存しています。
Threading.Thread.CurrentThread.CurrentCultureに設定されている値は、ユーザーのOSの設定を引き継いでいるため、処理によってはユーザーの好みにあわせた表示形式を使用でき便利な面もありますが、DateDiff()の場合は明らかに挙動がおかしいです。
カルチャに依存しないCaltureInfoを設定してから、DateDiff()を実行することで対処します。
'VB.NETのサンプル '元のCaltureInfoを覚えておく Dim originalCalture As Globalization.CultureInfo = _ Globalization.CultureInfo.CurrentCulture '現在のスレッドのカルチャをカルチャに依存しないCultureInfoに差し替える Threading.Thread.CurrentThread.CurrentCulture = _ System.Globalization.CultureInfo.InvariantCulture Dim a As Date = #1/1/1985# '1985年1月1日(昭和60年1月1日) Dim b As Date = #1/1/2000# '2000年1月1日(平成12年1月1日) Dim diffYear As Long diffYear = DateDiff(DateInterval.Year, a, b) MsgBox("差は、" & diffYear.ToString() & " 年です。") '他に影響が出ないように元のCaltureInfoに戻す Threading.Thread.CurrentThread.CurrentCulture = originalCalture
それにしても、特に年号なんてない文化圏のユーザーは、こんなことは意識できないでしょうから、期待しない動作になりやすく、ちょっと困りものですね…
追記
この問題は、Microsoftにフィードバックを投稿済みです。次のページをご参照ください。
http://connect.microsoft.com/VisualStudioJapan/feedback/ViewFeedback.aspx?FeedbackID=522852
関連記事
TrackBack URL :
Comments (2)
DateDiffのヘルプに以下の記述があります。date2.Year – date1.Year とすれば簡単に西暦での年の差が求められるようです。
複数の時代 (年号) がある日本語などのカルチャの場合、差が 2 つ以上の時代 (年号) にまたがっている場合は DateDiff メソッドは差を年数で返しません。 代わりに、次の例に示すように、Year プロパティが返した値の違いを計算できます。date2.Year – date1.Year
コメント by nori — 2014/4/24 木曜日 @ 2:46:26
nori様 情報ありがとうございます。
ヘルプの記述は私が出したフィードバックを元に追記されたものと思いますが、
Connectのページがすでにリンク切れになっているようです。
当時のMicrosoftの設計者の返答としては、
「DateDiffが返すのは単なる年の差ではなく、
2つのDateの間にいくつの年の変化があるのかという概念であるため、
DateDiffで複数のEraをまたぐ場合の挙動については、どういった計算が正しいとは
一概に言えない。
(たとえば、12月31日と1月1日の間は1だが、
昭和から平成に変わった日は、DateDiffとしては0 と考えるのか1と考えるのか判断が分かれる)
DateDiffの挙動は修正せずに代わりにDateDiffのヘルプにカルチャによって挙動が変わることの注意書きを載せる。」
というものでした。
これについては日本人プログラマならまだしも、
西暦しか使用しない文化圏のプログラマなら一生気づかないでしょうし、
そのままにされては困るという意味で納得のいかないところもあるのですが、
逆に日本以外のEraの概念も考慮すると、どういったものが最善なのか判断が難しく、
.NET Frameworkの修正はなしという結論で終わっています。
それにしたってマイナスになるDateDiffの挙動はだと思うのですが、
それは置いておくとして、
仰る通り、差を求める場合はYearの差分を求めるのが正解と思われます。
コメント by Nakai — 2014/4/24 木曜日 @ 3:50:07