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

当然,我可以将转换应用于每种格式的每个DateTime.ToString()调用或实现一些帮助器实用程序,但这是一项耗时的任务,并且还有一些第三方组件很难配置自定义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。

最佳答案

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

选项1

  • 首先,确定要使用的时区数据类型。有两种可用的类型,可以使用TimeZoneInfo类访问的Microsoft时区,或者世界其他地方使用的IANA / Olson时区。 Read here for more info。我的建议是使用NodaTime提供的实现。
  • 然后确定要转换为哪个时区。您应该允许用户在某处进行设置以选择他们的时区。
  • 您可能会显示一个下拉列表以选择几个时区之一,或者您可能会做一些更有用的事情,例如显示他们可以单击以选择其时区的世界 map 。有几个库可以用Javascript做到这一点,但是我最喜欢的是this one
  • 您可能想要猜测要使用的默认时区,因此在它们从列表(或 map )中选择之前,您可能会尽可能地接近准确。有一个很棒的库jsTimeZoneDetect。它将询问浏览器的时钟,并最好地猜测它可能位于哪个时区。这是相当不错的,但仍然只是一个猜测。不要盲目使用它-而是使用它来确定起点。 更新现在,您还可以在moment.js的moment-timezone组件中使用moment.tz.guess()进行此操作。
  • 现在您知道用户的时区,可以使用该值将UTC的DateTime值转换为该本地时区。不幸的是,您无法在线程上进行任何设置。更改系统时区时,它对于所有进程和线程都是全局的。因此,您别无选择,只能将时区传递回您要发送的每个地方。 (我相信这是您的主要问题。)See this almost duplicate here.
  • 在将其转换为字符串之前,您还需要知道用户的语言环境(可以从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。它将自动获取浏览器的本地时间设置。一种方法是在JavaScript中使用内置的Date对象,如下所示:
    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设计缺陷的影响。如果要处理的日期与当前生效的夏令时规则集不同,则此方法会起作用。您可以阅读更多in this questionon my blog

    如果我们在HTTP header 中包含一些时区信息,那么会容易得多,但是不幸的是我们没有。这是许多困难,但这是兼具灵活性和准确性的最佳方法。

    10-08 05:19
    查看更多