How to properly work with Timezone?

2019-03-21 03:04发布

问题:

I'm reading a lot about timezone, offset, utc, local time, javascript functions, DST, bacon and I 'm trying to put this all together to build a solid/correct structure for my app.

Suppose my app is something like StackOverflow.

That is how I'm doing ...

  • The server is in another country, so I set it to UTC 00:00.
  • I'm storing date as DateTimeOffset.
  • I'm not storing TimeZoneID.
  • Date is being sent to the client in this format: 2012-07-19T14:30:00-03:00.
  • I'm using angular filter to convert it to local time.

I have a few questions about it ...

1º Server TimeZone?

About my server (single server) ... should it be running with a "neutral" UTC (+00:00)? And what if, in the future, we move to a farm where servers run on different locations?

2º What should I store?

Currently, I'm storing just date as DateTimeOffset. I'm reading about saving the TimeZoneID but I'm seeing no use at all for this. Am I missing something?

Or should I store date as DateTimeUtc with a TimeZoneID and manually convert every date with the TimeZone class?

3º How to convert to local time?

Is it safe to convert data on the client? Or date conversions should be always on the server side?

4º About DST.

Using my current approach. Will DST be respected?

回答1:

One very important thing to understand about date/time is that there is no one right way for doing everything. The common answer "use UTC" is not always applicable. Context is very important, and there are different techniques and approaches based on what the values you are working with are representing. If you could elaborate on what they are used for in your application, I will update my answer accordingly. In the meantime, I'll try to address the specific points you have brought up already:

#1 - Server Time Zone

Keeping your server at UTC is a best practice, and it is what you can expect from cloud providers like Azure or AWS also. But it isn't something that you should be dependent on. Your server should be able to be set to any time zone without it affecting your application. As long as the clock is in sync with an NTP server, choice of time zone should not matter.

So how do you ensure that? Simple, just make sure your application avoids all of the following:

  • DateTime.Now
  • DateTimeKind.Local
  • TimeZone (the entire class)
  • TimeZoneInfo.Local
  • DateTime.ToLocalTime()
  • DateTime.ToUniversalTime() (because it assumes the input is local)
  • Misc. other methods that assume a local input or output, such as TimeZoneInfo.ConvertTimeToUtc(DateTime) (this particular overload doesn't take a time zone, so it assumes the local time zone)

See also my blog post: The Case Against DateTime.Now.

Note that I didn't include DateTimeOffset.Now in the list. Although it's a little bit of a design smell, it is still "safe" to use.

#2 - What to store

I suggest you read my answer to DateTime vs DateTimeOffset. It should clarify some things. Without regurgitating the whole thing, the main point is that while both represent a point in time accurately, a DateTimeOffset provides perspective, while a UTC DateTime does not.

You also asked when you should store a TimeZoneInfo.Id. There are at least two scenarios where this is required:

  • If you are recording events in the past or present, and you plan on allowing modifications to the recorded timestamps. You need the time zone to determine what the new offset should be, or how the new input converts back to UTC.

  • If you are scheduling time out into the future, you will need the time zone as part of the recurrence pattern (even for a single occurrence). See here and here also, (while for other languages, the same principles apply).

Again, the exact answer depends on what exactly the timestamps represent. There is no one ring to rule them all.

#3 - Client Safety

If it's a .NET client, sure you can convert there. But I think you are asking about a JavaScript client browser.

"Safe" is a relative term. If you're asking for exact perfectness, then no. JavaScript isn't safe for that, due to an error in the ECMAScript specification (ES1 through ES5.1. It is being worked on for ES6). You can read more in my blog post: JavaScript Date type is horribly broken.

However, if you are working with relatively current data, and the users of your application are not in a part of the world where time zones are volatile, or you don't require precise results 100% of the time, then you can "safely" use JavaScript to convert to the user's local time zone.

You might avoid some of these issues with libraries that implement the IANA TZDB in JavaScript, such as the ones I list here. But many of them are still dependent on JS Date, so they still have issues. (Side note - I'm working on a JS library that will counter this, but it is not ready to share yet).

Conversions on the server side are a much better choice, as long as you can ask the user for their time zone. Most of the time, I think this is doable.

You might consider asking using a map-based timezone picker, such as this one or this one. Both of which will require you use IANA time zones, which for .NET means using Noda Time, which is a great idea anyway (IMHO).

#4 - Daylight Saving Time

With your current approach, DST will be respected within the definition of the current DST rules for the time zone the user has set for their local browser. (Again, refer to my blog post for why this is the case).

A conversion from any value with an offset (whether -03:00 or Z) that passes through the Date object (which I believe an Angular filter will do), will properly convert to the specific unix timestamp.

The errors that would crop up with DST conversions for prior DST rules are because going from the unix timestamp inside the Date object to the local time zone will always assume that the current DST rule is applicable, even if the time fell into a period that had a different rule.



回答2:

This actually depends on the actual application you're writing, but the most simple/robust approach IMO is to store/compute all your dates using UTC, and convert it back to the local time zone when you display it to the user.