将UTCdate时间全球转换为用户指定的本地date时间

我将所有DateTime字段存储为UTC时间。 当用户请求一个网页,我想采取他首选的本地时区(而不是服务器的本地时区),并自动显示所有的Web窗体中的所有date时间字段作为本地date。

当然,我可以在每个窗体的每个DateTime.ToString()调用上应用转换,或者实现一些辅助工具,但这是一个耗时的任务,还有一些第三方组件很难用自定义的DateTime显示模板进行configuration。

本质上,我想使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,如果它的事。

你不能直接做你所要求的,但我会build议一些替代scheme。 正如Nicholas所指出的那样,HTTP中没有任何东西可以直接给你时区。

选项1

  • 首先,确定要使用哪种types的时区数据。 有两种不同的types,可以使用TimeZoneInfo类访问的Microsoft时区,或者世界其他地区使用的IANA / Olson时区。 在这里阅读更多信息 。 我的build议是后者,使用NodaTime提供的实施 。

  • 然后确定您想要转换为哪个时区。 你应该允许你的用户在某个地方select他们的时区。

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

    • 您可能想要猜测一个默认的时区,以便在从列表(或地图)中select之前,尽可能地精确。 这个叫做jsTimeZoneDetect的有一个很棒的库。 它会询问浏览器的时钟,并猜测它可能是什么时区。 这是相当不错的,但它仍然只是一个猜测。 不要盲目使用它,而是使用它来确定一个起点。 更新您现在也可以在moment.tz.guess()的moment-timezone组件中使用moment.tz.guess()做到这一点。

  • 现在您知道了用户的时区,您可以使用该值将UTC DateTime时间值转换为当地时区。 不幸的是,没有什么可以在线程上设置的。 当您更改系统时区时,它对所有进程和线程是全局的。 所以你别无select,只能将时区传递给每一个你寄回去的地方。 (我相信这是你的主要问题。) 看到这里几乎重复。

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

选项2

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

  • 既然你说你正在使用UTC值,请确保他们的.Kind属性是Utc 。 从数据库加载时,应该可以这样做,但是如果必须的话,可以手动执行:

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

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

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

    但是,这只适用于支持ISO-8601格式的较新浏览器。 相反,我build议使用moment.js库。 它在浏览器中是一致的,并且更好地支持ISOdate和本地化。 另外你会得到很多其他有用的parsing和格式化function。

     // 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和浏览器的本地时区。 所以,如果你需要转换到其他区域,效果不好。

您还应该意识到,如果您select选项#2,则可能会受到ECMAScript 5.1devise中的缺陷的影响。 如果您正在处理由当前有效的夏令时规则所覆盖的date,则这会起作用。 你可以在这个问题和我的博客上阅读更多内容。

如果我们在HTTP标头中有一些时区信息,那将会容易得多,但不幸的是,我们没有。 这是一个很大的跳跃,但这是兼顾灵活性和准确性的最佳方式。

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

你要么

  • 询问用户的首选时区,或
  • 有客户端JavaScript以某种方式报告给你(cookie?ajax?其他?)

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