Thomas
Thomas

Reputation: 12107

Handling timezones with daylight saving in C#

We have an application related to aviation, and specifically flights.

Times have to be stored in local, so I chose to use UTC Time + an offset, but then now I realize it is a bad choice:

By storing the timezone as an offset, I lose track of the original timezone and this has implications when dealing with daylight savings.

For example I can store a time in Alpine, UT as UTC time and a -6 offset and also a time in Phoenix, AZ as UTC time and a -6 offset.

But when daylight saving comes, the time will change in Alpine, but not in Phoenix.

So, I need to store the proper timezone and I have seen that there are also different lists with a different syntax, so I am assuming there are different standards.

In C#, what would be the best option to store a local time with the local time zone to make it work with daylight saving changes?

Upvotes: 3

Views: 13780

Answers (3)

ksemenenko
ksemenenko

Reputation: 11

You should use TimeZoneInfo.ConvertTime to convert DateTime between time zones if you need to handle Daylight Savings.

TimeZoneInfo.ConvertTime(myDateTime, timeZone);

TimeZoneInfo.ConvertTime(myDateTime, fromTimeZone, toTimeZone);

Upvotes: 0

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241525

From the discussion in the question's comments, I understand that you are working with flight time schedules - that is, the time a future flight is intended to depart. This is indeed a case where the local time is more important than the UTC time.

Since you have the local time and location of departure (ex: 5:00 PM in Salt Lake City), then you should be storing in your database of scheduled departure times two values:

  • 17:00 - The relevant local time of the departure
  • SLC - The location where the time is relevant

If this is a specific occurrence of this flight, then you should store the date as well:

  • 2018-06-01T17:00 - The specific relevant local time of the departure
  • SLC - The location where the local time is relevent

These are the details that are contextually relevant to your business use case. Do not lose sight of them by converting them to UTC.

That said, you might consider storing them as a DateTimeOffset (2018-06-01T17:00-06:00), which makes converting to UTC trivial for a given instance. However there are two problems with this approach:

  • It cannot work with recurrences, because the offset may change.
  • Even for a single instance, the offset might change - if the government controlling the time zone decides to change their standard offset or daylight saving time rules before that occurrence takes effect. If you do take a DateTimeOffset approach, or a UTC-based approach, you must be prepared to recalculate future events in the face of such changes. (For more on this, see my blog articles: On the Timing of Time Zone Changes and Time Zone Chaos Inevitable in Egypt. There are countless other examples.)

With regards to the location - because you are working with data that is contextually applicable to the airline industry, I recommend using IATA airport codes like the SLC that I showed above. In other contexts, one might store an IANA time zones identifier like America/Denver, or a Windows time zone identifier like Mountain Standard Time.

You may find my "Airport Time Zones" gist (code and output table) useful for working with IATA airport codes. You'll have to decide how that data will flow through your system. If you are running on Windows and want to use the TimeZoneInfo class to convert times to different time zones, then use the Windows time zone IDs shown there. If you want to use the IANA time zone IDs instead, consider using Noda Time, or you can use my TimeZoneConverter library. There are several different options here, so explore them all carefully and pick ones that make sense to you.

Noda Time would be a great choice, IMHO. Not only would you get excellent time zone support, but you'd also be able to use types like LocalTime or LocalDateTime which align well with the scenarios described.

Upvotes: 6

Zohar Peled
Zohar Peled

Reputation: 82474

As I wrote in my comment, do not store local dates. Instead, store datetime values as UTC, and convert to local datetime when you need to display it.
You can use the ConvertTimeFromUtc method of the TimeZoneInfo class for that.

This means you will have to also keep a list of locations and whatever TimeZoneInfo they are associated in - For example,
Jerusalem would be associated with Israel Standard Time,
Rome with W. Europe Standard Time,
Hawaii with Hawaiian Standard Time
and so on. (I'll bet you can find such a list online somewhere.)
Please note that the ConvertTimeFromUtc method handles the daylight savings problem for you as well.

Then you can do something like this to get the local time by location:

DateTime GetLocalDateByCityName(DateTime utc, string cityName)
{
    var timeZoneInfoId = GetTimeZoneInfoIdByCityName(string cityName);
    return TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.FindSystemTimeZoneById(timeZoneInfoId);
}

And of course, in GetTimeZoneInfoIdByCityName you get the TimeZoneInfoId for the specific city.

Upvotes: 1

Related Questions