从angular(时区)到服务器(utc)然后utc到Timezone

时间:2015-12-22 14:05:31

标签: angularjs date asp.net-web-api timezone utc

我遇到了在正确的时区重新开始日期的问题。

在本地机器上一切都很好,一切正常,但不在服务器中:服务器托管在美国,客户大多在澳大利亚。

所以从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”,我将日期转换为字符串然后再转换为日期时间格式,似乎没有任何工作。

我该怎么办?

1 个答案:

答案 0 :(得分:3)

一些事情:

  • 你的例子不完整,所以我只能推测某些方面。最好显示双方,包括如何在Angular中加载和解析数据,以及数据在线上的样子。

  • 您不应以"12/23/2015 11:00:00 AM"等特定于语言环境的格式来回发送日期。您可以在UI中使用它们,但它们不适合通过线路(在您的JSON中)。相反,您应该使用ISO8601 / RFC3339,例如"2015-12-23T11:00:00Z"。 (如果你正在使用WebAPI,你可能已经这样做了。)

  • 序列化为ISO8601格式的DateTime对象与DateTimeKind属性中的关联Kind相关联。

    • 如果KindUtc,则ISO8601字符串将以Z结尾。
    • 如果KindLocal,则ISO8601字符串将以该时间戳的机器本地偏移量结束,例如-08:00
    • 如果KindUnspecified,那么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。因此,计算机的本地时区将反映在转换后的值中。最好完全避免ToUniversalTimeToLocalTime。仅使用TimeZoneInfo上的转换方法。

  • ToolsHelper.OlsonTimeZoneToTimeZoneInfo不是我们所知道的。但是我假设它执行类似于this one的CLDR映射。但是,如果您仍在使用Olson时区,更好的方法是根本不使用TimeZoneInfo。相反,请使用Noda Time及其对tzdb时区的本机支持。