Wie durchlaufe ich einen Datumsbereich?


196

Ich bin mir nicht einmal sicher, wie ich das machen soll, ohne eine schreckliche Lösung vom Typ Loop / Counter zu verwenden. Hier ist das Problem:

Ich habe zwei Daten, ein Startdatum und ein Enddatum, und in einem bestimmten Intervall muss ich etwas unternehmen. Zum Beispiel: Für jedes Datum zwischen dem 10.03.2009 an jedem dritten Tag bis zum 26.03.2009 muss ich einen Eintrag in einer Liste erstellen. Meine Eingaben wären also:

DateTime StartDate = "3/10/2009";
DateTime EndDate = "3/26/2009";
int DayInterval = 3;

und meine Ausgabe wäre eine Liste mit den folgenden Daten:

13.03.2009 16.03.2009 19.03.2009 22.03.2009 25.03.2009

Wie zum Teufel würde ich so etwas tun? Ich dachte darüber nach, eine for-Schleife zu verwenden, die zwischen jedem Tag im Bereich mit einem separaten Zähler wie folgt iteriert:

int count = 0;

for(int i = 0; i < n; i++)
{
     count++;
     if(count >= DayInterval)
     {
          //take action
          count = 0;
     }

}

Aber es scheint, als gäbe es einen besseren Weg?


1
Ich würde vermuten, dass C # eine Datenstruktur für Daten hat, die Sie verwenden könnten.
Anna

Antworten:


468

Nun, Sie müssen sie auf die eine oder andere Weise durchlaufen. Ich bevorzuge es, eine Methode wie diese zu definieren:

public IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
    for(var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
        yield return day;
}

Dann können Sie es so verwenden:

foreach (DateTime day in EachDay(StartDate, EndDate))
    // print it or whatever

Auf diese Weise können Sie jeden zweiten Tag, jeden dritten Tag, nur Wochentage usw. treffen. Um beispielsweise jeden dritten Tag ab dem Startdatum zurückzukehren, können Sie AddDays(3)stattdessen einfach in der Schleife anrufen AddDays(1).


18
Sie können sogar einen weiteren Parameter für das Intervall hinzufügen.
Justin Drury

Dies schließt den ersten Termin ein. Wenn Sie das nicht möchten, ändern Sie einfach 'var day = from.Date' in 'var day = from.Date.AddDays (dayInterval)'
SwDevMan81

3
Eine wirklich schöne Lösung für ein interessantes und echtes Wortproblem. Mir gefällt, wie dies einige nützliche Techniken der Sprache zeigt. Und das erinnert mich daran, dass for loop nicht nur für (int i = 0; ...) (-.
Audrius

9
Wenn Sie dies zu einer Erweiterungsmethode für datetime machen, wird es möglicherweise noch besser.
MatteS

1
Schauen Sie sich meine Antwort für Verlängerungen für Tage und Monate an;) Nur zu Ihrem Vergnügen: D
Jacob Sobus

30

Ich habe eine RangeKlasse in MiscUtil, die Sie nützlich finden könnten. In Kombination mit den verschiedenen Erweiterungsmethoden können Sie Folgendes tun:

foreach (DateTime date in StartDate.To(EndDate).ExcludeEnd()
                                   .Step(DayInterval.Days())
{
    // Do something with the date
}

(Vielleicht möchten Sie das Ende ausschließen oder nicht - ich dachte nur, ich würde es als Beispiel geben.)

Dies ist im Grunde eine fertig gerollte (und allgemeinere) Form der Lösung von mquander.


2
Sicherlich nur eine Frage des Geschmacks, ob Sie solche Dinge als Erweiterungsmethoden mögen oder nicht. ExcludeEnd()ist niedlich.
mqp

Sie können dies natürlich alles tun, ohne die Erweiterungsmethoden zu verwenden. Es wird nur viel hässlicher und schwieriger sein, IMO zu lesen :)
Jon Skeet

1
Wow - was für eine großartige Ressource MiscUtil ist - danke für deine Antwort!
Onekidney

1
Falls jemand anderes als ich DayInterval als Struktur / Klasse verwechselt hat, ist es in diesem Beispiel tatsächlich eine Ganzzahl. Es ist natürlich offensichtlich, wenn Sie die Frage sorgfältig lesen, was ich nicht getan habe.
März

23

Für Ihr Beispiel können Sie versuchen

DateTime StartDate = new DateTime(2009, 3, 10);
DateTime EndDate = new DateTime(2009, 3, 26);
int DayInterval = 3;

List<DateTime> dateList = new List<DateTime>();
while (StartDate.AddDays(DayInterval) <= EndDate)
{
   StartDate = StartDate.AddDays(DayInterval);
   dateList.Add(StartDate);
}

1
Das ist das gleiche, was ich gedacht habe (obwohl mir auch die Antwort von mquander oben gefällt), aber ich kann nicht herausfinden, wie Sie so schnell ein schönes Codebeispiel veröffentlichen!
TLiebe

3
Ich denke, wir brauchen StartDate.AddDays (DayInterval); einmal in dieser Schleife nicht zweimal.
Abdul Saboor

15

Code von @mquander und @Yogurt The Wise, der in Erweiterungen verwendet wird:

public static IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
    for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
        yield return day;
}

public static IEnumerable<DateTime> EachMonth(DateTime from, DateTime thru)
{
    for (var month = from.Date; month.Date <= thru.Date || month.Month == thru.Month; month = month.AddMonths(1))
        yield return month;
}

public static IEnumerable<DateTime> EachDayTo(this DateTime dateFrom, DateTime dateTo)
{
    return EachDay(dateFrom, dateTo);
}

public static IEnumerable<DateTime> EachMonthTo(this DateTime dateFrom, DateTime dateTo)
{
    return EachMonth(dateFrom, dateTo);
}

Was ist der Sinn von EachDayTound EachMonthTo? Ich glaube, ich habe hier etwas verpasst.
Alisson

@Alisson das sind die Erweiterungsmethoden, die an dateFrom-Objekten arbeiten :) Sie können sie also bereits für erstellte DateTime-Objekte verwenden, die flüssiger sind (mit nur. After-Instanz). Weitere Informationen zu
Jacob Sobus

8
DateTime startDate = new DateTime(2009, 3, 10);
DateTime stopDate = new DateTime(2009, 3, 26);
int interval = 3;

for (DateTime dateTime=startDate;
     dateTime < stopDate; 
     dateTime += TimeSpan.FromDays(interval))
{

}

8

1 Jahr später, möge es jemandem helfen,

Diese Version enthält ein Prädikat , um flexibler zu sein.

Verwendung

var today = DateTime.UtcNow;
var birthday = new DateTime(2018, 01, 01);

Täglich zu meinem Geburtstag

var toBirthday = today.RangeTo(birthday);  

Monatlich zu meinem Geburtstag, Schritt 2 Monate

var toBirthday = today.RangeTo(birthday, x => x.AddMonths(2));

Jährlich zu meinem Geburtstag

var toBirthday = today.RangeTo(birthday, x => x.AddYears(1));

Verwenden Sie RangeFromstattdessen

// same result
var fromToday = birthday.RangeFrom(today);
var toBirthday = today.RangeTo(birthday);

Implementierung

public static class DateTimeExtensions 
{

    public static IEnumerable<DateTime> RangeTo(this DateTime from, DateTime to, Func<DateTime, DateTime> step = null)
    {
        if (step == null)
        {
            step = x => x.AddDays(1);
        }

        while (from < to)
        {
            yield return from;
            from = step(from);
        }
    }

    public static IEnumerable<DateTime> RangeFrom(this DateTime to, DateTime from, Func<DateTime, DateTime> step = null)
    {
        return from.RangeTo(to, step);
    }
}

Extras

Sie könnten eine Ausnahme auslösen, wenn die fromDate > toDate, aber ich ziehe es vor, stattdessen einen leeren Bereich zurückzugeben[]


Wow - das ist wirklich umfassend. Danke Ahmad!
Onekidney

3
DateTime startDate = new DateTime(2009, 3, 10);
DateTime stopDate = new DateTime(2009, 3, 26);
int interval = 3;

while ((startDate = startDate.AddDays(interval)) <= stopDate)
{
    // do your thing
}

Beachten Sie, dass dies nicht das Startdatum enthält, da beim ersten Ausführen ein Tag whilehinzugefügt wird.
John Washam

2

Je nach Problem können Sie dies versuchen ...

// looping between date range    
while (startDate <= endDate)
{
    //here will be your code block...

    startDate = startDate.AddDays(1);
}

Vielen Dank......


2
DateTime begindate = Convert.ToDateTime("01/Jan/2018");
DateTime enddate = Convert.ToDateTime("12 Feb 2018");
 while (begindate < enddate)
 {
    begindate= begindate.AddDays(1);
    Console.WriteLine(begindate + "  " + enddate);
 }

1

Sie können stattdessen einen Iterator schreiben, mit dem Sie die normale 'for'-Schleifensyntax wie' ++ 'verwenden können. Ich habe eine ähnliche Frage gesucht und gefunden, die hier auf StackOverflow beantwortet wurde und Hinweise darauf gibt, wie DateTime iterierbar gemacht werden kann.


1

Sie können die DateTime.AddDays()Funktion verwenden, um Ihre DayIntervalzu der hinzuzufügen StartDateund zu überprüfen, ob sie kleiner als die ist EndDate.


0

Sie müssen hier vorsichtig sein, um die Daten nicht zu verpassen, wenn in der Schleife eine bessere Lösung wäre.

Dies gibt Ihnen das erste Datum des Startdatums und verwendet es in der Schleife, bevor Sie es erhöhen. Es verarbeitet alle Daten einschließlich des letzten Datums des Enddatums, also <= Enddatum.

Die obige Antwort ist also die richtige.

while (startdate <= enddate)
{
    // do something with the startdate
    startdate = startdate.adddays(interval);
}

0

Sie können dies verwenden.

 DateTime dt0 = new DateTime(2009, 3, 10);
 DateTime dt1 = new DateTime(2009, 3, 26);

 for (; dt0.Date <= dt1.Date; dt0=dt0.AddDays(3))
 {
    //Console.WriteLine(dt0.Date.ToString("yyyy-MM-dd"));
    //take action
 }

Das ist wirklich prägnant. Nett!
Onekidney

0

Iterieren Sie alle 15 Minuten

DateTime startDate = DateTime.Parse("2018-06-24 06:00");
        DateTime endDate = DateTime.Parse("2018-06-24 11:45");

        while (startDate.AddMinutes(15) <= endDate)
        {

            Console.WriteLine(startDate.ToString("yyyy-MM-dd HH:mm"));
            startDate = startDate.AddMinutes(15);
        }

0

@ Jacob-Sobus und @ Mquander und @ Joghurt nicht genau richtig .. Wenn ich am nächsten Tag brauche, warte ich meistens 00:00 Uhr

    public static IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
    {
        for (var day = from.Date; day.Date <= thru.Date; day = day.NextDay())
            yield return day;
    }

    public static IEnumerable<DateTime> EachMonth(DateTime from, DateTime thru)
    {
        for (var month = from.Date; month.Date <= thru.Date || month.Year == thru.Year && month.Month == thru.Month; month = month.NextMonth())
            yield return month;
    }

    public static IEnumerable<DateTime> EachYear(DateTime from, DateTime thru)
    {
        for (var year = from.Date; year.Date <= thru.Date || year.Year == thru.Year; year = year.NextYear())
            yield return year;
    }

    public static DateTime NextDay(this DateTime date)
    {
        return date.AddTicks(TimeSpan.TicksPerDay - date.TimeOfDay.Ticks);
    }

    public static DateTime NextMonth(this DateTime date)
    {
        return date.AddTicks(TimeSpan.TicksPerDay * DateTime.DaysInMonth(date.Year, date.Month) - (date.TimeOfDay.Ticks + TimeSpan.TicksPerDay * (date.Day - 1)));
    }

    public static DateTime NextYear(this DateTime date)
    {
        var yearTicks = (new DateTime(date.Year + 1, 1, 1) - new DateTime(date.Year, 1, 1)).Ticks;
        var ticks = (date - new DateTime(date.Year, 1, 1)).Ticks;
        return date.AddTicks(yearTicks - ticks);
    }

    public static IEnumerable<DateTime> EachDayTo(this DateTime dateFrom, DateTime dateTo)
    {
        return EachDay(dateFrom, dateTo);
    }

    public static IEnumerable<DateTime> EachMonthTo(this DateTime dateFrom, DateTime dateTo)
    {
        return EachMonth(dateFrom, dateTo);
    }

    public static IEnumerable<DateTime> EachYearTo(this DateTime dateFrom, DateTime dateTo)
    {
        return EachYear(dateFrom, dateTo);
    }

0

Hier sind meine 2 Cent im Jahr 2020.

Enumerable.Range(0, (endDate - startDate).Days + 1)
.ToList()
.Select(a => startDate.AddDays(a));

Das ist fantastisch. 👍
onekidney
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.