16

我将所有 DateTime 字段存储为 UTC 时间。当用户请求网页时,我想采用他首选的本地时区(而不是服务器机器的本地时区)并自动将所有 Web 表单中的所有 DateTime 字段显示为本地日期。

当然,我可以在每个表单中的每个 DateTime.ToString() 调用上应用转换,或者实现一些辅助实用程序,但这是一项耗时的任务,而且还有一些第 3 方组件很难使用自定义 DateTime 显示模板进行配置.

本质上,我想让 DateTime 类的行为如下:

from this moment on for this web request, 
whenever some code calls DateTime.ToString(), convert it to the local time 
        using the timezone offset given at the very beginning of the web request, 
but if possible, please keep .NET core library DateTime.ToString() calls intact 
       (I don't want to mess up event logging timestamps etc.)

有什么办法吗?

顺便说一句,如果重要的话,我正在使用 ASP.NET MVC 4。

4

2 回答 2

34

你不能直接按照你的要求做,但我会建议一些替代方案。正如 Nicholas 指出的那样,HTTP 中没有任何内容可以直接为您提供时区。

选项1

  • 首先,决定要使用哪种类型的时区数据。有两种不同的类型可用,一种是您可以通过TimeZoneInfo课程访问的 Microsoft 时区,另一种是世界其他地区使用的 IANA/Olson 时区。 阅读此处了解更多信息。我的建议是后者,使用NodaTime提供的实现。

  • 然后确定要转换到哪个时区。您应该允许您的用户在某处设置以选择他们的时区。

    • 您可能会显示一个下拉列表来选择几个时区之一,或者您可能会做一些更有用的事情,例如显示世界地图,他们可以单击以选择他们的时区。有几个库可以在 Javascript 中做到这一点,但我最喜欢的是这个

    • 您可能想猜测要使用的默认时区,以便在他们从列表(或地图)中选择之前尽可能接近准确。有一个很棒的库,叫做jsTimeZoneDetect。它将询问浏览器的时钟并对可能的时区做出最佳猜测假设。这是相当不错的,但它仍然只是一个猜测。不要盲目地使用它——但一定要用它来确定起点。 更新您现在也可以在moment.js的moment-timezonemoment.tz.guess()组件中使用, 来执行此操作。

  • 现在您知道用户的时区,您可以使用该值将您的 UTCDateTime值转换为该本地时区。不幸的是,您无法在线程上设置任何内容。当您更改系统时区时,它对所有进程和线程都是全局的。因此,您别无选择,只能将时区传递给您将其发回的每个地方。(我相信这是你的主要问题。) 在这里看到这个几乎重复。

  • 在将其转换为字符串之前,您还需要知道用户的语言环境(可以从Request.UserLanguages值中获取)。您可以将其分配给当前线程,也可以将其作为参数传递给DateTime.ToString()方法。这不会进行任何时区转换 - 它只是确保数字位于正确的位置,使用正确的分隔符,以及工作日或月份名称的适当语言。

选项 2

根本不要将其转换为服务器上的本地时间。

  • 既然您说您正在使用 UTC 值,请确保它们的.Kind属性是Utc. 当您从数据库加载时,您可能应该执行此操作,但如果您必须手动执行此操作:

    myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Utc);
    
  • 将其作为纯 UTC 以 ISO8601 等不变格式发送回浏览器。换句话说:

    myDateTime.ToString("o");  // example:  "2013-05-02T21:01:26.0828604Z"
    
  • 在浏览器上使用一些 JavaScript 将其解析为 UTC。它将自动获取浏览器的本地时间设置。Date一种方法是使用JavaScript中的内置对象,如下所示:

    var dt = new Date('2013-05-02T21:01:26.0828604Z');
    

    但是,这仅适用于支持 ISO-8601 格式的较新浏览器。相反,我建议使用moment.js库。它在浏览器之间是一致的,并且对 ISO 日期和本地化有更好的支持。此外,您还可以获得许多其他有用的解析和格式化功能。

    // pass the value from your server
    var m = moment('2013-05-02T21:01:26.0828604Z');
    
    // use one of the formats supported by moment.js
    // this is locale-specific "long date time" format.
    var s = m.format('LLLL');
    

选项 1 的优点是您可以使用任何时区的时间。如果您可以从下拉列表中询问用户他们的时区,那么您不需要使用任何 Javascript。

选项 2 的优点是您可以让浏览器为您完成一些工作。如果您要发送原始数据,例如对 WebAPI 进行 AJAX 调用,这是最好的方法。但是,JavaScript 只知道 UTC 和浏览器的本地时区。因此,如果您需要转换到其他区域,则效果不佳。

您还应该注意,如果您选择选项 #2,您可能会受到 ECMAScript 5.1 设计缺陷的影响。如果您正在使用一组不同的夏令时规则所涵盖的日期,而不是当前有效的日期,这就会发挥作用。您可以在这个问题我的博客上阅读更多内容。

如果我们在 HTTP 标头中有一些时区信息会容易得多,但不幸的是我们没有。这些是很多需要跳过的障碍,但这是兼具灵活性和准确性的最佳方式。

于 2013-05-03T04:20:42.037 回答
3

简短的回答是你不能。HTTP 不要求(甚至不提供标准方式)用户代理(浏览器)在 HTTP 请求中提供本地时间或时区信息。

你要么需要

  • 询问用户他们的首选时区,或
  • 让客户端 javascript 以某种方式向您报告(cookie?ajax?其他?)

请记住,客户端 javascript 解决方案也不是完美的。禁用 Javascript(或不存在,对于某些浏览器)。Javascript 可能无法访问时区信息。等等。

于 2013-05-02T19:24:57.817 回答