views:

107

answers:

4

I have a method that returns the past x days and it currently does the following:

var dates = new List<DateTime>();

for (int i = 0; i < numDays; i++)
{
    dates.Add(DateTime.Today.AddDays(-i));
}

return dates;

I feel like there should be a more compact way of doing this, perhaps using LINQ. Suggestions? Also, if I do keep it the way I have it, is DateTime.Today such that this would be more efficient if I stored it in a variable outside the loop and then called AddDays on that value within the loop?

Edit: LINQ uses lazy evaluation, right? I'm getting crazy images in my head:

return DateTime.AllDaysInTheHistoryOfTimeEver.Where(day =>
    day.BeforeOrOn(DateTime.Today) &&
    day.After(DateTime.Today.AddDays(-numDays))
);
+11  A: 
var start = DateTime.Today;
var days = Enumerable.Range(0, numDays).Select(i => start.AddDays(-i)).ToList();

Capturing start first avoids a corner-case of running it at around midnight.

Marc Gravell
Interesting solution. in one line : var qry = Enumerable.Range(0, numDays).Select(i => DateTime.Today.AddDays(-i)).ToList();
Atømix
@Atomiton - now run it *just before* midnight ;-p
Marc Gravell
@Marc: I'm using your solution and I captured `DateTime.Today` as you did, but can you explain/give a link as to why this fixes a corner case? Something with closures?
Sarah Vessels
@Sarah - if the code ran through a few iterations just before midnight and then finished up after midnight, the value of DateTime.Today would change during the iteration! This would produce a duplicate value in the days collection.
Chris Shouts
@paper1337: Oh, der. Thanks!
Sarah Vessels
@Marc. Wow! Great point! Silly computers, not thinking for themselves. :-)
Atømix
A: 

Instead of collecting the values in a list you can return an IEnumerator or IEnumerable and use the yield keyword.

See the MSDN article

tafa
I don't want to read MSDN articles. Can you show an example of code to do this?
DOK
@DOK: Pharabus' answer uses `yield`: http://stackoverflow.com/questions/2134893/c-list-of-past-datetimes/2134956#2134956
Sarah Vessels
@DOK: Your comment made me lol!
Atømix
+1  A: 

maybe not linq but you can do it with an iterator, not sure about your exact requirements (i.e. numDays) but the below will get you the last 10 days.

var dates = GetDates(DateTime.Now.AddDays(-10),DateTime.Now);

 public IEnumerable<DateTime> GetDates(DateTime StartingDate, DateTime EndingDate)
        {
            while (StartingDate <= EndingDate)
            {
                yield return StartingDate;
                StartingDate = StartingDate.AddDays(1);
            }
        } 
Pharabus
I like your suggestion of using `yield`. I'll check and see how my method is being used and see if an iterator would make more sense.
Sarah Vessels
+1  A: 

Marc's answer provides a more persuasive reason to capture the start time, but reflector is one of my guilty pleasures, so I checked and discovered that capturing the start time would also be more efficient (though ... well, you know, unlikely to ever matter, etc..)

When you call DateTime.Today, it returns UtcNow.ToLocalTime() which adds a bit more processing overhead compared to using a date that's already DateTimeKind.Local:

public virtual DateTime ToLocalTime(DateTime time)
{
    if (time.Kind == DateTimeKind.Local)
    {
        return time;
    }
    bool isAmbiguousLocalDst = false;
    long utcOffsetFromUniversalTime = ((CurrentSystemTimeZone) CurrentTimeZone).GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
    return new DateTime(time.Ticks + utcOffsetFromUniversalTime, DateTimeKind.Local, isAmbiguousLocalDst);
}
Jeff Sternal
Thanks for the background info. :)
Sarah Vessels