如何将日期正确转换为 UTC,然后再转换回来?

Posted

技术标签:

【中文标题】如何将日期正确转换为 UTC,然后再转换回来?【英文标题】:How to convert a date to UTC properly and then convert it back? 【发布时间】:2014-03-18 19:56:12 【问题描述】:

我正在努力将 DateTime 转换为 UTC、这个概念以及所有我没有正确理解的东西。

当我得到一个日期时间字符串时,比如“7/10/2013”​​,我只是这样做

  Convert.ToDateTime("7/10/2013").ToUniversalTime();

这将在数据库中记录为“7/10/2013 4:00:00 AM”。服务器位于美国东海岸 (-5)。当然,在 2013 年 7 月期间,仍在观察 DST,因此这段时间的偏移量为 -4,因此额外的 4 小时4:00:00 AM”记录为 UTC。

在我写这篇文章时,现在是 2014 年 2 月,DST 还没有生效,所以现在的偏移量是 -5。在我的应用程序中,这是我在应用程序中选择的偏移量。

如果我将 -5 偏移量应用于“7/10/2013 4:00:00 AM”,则日期将为“7/09 /2013 11:00:00PM”。

哪一天错了。

问题 #1

那么我该如何正确地转换 UTC 时间呢?意思是,当用户现在在 2014 年 2 月加载我的应用程序时(当前时区偏移为 -5),2013 年 7 月 10 日凌晨 4:00:00 应该仍然是 2013 年 7 月 10 日,而不是 2013 年 7 月 9 日。

让我感到困惑的是,由于 .ToUniversalTime() 考虑了服务器 DST,是否存在不受服务器位置影响的硬设置“通用时间”????

问题 #2

当我在西海岸和东海岸都有服务器写入数据库时​​会发生什么?应用程序如何判断记录的 UTC 时间是基于东海岸还是西海岸?

基本上,代码如何判断“7/10/2013 4:00:00 AM”是在东海岸创建的 UTC 时间(表示美国东部为 7/10/2013 00:00:00AM海岸)而不是西海岸的服务器(这表明美国西海岸是 2013 年 7 月 9 日晚上 20:00:00”)?

对不起,如果这听起来很愚蠢。任何建议表示赞赏。

==========最终编辑,我目前的解决方案===============

MiMo 的回答很有道理。我对两件事感到困惑。

    存储在数据库中的 UTC 时间对服务器意味着什么? 服务器时间与应用用户有什么关系?

我的应用程序可供来自不同时区的用户使用,有些用户与服务器处于同一时区,有些则不在。有些旅行,因此即使它们与服务器处于同一时区,它们也可能始终处于不同的时区。我的应用程序允许他们选择他们所在的时区,从而适当地反映时间。

最初,我只是从数据库中获取 UTC 时间并从中减去用户的时区偏移量。正如 Mimo 建议的那样,这是错误的。原因可以在我上面的帖子中看到。

我最初的解决方案是立即获取服务器的时区偏移量并使用它来添加/减去 UTC,这也是错误的。截至 2013 年 7 月 10 日,当时服务器的偏移量为 -4。现在,在 2014 年 2 月,服务器时区偏移量为 -5。解决方案当然是使用 .ToLocalTime()

在我深入研究 Mimo 关于如何使用 TimeZone.ToLocalTime() 的建议之前,这里是我临时解决问题的方法。

    从数据库中获取 UTC 日期并转换为服务器显示的 .ToLocalTime。所以对于服务器,7/10/2013 4:00:00AM 变为 7/10/2013 12:00:00AM。

    获取服务器时区偏移量。它目前显示 -5,因为它位于美国东海岸。

    获取用户的时区偏移量。对于西海岸,用户现在选择-8。对于东海岸,用户现在选择-5。

    获取用户时区和服务器时区之间的差异。西海岸是-3。东海岸为 0。

    减去与 2013 年 7 月 10 日上午 12:00:00 之间的差异,因此西海岸的截止日期为 2013 年 7 月 9 日下午 21:00:00,东海岸的截止日期为 2013 年 7 月 10 日中午 12:00:00。

全部正确。

非常感谢各位。现在是时候深入研究 TimeZone.ToLocalTime() ,看看我是否可以减少步骤 2-5。

【问题讨论】:

考虑将时间(对象)放在一边,仅以 UTC 显示时间。 保存日期/时间的列的 SQL 数据类型是什么? 【参考方案1】:

如果要将本地时间存储为 UTC,则需要先将其转换为世界时:

 DateTime dbDateTime = localDateTime.ToUniversalTime();
 ... store dbDateTime in the database ...

当您从数据库中读回它时,它的 Kind 属性将设置为 Unspecified。您需要将其 Kind 属性显式设置为 UTC:

dbDateTime = ... get from database, e.g. (DateTime) reader["SomeDateTimeColumn"]
dbDateTime = DateTime.SpecifyKind(dbDateTime, DateTimeKind.Utc);

如果您想将其转换为当地时间,您可以使用:

DateTime localDateTime = dbDateTime.ToLocalTime();

【讨论】:

我认为您假设数据存储在 datetime 列而不是 datetimeoffset 列中。后者将包含时区偏移量。 @wdosanjos,是的,我正在做这个假设。 谢谢乔。看来我错过了两件事。 1. 转换服务器的 .ToLocalTime(), 2. 调整应用程序用户相对于服务器的时区。如果在同一时区,则使用服务器的本地时间,如果在不同时区,则计算差异。【参考方案2】:

您使用ToLocalTime() 转换回当地时间。它将看到日期/时间是 7 月,因此使用 DST,因此它将移动 4 小时而不是 5。

如果您有一个连接到服务器的客户端(例如 Web 浏览器),您最终会希望将日期/时间转换为客户端的本地时间,而不是服务器的本地时间。要做到这一点,最好的方法是使用TimeZone.ToLocalTime():将客户端所在的时区发送到服务端,然后一直直接转换到该时区。

永远不要加/减小时 - 总是经过时区并使用 TimeZoone.ToLocalTime()。当涉及 DST 时,加/减小时将不起作用。

请注意,无法从浏览器中获取当前(本地)时区。如果您的客户端是浏览器,您需要从某种配置中获取时区或让用户输入它。

另请注意,一旦您开始处理不同的时区,您就不再只有日期 - 您总是必须处理完整的日期时间:如果您剥离或松开时间部分,所有转换将不再有效。

关于问题 2:UTC 时间是通用的,不基于任何特定时区,因此一旦转换为 UTC,您就不必再担心服务器的时区了。

【讨论】:

感谢 MiMo。看来我错过了两件事。 1. 转换服务器的.ToLocalTime(), 2. 调整应用程序用户相对于服务器的时区。如果在同一时区,则使用服务器的本地时间,如果在不同时区,则计算差异。 米莫。知道了!!这解释了很多。我必须看看 TimeZone.ToLocalTime() 相对于用户浏览器的工作原理。【参考方案3】:

我遇到了同样的问题,我做了以下扩展方法。

    public const string UTC = "UTC";

   public static DateTime Convert(this DateTime date, string fromZone, string toZone)
    
        TimeZoneInfo to = TimeZoneInfo.FindSystemTimeZoneById(toZone);
        TimeZoneInfo from = TimeZoneInfo.FindSystemTimeZoneById(fromZone);
        return new DateTime(TimeZoneInfo.ConvertTime(date, from, to).Ticks, DateTimeKind.Unspecified); 
    

    public static bool IsDayLightSaving(this DateTime date, string zone)
    
        TimeZoneInfo to = TimeZoneInfo.FindSystemTimeZoneById(zone);
        return to.IsDaylightSavingTime(date);
    

    public static DateTime ConvertToUTC(this DateTime date, string fromZone)
    
        return date.Convert(fromZone, DateTime_Extension.UTC);
    

    public static DateTime ConvertToLocal(this DateTime date, string toZone)
    
        return date.Convert(DateTime_Extension.UTC, toZone);
    

我发现的问题实际上是找到当前用户的时区。由于这不是在标头中传递的(您可以通过 javascript 获取),我决定让用户在注册时选择他们的时区

希望对你有帮助

【讨论】:

凯文,非常有用!我假设“fromZone”和“toZone”是“东部时间”等英语短语? 抱歉,它们是用于区域的字符串。 TimeZoneInfo.GetSystemTimeZones() 返回所有时区的列表,例如“萨摩亚标准时间”和“太平洋标准时间”等。您只需将这些传入。 基本上,我为用户提供了一个列表以供选择,我使用以下代码将其存储在他们的个人资料中以呈现选择列表 ReadOnlyCollection tz = TimeZoneInfo.GetSystemTimeZones(); TimeZones = new List(); foreach (TimeZoneInfo tzi in tz) TimeZones.Add(new SelectListItem Text = tzi.DisplayName, Value = tzi.Id ); 当我执行DateTime.Now 时,如何在我的电脑中获得实际 TimeZoneWindows 区域配置 ?【参考方案4】:

也许早上太早了,我需要再喝一杯咖啡,但是(至少为了显示)不是仅使用TimeZoneInfo.ConvertTimeFromUtc 并指定用户所需的 TZ 的解决方案吗?还是 ConvertTimeFromUtc 没有考虑转换回所需 TZ 的日期/时间?深入思考:即使这样做了,有没有人知道它是否考虑了服务器已经转移到 DST 但用户所需的 TZ 还没有的边缘情况?

【讨论】:

使用TimeZoneInfo.ConvertTimeFromUtc的完整示例?【参考方案5】:

想要以当地时间存储日期吗?如果您只关心日期部分,那么您可能希望在将解析时间保存到数据库之前强制将其解析为 Utc:

DateTime utcDate = DateTime.SpecifyKind(Convert.ToDateTime("7/10/2013"),DateTimeKind.Utc);

这样,当它保存到数据库时,它已经采用 UTC,并且不会包含时间组件。

【讨论】:

如何保存在数据库SQL Server中? 类型 ? datetime2?

以上是关于如何将日期正确转换为 UTC,然后再转换回来?的主要内容,如果未能解决你的问题,请参考以下文章

从 JSONArray 转换为 String 然后再转换回来

将 UTC 纪元转换为本地日期

无法在JavaScript中将String BST日期格式转换为UTC

python / pytz问题从本地时区转换为UTC然后返回

如何转换为 UTC

javascript 不能正确地将 angular ui datepicker 日期转换为 UTC