我遇到了在正确的时区重新开始日期的问题。
在本地机器上一切都很好,一切正常,但不在服务器中:服务器托管在美国,客户大多在澳大利亚。
所以从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();
问题是:
当客户端请求存储在数据库中的某些日期时。存储在数据库中的日期将返回给客户端,如下所示:
bookview.StartDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(bookli.StartDateTime.Value, DateTimeKind.Utc), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
我查了一下,此时的日期是“12/23/2015 11:00:00 AM” - >转换是在服务器中(在服务器端我放了一个日志),
但角度显示为“12/23/2015 10:00:00 PM”
显然问题是当api将日期转移到客户端时,可能是转换为JSON时
我尝试了不同的方法没有用,我已经删除了“DateTime.SpecifyKind”,我将日期转换为字符串然后再转换为日期时间格式,似乎没有任何工作。
我该怎么办?
答案 0 :(得分:3)
一些事情:
你的例子不完整,所以我只能推测某些方面。最好显示双方,包括如何在Angular中加载和解析数据,以及数据在线上的样子。
您不应以"12/23/2015 11:00:00 AM"
等特定于语言环境的格式来回发送日期。您可以在UI中使用它们,但它们不适合通过线路(在您的JSON中)。相反,您应该使用ISO8601 / RFC3339,例如"2015-12-23T11:00:00Z"
。 (如果你正在使用WebAPI,你可能已经这样做了。)
序列化为ISO8601格式的DateTime
对象与DateTimeKind
属性中的关联Kind
相关联。
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的CLDR映射。但是,如果您仍在使用Olson时区,更好的方法是根本不使用TimeZoneInfo
。相反,请使用Noda Time及其对tzdb时区的本机支持。