views:

670

answers:

5

Since DateTime.AddDays() takes a double parameter, I'm concerned that when you add a days, there might be some rounding errors. For example let's say I have the following loop:

DateTime Now = DateTime.Today;
for (int i = 0; i < 365; ++i)
{
    Now = Now.AddDays(1);
    // do something
}

I'm concerned that Now might start drifting away from midnight. I'm always tempted to do something like this which may be a little slower, but relieves my paranoia:

for (int i = 0; i < 365; ++i)
{
    Now = Now.AddDays(1.01).Date;
    // do something
}
A: 

What do you mean by drifting away from midnight?

When run, the 1st code has the last date having 4/20/2010 12:00:00 AM.
I think that is what you expect. Isn't it?

shahkalpesh
A: 

As in your example, I added 1 day to a DateTime variable 100,000 times and still ended with a date with a midnight time value. It appears that there is no reason to be concerned.

I'm sure they are rounding to time ticks, which eliminates any issues with drifting.

Josh G
+8  A: 

As DateTime stores the date internaly as 64 bit integer where one tick represents 100 nanoseconds, there is no risk of an error. One day has 864,000,000,000 ticks and Double has a precision of at least 15 digits. So every error is vanished when rounding to ticks because Double has a higher resolution than one tick if 1.0 equals one day.

This would not be true for AddYears(), because Double has not enough precision to represent one tick if 1.0 equals one year. But if you look at the DateTime class, you see that the design respects that fact - AddMonths() and AddYears() have both integer and not floating point arguments.

To check this just execute the following code.

DateTime now = DateTime.Now;

// Displays 864000000000 
Console.WriteLine(now.AddDays(1.0).Ticks - now.Ticks);
Daniel Brückner
+1  A: 

The number of days is multiplied by an integer (the scale) and then added to the number of ticks stored by the DateTime. When you pass in an integer you'll end up adding an integer number of ticks.

On the other hand, I don't know what .NET does about leap seconds... I suspect it uses a pretty simple model which doesn't bother with them, leaving your code okay.

Don't forget that adding time zones add an extra complication - adding a day locally may add more than or less than a day in terms of UTC, and vice versa.

Jon Skeet
+1 for leap seconds Jon ;)
Andrei Rinea
+2  A: 

In general, I think you are correct to worry about rounding with doubles since not all real numbers are absolutely expressible with a double -- for instance, if you were to add 1/3 of a day three times, you might not end up with exactly one day's advance. However, in this case, 1 is a number that is absolutely expressible and as you are simply multiplying it by another number that is also absolutely expressible in a double (the number of ticks in a day), you should be ok. The second sample is probably overkill.

Example:

DateTime now = DateTime.Today;
for (int i = 0; i < 7; ++i )
{
    for (int j = 0; j < 7; ++j )
    {
         now = now.AddDays( 1 / 7.0 );
    }
}
Console.WriteLine( DateTime.Today);
Console.WriteLine( now );

Results in (on 4/20/2009)

4/20/2009 12:00:00 AM
4/26/2009 11:59:59 PM
tvanfosson
May be you should add a note to your example. The fact that 1 can be pricisely reprersented with Double might lead to the conclusion that this is true for all integers what of course is wrong.
Daniel Brückner
You'd have to add a very large number of days for that to ever be a problem. I suspect you'd overflow the date time structure first since a double has at least 15 significant digits.
tvanfosson