我将所有DateTime字段存储为UTC时间。当用户请求网页时,我想采用他首选的本地时区(而不是服务器机器的本地时区),并自动将所有网络表单中的所有DateTime字段显示为本地日期。
当然,我可以在每个表单中的每个DateTime.ToString()调用上应用转换,或者实现一些帮助程序实用程序,但这是一个耗时的任务,而且还有一些第三方组件,使用自定义配置很棘手日期时间显示模板。
基本上,我想使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,如果重要的话。
答案 0 :(得分:29)
你不能直接做你要求的,但我会建议一些替代方案。正如Nicholas指出的那样,HTTP中没有任何内容可以直接为您提供时区。
选项1
首先,确定您要使用哪种类型的时区数据。有两种不同的类型,可以使用TimeZoneInfo
类访问的Microsoft时区,也可以使用世界其他地方使用的IANA / Olson时区。 Read here for more info。我的推荐是后者,使用NodaTime提供的实现。
然后确定要转换为的时区。您应该允许用户在某个地方设置选择时区。
您可能会显示一个下拉列表来选择多个时区中的一个,或者您可以执行更有用的操作,例如显示他们可以单击以选择其时区的世界地图。有几个库可以在Javascript中执行此操作,但我最喜欢的是this one。
您可能想要猜测要使用的默认时区,因此在从列表(或地图)中挑选之前,您可以尽可能接近准确。这个名为jsTimeZoneDetect的图书馆很棒。它将询问浏览器的时钟并对其可能的时区做出最佳猜测。这是相当不错的,但它只是一个猜测。不要盲目使用它 - 但要用它来确定一个起点。 更新您现在也可以使用moment.tz.guess()
在moment.js的moment-timezone组件中执行此操作。
现在您已了解用户的时区,您可以使用该值将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 question和on my blog。
如果我们在HTTP标头中有一些时区信息会更容易,但不幸的是我们没有。这些都是很多可以跳过的环节,但这是获得灵活性和准确性的最佳方式。
答案 1 :(得分:3)
简短的回答是,你不能。 HTTP不要求(甚至提供标准方式)用户代理(浏览器)在HTTP请求中提供本地时间或时区信息。
您需要
请记住,客户端的JavaScript解决方案也不完美。 Javascript已禁用(或某些浏览器不存在)。 Javascript可能无法访问时区信息。等