Javascript Chrome将不带Z的ISO时间解释为UTC;C#问题

Javascript Chrome将不带Z的ISO时间解释为UTC;C#问题,javascript,asp.net,datetime,utc,Javascript,Asp.net,Datetime,Utc,在Chrome、FF和IE上运行以下JSFIDLE: 铬: 火狐: 即: 狩猎: 似乎没有说明如何解释没有尾随Z的字符串 我们的服务器(ASP.NET MVC4)将UTC时间作为DateTimes从数据库中提取出来,并将其简单地填充到JSON中。正如您所看到的,正因为如此,我们在浏览器上得到了不一致的结果 我们是否应该在服务器端将Z附加到它们 我们是否应该在服务器端将Z附加到它们 TL;DR是的,你可能应该这样做 遗憾的是,多年来,在规范和JavaScript引擎遵守规范方面,没有时区的

在Chrome、FF和IE上运行以下JSFIDLE:

铬:

火狐:

即:

狩猎:

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

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

我们是否应该在服务器端将Z附加到它们

我们是否应该在服务器端将Z附加到它们

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

遗憾的是,多年来,在规范和JavaScript引擎遵守规范方面,没有时区的日期和日期/时间的正确处理方式各不相同

当这个答案最初在2013年编写时,规范(第一个为JavaScript定义了日期/时间格式)是明确的:No timezone=UTC:

缺少时区偏移的值为“Z”

这与ES5日期/时间格式的基础不符,在ES5格式中,缺少时区指示器意味着“本地时间”。一些实现从未实现ES5的含义,而是坚持ISO-8601

在(又名“ES6”)中,将其更改为符合ISO-8601:

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

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

如果没有时区偏移,则仅日期表单将解释为UTC时间,日期时间表单将解释为本地时间

因此,
新日期(“2018-07-01”)
被解析为UTC,但
新日期(“2018-07-01T00”)
被解析为本地时间

自2017年欧洲标准日和即将到来的2018年欧洲标准日以来,该标准一直保持一致,也有上面的文字

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

功能测试(val,预期){
var结果=+val===预期?“良好”:“错误”;
log(val.toISOString(),expect.toISOString(),result);
}
测试(新日期(“2018-07-01”)、新日期(UTC日期(2018年6月1日));

测试(新日期(“2018-07-01T00:00:00”),新日期(2018,6,1))最后,如果我的服务器总是以所有浏览器都能正确处理的格式发送客户端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日期中缺少本地时区信息时附加它。这可以用来代替新日期()。部分源自此答案:

parseDate=function(/*String*/d){
如果(d.search(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/)==0){
var pad=函数(num){
norm=Math.abs(Math.floor(num));
返回值(范数<10?'0':'')+norm;
},
tzo=-(新日期(d)).getTimezoneOffset(),
符号=tzo>=0?'+':'-';
返回新日期(d+符号+pad(tzo/60)+':'+pad(tzo%60));
}否则{
返回新日期(d);
}
}
大卫·哈蒙德的电影很棒,但并没有玩所有的把戏;下面是一个修改版本:

  • 允许日期/时间字符串中的小数部分
  • 允许在日期/时间字符串中选择秒
  • 考虑跨越夏令时
appendTimezone=函数(/*String*/d){
//检查ISO 8601日期时间字符串(秒和小数部分可选)
如果(d.search(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\。\d{1-3})?$/)=0){
var pad=函数(num){
norm=Math.abs(Math.floor(num));
返回值(范数<10?'0':'')+norm;
},
tzo=-new Date(d).getTimezoneOffset(),
符号=tzo>=0?'+':'-';
调整后的var=d+符号+焊盘(tzo/60)+':'+焊盘(tzo%60);
//检查时区偏移是否相等;
//如果没有,则指定的日期正好在时钟关闭时的小时内
//已向前或向后翻转
如果(-new Date(adjusted).getTimezoneOffset()!=tzo){
//重新调整
tzo-=60;
调整后=d+符号+焊盘(tzo/60)+':'+焊盘(tzo%60);
}
调整收益率;
}否则{
返回d;
}
}
parseDate=函数(/*String*/d){
返回新日期(附加时区(d));
}

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

下面是.NetCore 1的解决方案

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

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

但是,您也应该注意到这一点。JS日期解析中没有任何内容是一致的:-(哇,这肯定是ECMAScript规范所说的-但它非常不一致,IMO-并且直接与ISO-8601相抵触,ISO-8601指出:“如果根据4.2.2.2到4.2.2.4使用本地时间,区域指示符是空的。”叹气。请注意,考虑到它将被解析为一个瞬间,我认为整个API都被破坏了。双叹气。@Bergi:这是因为这是ECMAScript 5的新版本。令人惊讶的是,ev
services.AddMvc().AddJsonOptions(o => o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc);
services
    .AddMvcCore()
    .AddJsonFormatters(o => o...);