我存储所有日期时间字段为UTC时间。 当用户请求一个网页,我想带他首选的本地时区(而不是服务器计算机的本地时区),并自动显示在所有的网络形式的本地日期的所有日期时间字段。
当然,我可以申请转换上每则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,如果它很重要。
你不能直接做你问什么,但我会建议一些替代品。 正如尼古拉斯指出的那样,没有什么HTTP,将直接给你的时区。
选项1
首先,决定你要使用哪种类型的时区的数据。 有两种不同类型的可用,要么你可以用访问Microsoft时区TimeZoneInfo
类,或者说,世界其他地区使用IANA /奥尔森时区。 读到这里获取更多信息 。 我的建议是后者,利用提供的实现NodaTime 。
然后决定你要转换到哪个时区。 你应该让你的用户设置的地方来接他们的时区。
你可能会显示一个下拉列表中进行选择的几个时区,或者你可能会做更有用的东西,像显示世界地图,他们可以单击以选中他们的时区。 有几个库,可以在Javascript中做到这一点,但我最喜欢的是这一个 。
你可能想猜使用默认时区,所以他们从列表中(或地图)挑选,才可以接近尽可能准确。 有这个一个伟大的图书馆称为jsTimeZoneDetect 。 它会询问浏览器的时钟,使的是什么时区,这可能是一个最好的猜测假设。 这是相当不错的,但它仍然只是一个猜测。 不要盲目地使用它 - 但不要用它来确定一个起点。 更新你现在也可以做到这一点与moment.tz.guess()
在那一刻,时区 moment.js的组成部分。
现在你知道用户的时区,您可以使用该值将UTC转换DateTime
值到本地时区。 不幸的是,没有什么可以将线程会做设定。 当您更改系统时区,这是对所有进程和线程。 所以,你别无选择,只能通过时间区的每一个要发送回来的地方。 (我相信这是你的主要问题。) 看到这个几乎重复这里。
在你把它转换为字符串,则需要也知道用户所在的区域(你可以从一开始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的好处是,你让浏览器做一些工作适合你。 这是如果你发送出的原始数据,如做AJAX调用到的WebAPI到最好的一段路要走。 然而,JavaScript是只知道UTC和浏览器的本地时区。 所以,如果你需要转换到其他区域它不工作这么好。
你也应该知道,如果你选择方案2,您可以通过在ECMAScript中5.1的设计缺陷的影响。 该进场,如果你是由一组不同的夏令时规则比覆盖当前有效日期的工作。 你可以阅读更多在这个问题上 ,以及在我的博客 。
这将是容易得多,如果我们有HTTP头中的一些时区信息,但遗憾的是我们没有。 这些都是很多箍跳通过,但它同时具有灵活性和准确性的最佳途径。
简短的回答是,你不能。 HTTP不需要(或甚至提供的标准方式)用户代理(浏览器),以提供本地时间或在HTTP请求时区信息。
您可能需要
- 问用户他们的时区,或
- 有客户端的JavaScript将其报告给你以某种方式(饼干吗?AJAX?其他?)
请记住,客户端JavaScript的解决方案是不完美的,无论是。 禁用JavaScript(或不存在的,对于一些浏览器)。 使用Javascript可能无法访问时区信息。 等等。