首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何优雅地处理时区

如何优雅地处理时区
EN

Stack Overflow用户
提问于 2011-09-28 09:28:54
回答 7查看 67.2K关注 0票数 148

我有一个网站,托管在不同的时区,而不是用户使用的应用程序。除此之外,用户还可以使用特定的时区。我想知道其他SO用户和应用程序是如何做到这一点的?最明显的部分是,在数据库中,日期/时间存储在UTC中。在服务器上时,所有日期/时间都应使用UTC处理。然而,我看到了三个我试图克服的问题:

以协调世界时为单位获取当前时间的

  1. (使用DateTime.UtcNow).

可轻松解决

  1. 从数据库中提取日期/时间并将其显示给用户。可能会有很多调用在不同的视图上打印日期。我在想在视图和控制器之间的某个层可以解决这个问题。或者在DateTime上有一个自定义的扩展方法(见下文)。主要的缺点是在视图中使用datetime的每个位置都必须调用扩展方法!

这也会增加使用像JsonResult这样的东西的难度。您不能再轻松地调用Json(myEnumerable),它必须是Json(myEnumerable.Select(transformAllDates))。也许AutoMapper可以在这种情况下提供帮助?

  1. 从用户获取输入(本地到协调世界时)。例如,POSTing一个带有日期的表单需要将日期转换为UTC之前。首先想到的是创建一个自定义的ModelBinder.

下面是我想在视图中使用的扩展:

代码语言:javascript
复制
public static class DateTimeExtensions
{
    public static DateTime UtcToLocal(this DateTime source, 
        TimeZoneInfo localTimeZone)
    {
        return TimeZoneInfo.ConvertTimeFromUtc(source, localTimeZone);
    }

    public static DateTime LocalToUtc(this DateTime source, 
        TimeZoneInfo localTimeZone)
    {
        source = DateTime.SpecifyKind(source, DateTimeKind.Unspecified);
        return TimeZoneInfo.ConvertTimeToUtc(source, localTimeZone);
    }
}

我认为,考虑到现在许多应用程序都是基于云的,其中服务器的本地时间可能与预期时区有很大不同,因此处理时区将是一件很常见的事情。

这个问题以前有没有被优雅地解决过?我是不是漏掉了什么?我们非常欣赏你的想法和想法。

编辑:为了澄清一些困惑,我想添加更多细节。现在的问题不是如何在数据库中存储UTC时间,而是从UTC->Local and Local->UTC的过程。正如@Max Zerbini指出的那样,将UTC->本地代码放在视图中显然是明智的,但使用DateTimeExtensions真的是答案吗?当从用户获取输入时,接受日期作为用户的本地时间(因为这是JS将使用的),然后使用ModelBinder将其转换为UTC,这有意义吗?用户的时区存储在数据库中,很容易检索。

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2011-09-28 20:58:06

这不是一个建议,它更多地分享了一种范式,但我所见过的在web应用程序中处理时区信息的最激进的方式(不是ASP.NET MVC独有的)是这样的:

  • 服务器上的所有日期时间均为协调世界时。这意味着,就像你说的那样,使用DateTime.UtcNow.

  • 尝试尽可能少地信任将日期传递给服务器的客户端。例如,如果您需要"now",则不要在客户端创建日期,然后将其传递给服务器。在GET中创建日期并将其传递给ViewModel或在POST时执行DateTime.UtcNow.

到目前为止,相当标准的票价,但这是事情变得“有趣”的地方。

  • 如果您必须接受来自客户端的日期,则使用javascript确保您要发布到服务器的数据是UTC格式的。客户端知道它所在的时区,因此它可以以合理的精度将时间转换为协调世界时。

  • 当呈现视图时,他们使用的是HTML5 <time>元素,他们永远不会在ViewModel中直接呈现datetimes。它被实现为类似于Html.Time(Model.when)HtmlHelper扩展。它将呈现<time datetime='[utctime]' data-date-format='[datetimeformat]'></time>

然后,他们将使用javascript将UTC时间转换为客户端本地时间。该脚本将查找所有数据元素,并使用date-format <time>属性来格式化日期并填充元素的内容。

这样,他们就不必跟踪、存储或管理客户的时区。服务器并不关心客户端所在的时区,也不必进行任何时区转换。它只是简单地输出UTC,并让客户端将其转换为合理的东西。这在浏览器中很容易,因为它知道自己所在的时区。如果客户端更改了他/她的时区,web应用程序将自动更新。它们唯一存储的是用户区域设置的日期时间格式字符串。

我不是说这是最好的方法,但这是一种我以前从未见过的不同的方法。也许你会从中收集到一些有趣的想法。

票数 108
EN

Stack Overflow用户

发布于 2013-05-10 09:24:18

经过几次反馈后,这是我的最终解决方案,我认为是干净和简单的,涵盖了夏令时的问题。

1-我们在模型级别处理转换。因此,在Model类中,我们写道:

代码语言:javascript
复制
    public class Quote
    {
        ...
        public DateTime DateCreated
        {
            get { return CRM.Global.ToLocalTime(_DateCreated); }
            set { _DateCreated = value.ToUniversalTime(); }
        }
        private DateTime _DateCreated { get; set; }
        ...
    }

2-在全局帮助器中,我们将自定义函数设置为"ToLocalTime":

代码语言:javascript
复制
    public static DateTime ToLocalTime(DateTime utcDate)
    {
        var localTimeZoneId = "China Standard Time";
        var localTimeZone = TimeZoneInfo.FindSystemTimeZoneById(localTimeZoneId);
        var localTime = TimeZoneInfo.ConvertTimeFromUtc(utcDate, localTimeZone);
        return localTime;
    }

3-我们可以进一步改进这一点,将时区id保存在每个用户配置文件中,以便我们可以从User类中检索,而不是使用常量“中国标准时间”:

代码语言:javascript
复制
public class Contact
{
    ...
    public string TimeZone { get; set; }
    ...
}

4-在这里,我们可以从下拉框中获取要显示给用户选择的时区列表:

代码语言:javascript
复制
public class ListHelper
{
    public IEnumerable<SelectListItem> GetTimeZoneList()
    {
        var list = from tz in TimeZoneInfo.GetSystemTimeZones()
                   select new SelectListItem { Value = tz.Id, Text = tz.DisplayName };

        return list;
    }
}

所以,现在上午9点25分在中国,网站托管在美国,日期保存在UTC数据库中,这是最终结果:

代码语言:javascript
复制
5/9/2013 6:25:58 PM (Server - in USA) 
5/10/2013 1:25:58 AM (Database - Converted UTC)
5/10/2013 9:25:58 AM (Local - in China)

编辑

感谢Matt Johnson指出了原始解决方案的弱点,很抱歉删除了原始帖子,但在获取正确的代码显示格式时遇到了问题……原来编辑器在混合“bulles”和"pre code“时有问题,所以我删除了这些bulles,这是可以的。

票数 18
EN

Stack Overflow用户

发布于 2011-09-28 11:20:58

sf4answers上的events section中,用户可以输入事件的地址、开始日期和可选的结束日期。这些时间将转换为SQL server中的datetimeoffset,用于说明与UTC的偏移量。

这与您面临的问题相同(尽管您使用的是不同的方法,因为您使用的是DateTime.UtcNow);您有一个位置,并且需要将时间从一个时区转换为另一个时区。

我主要做了两件事,这对我很有效。首先,始终使用DateTimeOffset structure。它说明了UTC的偏移量,如果你能从你的客户那里获得这些信息,它会让你的生活更容易一些。

其次,在执行转换时,假设您知道客户端所在的位置/时区,您可以使用public info time zone database将一个时间从协调世界时转换到另一个时区(或者,如果您愿意,可以在两个时区之间进行三角转换)。tz数据库(有时称为Olson database)的伟大之处在于,它可以说明历史上时区的变化;获取偏移量是想要获取偏移量的日期的函数(只需查看Energy Policy Act of 2005 that changed the dates when daylight savings time goes into effect in the US)。

有了数据库,您就可以使用ZoneInfo (tz database / Olson database) .NET API了。注意,这里没有二进制发行版,您必须下载latest version并自己编译它。

在撰写本文时,它目前正在解析最新数据分发中的所有文件(我实际上是在2011年9月25日对ftp://elsie.nci.nih.gov/pub/tzdata2011k.tar.gz文件运行它的;在2017年3月,您可以通过https://iana.org/time-zones或从ftp://fpt.iana.org/tz/releases/tzdata2017a.tar.gz获得它)。

因此,在sf4answers上,获取地址后,将其地理编码为经纬度组合,然后发送到第三方web服务,以获取与tz数据库中的条目相对应的时区。在此基础上,将开始和结束时间转换为具有适当协调时间偏移的DateTimeOffset实例,然后将其存储在数据库中。

至于在SO和网站上处理它,这取决于观众和你试图展示的内容。如果您注意到了,大多数社交网站(以及sf4answers上的events部分)以相对时间显示事件,或者,如果使用绝对值,则通常是协调世界时。

但是,如果您的受众期望的是本地时间,那么使用DateTimeOffset和一个将时区转换为的扩展方法将是很好的;SQL数据类型datetimeoffset将转换为.NET DateTimeOffset,然后您可以获得使用GetUniversalTime method的通用时间。在此基础上,您只需使用ZoneInfo类上的方法将UTC转换为本地时间(您需要做一些工作才能将其转换为DateTimeOffset,但这已经足够简单了)。

在哪里进行转换?这是你必须在某个地方付出的代价,而且没有“最佳”的方法。不过,我还是选择了视图,将时区偏移量作为视图模型的一部分呈现给视图。这样,如果视图的需求发生变化,您就不必更改视图模型来适应这种变化。您的JsonResult将只包含一个带有IEnumerable和偏移量的模型。

在输入端,使用模型绑定器?我会说绝对不可能。您不能保证所有的日期(现在或将来)都必须以这种方式转换,它应该是您的控制器的一个显式函数来执行此操作。同样,如果需求发生变化,您不必调整一个或多个ModelBinder实例来调整您的业务逻辑;而且它是业务逻辑,这意味着它应该在控制器中。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7577389

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档