257

I'm not even sure how to do this without using some horrible for loop/counter type solution. Here's the problem:

I'm given two dates, a start date and an end date and on a specified interval I need to take some action. For example: for every date between 3/10/2009 on every third day until 3/26/2009 I need to create an entry in a List. So my inputs would be:

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

and my output would be a list that has the following dates:

3/13/2009 3/16/2009 3/19/2009 3/22/2009 3/25/2009

So how the heck would I do something like this? I thought about using a for loop that would iterate between every day in the range with a separate counter like so:

int count = 0;

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

}

But it seems like there could be a better way?

Paul Mignard
  • 5,824
  • 6
  • 44
  • 60

17 Answers17

593

Well, you'll need to loop over them one way or the other. I prefer defining a method like this:

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

Then you can use it like this:

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

In this manner you could hit every other day, every third day, only weekdays, etc. For example, to return every third day starting with the "start" date, you could just call AddDays(3) in the loop instead of AddDays(1).

mqp
  • 70,359
  • 14
  • 95
  • 123
  • 18
    You can even add another parameter for the interval. – Justin Drury Dec 04 '09 at 15:18
  • This will be inclusive of the first date. If you dont want that, just change the 'var day = from.Date' to 'var day = from.Date.AddDays(dayInterval)' – SwDevMan81 Dec 04 '09 at 15:29
  • 5
    A really nice solution for an interesting and real word problem. I like how this shows quite a few useful techniques of the language. And this reminds me that for loop is not only for (int i = 0;...) (-. – Audrius Dec 04 '09 at 16:45
  • 9
    Making this an extension method to datetime might make it even better. – MatteS Jun 15 '11 at 07:58
  • for (var month = from.Date; month.Date <= thru.Date || month.Month == thru.Month ; month = month.AddMonths(1)) -- looping thru months. – Yogurt The Wise Dec 27 '12 at 22:45
  • 1
    Look at my answer for extensions for days and months ;) Just for your pleasure :D – Jacob Sobus Feb 05 '15 at 09:10
  • Can I just say this is the most elegant code I have seen in my 3 years using .NET? Oh, and one thing I don't understand is why `from.Date` instead of just `from`? – Yosef Bernal Nov 26 '20 at 11:17
33

I have a Range class in MiscUtil which you could find useful. Combined with the various extension methods, you could do:

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

(You may or may not want to exclude the end - I just thought I'd provide it as an example.)

This is basically a ready-rolled (and more general-purpose) form of mquander's solution.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    Certainly just a matter of taste whether you like such things to be extension methods or not. `ExcludeEnd()` is cute. – mqp Dec 04 '09 at 15:26
  • 1
    You can, of course, do all of that without using the extension methods. It'll just be a lot uglier and harder to read IMO :) – Jon Skeet Dec 04 '09 at 15:27
  • 1
    Wow - what a great resource MiscUtil is - thanks for your answer! – Paul Mignard Dec 04 '09 at 15:39
  • 1
    in case anybody else but me mistook DayInterval as a struct/class, it is actually an integer in this sample. it is of course obvious if you read the question carefully, which i did not. – marc.d Apr 05 '16 at 15:08
25

For your example you can try

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);
}
Adriaan Stander
  • 162,879
  • 31
  • 289
  • 284
  • 1
    That's the same thing I was thinking (though I like mquander's answer above too) but I can't figure out how you get a nice code sample posted so quickly! – TLiebe Dec 04 '09 at 15:20
  • 3
    I think we need StartDate.AddDays(DayInterval); once in this loop not twice. – Abdul Saboor Feb 12 '13 at 16:25
20

Code from @mquander and @Yogurt The Wise used in extensions:

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);
}
Jacob Sobus
  • 961
  • 16
  • 25
  • What's the point of `EachDayTo` and `EachMonthTo`? I think I missed something here. – Alisson Reinaldo Silva Dec 18 '17 at 00:09
  • 1
    @Alisson those are the extension methods working on dateFrom object :) So You can use them already on created DateTime objects more fluentish (using just . after instance). More about extension methods here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods – Jacob Sobus Dec 19 '17 at 12:16
14

1 Year later, may it help someone,

This version includes a predicate, to be more flexible.

Usage

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

Daily to my birthday

var toBirthday = today.RangeTo(birthday);  

Monthly to my birthday, Step 2 months

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

Yearly to my birthday

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

Use RangeFrom instead

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

Implementation

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

You could throw an Exception if the fromDate > toDate, but I prefer to return an empty range instead []

Dominic Jonas
  • 4,717
  • 1
  • 34
  • 77
amd
  • 20,637
  • 6
  • 49
  • 67
9
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))
{

}
gilbertc
  • 1,049
  • 1
  • 10
  • 19
5
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);
 }
Sunil Joshi
  • 63
  • 2
  • 9
4

According to the problem you can try this...

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

    startDate = startDate.AddDays(1);
}

thanks......

Rejwanul Reja
  • 1,339
  • 1
  • 17
  • 19
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
}
devnull
  • 2,790
  • 22
  • 25
3

Here are my 2 cents in 2020.

Enumerable.Range(0, (endDate - startDate).Days + 1)
.ToList()
.Select(a => startDate.AddDays(a));
mko
  • 6,638
  • 12
  • 67
  • 118
2

You can use the DateTime.AddDays() function to add your DayInterval to the StartDate and check to make sure it is less than the EndDate.

Jawa
  • 2,336
  • 6
  • 34
  • 39
TLiebe
  • 7,913
  • 1
  • 23
  • 28
1

You might consider writing an iterator instead, which allows you to use normal 'for' loop syntax like '++'. I searched and found a similar question answered here on StackOverflow which gives pointers on making DateTime iterable.

Community
  • 1
  • 1
REDace0
  • 131
  • 1
  • 7
1

you can use this.

 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
 }
porya ras
  • 458
  • 5
  • 15
0

you have to be careful here not to miss the dates when in the loop a better solution would be.

this gives you the first date of startdate and use it in the loop before incrementing it and it will process all the dates including the last date of enddate hence <= enddate.

so the above answer is the correct one.

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

Iterate every 15 minutes

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);
        }
McNiel Viray
  • 61
  • 1
  • 3
0

@jacob-sobus and @mquander and @Yogurt not exactly correct.. If I need the next day I wait 00:00 time mostly

    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);
    }
DantaliaN
  • 155
  • 10
0

If you convert your dates to OADate you can loop thru them as you would do with any double number.

DateTime startDate = new DateTime(2022, 1, 1);
DateTime endDate = new DateTime(2022, 12, 31);

for (double loopDate = startDate.ToOADate(); loopDate <= endDate.ToOADate(); loopDate++)
{
    DateTime selectedDate;
    selectedDate = DateTime.FromOADate(loopDate);
}
GDavoli
  • 517
  • 4
  • 8