日付を比較するには?[C#/VB].NET TIPS

DateTime構造体/文字列を使って表されている「日付」を比較する方法を解説。また、DateTimeOffset構造体を使っているときの注意点も取り上げる。

» 2018年03月28日 05時00分 公開
[山本康彦BluewaterSoft/Microsoft MVP for Windows Development]
「.NET TIPS」のインデックス

連載「.NET TIPS」

 日付を比較するにはどうしたらよいだろうか? 本稿では、2つの日付が同じ日なのか、違うならどちらが新しい(または、古い)日付なのかを判定する方法を解説する。

POINT 日付を比較する方法

日付を比較する方法まとめ 日付を比較する方法まとめ


 特定のトピックをすぐに知りたいという方は以下のリンクを活用してほしい。

 なお、いずれの方法も.NET Frameworkバージョン2.0から利用できるが、本稿に掲載したサンプルコードをそのまま試すにはVisual Studio 2015以降が必要である。また、サンプルコードはコンソールアプリの一部であり、コードの冒頭に以下の宣言が必要となる。

using System;
using static System.Console;

Imports System.Console

本稿のサンプルコードに必要な宣言(上:C#、下:VB)

DateTime構造体で日付を表現している場合

 日付や時刻をDateTime構造体(System名前空間)で表現しているのなら、Dateプロパティを比較すればよい(次のコード)。.NET Frameworkに日付のみを表す型はないが、その代わりとしてDateプロパティは時刻を切り捨ててその日の午前0時にしたDateTime構造体を返してくれる。

var datetime1 = new DateTime(2018, 3, 1, 3, 0, 0);
var datetime2 = new DateTime(2018, 3, 1, 15, 0, 0); // datetime1と同じ日
var datetime3 = new DateTime(2018, 3, 3, 0, 0, 0); // datetime1の2日後

// datetime1とdatetime2は同じ日か?
bool compare1and2 = (datetime1.Date == datetime2.Date);
WriteLine($"(datetime1.Date == datetime2.Date) ⇒ {compare1and2}");
// 出力:(datetime1.Date == datetime2.Date) ⇒ True

// datetime2はdatetime3より前の日か?
bool compare2and3 = (datetime2.Date < datetime3.Date);
WriteLine($"(datetime2.Date < datetime3.Date) ⇒ {compare2and3}");
// 出力:(datetime2.Date < datetime3.Date) ⇒ True

Dim datetime1 = New DateTime(2018, 3, 1, 3, 0, 0)
Dim datetime2 = New DateTime(2018, 3, 1, 15, 0, 0) ' datetime1と同じ日
Dim datetime3 = New DateTime(2018, 3, 3, 0, 0, 0) ' datetime1の2日後

' datetime1とdatetime2は同じ日か?
Dim compare1and2 As Boolean = (datetime1.Date = datetime2.Date)
WriteLine($"(datetime1.Date = datetime2.Date) ⇒ {compare1and2}")
' 出力:(datetime1.Date == datetime2.Date) ⇒ True

' datetime2はdatetime3より前の日か?
Dim compare2and3 As Boolean = (datetime2.Date < datetime3.Date)
WriteLine($"(datetime2.Date < datetime3.Date) ⇒ {compare2and3}")
' 出力:(datetime2.Date < datetime3.Date) ⇒ True

DateTimeの日付を比較する例(上:C#、下:VB)
DateTime構造体のDateプロパティを等値演算子(「==」演算子(C#)/「=」演算子(VB)など)や関係演算子(「<」演算子/「>」演算子など)を使って比較すれば、日付の比較になる。

 なお、DateプロパティではなくDateTime構造体のインスタンス自体を比較してしまうと、時刻も含めた比較になってしまって、「同じ日かどうか」という意味では判定を間違えるので注意しよう(次のコード)。

var datetime1 = new DateTime(2018, 3, 1, 3, 0, 0);
var datetime2 = new DateTime(2018, 3, 1, 15, 0, 0); // datetime1と同じ日

// 同じ日なのに時刻が違うと別の日だと間違えてしまう
WriteLine($"(datetime1 == datetime2) ⇒ {datetime1 == datetime2}");
// 出力:(datetime1 == datetime2) ⇒ False

Dim datetime1 = New DateTime(2018, 3, 1, 3, 0, 0)
Dim datetime2 = New DateTime(2018, 3, 1, 15, 0, 0) ' datetime1と同じ日

' 同じ日なのに時刻が違うと別の日だと間違えてしまう
WriteLine($"(datetime1 = datetime2) ⇒ {datetime1 = datetime2}")
' 出力:(datetime1 = datetime2) ⇒ False

DateTimeの日付を比較する間違った例(上:C#、下:VB)
DateプロパティではなくDateTime構造体のインスタンス自体を比較すると、同じ日であっても時刻が違うと同日かどうかの判定を間違ってしまう。

DateTime構造体で前後関係を一度に求めたい場合

 同じ日か違う日かを判定するだけなら前項のように単純に比較すればよいのだが、3通りの判定(同じ/前/後)をしたいこともある。そのようなときは、DateTime構造体のDateプロパティをCompareToメソッドで比較する(次のコード)。

var datetime1 = new DateTime(2018, 3, 1, 3, 0, 0);
var datetime2 = new DateTime(2018, 3, 1, 15, 0, 0); // datetime1と同じ日
var datetime3 = new DateTime(2018, 3, 3, 0, 0, 0); // datetime1の2日後

// datetime1とdatetime2の前後関係は?(0:同じ日)
int compare1and2 = datetime1.Date.CompareTo(datetime2.Date);
WriteLine($"datetime1.Date.CompareTo(datetime2.Date) ⇒ {compare1and2}");
// 出力:datetime1.Date.CompareTo(datetime2.Date) ⇒ 0

// datetime2とdatetime3の前後関係は?(-1:datetime2が小さい=前)
int compare2and3 = datetime2.Date.CompareTo(datetime3.Date);
WriteLine($"datetime2.Date.CompareTo(datetime3.Date) ⇒ {compare2and3}");
// 出力:datetime2.Date.CompareTo(datetime3.Date) ⇒ -1

// datetime3とdatetime1の前後関係は?(1:datetime3が大きい=後)
int compare3and1 = datetime3.Date.CompareTo(datetime1.Date);
WriteLine($"datetime3.Date.CompareTo(datetime1.Date) ⇒ {compare3and1}");
// 出力:datetime3.Date.CompareTo(datetime1.Date) ⇒ 1

Dim datetime1 = New DateTime(2018, 3, 1, 3, 0, 0)
Dim datetime2 = New DateTime(2018, 3, 1, 15, 0, 0) ' datetime1と同じ日
Dim datetime3 = New DateTime(2018, 3, 3, 0, 0, 0) ' datetime1の2日後

' datetime1とdatetime2の前後関係は?(0:同じ日)
Dim compare1and2 As Integer = datetime1.Date.CompareTo(datetime2.Date)
WriteLine($"datetime1.Date.CompareTo(datetime2.Date) ⇒ {compare1and2}")
' 出力:datetime1.Date.CompareTo(datetime2.Date) ⇒ 0

' datetime2とdatetime3の前後関係は?(-1:datetime2が小さい=前)
Dim compare2and3 As Integer = datetime2.Date.CompareTo(datetime3.Date)
WriteLine($"datetime2.Date.CompareTo(datetime3.Date) ⇒ {compare2and3}")
' 出力:datetime2.Date.CompareTo(datetime3.Date) ⇒ -1

' datetime3とdatetime1の前後関係は?(1:datetime3が大きい=後)
Dim compare3and1 As Integer = datetime3.Date.CompareTo(datetime1.Date)
WriteLine($"datetime3.Date.CompareTo(datetime1.Date) ⇒ {compare3and1}")
' 出力:datetime3.Date.CompareTo(datetime1.Date) ⇒ 1

DateTimeの日付をCompareToメソッドで比較する例(上:C#、下:VB)
CompareToメソッドは大小関係を0、1、-1で返してくる。その正負の符号は、次に示す日数差の符号と同じになる。

 あるいは、2つの日付の間に何日の差があるかを求めてもよい(次のコード)。

var datetime1 = new DateTime(2018, 3, 1, 3, 0, 0);
var datetime2 = new DateTime(2018, 3, 1, 15, 0, 0); // datetime1と同じ日
var datetime3 = new DateTime(2018, 3, 3, 0, 0, 0); // datetime1の2日後

// datetime1とdatetime2の日数差は?(0:同じ日)
double days1and2 = (datetime1.Date - datetime2.Date).TotalDays;
WriteLine($"(datetime1.Date - datetime2.Date).TotalDays ⇒ {days1and2}");
// 出力:(datetime1.Date - datetime2.Date).TotalDays ⇒ 0

// datetime2とdatetime3の日数差は?(-2:datetime2が2日前)
double days2and3 = (datetime2.Date - datetime3.Date).TotalDays;
WriteLine($"(datetime2.Date - datetime3.Date).TotalDays ⇒ {days2and3}");
// 出力:(datetime2.Date - datetime3.Date).TotalDays ⇒ -2

// datetime3とdatetime1の日数差は?(2:datetime3が2日後)
double days3and1 = (datetime3.Date - datetime1.Date).TotalDays;
WriteLine($"(datetime3.Date - datetime1.Date).TotalDays ⇒ {days3and1}");
// 出力:(datetime3.Date - datetime1.Date).TotalDays ⇒ 2

Dim datetime1 = New DateTime(2018, 3, 1, 3, 0, 0)
Dim datetime2 = New DateTime(2018, 3, 1, 15, 0, 0) ' datetime1と同じ日
Dim datetime3 = New DateTime(2018, 3, 3, 0, 0, 0) ' datetime1の2日後

' datetime1とdatetime2の日数差は?(0:同じ日)
Dim days1and2 As Double = (datetime1.Date - datetime2.Date).TotalDays
WriteLine($"(datetime1.Date - datetime2.Date).TotalDays ⇒ {days1and2}")
' 出力:(datetime1.Date - datetime2.Date).TotalDays ⇒ 0

' datetime2とdatetime3の日数差は?(-2:datetime2が2日前)
Dim days2and3 As Double = (datetime2.Date - datetime3.Date).TotalDays
WriteLine($"(datetime2.Date - datetime3.Date).TotalDays ⇒ {days2and3}")
' 出力:(datetime2.Date - datetime3.Date).TotalDays ⇒ -2

' datetime3とdatetime1の日数差は?(2:datetime3が2日後)
Dim days3and1 As Double = (datetime3.Date - datetime1.Date).TotalDays
WriteLine($"(datetime3.Date - datetime1.Date).TotalDays ⇒ {days3and1}")
' 出力:(datetime3.Date - datetime1.Date).TotalDays ⇒ 2

DateTimeの日付を日数差で比較する例(上:C#、下:VB)
DateTime型の引き算をすると、その結果はTimeSpan構造体(System名前空間)になる。そのTimeSpan構造体のTotalDaysプロパティで、それが何日間のことなのかが分かる。

日数差の正負の符号は、前掲のCompareToメソッドの正負と同じだ。

なお、引き算の代わりに、DateTime構造体のSubtractメソッドを使ってもよい。


文字列で日付を表現している場合

 日付を「yyyyMMdd」というフォーマットの8桁整数の文字列として保持しているシステムもある。その場合は、StringクラスのCompareToメソッドを使って比較できる(次のコード)。

string date1 = "20180228";
string date2 = "20180228"; // date1と同じ日
string date3 = "20180302"; // date1の2日後

// date1とdate2の前後関係は?(0:同じ日)
WriteLine($"date1.CompareTo(date2) ⇒ {date1.CompareTo(date2)}");
// 出力:date1.CompareTo(date2) ⇒ 0

// date2とdate3の前後関係は?(-1:date2が小さい=前)
WriteLine($"date2.CompareTo(date3) ⇒ {date2.CompareTo(date3)}");
// 出力:date2.CompareTo(date3) ⇒ -1

// date3とdate1の前後関係は?(1:date3が大きい=後)
WriteLine($"date3.CompareTo(date1) ⇒ {date3.CompareTo(date1)}");
// 出力:date3.CompareTo(date1) ⇒ 1

Dim date1 As String = "20180228"
Dim date2 As String = "20180228" ' date1と同じ日
Dim date3 As String = "20180302" ' date1の2日後

' date1とdate2の前後関係は?(0:同じ日)
WriteLine($"date1.CompareTo(date2) ⇒ {date1.CompareTo(date2)}")
' 出力:date1.CompareTo(date2) ⇒ 0

' date2とdate3の前後関係は?(-1:date2が小さい=前)
WriteLine($"date2.CompareTo(date3) ⇒ {date2.CompareTo(date3)}")
' 出力:date2.CompareTo(date3) ⇒ -1

' date3とdate1の前後関係は?(1:date3が大きい=後)
WriteLine($"date3.CompareTo(date1) ⇒ {date3.CompareTo(date1)}")
' 出力:date3.CompareTo(date1) ⇒ 1

8桁数字の日付をCompareToメソッドで比較する例(上:C#、下:VB)
同じ日かどうかの判定には、等値演算子(「==」演算子(C#)/「=」演算子(VB)など)も使える。前後関係の判定には、string型では関係演算子(「<」演算子/「>」演算子など)が使えないので、CompareToメソッドを使うしかない。

 また、日数差を求めるには、DateTime型に変換してから計算する(次のコード)。この例の「yyyy/M/d」フォーマットのようにCompareToメソッドで比較しても正しく前後関係を求められない日付文字列の場合は、DateTime型に変換して比較するしかない。

 なお、日付文字列をDateTime型に変換する方法については、次の.NET TIPSをご覧いただきたい。

var date1 = "2018/2/28";
var date2 = "2018/2/28"; // date1と同じ日
var date3 = "2018/3/2"; // date1の2日後

// DateTime型に変換する
DateTime dt1 = DateTime.ParseExact(date1, "yyyy/M/d", null);
DateTime dt2 = DateTime.ParseExact(date2, "yyyy/M/d", null);
DateTime dt3 = DateTime.ParseExact(date3, "yyyy/M/d", null);

// date1とdate2の日数差は?(0:同じ日)
WriteLine($"(dt1 - dt2).TotalDays ⇒ {(dt1 - dt2).TotalDays}");
// 出力:(dt1 - dt2).TotalDays ⇒ 0

// date2とdate3の日数差は?(-2:date2が2日前)
WriteLine($"(dt2 - dt3).TotalDays ⇒ {(dt2 - dt3).TotalDays}");
// 出力:(dt2 - dt3).TotalDays ⇒ -2

// date3とdate1の日数差は?(2:date3が2日後)
WriteLine($"(dt3 - dt1).TotalDays ⇒ {(dt3 - dt1).TotalDays}");
// 出力:(dt3 - dt1).TotalDays ⇒ 2

Dim date1 As String = "2018/2/28"
Dim date2 As String = "2018/2/28" ' date1と同じ日
Dim date3 As String = "2018/3/2" ' date1の2日後

' DateTime型に変換する
Dim dt1 As DateTime = DateTime.ParseExact(date1, "yyyy/M/d", Nothing)
Dim dt2 As DateTime = DateTime.ParseExact(date2, "yyyy/M/d", Nothing)
Dim dt3 As DateTime = DateTime.ParseExact(date3, "yyyy/M/d", Nothing)

' date1とdate2の日数差は?(0:同じ日)
WriteLine($"(dt1 - dt2).TotalDays ⇒ {(dt1 - dt2).TotalDays}")
' 出力:(dt1 - dt2).TotalDays ⇒ 0

' date2とdate3の日数差は?(-2:date2が2日前)
WriteLine($"(dt2 - dt3).TotalDays ⇒ {(dt2 - dt3).TotalDays}")
' 出力:(dt2 - dt3).TotalDays ⇒ -2

' date3とdate1の日数差は?(2:date3が2日後)
WriteLine($"(dt3 - dt1).TotalDays ⇒ {(dt3 - dt1).TotalDays}")
' 出力:(dt3 - dt1).TotalDays ⇒ 2

文字列の日付から日数差を求める例(上:C#、下:VB)

DateTimeOffset構造体で日付を表現している場合

 タイムゾーンを考慮したシステムでは、日時をDateTimeOffset構造体で表現しているだろう。その場合も、前述したようなDateプロパティを使った比較をすればよい。ただし、比較する前に特定のタイムゾーンのDateTimeOffset構造体にそろえておく必要がある(次のコード)。

DateTimeOffset dto1
  = new DateTimeOffset(new DateTime(2018, 3, 1, 3, 0, 0), TimeSpan.FromHours(9.0));
DateTimeOffset dto2
  = new DateTimeOffset(new DateTime(2018, 2, 28, 21, 0, 0), TimeSpan.FromHours(0.0));
DateTimeOffset dto3
  = new DateTimeOffset(new DateTime(2018, 3, 2, 0, 0, 0), TimeSpan.FromHours(9.0));

// 日本標準時(JST)にそろえてから日付を求める
TimeSpan offsetJST = TimeSpan.FromHours(9.0);
DateTime date1 = dto1.ToOffset(offsetJST).Date;
DateTime date2 = dto2.ToOffset(offsetJST).Date;
DateTime date3 = dto3.ToOffset(offsetJST).Date;

WriteLine($"dto1の日付(JST) = {date1:yyyy/MM/dd}");
// 出力:dto1の日付(JST) = 2018/03/01
WriteLine($"dto2の日付(JST) = {date2:yyyy/MM/dd}");
// 出力:dto2の日付(JST) = 2018/03/01
WriteLine($"dto3の日付(JST) = {date3:yyyy/MM/dd}");
// 出力:dto3の日付(JST) = 2018/03/02

// モルディブ標準時(MVT)にそろえてから日付を求める
TimeSpan offsetMVT = TimeSpan.FromHours(5.0);
date1 = dto1.ToOffset(offsetMVT).Date;
date2 = dto2.ToOffset(offsetMVT).Date;
date3 = dto3.ToOffset(offsetMVT).Date;

WriteLine($"dto1の日付(MVT) = {date1:yyyy/MM/dd}");
// 出力:dto1の日付(MVT) = 2018/02/28
WriteLine($"dto2の日付(MVT) = {date2:yyyy/MM/dd}");
// 出力:dto2の日付(MVT) = 2018/03/01
WriteLine($"dto3の日付(MVT) = {date3:yyyy/MM/dd}");
// 出力:dto3の日付(MVT) = 2018/03/01

Dim dto1 As DateTimeOffset _
  = New DateTimeOffset(New DateTime(2018, 3, 1, 3, 0, 0), TimeSpan.FromHours(9.0))
Dim dto2 As DateTimeOffset _
  = New DateTimeOffset(New DateTime(2018, 2, 28, 21, 0, 0), TimeSpan.FromHours(0.0))
Dim dto3 As DateTimeOffset _
  = New DateTimeOffset(New DateTime(2018, 3, 2, 0, 0, 0), TimeSpan.FromHours(9.0))

' 日本標準時(JST)にそろえてから日付を求める
Dim offsetJST As TimeSpan = TimeSpan.FromHours(9.0)
Dim date1 As DateTime = dto1.ToOffset(offsetJST).Date
Dim date2 As DateTime = dto2.ToOffset(offsetJST).Date
Dim date3 As DateTime = dto3.ToOffset(offsetJST).Date

WriteLine($"dto1の日付(JST) = {date1:yyyy/MM/dd}")
' 出力:dto1の日付(JST) = 2018/3/1
WriteLine($"dto2の日付(JST) = {date2:yyyy/MM/dd}")
' 出力:dto2の日付(JST) = 2018/3/1
WriteLine($"dto3の日付(JST) = {date3:yyyy/MM/dd}")
' 出力:dto3の日付(JST) = 2018/3/2

' モルディブ標準時(MVT)にそろえてから日付を求める
Dim offsetMVT As TimeSpan = TimeSpan.FromHours(5.0)
date1 = dto1.ToOffset(offsetMVT).Date
date2 = dto2.ToOffset(offsetMVT).Date
date3 = dto3.ToOffset(offsetMVT).Date

WriteLine($"dto1の日付(MVT) = {date1:yyyy/MM/dd}")
' 出力:dto1の日付(MVT) = 2018/2/28
WriteLine($"dto2の日付(MVT) = {date2:yyyy/MM/dd}")
' 出力:dto2の日付(MVT) = 2018/3/1
WriteLine($"dto3の日付(MVT) = {date3:yyyy/MM/dd}")
' 出力:dto3の日付(MVT) = 2018/3/1

DateTimeOffsetから日付を求める例(上:C#、下:VB)
DateTimeOffset構造体のToOffsetメソッドで、指定したタイムゾーンのDateTimeOffset構造体に変換できる。この例では、JST(日本)とMVT(モルディブ)の2つのタイムゾーンに変換しているが、前者ではdto1とdto2が同じ日付になるのに対して、後者ではdto2とdto3が同じ日付になっている。

まとめ

 日付がDateTime構造体/DateTimeOffset構造体で表現されているときは、Dateプロパティで日付を取り出して、等値演算子/関係演算子かCompareToメソッドで比較する。ただし、DateTimeOffset構造体では先にタイムゾーンをそろそろえておく。

 日付を文字列で表現している場合で、文字列のまま比較できるフォーマットのときはCompareToメソッドで比較する。直接比較できないフォーマットのときは、DateTime構造体に変換してから比較する。

「.NET TIPS」のインデックス

.NET TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。