views:

452

answers:

2

Following from my last question which @Jon Skeet gave me a lot of help with (thanks again!)

I am now wondering how I can safely work with date/times, stored as UTC, when they are converted back to Local Date/Time.

As Jon indicated in my last question using DateTimeOffset represents an instant in time and there is no way to predict what the local time would be say a minute later. I need to be able to do calculations based on these date/times.

So how can I assure when I pull the dates from the database, convert them to local date/time and do specific calculations on them they are going to be accurate?

Scenario

My application records information sent in via email. The date/time the email is received is recorded as the submission time. Emails are pulled from exchange.

What I need to know is:

1) If these emails are coming from different countries, do I just convert the Recieved date/time of the email to UTC format and store that? e.g. Email.Received.ToUniversalTime()

+2  A: 

No, you can't assume that. UTC time is completely linear, and you can safely do calculations on them. Once you converted it to a local time, it's no longer completely linear.

When daylight savings time changes occur, there is an overlap or gap in the local time. If you do a calculation that spans over a daylight savings time change, the result will be off by one hour (if that is how much the time changes, which is the most common).

If you do the calculation before converting the DateTime/DateTimeOffset value to local time, the result will always be correct. Note however that converting the value to a local DateTime value can make it ambiguous, if the value happens to fall inside an overlap at the daylight savings time change, it's impossible to tell if it's the first or second time that exact time occurs that day.

Guffa
@Guffa: So when storing a date/time (for say Aussie) is it safe to convert that UTC time to the specific timezone equivalent before saving? Does this still represent UTC time? (I don't mean using `ToLocalTime()` I mean something like `ConvertTimeBySystemTimeZoneId`.
James
@Guffa: I am not actually assuming anything :)
James
@James: No, when you have converted the value to a time zone, it's no longer UTC time. However, if you are using DateTimeOffset it's offset will be changed accordingly, so you can convert it back to the original UTC time. The DateTimeOffset value contains the offset from the UTC time, but not the time zone, so while converting it back to UTC is safe, calculations on DateTimeOffset values doesn't take daylight savings time into account. If you concvert to UTC, do the calculation and convert back, the calculation will be correct.
Guffa
Please see updated post.
James
@James: The recieved date in the mail has a time offset, so it corresponds to a DateTimeOffset value. If you can successfully parse it to a DateTimeOffset value, you can safely convert it to UTC, and save a DateTime value as an unambiguous point in time. From that you can convert it to any time zone so that you can display it in anyones local time if you like.
Guffa
@Guffa thanks I think I am finally beginning to understand all this! Just one more thing tho. Sometimes I need to parse dates sent in from the users themselves (for example in the body of an email). I would have to treat this date as Local time. How would I store that as UTC? Would I need to get the time offset (based on the timezone) and then convert it to `DateTimeOffset` and then UTC datetime?
James
@James: To convert the date to UTC you need either a time zone (DateTime + TimeZoneInfo) or a time offset (DateTimeOffset). For time zone conversion you use the `TimeZoneInfo.ConvertTimeToUtc` method. For time offset conversion you use the `DateTimeOffset.ToUniversalTime` method.
Guffa
Thanks Guffa, finally got my application working as expected!
James
+2  A: 

The safest way to handle date/time correctly is to store everything as UTC and display it in local time. All date/time math should be done in UTC as Guffa suggests. Store in UTC and convert to local time on the fly as you display it.

How to make a Time Zone aware date/time

Microsoft has an article on how to encapsulate a DateTime and TimeZoneInfo variable into a structure here.

Here's Microsoft's sample structure with 1 property added to easily get local time. This needs more work to be a fully useful, but it's a good start.

public struct TimeZoneTime
{
   public TimeZoneInfo TimeZone;
   public DateTimeOffset Time;

   public TimeZoneTime(DateTimeOffset time)
   {
      this.TimeZone = TimeZone.Local;
      this.Time = time;   
   }

   public TimeZoneTime(TimeZoneInfo tz, DateTimeOffset time)
   {
      if (tz == null) 
         throw new ArgumentNullException("The time zone cannot be a null reference.");

      this.TimeZone = tz;
      this.Time = time;   
   }

   public TimeZoneTime AddTime(TimeSpan interval)
   {
      // Convert time to UTC
      DateTimeOffset utcTime = TimeZoneInfo.ConvertTime(this.Time, TimeZoneInfo.Utc);      
      // Add time interval to time
      utcTime = utcTime.Add(interval);
      // Convert time back to time in time zone
      return new TimeZoneTime(this.TimeZone, TimeZoneInfo.ConvertTime(utcTime, this.TimeZone));
   }

    public DateTime LocalDate 
    {
        get { return Time.ToOffset(TimeZone); }
    }
}

Your Scenario

  1. Yes, use either the mail object's ReceivedTime or SentOn and convert it to UTC for storage & calculations. This is much less complex than the samples above.

    Message msg = new Message();
    DateTime received = msg.ReceivedTime.ToUniversalTime();
    received.AddDays(7);
    Console.WriteLine(received.ToLocalTime());
    
chilltemp
@chilltemp: Should I store it as server timezone UTC or local timezone UTC?
James
@James: I don't understand what you mean between server and local timezone UTC. UTC is UTC, there are no variations.
chilltemp
@James - If you don't know whether to use a server's time or a client's time, then always use the time on the server. (In any case it should be UTC as @chilltemp says.) Using the time on the server makes the data consistant for all users, and it is better for security, since you generally don't control the client's time settings.
Jeffrey L Whitledge
@Jeffrey: That was what I mean lol thanks!
James
@James: @Jeffery is correct. Server time is best for constancy. The exception would be offline clients. You will need to use the client's time (as UTC) if the client does not have an active channel to the server, and will be communicating the data at a later time (such as batch communications).
chilltemp
Please see updated post
James