我正在努力将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:00 PM “。
这是错误的,有一天关闭。
问题#1
如何正确转换UTC时间?这意味着,当用户现在在2014年2月加载我的应用程序时(目前时区偏移为-5),7/10/2013 4:00:00 AM应该仍然是2013年7月10日,而不是2013年9月7日。
令我困惑的是,因为.ToUniversalTime()考虑了服务器DST,是否存在一个硬盘“通用时间”,它不受服务器所在位置的影响????
问题#2
当我在西海岸和东海岸都有服务器写入数据库时会发生什么?如果记录的UTC时间是基于东海岸或西海岸,应用程序如何判断?
基本上,代码怎么说,“7/10/2013 4:00:00 AM”是在东海岸创建的UTC时间(表示美国东部时间7/10/2013 00:00:00 AM海岸)而不是西海岸的服务器(这表明它是美国西海岸的7/09/2013 20:00:00 pm)?
对不起,如果这听起来很愚蠢。任何建议都表示赞赏。
==========最终编辑,我目前的解决方案===============
MiMo的回答是有道理的。我对两件事感到困惑。
我的应用程序可供来自不同时区的用户使用,有些用户与服务器位于同一时区,有些则不是。有些旅行,所以即使它们与服务器位于同一时区,它们也可能一直落在不同的时区。我的应用程序允许他们选择他们所在的时区,并适当地反映时间。
最初,我只是将UTC时间从数据库中取出并从中减去用户的时区偏移量。正如Mimo所说,这是错误的。原因可以在我上面的帖子中看到。
我原来的解决方案是立即获取服务器的时区偏移并使用它来加减速,这也是错误的。截至2013年7月10日,当时服务器的偏移量为-4。目前,在2014年2月,服务器时区偏移为-5。解决方案当然是使用.ToLocalTime()
在深入研究Mimo关于如何使用TimeZone.ToLocalTime()的建议之前,我就采取了以下措施来暂时解决问题。
从数据库中获取UTC日期并转换为.ToLocalTime,这是服务器显示的内容。所以到服务器,7/10/2013 4:00:00 AM成为7/10/2013 12:00:00 AM。
获取服务器时区偏移量。目前显示-5,因为它在美国东海岸。
获取用户的时区偏移量。对于西海岸,用户现在选择-8。对于东海岸,用户现在选择-5。
获取用户时区和服务器时区之间的差异。西海岸是-3。东海岸是0.
从7/10/2013 12:00:00 AM减去差异,因此西海岸的截止日期为7/09/2013 21:00:00 PM,东海岸的截止日期为7/10/2013 12:00:00 AM。
一切正确。
非常感谢你们。现在是时候深入了解TimeZone.ToLocalTime()并看看我是否可以减少步骤2-5。
答案 0 :(得分:9)
如果您希望将数据库中的本地时间存储为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();
答案 1 :(得分:6)
使用ToLocalTime()
转换回本地时间。它会看到日期/时间是7月,因此使用DST,因此它将移动4小时而不是5小时。
如果您有连接到服务器的客户端(例如Web浏览器),您最终希望将日期/时间转换为客户端的本地时间,而不是服务器的本地时间。要做到这一点,最好的方法是使用TimeZone.ToLocalTime()
:向服务器发送客户端所在的时区,然后直接转换到该时区。
永远不要添加/减去小时数 - 始终浏览时区并使用TimeZoone.ToLocalTime()。当涉及DST时,添加/减去小时数将不起作用。
请注意,无法从浏览器中获取当前(本地)时区。如果您的客户端是浏览器,您需要从某种配置中获取时区或让用户输入它。
另请注意,一旦您开始处理不同的时区,您不再只有日期 - 您始终必须处理完整的日期时间:如果您剥离或松开时间部分,则所有转换将不再起作用。
关于问题2:UTC时间是通用的,不是基于任何特定时区,因此一旦转换为UTC,您就不必再担心服务器的时区了。
答案 2 :(得分:1)
我遇到了同样的问题,我做了以下扩展方法。
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);
}
我发现的问题实际上是找到当前用户的时区。因为这没有在Header中传递(你可以通过JavaScript获得)我决定要求用户在注册时选择他们的时区
希望这有帮助
答案 3 :(得分:0)
您想要以当地时间存储日期吗?如果您只关心日期部分,那么您可能希望强制将解析的时间转换为Utc,然后再将其保存到数据库中:
DateTime utcDate = DateTime.SpecifyKind(Convert.ToDateTime("7/10/2013"),DateTimeKind.Utc);
这样当它保存到数据库时,它已经已经在UTC中,并且不包含时间组件。
答案 4 :(得分:0)
也许早上太早了,我需要另外一杯咖啡,但是(至少显示)不是仅使用TimeZoneInfo.ConvertTimeFromUtc
并指定用户所需的TZ的解决方案?或者转换时间转换器没有考虑转换回所需TZ的日期/时间?深入思考:即使它确实如此,是否有人知道它是否考虑到服务器已转移到DST但用户所需的TZ还没有的边缘情况?