日期从 angular(时区)到服务器(utc)然后 utc 到时区
Date from angular(timezone) to server (utc) then utc to Timezone
我无法将日期恢复到正确的时区。
首先在本地机器上一切正常,但在服务器上却不行:服务器托管在美国,客户端大部分在澳大利亚。
所以一个日期从angular app("12/23/2015 11:00:00 AM") 发送到服务器,服务器在数据库中存储一个日期作为utc,直到这一点一切正常(我检查过并且日期存储在正确的 utc 中)
book.StartDateTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(book.StartDateTime.Value, DateTimeKind.Unspecified), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(locationDetails.TimeZone)); // book.CreatedDate.Value.ToUniversalTime();
问题是:
当客户端请求存储在数据库中的一些日期时。存储在数据库中的日期是 return 返回给客户端这样的:
bookview.StartDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(bookli.StartDateTime.Value, DateTimeKind.Utc), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
我检查了一下,此时日期是“12/23/2015 11:00:00 AM”-> 转换就在服务器中(在服务器端我放了一个日志),
但在 angular 中显示为“12/23/2015 10:00:00 PM”
很明显,问题是 api 将日期传输给客户端的时间,也许是什么时候转换为 JSON
我尝试了不同的方法都没有用,我删除了 "DateTime.SpecifyKind",我将日期转换为字符串,然后又转换回日期时间格式,但似乎没有用。
我能做什么?
几件事:
你的例子不完整,所以我只能推测一些地方。最好展示双方,包括如何加载和解析 Angular 中的数据,以及数据在网络上的样子。
您不应该以 "12/23/2015 11:00:00 AM"
等特定于语言环境的格式来回发送日期。您可以在 UI 中使用它们,但它们不适用于网络(在您的 JSON 中)。相反,您应该使用 ISO8601/RFC3339,例如 "2015-12-23T11:00:00Z"
。 (如果您正在使用 WebAPI,您可能已经在这样做了。)
序列化为 ISO8601 格式的 DateTime
对象与 Kind
属性.
中的关联 DateTimeKind
耦合
- 如果
Kind
为 Utc
,则 ISO8601 字符串将以 Z
. 结尾
- 如果
Kind
为 Local
,则 ISO8601 字符串将以该时间戳的机器本地偏移量结尾,例如 -08:00
.
- 如果
Kind
是 Unspecified
,那么 ISO8601 字符串将没有 Z
或偏移量 - 这意味着它不能明确表示特定的时刻。
这最终是错误的原因。您正在将 DateTime
转换为另一个时区,这会留下 Unspecified
种类,然后在没有偏移的情况下进行序列化 - 因此在客户端(可能)在本地时区进行解释浏览器。
更好的方法是使用 DateTimeOffset
而不是 DateTime
。那么你就不用担心Kind
了,偏移量一直存在。如果您将 bookview.StartDateTime
更改为 DateTimeOffset
类型,您可以执行以下操作来解决问题:
DateTimeOffset dto = new DateTimeOffset(bookli.StartDateTime.Value, TimeSpan.Zero);
bookView.StartDTO = TimeZoneInfo.ConvertTime(dto, ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
这将确保偏移量保留在数据中。
在客户端,要特别注意ISO字符串是如何被解析的。如果它被加载到 Date
对象中,那么它确实会被转换为客户端的时区。相反,您可以查看 moment.js 以了解客户端时间格式。特别是,使用 moment.parseZone
将值保持在它所呈现的相同偏移量中。例如:
var s = moment.parseZone("2015-12-31T11:00:00+00:00").format("L LT"); // "12/31/2015 11:00 AM"
在注释代码中,您还显示了对 DateTime.ToUniversalTime
的调用 - 对此要非常小心。如果源种类为 Unspecified
,则将其视为 Local
。因此,计算机的本地时区反映在转换值中。最好完全避免使用 ToUniversalTime
和 ToLocalTime
。仅使用 TimeZoneInfo
.
上的转换方法
ToolsHelper.OlsonTimeZoneToTimeZoneInfo
也不是我们所知道的。但我假设它执行类似于 this one. However, if you're working with Olson time zones anyway, the better approach would be to not use TimeZoneInfo
at all. Instead, use Noda Time 的 CLDR 映射,它对 tzdb 时区提供本机支持。
我无法将日期恢复到正确的时区。
首先在本地机器上一切正常,但在服务器上却不行:服务器托管在美国,客户端大部分在澳大利亚。
所以一个日期从angular app("12/23/2015 11:00:00 AM") 发送到服务器,服务器在数据库中存储一个日期作为utc,直到这一点一切正常(我检查过并且日期存储在正确的 utc 中)
book.StartDateTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(book.StartDateTime.Value, DateTimeKind.Unspecified), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(locationDetails.TimeZone)); // book.CreatedDate.Value.ToUniversalTime();
问题是:
当客户端请求存储在数据库中的一些日期时。存储在数据库中的日期是 return 返回给客户端这样的:
bookview.StartDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(bookli.StartDateTime.Value, DateTimeKind.Utc), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
我检查了一下,此时日期是“12/23/2015 11:00:00 AM”-> 转换就在服务器中(在服务器端我放了一个日志),
但在 angular 中显示为“12/23/2015 10:00:00 PM”
很明显,问题是 api 将日期传输给客户端的时间,也许是什么时候转换为 JSON
我尝试了不同的方法都没有用,我删除了 "DateTime.SpecifyKind",我将日期转换为字符串,然后又转换回日期时间格式,但似乎没有用。
我能做什么?
几件事:
你的例子不完整,所以我只能推测一些地方。最好展示双方,包括如何加载和解析 Angular 中的数据,以及数据在网络上的样子。
您不应该以
"12/23/2015 11:00:00 AM"
等特定于语言环境的格式来回发送日期。您可以在 UI 中使用它们,但它们不适用于网络(在您的 JSON 中)。相反,您应该使用 ISO8601/RFC3339,例如"2015-12-23T11:00:00Z"
。 (如果您正在使用 WebAPI,您可能已经在这样做了。)序列化为 ISO8601 格式的
中的关联DateTime
对象与Kind
属性.DateTimeKind
耦合- 如果
Kind
为Utc
,则 ISO8601 字符串将以Z
. 结尾
- 如果
Kind
为Local
,则 ISO8601 字符串将以该时间戳的机器本地偏移量结尾,例如-08:00
. - 如果
Kind
是Unspecified
,那么 ISO8601 字符串将没有Z
或偏移量 - 这意味着它不能明确表示特定的时刻。
这最终是错误的原因。您正在将
DateTime
转换为另一个时区,这会留下Unspecified
种类,然后在没有偏移的情况下进行序列化 - 因此在客户端(可能)在本地时区进行解释浏览器。- 如果
更好的方法是使用
DateTimeOffset
而不是DateTime
。那么你就不用担心Kind
了,偏移量一直存在。如果您将bookview.StartDateTime
更改为DateTimeOffset
类型,您可以执行以下操作来解决问题:DateTimeOffset dto = new DateTimeOffset(bookli.StartDateTime.Value, TimeSpan.Zero); bookView.StartDTO = TimeZoneInfo.ConvertTime(dto, ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
这将确保偏移量保留在数据中。
在客户端,要特别注意ISO字符串是如何被解析的。如果它被加载到
Date
对象中,那么它确实会被转换为客户端的时区。相反,您可以查看 moment.js 以了解客户端时间格式。特别是,使用moment.parseZone
将值保持在它所呈现的相同偏移量中。例如:var s = moment.parseZone("2015-12-31T11:00:00+00:00").format("L LT"); // "12/31/2015 11:00 AM"
在注释代码中,您还显示了对
上的转换方法DateTime.ToUniversalTime
的调用 - 对此要非常小心。如果源种类为Unspecified
,则将其视为Local
。因此,计算机的本地时区反映在转换值中。最好完全避免使用ToUniversalTime
和ToLocalTime
。仅使用TimeZoneInfo
.ToolsHelper.OlsonTimeZoneToTimeZoneInfo
也不是我们所知道的。但我假设它执行类似于 this one. However, if you're working with Olson time zones anyway, the better approach would be to not useTimeZoneInfo
at all. Instead, use Noda Time 的 CLDR 映射,它对 tzdb 时区提供本机支持。