C# 安全地将UTC日期时间转换为本地时间(基于TZ)进行计算?
下面是我的博客,它给了我很多帮助(再次感谢!) 我现在想知道,当日期/时间转换回本地日期/时间时,如何安全地使用存储为UTC的日期/时间 正如Jon在我的最后一个问题中所指出的那样,DateTimeOffset表示时间上的一个瞬间,无法预测一分钟后的本地时间。我需要能够根据这些日期/时间进行计算 那么,当我从数据库中提取日期,将其转换为本地日期/时间,并对其进行具体计算时,我如何确保它们是准确的呢 情景 我的应用程序记录通过电子邮件发送的信息。收到电子邮件的日期/时间记录为提交时间。电子邮件是从exchange中提取的 我需要知道的是:C# 安全地将UTC日期时间转换为本地时间(基于TZ)进行计算?,c#,datetime,timezone,utc,datetimeoffset,C#,Datetime,Timezone,Utc,Datetimeoffset,下面是我的博客,它给了我很多帮助(再次感谢!) 我现在想知道,当日期/时间转换回本地日期/时间时,如何安全地使用存储为UTC的日期/时间 正如Jon在我的最后一个问题中所指出的那样,DateTimeOffset表示时间上的一个瞬间,无法预测一分钟后的本地时间。我需要能够根据这些日期/时间进行计算 那么,当我从数据库中提取日期,将其转换为本地日期/时间,并对其进行具体计算时,我如何确保它们是准确的呢 情景 我的应用程序记录通过电子邮件发送的信息。收到电子邮件的日期/时间记录为提交时间。电子邮件是从
1) 如果这些电子邮件来自不同的国家,我是否将收到的
电子邮件的日期/时间转换为UTC格式并存储?e、 g.Email.Received.ToUniversalTime()
不,你不能这样假设。UTC时间是完全线性的,您可以安全地对其进行计算。一旦将其转换为本地时间,它就不再是完全线性的
当夏令时发生变化时,本地时间存在重叠或间隔。如果您执行的计算跨越夏令时更改,则结果将减少一小时(如果是时间更改的程度,这是最常见的)
如果在将DateTime/DateTimeOffset值转换为本地时间之前进行计算,结果将始终正确。但是请注意,如果在夏令时更改时该值恰好位于重叠范围内,则将该值转换为本地日期时间值可能会使其变得不明确,无法判断当天的确切时间是第一次还是第二次出现。正确处理日期/时间的最安全方法是将所有内容存储为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); }
}
}
你的情景
可以,使用邮件对象的ReceivedTime或SentOn并将其转换为UTC进行存储和计算。这比上面的示例要简单得多
Message msg = new Message();
DateTime received = msg.ReceivedTime.ToUniversalTime();
received.AddDays(7);
Console.WriteLine(received.ToLocalTime());
在将结果转换为本地时间之前,您不能在UTC日期/时间上执行计算吗?它们是否为本地时间有关系吗?如果DateTimeOffset只给我一个瞬间的时间,那么任何计算都是准确的吗?你真的错过了UTC的时间点。世界上任何地方都是一样的。不要转换。@Hans:我的意思是-假设我在英国有一个日期/时间,这个日期/时间在不同的时区是不一样的。如果是澳大利亚时间,我需要将UTC时间转换为当地时间,以便对其进行计算(因为英国时间中15/04/2010 18:00
的日期会导致类似16/04/2010 04:00
Aussie time)@Guffa:所以在存储日期/时间时(例如澳大利亚时间)保存前将UTC时间转换为特定时区是否安全?这仍然代表UTC时间吗?(我不是说使用ToLocalTime()
我指的是类似于ConvertTimeBySystemTimeZoneId
。@Guffa:我并不是在假设任何事情:)@James:不,当你将值转换成时区时,就不再是UTC时间了。但是,如果您使用的是DateTimeOffset,其偏移量将相应更改,因此您可以将其转换回原始UTC时间。DateTimeOffset值包含UTC时间的偏移量,但不包含时区,因此在将其转换回UTC时是安全的,对DateTimeOffset值的计算不考虑夏令时。如果您转换为UTC,进行计算并转换回UTC,则计算将是正确的。@James:邮件中的接收日期有一个时间偏移量,因此它对应一个DateTimeOffset值。如果可以成功地将其解析为DateTimeOffset值,则可以安全地将其转换为UTC,并将DateTime值保存为明确的时间点。从那以后,你可以把它转换成任何时区,这样你就可以在任何人的当地时间显示它,如果你愿意的话。@Guffa谢谢,我想我终于开始明白这一切了!还有一件事。有时我需要解析用户自己发送的日期(例如在电子邮件正文中)。我必须把这个日期当作当地时间。如何将其存储为UTC?我是否需要获取时间偏移量(基于时区),然后将其转换为DateTimeOffset
,然后再转换为UTC datetime?@chilltemp:我应该将其存储为服务器时区UTC还是本地时区UTC?@James:我不明白服务器和本地时区UTC之间的含义。UTC是UTC,没有变化。@James-如果您不知道是使用服务器时间还是客户端时间,请始终使用服务器上的时间。(在任何情况下都应该是UTC,如@chilltemp所说。)使用服务器上的时间可以使所有用户的数据保持一致,而且更安全,因为您通常不控制客户端的时间设置。@James:@Jeffery是正确的。服务器时间最有利于稳定。例外情况是脱机客户端。如果客户端没有到服务器的活动通道,并且将在以后(例如批处理通信)通信数据,则需要使用客户端的时间(UTC)。