在数据库中将日期/时间存储为UTC

时间:2010-04-05 19:07:29

标签: c# datetime utc timezone

我将数据库中的日期/时间存储为UTC,并根据特定时区将其在我的应用程序中计算回本地时间。比方说我有以下日期/时间:

01/04/2010 00:00

说这是一个国家,例如英国观察夏令时(夏令时),在这个特定的时间,我们在夏令时。当我将此日期转换为UTC并将其存储在数据库中时,它实际上存储为:

31/03/2010 23:00

因为DST的日期将调整为-1小时。当您在提交时观察DST时,此工作正常。但是,当时钟调整回来时会发生什么?当我从数据库中提取该日期并将其转换为本地时间时,特定日期时间将被视为31/03/2009 23:00,而实际上它被处理为01/04/2010 00:00

如果我错了,请纠正我,但在将时间存储为UTC时这不是一个缺陷吗?

时区转换示例

基本上我正在做的是存储信息提交到我的系统的日期/时间,以便允许用户执行范围报告。以下是我存储日期/时间的方法:

public DateTime LocalDateTime(string timeZoneId)
{
    var tzi = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
    return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi).ToUniversalTime().ToLocalTime(); 
}

以UTC身​​份存储:

var localDateTime = LocalDateTime("AUS Eastern Standard Time");
WriteToDB(localDateTime.ToUniversalTime());

6 个答案:

答案 0 :(得分:31)

您不会根据您当前是 观察它们来调整DST更改的日期 - 您可以根据在您描述的瞬间是否观察到DST来调整 / em>的。因此,在1月份的情况下,您不会应用调整。

然而 是一个问题 - 有些地方时间不明确。例如,2010年10月31日凌晨1:30在英国可以代表UTC 01:30或UTC 02:30,因为时钟从早上2点回到凌晨1点。您可以从UTC中表示的任何 instant 到达当时显示的本地时间,但操作不可逆。

同样地,你很可能有一个从未发生过的当地时间 - 例如,2010年3月28日凌晨1:30在英国没有发生 - 因为凌晨1点,时钟跳到凌晨2点。

它的长短是因为如果你想要及时表示,你可以使用UTC并获得明确的表示。如果您尝试在特定时区中表示时间,则需要时区本身(例如欧洲/伦敦)以及当前的UTC表示或本地日期和时间以及该特定时间的偏移量(消除DST过渡的歧义)。另一种方法是存储UTC及其偏移量;这允许你告诉当时的当地时间,但这意味着你无法预测一分钟之后的当地时间,因为你真的不知道时区。 (这基本上是DateTimeOffset存储的。)

我们希望Noda Time能够轻松处理这个问题,但您仍需要将其视为一种可能性。

编辑:

您显示的代码不正确。这就是原因。我已经更改了代码的结构以便于查看,但您会看到它正在执行相同的调用。

var tzi = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
var aussieTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi);
var serverLocalTime = aussieTime.ToLocalTime(); 
var utcTime = serverLocalTime.ToUniversalTime();

所以,我们现在就考虑一下 - 当地时间是13:38(伦敦的UTC + 1),悉尼时间12:38,22:39。

您的代码将提供:

aussieTime = 22:39 (correct)
serverLocalTime = 23:39 (*not* correct)
utcTime = 22:39 (*not* correct)

你应该ToLocalTime的结果上调用TimeZoneInfo.ConvertTimeFromUtc - 它会假设它是在UTC DateTime上调用的(除非它实际上得到了DateTimeKind) .Local,在这种情况下不会。)

因此,如果您在这种情况下准确保存22:39,则 不能准确地以UTC格式保存当前时间。

答案 1 :(得分:2)

您尝试将日期和时间存储为UTC是件好事。通常最好也最容易将UTC视为实际日期和时间,而本地时间只是假名。如果您需要对日期/时间值进行任何数学计算以获得时间跨度,那么UTC绝对是至关重要的。我通常在内部操作日期为UTC,并且只在向用户显示值时转换为本地时间(如果有必要)。

您遇到的错误是您错误地将本地时区分配给日期/时间值。 1月在英国将当地时间解释为夏令时区是不正确的。您应该使用在时间值表示的时间和位置生效的时区。

将时间转换回显示完全取决于系统的要求。您可以将时间显示为用户的本地时间,也可以显示为数据的源时间。但无论哪种方式,夏令时/夏令时调整都应适当适用于目标时区和时间。

答案 2 :(得分:1)

您可以通过存储转换为UTC时使用的特定偏移来解决此问题。在您的示例中,您将日期存储为

31/12/2009 23:00 +0100

向用户显示此内容时,您可以根据需要使用相同的偏移量或其当前的本地偏移量。

这种方法也有自己的问题。时间是一件混乱的事情。

答案 3 :(得分:1)

TimeZoneInfo.ConvertTimeFromUtc()方法将解决您的问题:

using System;

class Program {
  static void Main(string[] args) {
    DateTime dt1 = new DateTime(2009, 12, 31, 23, 0, 0, DateTimeKind.Utc);
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
    Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(dt1, tz));
    DateTime dt2 = new DateTime(2010, 4, 1, 23, 0, 0, DateTimeKind.Utc);
    Console.WriteLine(TimeZoneInfo.ConvertTimeFromUtc(dt2, tz));
    Console.ReadLine();
  }
}

输出:

12/31/2009 11:00:00 PM 
4/2/2010 12:00:00 AM

您需要.NET 3.5或更高版本,并在保持历史夏令时更改(Vista,Win7或Win2008)的操作系统上运行。

答案 4 :(得分:0)

  

如果我错了,请纠正我,但不是   存储时这有点瑕疵   是UTC的时间?

是的。此外,调整的天数将是23或25小时,因此前一天的成语同时是当地时间 - 一年2天24小时是错误的。

修复程序正在选择一个标准并坚持使用它。将日期存储为UTC并显示为本地是非常标准的。只是不要使用本地计算的快捷方式(+ - somthing)=新时间,你没事。

答案 5 :(得分:0)

这是一个巨大的缺陷,但它不是以UTC存储时间的缺陷(因为这是唯一合理的事情 - 存储本地时间总是是一场灾难)。这是一个缺陷,是夏令时的概念。 真正的问题是时区信息会发生变化。 DST规则是动态和历史性的。他们在2010年从美国开始的DST在2000年开始时并不相同。直到最近,Windows甚至没有包含这些历史数据,所以基本上不可能正确地做事。您必须使用tz database才能使其正确。现在我只是用谷歌搜索它,似乎.NET 3.5和Vista(我也假设Windows 2008)已经做了一些改进,System.TimeZoneInfo实际上处理历史数据。看看this

但基本上DST必须去。