在过去的几天里,我一直在经历同样的“问题”。
我将 Angular 7 用于前端,将 .Net Core Web API 用于后端。
Web API 将 JSON 有效负载中的日期时间作为以下格式的字符串返回给客户端:
2020-04-23T01:00:00
TypeScript/JavaScript 对象模型将日期字符串的数据类型保留为字符串类型。
当 bsDatepicker 和 timepicker 已按照以下示例代码附加(在本例中使用两种方式模型绑定)时,模型中日期字符串的类型从字符串类型更改为 JavaScript 日期类型。
<input name="AppliesEndDate{{index}}" type="text" class="form-control form-control-sm" bsDatepicker [(ngModel)]="costCentre.appliesEndDate" [disabled]="costCentre.totalActiveAssignments!==0" />
<timepicker [(ngModel)]="costCentre.appliesEndDate" [showMeridian]="showMeridian" [disabled]="costCentre.totalActiveAssignments!==0" [minuteStep]="1"></timepicker>
当模型通过 HTTP POST 或 PUT 发送回 Web API 服务器时,对象模型上的 Date 数据类型被序列化回字符串作为 UTC 格式的日期,如 Z 后缀所示:
2020-04-23T00:00:00.000Z
从这个可以得出结论,这是发生在客户端并且与 Angular 没有特别关系。这可能与 JavaScript 将 Date 类型转换为字符串的方式有关。
举这些例子。我特意选了两个日期。一个在英国夏令时 (GMT+0000) 之外,另一个在英国夏令时 (GMT+0100) 内
var date1 = new Date('2019-02-01T14:14:00');
var date2 = new Date('2019-05-01T14:14:00');
2019 年 2 月 1 日星期五 14:14:00 GMT+0000(格林威治标准时间)
2019 年 5 月 1 日星期三 14:14:00 GMT+0100(英国夏令时间)
运行以下语句以转换为 ISO 字符串:
console.log('toISOString ' + date1.toISOString());
console.log('toISOString ' + date2.toISOString());
产生以下结果:
toISOString 2019-02-01T14:14:00.000Z
toISOString 2019-05-01T13:14:00.000Z
两个日期都转换为 UTC 格式。请注意,第二个示例的时间是 13:14 而不是 14:14。这实际上是正确的,因为 01/05/2019 在 BST 内,即 GMT+0100。所以真正的UTC实际上是13:14。
任何其他语言环境/时区都是如此。UTC 日期时间将根据客户端的语言环境/区域计算。
在幕后,我相信这是在将模型发布回有效负载到服务器时使用 Angular 客户端发生的事情。它只是调用'toISOString'。
您还可以使用“JSON.stringify”获得相同的结果,我相信它在幕后称为“toISOString”。
console.log(JSON.stringify(date1));
console.log(JSON.stringify(date2));
尝试在 Chrome Sources > Snippets 或其他 JavaScript 暂存器中运行其中一些命令。
var date1 = new Date('2019-02-01T14:14:00');
var date2 = new Date('2019-05-01T14:14:00');
console.log(date1);
console.log(date2);
console.log(JSON.stringify(date1));
console.log(JSON.stringify(date2));
console.log('toISOString ' + date1.toISOString());
console.log('toISOString ' + date2.toISOString());
console.log('toGMTString ' + date1.toGMTString());
console.log('toGMTString ' + date2.toGMTString());
console.log('toLocaleString ' + date1.toLocaleString());
console.log('toLocaleString ' + date2.toLocaleString());
console.log('toUTCString ' + date1.toUTCString());
console.log('toUTCString ' + date2.toUTCString());
console.log(date1.getTimezoneOffset());
console.log(date2.getTimezoneOffset());
因此,总而言之,我们正在从客户端获取 UTC 日期时间。
这绝对是一件好事,并且使日期时间语言环境/区域不可知。
有了这种理解,如果您想在数据库中存储本地日期时间而不是 UTC 日期时间,则有一些解决方法。
上面的客户端代码建议肯定会“修复”偏移量,但考虑到数据仍将作为 UTC 日期字符串序列化回客户端。以这种方式删除偏移量,您实际上已经打破了 UTC 日期。在服务器上,Web API 仍将接收 Z 后缀,并将其视为 UTC 日期。因此,您实际上会调整日期偏移量,但不会调整 UTC 格式的上下文。
fixDate(d: Date): Date {
d.setHours(d.getHours() - d.getTimezoneOffset() / 60);
return d;
}
但是,如果您要将日期作为字符串发送回(自行转换并删除 Z 后缀)回此格式:
2020-04-23T01:00:00
那么您就不会暗示该日期是 UTC 日期。
考虑到上述所有情况,我的观点(经过几天的调查)是最好让客户端将日期类型序列化为 UTC。接受这就是它的工作方式,并且从一致性的角度来看,始终返回 UTC 字符串日期是一件好事。
就服务器端(在我的情况下为 Web API)而言,日期可以作为 UTC 日期接收,并且可以作为 UTC 日期存储在您的数据库/数据存储中,或者您可以将其服务器端转换为您选择的任何语言环境/区域。请记住,UTC 与区域无关,这对全球应用程序来说是件好事。
就我而言,我选择在存储到我们的数据库之前使用以下代码片段将传入的 UTC 日期时间转换为本地日期时间。
o.AppliesStartDate.ToLocalTime();
我希望这对某人有所帮助,并希望能澄清事情的运作方式。两天前我根本没有得到这个……但现在有点道理。
您还可以在 Web API C# 上执行以下操作:
TimeZoneInfo ist = TimeZoneInfo.FindSystemTimeZoneById("India Standard Time");
TimeZoneInfo gmt = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var a1 = new DateTime(2019, 5, 30, 12, 30, 30, DateTimeKind.Utc);
Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(a1, gmt));
Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(a1, ist ));
这可能会进一步阐明日期是在 Web API 上作为 UTC 日期类型接收的。如果您在反序列化的日期时间结构 Web API 端检查“种类”属性,您将看到“UTC”。
我希望这可以帮助你。