我发现以下规则是最好的解决方案:
- 在服务器端使用 UTC。根本不需要处理时区。这意味着如果您将日期存储在数据库中,请使用 UTC 日期,如果您在服务器端生成日期,则通常使用 UTC(例如:)
var nowDate =DateTime.UtcNow
。服务器日期应该与位置无关,这样就不会有混淆。
在客户端处理原始数据时也使用 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.