25

I would like to get dates between two dates. Instead of expected 9 different dates, I get 875287 and run out of memory. What would be the problem with the code below?

StartDate value is 01/04/2016 00:00:00

EndDate value is 10/04/2016 00:00:00

var selectedDates = new List<DateTime?>();

for (var date = StartDate; date <= EndDate; date.Value.AddDays(1))
{
    selectedDates.Add(date);
}
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
threesixnine
  • 1,733
  • 7
  • 30
  • 56
  • 2
    @JanesAbouChleih Apologies, tired brain, didn't see how they were getting the infinite loop – Draken Apr 22 '16 at 08:50
  • 1
    Possible duplicate of *[Getting all DateTimes between two 'DateTime's in C#](http://stackoverflow.com/questions/3227927/getting-all-datetimes-between-two-datetimes-in-c-sharp)* – Peter Mortensen Apr 22 '16 at 22:15

5 Answers5

41

You aren't assigning the value of date.Value.AddDays(1) to anything, so it ends up in an infinite loop. You'd need to change your code so that date is set to the result of AddDays.

for (var date = StartDate; date <= EndDate; date = date.AddDays(1))
{
    selectedDates.Add(date);
}
Itai Bar-Haim
  • 1,686
  • 16
  • 40
DoctorMick
  • 6,703
  • 28
  • 26
  • This. Like `String`, manipulations on `DateTime` objects return a _new object_ instead of modifying the existing one. – Nyerguds Apr 22 '16 at 08:41
  • Based on OP's explanation (which means this code compiles and works), this `date` looks like `DateTime?`, not `DateTime` because `DateTime` does not have a property called `Value`. – Soner Gönül Apr 22 '16 at 08:49
  • Your updated code does not compile either since `Nullable` does not have method called `AddDays`. – Soner Gönül Apr 22 '16 at 08:51
32

LINQ solution (let's generate selectedDates):

  var selectedDates = Enumerable
    .Range(0, int.MaxValue)
    .Select(index => new DateTime?(StartDate.AddDays(index)))
    .TakeWhile(date => date <= EndDate)
    .ToList();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
  • 2
    This is the best answer because it avoid incremental computation which is an anti-pattern. It's not even certain that the `AddDays(1)` idea is even correct. – usr Apr 22 '16 at 10:08
  • 3
    Why does it need to create a new `DateTime?` ? What would be the issue with selecting the return value of `StartDate.AddDays(index)`? – Ash Burlaczenko Apr 22 '16 at 14:36
  • 2
    @AshBurlaczenko: `StartDate.AddDays` has a return value of `DateTime`, but the asker had used a `List` in his question. `new DateTime?` is simply a way of forcing the return value to the target type. – Jean Hominal Apr 22 '16 at 18:11
  • @Mou: If I understand you right, you're puzzled that I don't add any items. Well, instead of *adding* into *existing* list `var selectedDates = new List(); for ... Add ...` I just *create* it: after executing *Linq* query `selectedDates` will appear to be a `List` which contains the required dates – Dmitry Bychenko Mar 01 '17 at 14:29
12

As far as I can see, since AddDays method returns a new instance of a DateTime, it does not change the current instance since DateTime is immutable.

Looks like your date is DateTime?, you can change this part as;

for (var date = StartDate; date <= EndDate; date = date.Value.AddDays(1))
{
    selectedDates.Add(date);
}

As usr pointed, this might be affected on DST. You might wanna check Dmitry's answer as well.

Community
  • 1
  • 1
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
  • Doesn't this have a bug due to the incremental nature of the computation? If StartDate is at 2:30AM the time will shift if you ever hit a daylight savings day. AddDays does not behave like an integer. – usr Apr 22 '16 at 09:29
  • @usr You are right, `DateTime` structure does _not_ consider DST in arithmetic operations. But based on OP's start and end dates, that shouldn't be a case. Updated my answer to point Dmitry's answer as well. Thank you. – Soner Gönül Apr 22 '16 at 10:30
  • Performance of this is horrible, as it expands the list many times over. – jessehouwing Apr 29 '16 at 11:52
7

A shorter notation using Linq's Range method uses the ability to already figure out the number of days using the TimeSpan.Days property after subtracting start from end.

Assuming the start is before end you'd end up with:

DateTime StartDate = new DateTime(1979, 10, 4);
DateTime EndDate = new DateTime(2016, 10, 4);

var dates = Enumerable.Range(0, (EndDate - StartDate).Days + 1)
    .Select(day => StartDate.AddDays(day))

If you need it to be Nullable, add:

    .Cast<DateTime?>()

If you need this to be a List, add:

    .ToList()

It's probably quite a bit more efficient than the other LINQ based solution.

jessehouwing
  • 106,458
  • 22
  • 256
  • 341
1

Decided to change it up with a do/while

var selectedDates = new List<DateTime?>();
DateTime? StartDate = DateTime.Parse("01/04/2016 00:00:00");
DateTime? EndDate = DateTime.Parse("10/04/2016 00:00:00");

do
{
    selectedDates.Add(StartDate);
    StartDate = StartDate.Value.AddDays(1);
}while(StartDate < EndDate);
CathalMF
  • 9,705
  • 6
  • 70
  • 106