且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

将 UTC 日期时间全局转换为用户指定的本地日期时间

更新时间:2023-02-03 18:03:50

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

You can't do directly what you asked for, but I will suggest some alternatives. As Nicholas pointed out, there is nothing in HTTP that would give you the time zone directly.

选项 1

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

  • First, decide which type of time zone data you want to work with. There are two different types available, either the Microsoft time zones that you can access with the TimeZoneInfo class, or the IANA/Olson time zones that the rest of the world uses. Read here for more info. My recommendation would be the latter, using the implementation provided by NodaTime.

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

Then determine which time zone you want to convert to. You should allow your user a setting somewhere to pick their time zone.

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

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

You might want to guess a default time zone to use, so you can be as close to accurate as possible before they pick from the list (or map). There is a great library for this called jsTimeZoneDetect. It will interrogate the browser's clock and make a best guess assumption of what time zone it might be. It is fairly good, but it is still just a guess. Don't use it blindly - but do use it to determine a starting point. Update You can now also do this with moment.tz.guess(), in the moment-timezone component of moment.js.

既然您知道用户所在的时区,您就可以使用该值将您的 UTC DateTime 值转换为该本地时区.不幸的是,您无法在线程上设置任何东西来做到这一点.当您更改系统时区时,它对所有进程和线程都是全局的.因此,您别无选择,只能将时区传递给您将其发回的每个地方.(我相信这是您的主要问题.)在此处查看几乎重复.

Now that you know the time zone of the user, you can use that value to convert your UTC DateTime values to that local time zone. Unfortunately, there is nothing you can set on the thread that will do that. When you change the system time zone, it is global for all processes and threads. So you have no choice but to pass the time zone to each and every place you are sending it back. (I believe this was your main question.) See this almost duplicate here.

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

Before you convert it to a string, you will need to also know the user's locale (which you can get from the Request.UserLanguages value). You can assign it to the current thread, or you can pass it as a parameter to the DateTime.ToString() method. This doesn't do any time zone conversion - it just makes sure that the numbers are in the correct position, using the correct separators, and the appropriate language for names of weekdays or months.

选项 2

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

Don't convert it to local time on the server at all.

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

  • Since you said you are working with UTC values, make sure their .Kind property is Utc. You should probably do this when you load from your database, but if you have to you can do it manually:

myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Utc);

  • 将其作为纯 UTC 格式以 ISO8601 等不变格式发送回浏览器.换句话说:

  • Send it back to the browser as pure UTC, in an invariant format like ISO8601. In other words:

    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 日期和本地化.此外,您还可以获得许多其他有用的解析和格式化功能.

    However, this will only work in newer browsers that support the ISO-8601 format. Instead, I recommend using the moment.js library. It is consistent across browsers, and it has better support for ISO dates, and localization. Plus you get a lot of other useful parsing and formatting functions.

    // 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.

    The advantage of Option 1 is that you can work with times in any time zone. If you can ask the user for their timezone from a dropdown list, then you need not use any Javascript.

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

    The advantage of Option 2 is that you get the browser to do some of the work for you. This is the best way to go if you're sending out raw data, such as making AJAX calls to a WebAPI. However, JavaScript is only aware of UTC and the browser's local time zone. So it doesn't work so well if you need to convert to other zones.

    您还应该注意,如果您选择选项 #2,您可能会受到 ECMAScript 5.1 设计缺陷的影响.如果您正在处理的日期与当前有效的夏令时规则集不同,那么这将起作用.您可以阅读更多在这个问题在我的博客上.

    You should also be aware that if you choose Option #2, you may be affected by a flaw in the design of ECMAScript 5.1. This comes into play if you are working with dates that are covered by a different set of daylight saving time rules than are currently in effect. You can read more in this question, and on my blog.

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

    It would be so much easier if we had some time zone information in the HTTP headers, but unfortunately we don't. These are a lot of hoops to jump through, but it's the best way to have both flexibility and accuracy.