Converting any date time from server time to user

2019-09-07 03:38发布

I am working on a web app using .NET MVC 3 and SQL server 2008 (r2).

Anyway, I have a date time object and I want to convert it to user time. It is fairly trivial to convert right now to user time; I have some java script that will get me the users offset from UTC. And I know my date times offset to UTC.

What I realized yesterday, is that my user's offsets will change, if they live in a pesky area of the world. And that would cause old dates and time to be wrong by some amount of time.

Now I know C# has some utilities to convert into a time zones time, but do they really handle all of the intricacies of daylights savings?

For example. If I have a time, 10-10-2001 8:00:00 how do I get that into pacific time? Or if i have 6-6-2004, how do i get that into pacific time?

Since the dates change for the roll overs, it is pretty complex, and I would like a general solution that does not require maintenance to set up date ranges for time zones.

I know this is a classic problem in CS, but I cannot find something that really answers my question 100%. From what I have seen: C# uses the current year's daylights savings dates to change every years date times. Which could cause some mistakes.

Thank you so much for the help.

2条回答
Ridiculous、
2楼-- · 2019-09-07 04:21

I have found the following rules to be the best solution:

  1. At server-side use UTC. No need to deal with Time Zones at all. That means that if you store dates in database, use UTC dates, if you generate dates in server side use generally UTC (e.g: var nowDate =DateTime.UtcNow). The server dates should be location agnostic and that way there is no confusion.
  2. At client-side use UTC as well when dealing with raw data, but display the dates in the UI converted to the browser's local time (which will be whatever time is configured in the operating system). The good news is that this is already a built in capability in browsers! we just need to make it easier for them to handle it. This means that if we have a javascript client code POSTing/PUTing/PATCHing data to a server and GETting data from the server using JSON, the JSON should use the ISO 8601 (natively supported) in UTC (ended in Z) as if you were doing:

    var dateNow = new Date(); //Wed May 11 2016 13:06:21 GMT+1200 (New Zealand Standard Time)
    var dateNowIso = dateNow.toISOString(); //"2016-05-11T01:06:21.147Z"
    

And then, simply rely on the browser capability to convert automatically any UTC date to the current local time and vice versa. The browser's javascript engine will be able to display in the UI the correct local time as long as the format is a Date object.

NOTE on how to implement it for a .NET server & Javascript client scenario: For example, I am using AngularJS (with restangular) in my client javascript code and MVC 6 (ASP.NET Core 1.0) in server side.

The JSON that comes from the server might contain date properties, but in JSON the type is a string such as:

{
  "myDateField":"2016-05-11T05:00:00Z"
}

In order to rely on the browser's capability to handle properly UTC times and convert them to local browser's time I need to parse this UTC date string into a real javascript Date object, so I use a regular expression to match any text value that looks like a UTC date in ISO 8601 format (in my case this code is in a restangular response interceptor but it could be anywhere):

const regexIso8601 = /((((\d{4})(-((0[1-9])|(1[012])))(-((0[1-9])|([12]\d)|(3[01]))))(T((([01]\d)|(2[0123]))((:([012345]\d))((:([012345]\d))(\.(\d+))?)?)?)(Z|([\+\-](([01]\d)|(2[0123]))(:([012345]\d))?)))?)|(((\d{4})((0[1-9])|(1[012]))((0[1-9])|([12]\d)|(3[01])))(T((([01]\d)|(2[0123]))(([012345]\d)(([012345]\d)(\d+)?)?)?)(Z|([\+\-](([01]\d)|(2[0123]))([012345]\d)?)))?))/;

function convertDateStringsToDates(input: any): void {
    // Ignore things that aren't objects.
    if (typeof input !== "object") {
        return input;
    }

    for (var key in input) {
        if (!input.hasOwnProperty(key)) {
            continue;
        }

        let value = input[key];
        let match: RegExpMatchArray;
        // Check for string properties which look like dates.
        if (angular.isArray(value)) {
            angular.forEach(value, (val, key) => {
                convertDateStringsToDates(val);
            });
        } else if (angular.isString(value) && (match = value.match(regexIso8601))) {
            let milliseconds = Date.parse(match[0]);
            if (!isNaN(milliseconds)) {
                input[key] = new Date(milliseconds);
            }
        } else if (angular.isObject("object")) {
            // Recurse into object
            convertDateStringsToDates(value);
        }
    }
}

That way I can use my javascript object with proper Date objects.

In the server side (either MVC6 or WebAPI2..) I have an instruction to tell my JSON parser to ALWAYS use UTC formats when parsing Dates, otherwise it wouldn't add the Z character at the end, which is very important. Example for MVC6:

services.AddMvc().AddJsonOptions(opt => {
  // json dates always in javascript date format with UTC e.g: "2014-01-01T23:28:56.782Z"
  opts.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
});

Inspired by this article UPDATE: There are scenarios where it might be interesting to store data in local time as stated in the comments below.

查看更多
Viruses.
3楼-- · 2019-09-07 04:40

Now I know C# has some utilities to convert into a time zones time, but do they really handle all of the intricacies of daylights savings?

TimeZoneInfo does, yes1. (It's part of the .NET framework, not part of C# - C# is just the language you happen to be using.) However, I don't think that's what you really want to do.

Why are you storing the DateTime in the server's time zone anyway? It would be more sensible to store it in UTC, in most cases. Aside from anything else, if your server is in a time zone which observes daylight saving time, you will end up with ambiguity for one hour per year, when the clock goes back. (The same local time occurs twice.)

Once you've stored it as UTC, you should give it to your Javascript client as UTC too. While you say that you have "some java script that will get me the users offset from UTC" - that will depend on the exact instant in time. For example, as I'm in the UK, my offset is sometimes 0 and sometimes +1 hour. If you pass the UTC back to the client, that can work out the local time from that UTC time. Your server can't, unless you can get an accurate representation of the time zone from the client to the server, which is generally a tricky thing to do.

From what I have seen: C# uses the current year's daylights savings dates to change every years date times.

Again, C# itself isn't relevant here. It's not clear which part of the .NET framework you mean - TimeZone? TimeZoneInfo? DateTime? TimeZoneInfo has historical data, but only if you're on an operating system version which supports it.


1 Well, as far as you're likely to care. It doesn't have as much historical data as TZDB, and it has some very odd representations for Russia and Namibia, but it does generally have the idea of rules changing.

查看更多
登录 后发表回答