Javascript Chrome将不带Z的ISO时间解释为UTC;C#问题
在Chrome、FF和IE上运行以下JSFIDLE: 铬: 火狐: 即: 狩猎: 似乎没有说明如何解释没有尾随Z的字符串 我们的服务器(ASP.NET MVC4)将UTC时间作为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引擎遵守规范方面,没有时区的
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...);