Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subtracting UTC and non-UTC DateTime in C#

I assumed that when subtracting 2 datetimes the framework will check their timezone and make the appropriate conversions.

I tested it with this code:

Console.WriteLine(DateTime.Now.ToUniversalTime() - DateTime.UtcNow.ToUniversalTime());
Console.WriteLine(DateTime.Now.ToUniversalTime() - DateTime.UtcNow);
Console.WriteLine(DateTime.Now - DateTime.UtcNow);

Output:

-00:00:00.0020002
-00:00:00.0020001
01:59:59.9989999

To my surprise, DateTime.Now - DateTime.UtcNow does not make the appropriate conversion automatically. At the same time, DateTime.UtcNow is the same as DateTime.UtcNow.ToUniversalTime(), so obviously there is some internal flag that indicates the time zone.

Is that correct, does that framework not perform the appropriate timezone conversion automatically, even if the information is already present? If so, is applying ToUniversalTime() safe for both UTC and non-UTC datetimes, i.e. an already UTC datetime will not be incorrectly corrected by ToUniversalTime()?

like image 717
sashoalm Avatar asked Oct 12 '16 07:10

sashoalm


2 Answers

The type System.DateTime does not hold time zone information, only a .Kind property that specifies whether it is Local or UTC. But before .NET 2.0, there was not even a .Kind property.

When you subtract (or do other arithmetic, like == or >, on) two DateTime values, their "kinds" are not considered at all. Only the numbers of ticks are considered. This gives compatibility with .NET 1.1 when no kinds existed.

The functionality you ask for (and expect) is in the newer and richer type System.DateTimeOffset. In particular, if you do the subtraction DateTimeOffset.Now - DateTimeOffset.UtcNow you get the result you want. The DateTimeOffset structure does not have a local/UTC flag; instead, it holds the entire time zone, such as +02:00 in your area.

like image 68
Jeppe Stig Nielsen Avatar answered Nov 10 '22 15:11

Jeppe Stig Nielsen


The framework does nothing with the date if it is already UTC:

 internal static DateTime ConvertTimeToUtc(DateTime dateTime, TimeZoneInfoOptions flags)
{
    if (dateTime.Kind == DateTimeKind.Utc)
    {
        return dateTime;
    }
    CachedData cachedData = s_cachedData;
    return ConvertTime(dateTime, cachedData.Local, cachedData.Utc, flags, cachedData);
}
like image 1
Michael Avatar answered Nov 10 '22 15:11

Michael