Chrome将没有Z的ISO时间解释为UTC;C#问题



运行这个jsfiddle:http://jsfiddle.net/E9gq9/7/在Chrome、FF和IE上,您可以获得:

铬:

铬http://images.devs-on.net/Image/vBTz86J0f4o8zlL3-Region.png

Firefox:

Firefoxhttp://images.devs-on.net/Image/aNPxNPUpltyjVpSX-Region.png

IE:

IEhttp://images.devs-on.net/Image/WXLM5Ev1Viq4ecFq-Region.png

Safari:

Safarihttp://images.devs-on.net/Image/AEcyUouX04k2yIPo-Region.png

ISO 8601似乎没有说明应该如何解释没有尾随Z的字符串。

我们的服务器(ASP.NET MVC4)将UTC时间作为DateTimes从数据库中提取出来,并简单地将其填充到JSON中。正如你所看到的,正因为如此,我们在浏览器上得到了不一致的结果。

我们应该在服务器端将Z附加到它们上吗?

我们应该在服务器端将Z附加到它们上吗?

TL;DR是的,您可能应该这样做。

遗憾的是,在没有时区的情况下,对日期和日期/时间的正确处理多年来一直存在差异——无论是在规范方面还是在JavaScript引擎对规范的遵守方面。

当这个答案最初写在2013年时,ES5规范(第一个为JavaScript定义日期/时间格式的规范,本应是ISO-8601的子集)是明确的:无时区=UTC:

缺少时区偏移量的值为"Z"。

但这与ISO-8601不一致,在ISO-8601中,没有时区指示器意味着";当地时间"有些实现从未实现ES5的含义,而是坚持使用ISO-8601。

在ES2015(又名"ES6")中,它被更改为匹配ISO-8601:

如果没有时区偏移,则将日期时间解释为本地时间。

然而,这导致了与现有代码的不兼容问题,尤其是与2018-07-01等仅限日期的表单的不兼容,因此在ES2016中再次更改:

如果没有时区偏移,则只显示日期的表格将被解释为UTC时间,日期-时间的表格将解释为本地时间。

因此new Date("2018-07-01")被解析为UTC,但new Date("2018-07-01T00")被解析为本地时间。

自2017年ES2017和即将到来的2018年ES2018以来,它一直保持一致;这是当前编辑草稿的链接,该草稿不再有确切的文本,但仍以相同的方式定义(尽管IMHO不太清楚)。

你可以在这里测试你当前的浏览器:

function test(val, expect) {
  var result = +val === +expect ? "Good" : "ERROR";
  console.log(val.toISOString(), expect.toISOString(), result);
}
test(new Date("2018-07-01"), new Date(Date.UTC(2018, 6, 1)));
test(new Date("2018-07-01T00:00:00"), new Date(2018, 6, 1));

截至2021年9月的状态:

  • 现代版本的Firefox、Chrome和Safari都做到了这一点,包括iOS浏览器(目前都使用苹果的JavaScriptCore JavaScript引擎,因为如果他们使用JIT,苹果不会允许他们使用自己的)
  • IE11做对了(有趣的是)

截至2018年4月的状态:

  • IE11做对了(有趣的是)
  • Firefox做对了
  • Chrome 65(台式机、安卓系统)做到了
  • Chrome 64(iOS v11.3)的日期/时间格式错误(解析为UTC)
  • iOS Safari v11.3中的日期/时间格式错误(解析为UTC)

奇怪的是,我在v8问题列表中找不到在v6.4(Chrome 64中的v8)和v6.5(Chrome 65中的v八)之间修复的问题;我只能发现这个问题仍然悬而未决,但似乎已经解决了。

如果我的服务器总是以所有浏览器都能正确处理的格式发送客户端DateTime对象,那么我在这个应用程序中面临的问题就可以解决了。

这意味着末端必须有"Z"。事实证明,ASP.NET MVC4 Json序列化程序是基于Json.NET的,但默认情况下没有打开Utc。DateTimeZoneHandling的默认值似乎是RoundtripKind,这不会输出带有Z的值,即使是DateTime.Kind == Utc,这也很烦人。

因此,修复方法似乎是,将Json.NET处理时区的方式设置为DateTimeZoneHandling.Utc:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
// Force Utc formatting ('Z' at end). 
json.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;

现在,从我的服务器到浏览器的所有连接都被格式化为ISO-8601,末尾有一个"Z"。我测试过的所有浏览器都能用它做正确的事情。

我遇到了这个问题,用本地时区解释日期比更改为"Z"更有意义,至少对我的应用程序来说是这样。我创建这个函数是为了在ISO日期中缺少本地时区信息时附加它。这可以用来代替新的Date()。部分源自这个答案:ISO 8601如何在JavaScript中设置带时区偏移的日期格式?

parseDate = function (/*String*/ d) {
    if (d.search(/^d{4}-d{2}-d{2}Td{2}:d{2}:d{2}$/) == 0) {
        var pad = function (num) {
            norm = Math.abs(Math.floor(num));
            return (norm < 10 ? '0' : '') + norm;
        },
        tzo = -(new Date(d)).getTimezoneOffset(),
        sign = tzo >= 0 ? '+' : '-';
        return new Date(d + sign + pad(tzo / 60) + ':' + pad(tzo % 60));
    } else {
        return new Date(d);
    }
}

David Hammond的回答很好,但并没有发挥出所有的技巧;所以这里有一个修改版本:

  • 允许在日期/时间字符串中使用小数部分
  • 允许在日期/时间字符串中选择秒
  • 考虑跨越夏令时
appendTimezone = function (/*String*/ d) {
    // check for ISO 8601 date-time string (seconds and fractional part are optional)
    if (d.search(/^d{4}-d{2}-d{2}Td{2}:d{2}(?::d{2}(?:.d{1-3})?)?$/) == 0) {
        var pad = function (num) {
            norm = Math.abs(Math.floor(num));
            return (norm < 10 ? '0' : '') + norm;
        },
        tzo = -new Date(d).getTimezoneOffset(),
        sign = tzo >= 0 ? '+' : '-';
        var adjusted = d + sign + pad(tzo / 60) + ':' + pad(tzo % 60);
        // check whether timezone offsets are equal;
        // if not then the specified date is just within the hour when the clock
        // has been turned forward or back
        if (-new Date(adjusted).getTimezoneOffset() != tzo) {
            // re-adjust
            tzo -= 60;
            adjusted = d + sign + pad(tzo / 60) + ':' + pad(tzo % 60);
        }
        return adjusted;
    } else {
        return d;
    }
}
parseDate = function (/*String*/ d) {
    return new Date(appendTimezone(d));
}

除了@tig的答案(这正是我想要的):

以下是.NetCore 1 的解决方案

services.AddMvc();
services.Configure<MvcJsonOptions>(o =>
{
    o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
});

services.AddMvc().AddJsonOptions(o => o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc);

对于.NetCore 1.0.1

services
    .AddMvcCore()
    .AddJsonFormatters(o => o...);

相关内容

  • 没有找到相关文章

最新更新