Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does DateTime.AddDays round to nearest millisecond?

Tags:

c#

.net

datetime

I am dividing two doubles in .NET and using the result object to work out an end date from a start date by calling (dtStart is predefined):

var dValue = 1500.0/8400.0;
var dtEnd = dtStart.AddDays(dValue);

After inspecting dtEnd I found that the result was only accurate to the nearest millisecond. After looking it up I found that .AddMilliseconds etc. all round the number and TimeSpan.FromDays does a similar thing. I was wondering if there was a reason why this rounding was done since it seems like the only way to get the correct value here is to use .AddTicks?

For reference .AddDays calls (where MillisPerDay = 86400000)

public DateTime AddDays(double value) 
{
    return Add(value, MillisPerDay);
}

which calls

private DateTime Add(double value, int scale) 
{
    long millis = (long)(value * scale + (value >= 0? 0.5: -0.5));
    if (millis <= -MaxMillis || millis >= MaxMillis) 
    throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue"));
    return AddTicks(millis * TicksPerMillisecond);
}
like image 859
Laurence Bargery Avatar asked Nov 19 '14 21:11

Laurence Bargery


People also ask

How to increase DateTime in c#?

AddDays() Method in C# This method is used to return a new DateTime that adds the specified number of days to the value of this instance. Syntax: public DateTime AddDays (double value);

How to add 1 day in DateTime in c#?

AddDays() method in C# is used to add the specified number of days to the value of this instance. This method returns a new DateTime.


1 Answers

Edit: After thinking things over, I now realize the first version of my answer was wrong.

Here are the comments in Microsoft's source code:

// Returns the DateTime resulting from adding a fractional number of
// xxxxs to this DateTime. The result is computed by rounding the
// fractional number of xxxxs given by value to the nearest
// millisecond, and adding that interval to this DateTime. The
// value argument is permitted to be negative.

These comments appear on five different AddXxxxs(double value) methods, where Xxxx = Days, Hours, Milliseconds, Minutes and Seconds.

Note that this is only for the methods that accept a floating point value. (And one may question whether or not it is a good idea to involve floating point values in date calculations - but that's a topic for another day.)

Now, as the OP correctly points out, these five methods all call this method:

// Returns the DateTime resulting from adding a fractional number of
// time units to this DateTime.
private DateTime Add(double value, int scale) {
    long millis = (long)(value * scale + (value >= 0? 0.5: -0.5));
    if (millis <= -MaxMillis || millis >= MaxMillis) 
        throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue"));
    return AddTicks(millis * TicksPerMillisecond);
}

So what is being done is that the value being added to the DateTime is rounded to the nearest number of millisecond before being added. But not the result - only the value being added (or subtracted).

This is actually documented, for example http://msdn.microsoft.com/en-us/library/system.datetime.adddays%28v=vs.110%29.aspx "The value parameter is rounded to the nearest millisecond."

Why it does this I don't know. Maybe the programmers figured that if you're using floating point values you should be aware that your values are typically not completely accurate. Or maybe they want to simulate to some degree Java-style times, which are based on milliseconds.

like image 134
RenniePet Avatar answered Oct 11 '22 16:10

RenniePet