2

继我最后一个问题之后,@Jon Skeet给了我很多帮助(再次感谢!)

我现在想知道当日期/时间转换回本地日期/时间时,如何安全地使用存储为 UTC 的日期/时间。

正如乔恩在我的最后一个问题中指出的那样,使用DateTimeOffset表示时间的瞬间,并且无法预测一分钟后的当地时间。我需要能够根据这些日期/时间进行计算。

那么,当我从数据库中提取日期、将它们转换为本地日期/时间并对它们进行特定计算时,我如何确保它们是准确的呢?

设想

我的申请记录了通过电子邮件发送的信息。收到电子邮件的日期/时间记录为提交时间。电子邮件是从交换中提取的。

我需要知道的是:

1)如果这些电子邮件来自不同的国家,我是否只需将Recieved电子邮件的日期/时间转换为 UTC 格式并存储它?例如Email.Received.ToUniversalTime()

4

2 回答 2

6

不,你不能这样假设。UTC 时间是完全线性的,您可以安全地对其进行计算。将其转换为本地时间后,它不再是完全线性的。

当夏令时发生更改时,本地时间会出现重叠或差距。如果您进行跨越夏令时更改的计算,则结果将相差一小时(如果这是时间更改的量,这是最常见的)。

如果在将 DateTime/DateTimeOffset 值转换为本地时间之前进行计算,则结果将始终正确。但是请注意,将值转换为本地 DateTime 值可能会使其模棱两可,如果该值恰好落在夏令时更改时的重叠范围内,则无法判断该确切时间是第一次还是第二次出现。

于 2010-04-15T17:36:17.667 回答
2

正确处理日期/时间的最安全方法是将所有内容存储为 UTC 并以本地时间显示。正如 Guffa 建议的那样,所有日期/时间数学都应该在 UTC 中完成。以 UTC 存储并在显示时即时转换为本地时间。

如何使时区感知日期/时间

Microsoft 在此处有一篇关于如何将 DateTime 和 TimeZoneInfo 变量封装到结构中的文章。

这是 Microsoft 的示例结构,添加了 1 个属性以轻松获取本地时间。这需要更多的工作才能完全有用,但这是一个好的开始。

public struct TimeZoneTime
{
   public TimeZoneInfo TimeZone;
   public DateTimeOffset Time;

   public TimeZoneTime(DateTimeOffset time)
   {
      this.TimeZone = TimeZone.Local;
      this.Time = time;   
   }

   public TimeZoneTime(TimeZoneInfo tz, DateTimeOffset time)
   {
      if (tz == null) 
         throw new ArgumentNullException("The time zone cannot be a null reference.");

      this.TimeZone = tz;
      this.Time = time;   
   }

   public TimeZoneTime AddTime(TimeSpan interval)
   {
      // Convert time to UTC
      DateTimeOffset utcTime = TimeZoneInfo.ConvertTime(this.Time, TimeZoneInfo.Utc);      
      // Add time interval to time
      utcTime = utcTime.Add(interval);
      // Convert time back to time in time zone
      return new TimeZoneTime(this.TimeZone, TimeZoneInfo.ConvertTime(utcTime, this.TimeZone));
   }

    public DateTime LocalDate 
    {
        get { return Time.ToOffset(TimeZone); }
    }
}

你的场景

  1. 是的,使用邮件对象的 ReceivedTime 或 SentOn 并将其转换为 UTC 以进行存储和计算。这比上面的示例要简单得多。

    Message msg = new Message();
    DateTime received = msg.ReceivedTime.ToUniversalTime();
    received.AddDays(7);
    Console.WriteLine(received.ToLocalTime());
    
于 2010-04-15T17:55:16.637 回答