Wie Sie feststellen können, ob eine DateTime zwischen einem DateRange in C # liegt

78

Ich muss wissen, ob ein Datum zwischen einem DateRange liegt. Ich habe drei Daten:

// The date range
DateTime startDate;
DateTime endDate;

DateTime dateToCheck;

Die einfache Lösung ist ein Vergleich, aber gibt es eine intelligentere Möglichkeit, dies zu tun?

Daniel Peñalba
quelle
1
Eine intelligentere Methode, als nur zu überprüfen, ob das Datum zwischen diesen Daten liegt?
Tomas Jansson
4
Was ist also schlauer als eine einfache Lösung?
Polyfun

Antworten:

135

Nein, ein einfacher Vergleich sieht für mich gut aus:

return dateToCheck >= startDate && dateToCheck < endDate;

Dinge, über die man nachdenken sollte:

  • DateTimeist ein etwas seltsamer Typ in Bezug auf Zeitzonen. Es könnte UTC sein, es könnte "lokal" sein, es könnte mehrdeutig sein. Stellen Sie sicher, dass Sie Äpfel sozusagen mit Äpfeln vergleichen.
  • Überlegen Sie, ob Ihre Start- und Endpunkte inklusive oder exklusiv sein sollen. Ich habe den obigen Code dazu gebracht, ihn als inklusive Untergrenze und als exklusive Obergrenze zu behandeln.
Jon Skeet
quelle
Ja. Es DateTimeist wichtig sicherzustellen, dass die von Ihnen verglichenen s von derselben Art sind (UTC / Lokal). Bei verschiedenen Arten wird die Rohzeit verglichen, anstatt beide in eine gemeinsame Art umzuwandeln.
CodesInChaos
6
return startDate <= dateToCheck && dateToCheck < endDatescheint etwas lesbarer.
Mauricio Morales
7
@ MauricioMorales: Es kommt darauf an; Einige Leute finden es einfacher zu lesen, dass "das Bit, das variiert" (das zu überprüfende Datum) konsistent auf der linken Seite ist. Das ist es, was ich gerne mache. Ich kann auch die Vorteile des Ansatzes "in chronologischer Reihenfolge" erkennen, aber ich persönlich würde es vorziehen, wie ich es habe.
Jon Skeet
64

Normalerweise erstelle ich für solche Dinge die Fowler's Range- Implementierung.

public interface IRange<T>
{
    T Start { get; }
    T End { get; }
    bool Includes(T value);
    bool Includes(IRange<T> range);
}

public class DateRange : IRange<DateTime>         
{
    public DateRange(DateTime start, DateTime end)
    {
        Start = start;
        End = end;
    }

    public DateTime Start { get; private set; }
    public DateTime End { get; private set; }

    public bool Includes(DateTime value)
    {
        return (Start <= value) && (value <= End);
    }

    public bool Includes(IRange<DateTime> range)
    {
        return (Start <= range.Start) && (range.End <= End);
    }
}

Die Verwendung ist ziemlich einfach:

DateRange range = new DateRange(startDate, endDate);
range.Includes(date)
Sergey Berezovskiy
quelle
2
Nur eine dumme Beobachtung, muss der Konstruktor nicht sicherstellen, dass Start kleiner als Ende ist, wenn dies nicht der Fall ist, würde dies die Logik brechen ... insbesondere im Bereich Includes (IRange <DateTime>)
Adrian Hum
2
Mann, das ist so eine gute Lösung, dass es sogar mit LINQ funktioniert. Wie folgt: var valueFromVacationsList = HolidayBookingsForThisMonth.FirstOrDefault (s => (s.Id == currentUser.Id) && new DateRange (s.StartDateTime, s.EndDateTime) .Includes (itemDate));
Kadaj
46

Sie können Erweiterungsmethoden verwenden, um die Lesbarkeit zu verbessern:

public static class DateTimeExtensions
{
    public static bool InRange(this DateTime dateToCheck, DateTime startDate, DateTime endDate)
    {
        return dateToCheck >= startDate && dateToCheck < endDate;
    }
}

Jetzt können Sie schreiben:

dateToCheck.InRange(startDate, endDate)
Elian Ebbing
quelle
28
netter Ansatz, obwohl ich IsInRange()als Funktionsname verwenden würde
Andrew
9

Sie können verwenden:

return (dateTocheck >= startDate && dateToCheck <= endDate);
WraithNath
quelle
5

Ich habe festgestellt, dass die folgende Bibliothek am hilfreichsten ist, wenn Sie eine Datumsberechnung durchführen. Ich bin immer noch erstaunt, dass nichts davon Teil des .Net-Frameworks ist.

http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET

Landerud
quelle
Genau. Ich kann nicht glauben, dass .NET keine nativen Datums-Mathematik-Assemblys hat. Vielen Dank für das Teilen der Bibliothek
BoundForGlory
Ich habe die Zeitperiodenbibliothek für ein Projekt verwendet, das Zeitpläne für die Anzeige von Bildern auf elektronischen Schildern erstellt hat, und dies hat viel Zeit gespart. Ich kann mir nicht vorstellen, dieses Projekt ohne die Period Period-Bibliothek abgeschlossen zu haben.
PoorInRichfield
2

Im Anschluss an Sergeys Antwort , ich denke , die generische Ausführung mehr im Einklang mit Fowler ist RangeIdee und löst einige der Probleme mit dieser Antwort wie in der Lage , das haben IncludesMethoden innerhalb einer generischen Klasse von Zwang Twie IComparable<T>. Es ist auch unveränderlich, wie Sie es von Typen erwarten würden, die die Funktionalität anderer Werttypen wie erweitern DateTime.

public struct Range<T> where T : IComparable<T>
{
    public Range(T start, T end)
    {
        Start = start;
        End = end;
    }

    public T Start { get; }

    public T End { get; }

    public bool Includes(T value) => Start.CompareTo(value) <= 0 && End.CompareTo(value) >= 0;

    public bool Includes(Range<T> range) => Start.CompareTo(range.Start) <= 0 && End.CompareTo(range.End) >= 0;
}
Neo
quelle