0

我正在使用 .NET MVC 3 和 SQL Server 2008 (r2) 开发 Web 应用程序。

无论如何,我有一个日期时间对象,我想将其转换为用户时间。现在转换为用户时间相当简单;我有一些 java 脚本可以让我从 UTC 偏移用户。而且我知道我的日期时间偏移到 UTC。

我昨天意识到,如果我的用户生活在世界上令人讨厌的地区,他们的偏移量将会改变。这会导致旧日期和时间在一段时间内出现错误。

现在我知道 C# 有一些实用程序可以转换为时区时间,但它们真的能处理所有复杂的夏令时吗?

例如。如果我有时间,2001 年 10 月 10 日 8:00:00 我怎么把它变成太平洋时间?或者,如果我有 2004 年 6 月 6 日,我该如何进入太平洋时间?

由于翻转的日期会发生变化,所以它非常复杂,我想要一个不需要维护来设置时区日期范围的通用解决方案。

我知道这是 CS 中的一个经典问题,但我找不到真正 100% 回答我的问题的东西。据我所知:C# 使用当年的夏令时日期来更改每年的日期时间。这可能会导致一些错误。

十分感谢你的帮助。

4

2 回答 2

2

现在我知道 C# 有一些实用程序可以转换为时区时间,但它们真的能处理所有复杂的夏令时吗?

TimeZoneInfo是的,是的1。(它是 .NET 框架的一部分,而不是 C# 的一部分 - C# 只是您碰巧使用的语言。)但是,我认为这不是您真正想要做的。

你为什么要存储DateTime服务器的时区呢?在大多数情况下,将其存储在 UTC 中会更明智。除此之外,如果您的服务器位于遵守夏令时的时区,那么当时钟倒退时,您每年都会有一个小时的歧义。(同一本地时间出现两次。)

将其存储为 UTC 后,您也应该将其作为 UTC 提供给 Javascript 客户端。虽然您说您有“一些 java 脚本可以让我的用户从 UTC 偏移” - 这将取决于确切的时间。例如,当我在英国时,我的偏移量有时为 0,有时为 +1 小时。如果您将 UTC 传递回客户端,则可以从该 UTC 时间计算出本地时间。您的服务器不能,除非您可以准确地表示从客户端到服务器的时区,这通常是一件棘手的事情。

据我所知:C# 使用当年的夏令时日期来更改每年的日期时间。

同样,C# 本身在这里不相关。目前尚不清楚您指的是 .NET 框架的哪一部分 - TimeZoneTimeZoneInfo? DateTime? TimeZoneInfo有历史数据,但前提是您使用的操作系统版本支持它。


1好吧,就你可能关心的问题而言。它没有 TZDB 那么多的历史数据,而且它对俄罗斯和纳米比亚有一些非常奇怪的表示,但它通常确实有规则改变的想法。

于 2012-11-29T16:44:29.000 回答
0

我发现以下规则是最好的解决方案:

  1. 在服务器端使用 UTC。根本不需要处理时区。这意味着如果您将日期存储在数据库中,请使用 UTC 日期,如果您在服务器端生成日期,则通常使用 UTC(例如:)var nowDate =DateTime.UtcNow服务器日期应该与位置无关,这样就不会有混淆。
  2. 在客户端处理原始数据时也使用 UTC,但在 UI 中显示转换为浏览器本地时间的日期(这将是操作系统中配置的任何时间)。 好消息是这已经是浏览器的内置功能了!我们只需要让他们更容易处理它。这意味着,如果我们有一个 javascript 客户端代码 POST/PUTing/PATCHing 数据到服务器并使用 JSON 从服务器获取数据,则 JSON 应该使用 UTC(以 Z 结尾)的 ISO 8601(本机支持),就好像你是正在做:

    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"
    

然后,只需依靠浏览器功能将任何 UTC 日期自动转换为当前本地时间,反之亦然。只要格式是Date对象,浏览器的 javascript 引擎就能够在 UI 中显示正确的本地时间。

关于如何为 .NET 服务器和 Javascript 客户端场景实现它的注意事项:例如,我在客户端 javascript 代码中使用 AngularJS(带有 restangular),在服务器端使用 MVC 6(ASP.NET Core 1.0)。

来自服务器的 JSON 可能包含日期属性,但在 JSON 中,类型是字符串,例如:

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

为了依靠浏览器正确处理 UTC 时间并将它们转换为本地浏览器时间的能力,我需要将此 UTC 日期字符串解析为真正的 javascript Date 对象,因此我使用正则表达式来匹配任何看起来像的文本值ISO 8601 格式的 UTC 日期(在我的情况下,此代码在一个 restangular 响应拦截器中,但它可能在任何地方):

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);
        }
    }
}

这样我就可以将我的 javascript 对象与适当的 Date 对象一起使用。

在服务器端(MVC6 或 WebAPI2..)我有一条指令告诉我的 JSON 解析器在解析日期时始终使用 UTC 格式,否则它不会在末尾添加 Z 字符,这非常重要。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.

于 2016-05-11T01:24:57.153 回答