9

My requirement here is fairly standard; I need to allow users to select their current TimeZone and save it against their account. I will then use this value to to convert stored DateTime values to a localised time.

My current thinking is that I will allow users to select a .NET TimeZoneInfo.Id from a list (held in the DB to allow more friendly descriptions but constructed using TimeZoneInfo.GetSystemTimeZones()) - I will then use this value to return the relevant TimeZoneInfo instance using TimeZoneInfo.FindSystemTimeZoneById() and perform the conversion. The main problem I see here is that TimeZoneInfo.Id is a value held in the registry and is not a "standard" Id (compared to Olson for example). As such, it is possible that server updates/migrations may invalidate the stored Ids completely and break the conversions..

In short - is this approach valid/safe? If not, is there a better way of storing a users timezone preference while also handling daylight savings etc without tons of additional logic?

Matt Weldon
  • 1,413
  • 11
  • 18
  • Have you looked at [`DateTimeOffset`](http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx)? Or [`NodaTime`](http://code.google.com/p/noda-time/)? – Oded Sep 01 '12 at 21:10
  • Related: http://stackoverflow.com/q/2532729/1583 – Oded Sep 01 '12 at 21:14
  • 1
    @Oded The problem with DateTimeOffset is that it is not Day Light Savings aware – paparazzo Sep 01 '12 at 21:18
  • @Blam - Yes, I am well aware of that. That's why I also suggested `NodaTime`. – Oded Sep 01 '12 at 21:18
  • If you want the local time zone why not just use TimeZoneInfo.Local.Id? If you want a list to select from then can use TimeZoneInfo.GetSystemTimeZones(). – paparazzo Sep 01 '12 at 21:34
  • @Blam - As I mentioned in my question, my first thought would be to use the `TimeZoneInfo.Id` from a list created using `TimeZoneInfo.GetSystemTimeZones()`. My main concern is that these Ids don't appear to be standardised and therefore subject to change across servers (due to different versions of .NET/Windows) - my concerns here may well be unfounded though. – Matt Weldon Sep 02 '12 at 21:06
  • No .GetSystemTimeZones() is NOT in your current question statement. – paparazzo Sep 02 '12 at 22:34
  • Apologies - updated question to reduce ambiguity about what method would be used to retrieve the TimeZoneId's.. – Matt Weldon Sep 03 '12 at 07:53

1 Answers1

9

I just found this old-ish unanswered question, so thought I should take a stab at it.

In short - is this approach valid/safe?

It all depends on what you plan to do with them.

If you are just using it in server-side code, then yes. You simply store the Id of the timezone, and show the user the corresponding DisplayName. FindSystemTimeZoneById and GetSystemTimeZones are perfectly valid for this.

Keep in mind that while the Id values are always the same - the DisplayName properties will be different based on the language of the Windows operating system that you are running the code on. The implementation is not culture aware, so just setting a different target culture in .Net will not change the DisplayName string.

The records in the Microsoft Windows time zone database are reasonably stable, and are kept updated via Windows Update. Some of the information comes from the registry, but the localized resource strings come from tzres.dll. Of course, all of that is hidden from you by TimeZoneInfo and related classes.

However, if you are passing these time zone Id values to other systems - watch out. There are some variations with prior versions of Windows. For example, I know that the display names used to all say "GMT" in them, and now more correctly say "UTC". The Id values are the same, but who knows exactly what else is not consistent. Especially if the target computer hasn't received the same set of Windows Updates that you have. By the way - the updates are announced here.

You should also know a few things about the Windows time zones database:

  • It is incapable of representing more than two DST transitions per calendar year. For example - in 2010, Cairo, Egypt had four transitions. Microsoft issued a hotfix, but that only made a compromise correction, not a historically accurate one. There are other zones that have historical changes like this that cannot be represented properly.

  • Some things changed in between Windows XP/2003 and Vista/2008. There is some really good information about this here, along with a lot of detail about how the registry is used.

  • Microsoft is the only major player with their own time zone database. The rest of the world uses the IANA/Olson database. This leads to interoperability problems. I have a decent write-up of this in the timezone tag wiki.

You should also know that TimeZoneInfo is inextricably linked to the DateTime and DateTimeOffset classes. These have their own set of quirks. DateTimeOffset is somewhat usable, but DateTime is riddled with nuance. Read:

A far better solution to date and time in .Net is to use NodaTime. This library implements both time zone databases, including the CLDR mappings between them. It also provides a much safer API that doesn't let you get into trouble. It may take a bit of re-learning, but before you know it, you will be using classes like LocalDateTime, ZonedDateTime, OffsetDateTime and Instant.

You still have the problem that the time zone database is updated frequently, as we leave timekeeping rules up to the politicians. But the TZDB folks do a pretty good job of keeping the database historically accurate, and providing aliases/links when zone names change. You can see a list of the tz names here.

Also, if you go with NodaTime, you can choose to either stick with the copy of the TZDB compiled into the distribution, or you can ship your own copy with your application. You could (theoretically) write your own code to pull down the latest version from IANA and keep your application updated - all without relying on the host operating system.

Community
  • 1
  • 1
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • The whole timezoneinfo.ID thing does not make sense. The ID is expected to be an Identifier that follows some ISO standard way. However, I just found out that `FindSystemTimeZoneById` does not work correctly across different versions of Windows. In one version, the string returned is all lower case and the other one it is camel case and to make the matters worse, the FindSystemTimeZoneById is not case insensitive. Go figure! – Rajiv Nov 07 '15 at 00:07
  • @Rajiv - There are no ISO standards for time zones. IANA/Olson identifiers (mentioned above) are the closest thing we have to a standard. Regarding the Windows IDs, I've never encountered any that are lowercased, unless they were mangled by something else. As mentioned above, the IDs should be consistent across systems unless you're talking about a newly-introduced time zone that has been installed on one system but not another. Example, [KB3093504](https://support.microsoft.com/en-us/kb/3093503) introduced "North Korea Standard Time" in a hotfix. – Matt Johnson-Pint Nov 07 '15 at 05:06