如何优雅地处理时区

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (25)

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

  1. 在UTC中获取当前时间(易于用DateTime.UtcNow)。
  2. Pulling date/times from the database and displaying these to the user. There are potentially 批次在不同视图上打印日期的调用。我在想视图和控制器之间的某个层,它可以解决这个问题。或者有一个自定义扩展方法DateTime。主要的缺点是每一个在视图中使用日期时间的位置,必须调用扩展方法! 这也会增加使用类似于JsonResult。你再也不能轻易调用Json(myEnumerable),肯定是Json(myEnumerable.Select(transformAllDates))。也许AutoMapper能在这种情况下有所帮助?
  3. 从用户获取输入(本地到UTC)。例如,带有日期的表单将需要将日期转换为以前的UTC。首先想到的是创建一个自定义ModelBinder

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

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);
    }
}

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

这件事以前解决得很好吗?我遗漏了什么吗?想法和想法是非常值得赞赏的。

编辑:为了澄清一些困惑,我想增加一些更多的细节。现在的问题不是如何要在db中存储UTC时间,更多是关于从UTC->LocalandLocal->UTC的过程。正如@MaxZerbini所指出的,将UTC->Localcode放在视图中显然是明智的,但它使用的是DateTimeExtensions真的是答案吗?当从用户获得输入时,接受日期作为用户的本地时间(因为这就是JS将使用的),然后使用ModelBinder转变为世界协调时?用户的时区存储在DB中,并且很容易检索。

提问于
用户回答回答于

不是说这是一个建议,它更多的是一个范例的共享,但最重要的是累进我在Web应用程序(并不是ASP.NETMVC独有的)中处理时区信息的方式如下:

  • 服务器上的所有日期时间都是UTC。就像你说的那样,DateTime.UtcNow
  • 尽量不信任将日期传递给服务器的客户端。例如,如果需要“立即”,不要在客户端上创建日期,然后将其传递给服务器。要么在GET中创建日期并将其传递给ViewModel,要么在POST上执行DateTime.UtcNow
  • 如果必须接受来自客户端的日期,那么使用javascript来确保提交给服务器的数据是以UTC为单位的。客户端知道它在哪个时区,所以它可以以合理的精度将时间转换为UTC。
  • 在呈现视图时,它们使用的是HTML 5。<time>元素中,它们永远不会直接在ViewModel中呈现日期时间。它作为HtmlHelper扩展,有点像Html.Time(Model.when)。它会使<time datetime='[utctime]' data-date-format='[datetimeformat]'></time>。 然后,他们将使用javascript将UTC时间转换为本地时间。脚本会找到所有的<time>元素并使用date-format属性来格式化日期和填充元素的内容。

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

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

用户回答回答于

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

    public class Quote
    {
        ...
        public DateTime DateCreated
        {
            get { return CRM.Global.ToLocalTime(_DateCreated); }
            set { _DateCreated = value.ToUniversalTime(); }
        }
        private DateTime _DateCreated { get; set; }
        ...
    }

2-在全局助手中,我们将自定义函数“ToLocalTime”:

    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,这样我们就可以从用户类中检索,而不用使用“中国标准时间”:

public class Contact
{
    ...
    public string TimeZone { get; set; }
    ...
}

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

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的日期,最后的结果如下:

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)

扫码关注云+社区