DateOnly および TimeOnly 構造体の使用方法

DateOnly および TimeOnly 構造体は .NET 6 で導入されたもので、それぞれ特定の日付または時刻を表します。 .NET 6 より前では、また .NET Framework では常に、開発者は DateTime 型 (またはその他の代替手段) を使用して、次のいずれかを表しました。

  • 日付と時刻全体。
  • 時刻を無視した日付。
  • 日付を無視した時刻。

DateOnlyTimeOnly は、DateTime 型の特定の部分を表す型です。

重要

DateOnly および TimeOnly 型は、.NET Framework では使用できません。

DateOnly 構造体

DateOnly 構造体は、時間のない特定の日付を表します。 時間コンポーネントがないため、その日の始まりからその日の終わりまでの日付を表します。 この構造体は、生年月日、記念日、ビジネス関連の日付など、特定の日付を格納するのに最適です。

時間コンポーネントを無視して DateTime を使用することもできますが、DateTime よりも DateOnly を使用する利点がいくつかあります。

  • DateTime 構造体は、タイム ゾーンによってオフセットされると、前または次の日にロールインする可能性があります。 DateOnly はタイム ゾーンでオフセットできず、常に設定された日付を表します。

  • DateTime 構造体のシリアル化には時間コンポーネントが含まれます。これは、データの意図をあいまいにする可能性があります。 また、DateOnly は、より少ないデータをシリアル化します。

  • コードが SQL Server などのデータベースとやり取りする場合、一般に日付全体が date データ型として格納され、時刻は含まれません。 DateOnly は、データベースの種類によりよく一致します。

DateOnly には、DateTime と同様に 0001-01-01 から 9999-12-31 までの範囲があります。 DateOnly コンストラクターで特定のカレンダーを指定できます。 ただし、DateOnly オブジェクトは、作成に使用されたカレンダーに関係なく、常に先発グレゴリオ暦の日付を表します。 たとえば、ヘブライ暦から日付を作成できますが、日付はグレゴリオ暦に変換されます。

var hebrewCalendar = new System.Globalization.HebrewCalendar();
var theDate = new DateOnly(5776, 2, 8, hebrewCalendar); // 8 Cheshvan 5776

Console.WriteLine(theDate);

/* This example produces the following output:
 *
 * 10/21/2015
*/
Dim hebrewCalendar = New System.Globalization.HebrewCalendar()
Dim theDate = New DateOnly(5776, 2, 8, hebrewCalendar) ' 8 Cheshvan 5776

Console.WriteLine(theDate)

' This example produces the following output
'
' 10/21/2015

DateOnly の例

次の例を使用して、DateOnly について学習します。

DateTime を DateOnly に変換する

次のコードに示すように、DateOnly.FromDateTime 静的メソッドを使用して DateTime 型から DateOnly 型を作成します。

var today = DateOnly.FromDateTime(DateTime.Now);
Console.WriteLine($"Today is {today}");

/* This example produces output similar to the following:
 * 
 * Today is 12/28/2022
*/
Dim today = DateOnly.FromDateTime(DateTime.Now)
Console.WriteLine($"Today is {today}")

' This example produces output similar to the following
' 
' Today is 12/28/2022

日、月、年を加算または減算する

DateOnly 構造体の調整に使用するメソッドには、AddDaysAddMonthsAddYears の 3 つがあります。 各メソッドは整数パラメーターを受け取り、その測定値だけ日付を増やします。 負の数値を指定すると、日付はその測定値だけ減少します。 構造体は不変であるため、メソッドは DateOnly の新しいインスタンスを返します。

var theDate = new DateOnly(2015, 10, 21);

var nextDay = theDate.AddDays(1);
var previousDay = theDate.AddDays(-1);
var decadeLater = theDate.AddYears(10);
var lastMonth = theDate.AddMonths(-1);

Console.WriteLine($"Date: {theDate}");
Console.WriteLine($" Next day: {nextDay}");
Console.WriteLine($" Previous day: {previousDay}");
Console.WriteLine($" Decade later: {decadeLater}");
Console.WriteLine($" Last month: {lastMonth}");

/* This example produces the following output:
 * 
 * Date: 10/21/2015
 *  Next day: 10/22/2015
 *  Previous day: 10/20/2015
 *  Decade later: 10/21/2025
 *  Last month: 9/21/2015
*/
Dim theDate = New DateOnly(2015, 10, 21)

Dim nextDay = theDate.AddDays(1)
Dim previousDay = theDate.AddDays(-1)
Dim decadeLater = theDate.AddYears(10)
Dim lastMonth = theDate.AddMonths(-1)

Console.WriteLine($"Date: {theDate}")
Console.WriteLine($" Next day: {nextDay}")
Console.WriteLine($" Previous day: {previousDay}")
Console.WriteLine($" Decade later: {decadeLater}")
Console.WriteLine($" Last month: {lastMonth}")

' This example produces the following output
' 
' Date: 10/21/2015
'  Next day: 10/22/2015
'  Previous day: 10/20/2015
'  Decade later: 10/21/2025
'  Last month: 9/21/2015

DateOnly の解析と書式設定を行う

DateOnly は、DateTime 構造体と同様に、文字列から解析できます。 標準の .NET 日付ベースの解析トークンはすべて DateOnly と連携します。 DateOnly 型を文字列に変換する場合は、標準の .NET 日付ベースの書式設定パターンも使用できます。 日時書式指定文字列の詳細については、「標準の日時書式指定文字列」を参照してください。

var theDate = DateOnly.ParseExact("21 Oct 2015", "dd MMM yyyy", CultureInfo.InvariantCulture);  // Custom format
var theDate2 = DateOnly.Parse("October 21, 2015", CultureInfo.InvariantCulture);

Console.WriteLine(theDate.ToString("m", CultureInfo.InvariantCulture));     // Month day pattern
Console.WriteLine(theDate2.ToString("o", CultureInfo.InvariantCulture));    // ISO 8601 format
Console.WriteLine(theDate2.ToLongDateString());

/* This example produces the following output:
 * 
 * October 21
 * 2015-10-21
 * Wednesday, October 21, 2015
*/
Dim theDate = DateOnly.ParseExact("21 Oct 2015", "dd MMM yyyy", CultureInfo.InvariantCulture) ' Custom format
Dim theDate2 = DateOnly.Parse("October 21, 2015", CultureInfo.InvariantCulture)

Console.WriteLine(theDate.ToString("m", CultureInfo.InvariantCulture))     ' Month day pattern
Console.WriteLine(theDate2.ToString("o", CultureInfo.InvariantCulture))    ' ISO 8601 format
Console.WriteLine(theDate2.ToLongDateString())

' This example produces the following output
' 
' October 21
' 2015-10-21
' Wednesday, October 21, 2015

DateOnly を比較する

DateOnly は、他のインスタンスと比較できます。 たとえば、日付が別の日付より前か後か、または今日の日付が特定の日付と一致するかどうかを確認できます。

var theDate = DateOnly.ParseExact("21 Oct 2015", "dd MMM yyyy", CultureInfo.InvariantCulture);  // Custom format
var theDate2 = DateOnly.Parse("October 21, 2015", CultureInfo.InvariantCulture);
var dateLater = theDate.AddMonths(6);
var dateBefore = theDate.AddDays(-10);

Console.WriteLine($"Consider {theDate}...");
Console.WriteLine($" Is '{nameof(theDate2)}' equal? {theDate == theDate2}");
Console.WriteLine($" Is {dateLater} after? {dateLater > theDate} ");
Console.WriteLine($" Is {dateLater} before? {dateLater < theDate} ");
Console.WriteLine($" Is {dateBefore} after? {dateBefore > theDate} ");
Console.WriteLine($" Is {dateBefore} before? {dateBefore < theDate} ");

/* This example produces the following output:
 * 
 * Consider 10/21/2015
 *  Is 'theDate2' equal? True
 *  Is 4/21/2016 after? True
 *  Is 4/21/2016 before? False
 *  Is 10/11/2015 after? False
 *  Is 10/11/2015 before? True
*/
Dim theDate = DateOnly.ParseExact("21 Oct 2015", "dd MMM yyyy", CultureInfo.InvariantCulture) ' Custom format
Dim theDate2 = DateOnly.Parse("October 21, 2015", CultureInfo.InvariantCulture)
Dim dateLater = theDate.AddMonths(6)
Dim dateBefore = theDate.AddDays(-10)

Console.WriteLine($"Consider {theDate}...")
Console.WriteLine($" Is '{NameOf(theDate2)}' equal? {theDate = theDate2}")
Console.WriteLine($" Is {dateLater} after? {dateLater > theDate} ")
Console.WriteLine($" Is {dateLater} before? {dateLater < theDate} ")
Console.WriteLine($" Is {dateBefore} after? {dateBefore > theDate} ")
Console.WriteLine($" Is {dateBefore} before? {dateBefore < theDate} ")

' This example produces the following output
' 
' Consider 10/21/2015
'  Is 'theDate2' equal? True
'  Is 4/21/2016 after? True
'  Is 4/21/2016 before? False
'  Is 10/11/2015 after? False
'  Is 10/11/2015 before? True

TimeOnly 構造体

TimeOnly 構造体は、目覚まし時計や毎日の昼食時間など、時刻の値を表します。 TimeOnly は、特定の時刻である 00:00:00.0000000 - 23:59:59.9999999 の範囲に制限されます。

TimeOnly 型が導入される前は、プログラマは通常、DateTime 型または TimeSpan 型を使用して特定の時刻を表しました。 ただし、これらの構造体を使用して日付のない時刻をシミュレートすると、次のような問題が発生する可能性がありますが、TimeOnly はこれを解決します。

  • TimeSpan は、ストップウォッチで測定された時間など、経過時間を表します。 上限の範囲は 29,000 年を超え、その値を負にして、時間の逆行を示すことができます。 負の TimeSpan は、特定の時刻を示すわけではありません。

  • TimeSpan が時刻として使用されている場合は、24 時間外の値に操作される可能性があります。 TimeOnly にはこのリスクがありません。 たとえば、従業員の勤務シフトが 18:00 から始まり、8 時間続く場合、TimeOnly 構造体に 8 時間を加算すると、2:00 にロールオーバーされます

  • 時刻に DateTime を使用するには、任意の日付を時刻に関連付けた後、無視する必要があります。 日付として DateTime.MinValue (0001-01-01) を選択するのが一般的ですが、DateTime 値から時間を減算すると、OutOfRange 例外が発生する可能性があります。 TimeOnly では、24 時間の時間枠で循環して時刻が進んだり戻ったりするので、この問題はありません。

  • DateTime 構造体のシリアル化には日付コンポーネントが含まれます。これは、データの意図をあいまいにする可能性があります。 また、TimeOnly は、より少ないデータをシリアル化します。

TimeOnly の例

次の例を使用して、TimeOnly について学習します。

DateTime を TimeOnly に変換する

次のコードに示すように、TimeOnly.FromDateTime 静的メソッドを使用して DateTime 型から TimeOnly 型を作成します。

var now = TimeOnly.FromDateTime(DateTime.Now);
Console.WriteLine($"It is {now} right now");

/* This example produces output similar to the following:
 * 
 * It is 2:01 PM right now
*/
Dim now = TimeOnly.FromDateTime(DateTime.Now)
Console.WriteLine($"It is {now} right now")

' This example produces output similar to the following
' 
' It is 2:01 PM right now

時間を加算または減算する

TimeOnly 構造体の調整に使用するメソッドには、AddHoursAddMinutesAdd の 3 つがあります。 AddHoursAddMinutes の両方が整数パラメーターを受け取り、値を適宜調整します。 減算には負の値を使用し、加算には正の値を使用します。 構造体は不変であるため、メソッドは TimeOnly の新しいインスタンスを返します。 Add メソッドは TimeSpan パラメーターを受け取り、値を TimeOnly 値に加算または減算します。

TimeOnly は 24 時間の期間のみを表すので、これら 3 つのメソッドに指定された値を加算すると、前後に適宜ロールオーバーします。 たとえば、01:30:00 の値を使用して午前 1 時 30 分を表した後、その期間に -4 時間を加算すると、21:30:00 まで逆行します。これは午後 9 時 30 分です。 AddHoursAddMinutesAdd には、ロールオーバーされた日数を取り込むメソッド オーバーロードがあります。

var theTime = new TimeOnly(7, 23, 11);

var hourLater = theTime.AddHours(1);
var minutesBefore = theTime.AddMinutes(-12);
var secondsAfter = theTime.Add(TimeSpan.FromSeconds(10));
var daysLater = theTime.Add(new TimeSpan(hours: 21, minutes: 200, seconds: 83), out int wrappedDays);
var daysBehind = theTime.AddHours(-222, out int wrappedDaysFromHours);

Console.WriteLine($"Time: {theTime}");
Console.WriteLine($" Hours later: {hourLater}");
Console.WriteLine($" Minutes before: {minutesBefore}");
Console.WriteLine($" Seconds after: {secondsAfter}");
Console.WriteLine($" {daysLater} is the time, which is {wrappedDays} days later");
Console.WriteLine($" {daysBehind} is the time, which is {wrappedDaysFromHours} days prior");

/* This example produces the following output:
 * 
 * Time: 7:23 AM
 *  Hours later: 8:23 AM
 *  Minutes before: 7:11 AM
 *  Seconds after: 7:23 AM
 *  7:44 AM is the time, which is 1 days later 
 *  1:23 AM is the time, which is -9 days prior
*/
Dim wrappedDays As Integer
Dim wrappedDaysFromHours As Integer

Dim theTime = New TimeOnly(7, 23, 11)

Dim hourLater = theTime.AddHours(1)
Dim minutesBefore = theTime.AddMinutes(-12)
Dim secondsAfter = theTime.Add(TimeSpan.FromSeconds(10))
Dim daysLater = theTime.Add(New TimeSpan(hours:=21, minutes:=200, seconds:=83), wrappedDays)
Dim daysBehind = theTime.AddHours(-222, wrappedDaysFromHours)

Console.WriteLine($"Time: {theTime}")
Console.WriteLine($" Hours later: {hourLater}")
Console.WriteLine($" Minutes before: {minutesBefore}")
Console.WriteLine($" Seconds after: {secondsAfter}")
Console.WriteLine($" {daysLater} is the time, which is {wrappedDays} days later")
Console.WriteLine($" {daysBehind} is the time, which is {wrappedDaysFromHours} days prior")

' This example produces the following output
' 
' Time: 7:23 AM
'  Hours later: 8:23 AM
'  Minutes before: 7:11 AM
'  Seconds after: 7:23 AM
'  7:44 AM is the time, which is 1 days later 
'  1:23 AM is the time, which is -9 days prior

TimeOnly の解析と書式設定を行う

TimeOnly は、DateTime 構造体と同様に、文字列から解析できます。 標準の .NET 時間ベースの解析トークンはすべて TimeOnly と連携します。 TimeOnly 型を文字列に変換する場合は、標準の .NET 日付ベースの書式設定パターンも使用できます。 日時書式指定文字列の詳細については、「標準の日時書式指定文字列」を参照してください。

var theTime = TimeOnly.ParseExact("5:00 pm", "h:mm tt", CultureInfo.InvariantCulture);  // Custom format
var theTime2 = TimeOnly.Parse("17:30:25", CultureInfo.InvariantCulture);

Console.WriteLine(theTime.ToString("o", CultureInfo.InvariantCulture));     // Round-trip pattern.
Console.WriteLine(theTime2.ToString("t", CultureInfo.InvariantCulture));    // Long time format
Console.WriteLine(theTime2.ToLongTimeString());

/* This example produces the following output:
 * 
 * 17:00:00.0000000
 * 17:30
 * 5:30:25 PM
*/
Dim theTime = TimeOnly.ParseExact("5:00 pm", "h:mm tt", CultureInfo.InvariantCulture) ' Custom format
Dim theTime2 = TimeOnly.Parse("17:30:25", CultureInfo.InvariantCulture)

Console.WriteLine(theTime.ToString("o", CultureInfo.InvariantCulture))     ' Round-trip pattern.
Console.WriteLine(theTime2.ToString("t", CultureInfo.InvariantCulture))    ' Long time format
Console.WriteLine(theTime2.ToLongTimeString())

' This example produces the following output
' 
' 17:00:00.0000000
' 17:30
' 5:30:25 PM

DateOnly および TimeOnly 型をシリアル化する

.NET 7 以降で System.Text.Json は、DateOnly および TimeOnly 型のシリアル化と逆シリアル化をサポートしています。 次のオブジェクトを考えてみましょう。

sealed file record Appointment(
    Guid Id,
    string Description,
    DateOnly Date,
    TimeOnly StartTime,
    TimeOnly EndTime);
Public NotInheritable Class Appointment
    Public Property Id As Guid
    Public Property Description As String
    Public Property DateValue As DateOnly?
    Public Property StartTime As TimeOnly?
    Public Property EndTime As TimeOnly?
End Class

次の例は、Appointment オブジェクトをシリアル化し、結果の JSON を表示し、それを Appointment 型の新しいインスタンスに逆シリアル化しています。 最後に、元と新しく逆シリアル化されたインスタンスが等しいかどうかが比較され、結果がコンソールに出力されています。

Appointment originalAppointment = new(
    Id: Guid.NewGuid(),
    Description: "Take dog to veterinarian.",
    Date: new DateOnly(2002, 1, 13),
    StartTime: new TimeOnly(5,15),
    EndTime: new TimeOnly(5, 45));
string serialized = JsonSerializer.Serialize(originalAppointment);

Console.WriteLine($"Resulting JSON: {serialized}");

Appointment deserializedAppointment =
    JsonSerializer.Deserialize<Appointment>(serialized)!;

bool valuesAreTheSame = originalAppointment == deserializedAppointment;
Console.WriteLine($"""
    Original record has the same values as the deserialized record: {valuesAreTheSame}
    """);
        Dim originalAppointment As New Appointment With {
            .Id = Guid.NewGuid(),
            .Description = "Take dog to veterinarian.",
            .DateValue = New DateOnly(2002, 1, 13),
            .StartTime = New TimeOnly(5, 3, 1),
            .EndTime = New TimeOnly(5, 3, 1)
}
        Dim serialized As String = JsonSerializer.Serialize(originalAppointment)

        Console.WriteLine($"Resulting JSON: {serialized}")

        Dim deserializedAppointment As Appointment =
            JsonSerializer.Deserialize(Of Appointment)(serialized)

        Dim valuesAreTheSame As Boolean =
            (originalAppointment.DateValue = deserializedAppointment.DateValue AndAlso
            originalAppointment.StartTime = deserializedAppointment.StartTime AndAlso
            originalAppointment.EndTime = deserializedAppointment.EndTime AndAlso
            originalAppointment.Id = deserializedAppointment.Id AndAlso
            originalAppointment.Description = deserializedAppointment.Description)

        Console.WriteLine(
            $"Original object has the same values as the deserialized object: {valuesAreTheSame}")

上のコードでは以下の操作が行われます。

  • Appointment オブジェクトがインスタンス化され、appointment 変数に割り当てられます。
  • appointment インスタンスが JsonSerializer.Serialize を使用して JSON にシリアル化されます。
  • 結果の JSON がコンソールに出力されます。
  • この JSON は、JsonSerializer.Deserialize を使用して Appointment 型の新しいインスタンスに逆シリアル化されます。
  • 元と新しく逆シリアル化されたインスタンスが等しいかどうかが比較されます。
  • 比較の結果がコンソールに出力されます。

詳細については、「.NET で JSON のシリアル化と逆シリアル化を行なう方法」を参照してください。

TimeSpan と DateTime を操作する

TimeOnlyTimeSpan から作成したり、それに変換したりできます。 また、TimeOnlyDateTime と共に使用して、TimeOnly インスタンスを作成することも、日付が指定されている限り DateTime インスタンスを作成することもできます。

次の例では、TimeSpan から TimeOnly オブジェクトを作成し、それを元に戻します。

// TimeSpan must in the range of 00:00:00.0000000 to 23:59:59.9999999
var theTime = TimeOnly.FromTimeSpan(new TimeSpan(23, 59, 59));
var theTimeSpan = theTime.ToTimeSpan();

Console.WriteLine($"Variable '{nameof(theTime)}' is {theTime}");
Console.WriteLine($"Variable '{nameof(theTimeSpan)}' is {theTimeSpan}");

/* This example produces the following output:
 * 
 * Variable 'theTime' is 11:59 PM
 * Variable 'theTimeSpan' is 23:59:59
*/
' TimeSpan must in the range of 00:00:00.0000000 to 23:59:59.9999999
Dim theTime = TimeOnly.FromTimeSpan(New TimeSpan(23, 59, 59))
Dim theTimeSpan = theTime.ToTimeSpan()

Console.WriteLine($"Variable '{NameOf(theTime)}' is {theTime}")
Console.WriteLine($"Variable '{NameOf(theTimeSpan)}' is {theTimeSpan}")

' This example produces the following output
' 
' Variable 'theTime' is 11:59 PM
' Variable 'theTimeSpan' is 23:59:59

次の例では、選択した任意の日付で TimeOnly オブジェクトから DateTime を作成します。

var theTime = new TimeOnly(11, 25, 46);   // 11:25 AM and 46 seconds
var theDate = new DateOnly(2015, 10, 21); // October 21, 2015
var theDateTime = theDate.ToDateTime(theTime);
var reverseTime = TimeOnly.FromDateTime(theDateTime);

Console.WriteLine($"Date only is {theDate}");
Console.WriteLine($"Time only is {theTime}");
Console.WriteLine();
Console.WriteLine($"Combined to a DateTime type, the value is {theDateTime}");
Console.WriteLine($"Converted back from DateTime, the time is {reverseTime}");

/* This example produces the following output:
 * 
 * Date only is 10/21/2015
 * Time only is 11:25 AM
 * 
 * Combined to a DateTime type, the value is 10/21/2015 11:25:46 AM
 * Converted back from DateTime, the time is 11:25 AM
*/
Dim theTime = New TimeOnly(11, 25, 46) ' 11:   25 PM And 46 seconds
Dim theDate = New DateOnly(2015, 10, 21) ' October 21, 2015
Dim theDateTime = theDate.ToDateTime(theTime)
Dim reverseTime = TimeOnly.FromDateTime(theDateTime)

Console.WriteLine($"Date only is {theDate}")
Console.WriteLine($"Time only is {theTime}")
Console.WriteLine()
Console.WriteLine($"Combined to a DateTime type, the value is {theDateTime}")
Console.WriteLine($"Converted back from DateTime, the time is {reverseTime}")

' This example produces the following output
' 
' Date only is 10/21/2015
' Time only is 11:25 AM
' 
' Combined to a DateTime type, the value is 10/21/2015 11:25:46 AM
' Converted back from DateTime, the time is 11:25 AM

算術演算子と TimeOnly の比較

2 つの TimeOnly インスタンスを相互に比較でき、IsBetween メソッドを使用して、時刻が他の 2 つの時刻の間にあるかどうかを確認できます。 TimeOnly に対して加算または減算演算子を使用すると、期間を表す TimeSpan が返されます。

var start = new TimeOnly(10, 12, 01); // 10:12:01 AM
var end = new TimeOnly(14, 00, 53); // 02:00:53 PM

var outside = start.AddMinutes(-3);
var inside = start.AddMinutes(120);

Console.WriteLine($"Time starts at {start} and ends at {end}");
Console.WriteLine($" Is {outside} between the start and end? {outside.IsBetween(start, end)}");
Console.WriteLine($" Is {inside} between the start and end? {inside.IsBetween(start, end)}");
Console.WriteLine($" Is {start} less than {end}? {start < end}");
Console.WriteLine($" Is {start} greater than {end}? {start > end}");
Console.WriteLine($" Does {start} equal {end}? {start == end}");
Console.WriteLine($" The time between {start} and {end} is {end - start}");

/* This example produces the following output:
 * 
 * Time starts at 10:12 AM and ends at 2:00 PM
 *  Is 10:09 AM between the start and end? False
 *  Is 12:12 PM between the start and end? True
 *  Is 10:12 AM less than 2:00 PM? True
 *  Is 10:12 AM greater than 2:00 PM? False
 *  Does 10:12 AM equal 2:00 PM? False
 *  The time between 10:12 AM and 2:00 PM is 03:48:52
*/
Dim startDate = New TimeOnly(10, 12, 1) ' 10:12:01 AM
Dim endDate = New TimeOnly(14, 0, 53) ' 02:00:53 PM

Dim outside = startDate.AddMinutes(-3)
Dim inside = startDate.AddMinutes(120)

Console.WriteLine($"Time starts at {startDate} and ends at {endDate}")
Console.WriteLine($" Is {outside} between the start and end? {outside.IsBetween(startDate, endDate)}")
Console.WriteLine($" Is {inside} between the start and end? {inside.IsBetween(startDate, endDate)}")
Console.WriteLine($" Is {startDate} less than {endDate}? {startDate < endDate}")
Console.WriteLine($" Is {startDate} greater than {endDate}? {startDate > endDate}")
Console.WriteLine($" Does {startDate} equal {endDate}? {startDate = endDate}")
Console.WriteLine($" The time between {startDate} and {endDate} is {endDate - startDate}")

' This example produces the following output
' 
' Time starts at 10:12 AM And ends at 2:00 PM
'  Is 10:09 AM between the start And end? False
'  Is 12:12 PM between the start And end? True
'  Is 10:12 AM less than 2:00 PM? True
'  Is 10:12 AM greater than 2:00 PM? False
'  Does 10:12 AM equal 2:00 PM? False
'  The time between 10:12 AM and 2:00 PM is 03:48:52