views:

327

answers:

7

I'm creating a list of a month's worth of dates. I'm wondering what will be more efficient

List<DateTime> GetDates(DateTime StartDay) {
  List<DateTime> dates = new List<DateTime>();
  int TotalDays=StartDay.AddMonths(1).AddDays(-1).Day;
  for (int i=1; i<TotalDays; i++) {
    dates.Add(new DateTime(StartDay.Year, StartDay.Month, i));
  }
  return dates;
}

or

List<DateTime> GetDates(DateTime StartDay) {
  List<DateTime> dates = new List<DateTime>();
  DateTime NextMonth = StartDay.AddMonths(1);
  for (DateTime curr=StartDay; !curr.Equals(NextMonth); curr=curr.AddDays(1)) {
    dates.Add(curr);
  }
  return dates;
}

basically, is new DateTime() or DateTime.addDays more efficient.

UPDATE:

static void Main(string[] args) {
  System.Diagnostics.Stopwatch sw=new System.Diagnostics.Stopwatch();
  long t1, t2, total;
  List<DateTime> l;
  DateTime begin = DateTime.Now;
  total = 0L;
  for (int i=0; i<10; i++) {
    sw.Start();
    l = GetDates(begin);
    sw.Stop();


    sw.Stop();
    t1 = sw.ElapsedTicks;
    sw.Reset();
    sw.Start();

    l = GetDates2(begin);
    sw.Stop();
    t2=sw.ElapsedTicks;
    total +=  t1- t2;

    Console.WriteLine("Test {0} : {1} {2} : {3}", i,t1,t2, t1- t2);
  }
  Console.WriteLine("Total: {0}", total);

  Console.WriteLine("\n\nDone");
  Console.ReadLine();
}

static List<DateTime> GetDates(DateTime StartDay) {
  List<DateTime> dates = new List<DateTime>();
  int TotalDays=StartDay.AddMonths(10000).AddDays(-1).Day;
  for (int i=1; i<TotalDays; i++) {
    dates.Add(new DateTime(StartDay.Year, StartDay.Month, i));
  }
  return dates;
}


static List<DateTime> GetDates2(DateTime StartDay) {
  List<DateTime> dates = new List<DateTime>();
  DateTime NextMonth = StartDay.AddMonths(10000);
  for (DateTime curr=StartDay; !curr.Equals(NextMonth); curr=curr.AddDays(1)) {
    dates.Add(curr);
  }
  return dates;
}
Test 0 : 2203229 63086205 : -60882976
Test 1 : 63126483 102969090 : -39842607
Test 2 : 102991588 93487982 : 9503606
Test 3 : 93510942 69439034 : 24071908
Test 4 : 69465137 70660555 : -1195418
Test 5 : 70695702 68224849 : 2470853
Test 6 : 68248593 63555492 : 4693101
Test 7 : 63578536 65086357 : -1507821
Test 8 : 65108190 64035573 : 1072617
Test 9 : 64066128 64933449 : -867321
Total: -62484058

Done

results are consistently negative... way negative, so, looks like the constructor and integer test is the more efficient method.

+5  A: 

Measure it - write a test program and see which one takes less time.

Mark
then report your results here :)
Pieter Breed
+3  A: 

I believe datetime operations return new datetime structures so you will be creating new instances either way.

http://msdn.microsoft.com/en-us/library/system.datetime.aspx

Gratzy
You believe? Wouldn't it have just been better to quote it? "This method does not change the value of this DateTime. Instead, a new DateTime is returned whose value is the result of this operation."
R. Bemrose
Moreover, even though they both create new DateTime instances, the two constructors involve very different algorithms.
Jeff Sternal
Your critiquing my choice of words? I felt it more polite the way I wrote it and to include a link.
Gratzy
+2  A: 

Since they both do the same thing in the end, there isn't much of a difference.

If you're looking for efficiency, just use ticks. All (that I've seen) calls in DateTime are eventually converted into ticks before any math gets done.

Will
Though they both create new instances, they definitely do not do the same thing.
Jeff Sternal
They do the same thing **in the end**, and what difference there is between the two methods **isn't much**. Don't be anal, guy.
Will
Not just that, but while analing out about the first line, you completely ignored the second, which states the truth--the most efficient way to deal with the DateTime structure is to work in ticks only.
Will
Yikes, I submit. I agree with the second part.
Jeff Sternal
MY ANGER HAS BEEN AROUSED. MUST SMASH.
Will
+3  A: 

Unless you are doing some financial processing then I would worry more about readability than performance here. Only start worrying about performance somewhere like here if it's a proven bottleneck.

dove
+1, this is definitely the most important advice. I feel a little silly having dug so deeply into this. D:
Jeff Sternal
A: 

I agree with Mark. Test both methods yourself and see which one is faster. Use the Stopwatch class to get accurate timings of how long each method takes to run. My first guess is that since both end up creating new structures anyway, that any speed difference will be negligible. Also, with only generating a month's worth of dates (31 days maximum), I don't think either method will be that much slower than the other. Perhaps is you you were generating thousands or millions of dates, it would make a difference, but for 31 dates, it's probably premature optimization.

Kibbee
+1  A: 

It's really hard to imagine a case in which this would make a significant difference, but Reflector shows that the AddDays technique should be more efficient.

Compare the core logic of AddDays (from Add(Double, Int32))

long num = (long) ((value * scale) + ((value >= 0.0) ? 0.5 : -0.5));
if ((num <= -315537897600000L) || (num >= 0x11efae44cb400L)) {
    // Throw omitted
}
return this.AddTicks(num * 0x2710L);

To the core logic of the DateTime(int, int, int) constructor (from DateToTicks):

if (((year >= 1) && (year <= 0x270f)) && ((month >= 1) && (month <= 12)))
{
    int[] numArray = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
    if ((day >= 1) && (day <= (numArray[month] - numArray[month - 1])))
    {
        int num = year - 1;
        int num2 = ((((((num * 0x16d) + (num / 4)) - (num / 100)) + (num / 400)) + numArray[month - 1]) + day) - 1;
        return (num2 * 0xc92a69c000L);
    }
}
// Throw omitted

AddDays just converts the specified number of days to the equivalent number of ticks (a long) and adds it to the existing ticks.

Creating a new DateTime using the year/month/day constructor requires many more calculations. That constructor has to check whether the specified year is a leap year, allocate an array of days in each month, perform a bunch of extra operations, just to finally get the number of ticks those three numbers represent.


Edit: DateTime.AddDays(int) is faster than new DateTime(int, int, int), but your first algorithm is faster than the second algorithm. This is probably because the iteration costs are much higher in the second algorithm. As you observed in your edit, this might well be because DateTime.Equals is more expensive than comparing integers.

Jeff Sternal
Read my comments on the question, then step through GetDates()...
Jon Seigel
Oh, you want the algorithm to be *correct* too? ;)
Jeff Sternal
+1  A: 

Here is a working test program, with the algorithms implemented so that they can actually be compared (they still need work, though):

 class Program
    {
     static void Main(string[] args)
     {
      IList<DateTime> l1, l2;
      DateTime begin = new DateTime(2000, 1, 1);

      Stopwatch timer1 = Stopwatch.StartNew();
      for (int i = 0; i < 10000; i++)
       l1 = GetDates(begin);
      timer1.Stop();

      Stopwatch timer2 = Stopwatch.StartNew();
      for (int i = 0; i < 10000; i++)
       l2 = GetDates2(begin);
      timer2.Stop();

      Console.WriteLine("new DateTime: {0}\n.AddDays: {1}",
       timer1.ElapsedTicks, timer2.ElapsedTicks);
      Console.ReadLine();
     }

     static IList<DateTime> GetDates(DateTime StartDay)
     {
      IList<DateTime> dates = new List<DateTime>();

      int TotalDays = DateTime.DaysInMonth(StartDay.Year, StartDay.Month);

      for (int i = 0; i < TotalDays; i++)
       dates.Add(new DateTime(StartDay.Year, StartDay.Month, i + 1));

      return dates;
     }


     static IList<DateTime> GetDates2(DateTime StartDay)
     {
      IList<DateTime> dates = new List<DateTime>();

      DateTime NextMonth = StartDay.AddMonths(1);

      for (DateTime curr = StartDay; !curr.Equals(NextMonth); curr = curr.AddDays(1))
       dates.Add(curr);

      return dates;
     }
    } // class

Output (I added the commas):

new DateTime: 545,307,375
.AddDays: 180,071,512

These results seem pretty clear to me, though honestly I thought they'd be a lot closer.

Jon Seigel