Sdílet prostřednictvím


Jak používat struktury DateOnly a TimeOnly

DateOnly a TimeOnly struktury byly zavedeny s .NET 6 a představují konkrétní datum a čas dne, v tomto pořadí. Před rozhraním .NET 6 a vždy v rozhraní .NET Framework použili vývojáři typ DateTime (nebo jinou alternativu), aby představovali jednu z následujících možností:

  • Celé datum a čas.
  • Datum, které ignoruje čas.
  • Čas, který ignoruje datum.

DateOnly a TimeOnly jsou typy, které představují konkrétní části typu DateTime.

Důležitý

DateOnly a typy TimeOnly nejsou pro rozhraní .NET Framework k dispozici.

DateOnly – struktura

Struktura DateOnly představuje konkrétní datum bez času. Vzhledem k tomu, že nemá žádnou časovou komponentu, představuje datum od začátku dne do konce dne. Tato struktura je ideální pro ukládání konkrétních kalendářních dat, jako jsou datum narození, datum výročí nebo obchodní data.

I když byste mohli použít DateTime a současně ignorovat časovou komponentu, existuje několik výhod použití DateOnly přes DateTime:

  • Struktura DateTime se může vrátit do předchozího nebo následujícího dne, pokud je posunutá časovým pásmem. DateOnly nelze posunovat časovým pásmem a vždy představuje nastavené datum.

  • Serializace struktury DateTime zahrnuje časovou komponentu, která může překrývat záměr dat. DateOnly také serializuje méně dat.

  • Když kód komunikuje s databází, jako je SQL Server, jsou celá data obecně uložena jako datový typ date, což nezahrnuje čas. DateOnly lépe odpovídá typu databáze.

DateOnly má rozsah od 0001-01-01 do 9999-12-31, stejně jako DateTime. V konstruktoru DateOnly můžete zadat konkrétní kalendář. Objekt DateOnly však vždy představuje datum v proleptickém gregoriánském kalendáři bez ohledu na to, který kalendář byl použit k jeho vytvoření. Můžete například vytvořit datum z hebrejského kalendáře, ale datum se převede na gregoriánský:

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

Příklady DateOnly

Informace o DateOnlynajdete v následujících příkladech:

Převod DateTime na DateOnly

Pomocí DateOnly.FromDateTime statické metody vytvořte typ DateOnly z typu DateTime, jak je znázorněno v následujícím kódu:

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

Sčítání nebo odečítání dnů, měsíců, roků

Existují tři metody, jak upravit strukturu DateOnly: AddDays, AddMonthsa AddYears. Každá metoda přebírá celočíselné parametry a zvyšuje datum podle tohoto měření. Pokud je zadané záporné číslo, datum se zmenší tímto měřením. Metody vrací novou instanci DateOnly, protože struktura je neměnná.

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

Parsování a formátování DateOnly

DateOnly lze analyzovat z řetězce, stejně jako struktura DateTime. Všechny standardní tokeny analýzy založené na datech .NET fungují s DateOnly. Při převodu typu DateOnly na řetězec můžete použít standardní vzory formátování založené na datech .NET. Další informace o formátování řetězců naleznete v tématu Standardní řetězce formátu data a času.

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

Porovnat DateOnly

DateOnly lze porovnat s jinými instancemi. Můžete například zkontrolovat, jestli je datum před nebo po jiném, nebo jestli datum dnes odpovídá určitému datu.

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 – struktura

Struktura TimeOnly představuje časovou hodnotu během dne, například denní budík nebo v kolik hodin každý den obědváte. TimeOnly je omezena na rozsah 00:00:00.0000000 - 23:59:59,99999999999, konkrétní denní čas.

Před zavedením typu TimeOnly programátoři obvykle používali typ DateTime nebo typ TimeSpan, který představuje určitý čas. Použití těchto struktur k simulaci času bez data však může představovat některé problémy, které TimeOnly řeší:

  • TimeSpan představuje uplynulý čas, například čas měřený pomocí stopek. Horní rozsah je více než 29 000 let a jeho hodnota může být záporná, což označuje pohyb časem dozadu. Záporná TimeSpan nezoznačuje konkrétní čas dne.

  • Pokud se TimeSpan používá jako časový údaj dne, existuje riziko, že může být manipulováno na hodnotu, která přesahuje 24 hodin. TimeOnly toto riziko nemá. Pokud například pracovní směna zaměstnance začíná v 18:00 a trvá 8 hodin, přidání 8 hodin do struktury TimeOnly se přesune na 2:00.

  • Použití DateTime pro čas během dne vyžaduje, aby bylo k tomuto času přidruženo libovolné datum, které je později ignorováno. Je běžné zvolit DateTime.MinValue (0001-01-01-01) jako datum, ale pokud jsou hodiny odečteny od hodnoty DateTime, může dojít k OutOfRange výjimce. TimeOnly tento problém nemá, protože se čas posunuje dopředu a dozadu kolem 24hodinového časového rámce.

  • Serializace struktury DateTime zahrnuje komponentu data, která může překrývat záměr dat. TimeOnly také serializuje méně dat.

Příklady TimeOnly

Informace o TimeOnlynajdete v následujících příkladech:

Převod data a času na ČasOnly

Pomocí TimeOnly.FromDateTime statické metody vytvořte typ TimeOnly z typu DateTime, jak je znázorněno v následujícím kódu:

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

Sčítání nebo odečítání času

Existují tři metody, jak upravit strukturu TimeOnly: AddHours, AddMinutesa Add. AddHours i AddMinutes převezmou celočíselnou hodnotu a odpovídajícím způsobem upraví hodnotu. K odečítání můžete použít zápornou hodnotu a k přičítání kladnou hodnotu. Metody vracejí novou instanci TimeOnly, protože struktura je neměnná. Metoda Add vezme parametr TimeSpan a přičte nebo odečte hodnotu od hodnoty TimeOnly.

Vzhledem k tomu, že TimeOnly představuje pouze 24hodinovou dobu, při přidávání hodnot zadaných těmto třem metodám se převrací dopředu nebo dozadu. Pokud například použijete hodnotu 01:30:00 představující 1:30 ráno, pak přidáním -4 hodin z tohoto období se vrátíte zpět na 21:30:00, což je 21:30. Jsou přetížení metod pro AddHours, AddMinutesa Add, která zachycují počet přenesených dnů.

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

Parsování a formátování TimeOnly

TimeOnly lze analyzovat z řetězce, stejně jako struktura DateTime. Standardní časové tokeny pro analýzu .NET fungují všechny s TimeOnly. Při převodu typu TimeOnly na řetězec můžete použít standardní vzory formátování založené na datech .NET. Další informace o formátování řetězců naleznete v tématu Standardní řetězce formátu data a času.

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

Serializace typů DateOnly a TimeOnly

S rozhraním .NET 7 nebo novějším podporuje System.Text.Json serializaci a deserializaci typů DateOnly a TimeOnly. Představte si následující objekt:

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

Následující příklad serializuje Appointment objekt, zobrazí výsledný JSON a poté deserializuje zpět do nové instance typu Appointment. Nakonec se původní a nově deserializované instance porovnávají s rovností a výsledky se zapisují do konzoly:

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}")

V předchozím kódu:

  • Objekt Appointment je instancován a přiřazen proměnné appointment.
  • Instance appointment je serializována do formátu JSON pomocí JsonSerializer.Serialize.
  • Výsledný json se zapíše do konzoly.
  • JSON se deserializuje zpět do nové instance Appointment typu pomocí JsonSerializer.Deserialize.
  • Původní a nově deserializované instance jsou porovnávány pro shodnost.
  • Výsledek porovnání se zapíše do konzoly.

Další informace naleznete v tématu Jak serializovat a deserializovat JSON v rozhraní .NET.

Práce s TimeSpan a DateTime

TimeOnly lze vytvořit a převést na TimeSpan. TimeOnly lze také použít s DateTime, buď k vytvoření TimeOnly instance, nebo k vytvoření DateTime instance, pokud je k dispozici datum.

Následující příklad vytvoří objekt TimeOnly z TimeSpana pak ho převede zpět:

// 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

Následující příklad vytvoří DateTime z objektu TimeOnly s libovolným datem:

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

Aritmetické operátory a porovnání TimeOnly

Dvě instance TimeOnly se dají vzájemně porovnat a pomocí metody IsBetween můžete zkontrolovat, jestli je čas mezi dvěma jinými časy. Pokud se u TimeOnlypoužije operátor sčítání či odčítání, vrátí se TimeSpan představující dobu trvání.

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