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

时间:2013-02-17 17:35:19

标签: javascript asp.net datetime utc

在Chrome,FF和IE上运行此jsfiddle:http://jsfiddle.net/E9gq9/7/,您就会得到:

铬:

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

火狐:

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

IE:

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

Safari浏览器:

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

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

我们的服务器(ASP.NET MVC4)将UTC时间从我们的数据库中拉出DateTime并简单地将它们填入JSON中。正如您所看到的,我们在浏览器上得到了不一致的结果。

我们应该在服务器端向他们添加Z吗?

5 个答案:

答案 0 :(得分:11)

  

我们应该在服务器端向他们添加Z吗?

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

多年来,在规范和JavaScript引擎遵守规范方面,没有时区的日期和日期/时间的正确处理可能会有所不同。

当这个答案最初写于2013年时,ES5规范(第一个具有JavaScript的定义日期/时间格式)是明确的:没有时区= UTC:

  

缺席时区偏移的值为“Z”。

这与ES5日期/时间格式所基于的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")被解析为本地时间。

自ES2017和即将推出的ES2018以来,它一直保持一致; here's the link to the current draft standard,也有上述文字。

您可以在此处测试当前的浏览器:

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

截至2018年4月的状态:

  • IE11正确(有趣的是)
  • Firefox正确
  • Chrome 65(桌面版,Android版)正确
  • Chrome 64(iOS v11.3)错误地显示日期/时间表(解析为UTC)
  • v11.3中的iOS Safari获取错误的日期/时间表(解析为UTC)

奇怪的是,我在v6问题列表中找不到问题,这个问题已在v6.4(Chrome 64中的v8)和v6.5(Chrome 65中的v8)之间修复;我只能找到仍然打开的this issue,但似乎已经修复了。

答案 1 :(得分:6)

在一天结束时,如果我的服务器始终以所有浏览器正确处理的格式发送客户端DateTime对象,则可以解决我在此应用中遇到的问题。

这意味着最后必须有'Z'。原来ASP.NET MVC4 Json序列化程序基于Json.NET,但默认情况下没有打开Utc。 DateTimeZoneHandling的默认值似乎是RoundtripKind,即使DateTime.Kind == Utc DateTimeZoneHandling.Utc,也不会输出带有Z的值,这非常令人讨厌。

所以修复似乎是,设置Json.NET处理时区为var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; // Force Utc formatting ('Z' at end). json.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; 的方式:

{{1}}

现在,从我的服务器到浏览器的所有内容都被格式化为ISO-8601,最后带有'Z'。我测试过的所有浏览器都做对了。

答案 2 :(得分:2)

我遇到了这个问题,用本地时区解释日期比改为“Z”更有意义,至少在我的应用程序中。我创建了这个函数,以便在ISO日期中缺少本地时区信息。这可以用来代替新的Date()。部分源于这个答案: How to ISO 8601 format a Date with Timezone Offset in JavaScript?

parseDate = function (/*String*/ d) {
    if (d.search(/^\d{4}-\d{2}-\d{2}T\d{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);
    }
}

答案 3 :(得分:0)

大卫·哈蒙德的answer很棒,但并没有发挥所有技巧;所以这是一个修改版本:

  • 允许日期/时间字符串中的小数部分
  • 允许日期/时间字符串
  • 中的可选秒
  • 考虑跨越夏令时
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}T\d{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));
}

答案 4 :(得分:0)

除了@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...);