基于本地时区的DateTime (UTC)差异
本文关键字:UTC 差异 DateTime 于本地 时区 | 更新日期: 2023-09-27 18:14:45
我有两个DateTime
对象,其中包含两个UTC日期/时间和一个用户TimezoneId (tzdb)作为string
。我正试图编写一个方法,它接受这三个参数,并返回相对于时区的两个日期时间之间的总秒数(或Duration
)。
public static double GetDurationForTimezone(DateTime startUtc, DateTime endUtc, string timezoneId)
{
var timezone = DateTimeZoneProviders.Tzdb.GetZoneOrNull(timezoneId);
// convert UTC to timezone
var startInstantUtc = Instant.FromDateTimeUtc(startUtc);
var startZonedDateTime = startInstantUtc.InZone(timezone);
var endInstantUtc = Instant.FromDateTimeUtc(endUtc);
var endZonedDateTime = endInstantUtc.InZone(timezone);
return endZonedDateTime.ToInstant().Minus(startZonedDateTime.ToInstant()).ToTimeSpan().TotalSeconds;
}
我想在这个时区做,以确保它考虑到在这段时间内可能发生的任何可能的夏令时变化。
示例测试:
// DST starts (25h day -- DST starts: 10/4 @ 2am local time)
var result = GetDurationForTimezone(
new DateTime(2015, 10, 3, 15, 0, 0, DateTimeKind.Utc),
new DateTime(2015, 10, 4, 15, 0, 0, DateTimeKind.Utc),
"Australia/Sydney");
Assert.Equal(TimeSpan.FromHours(25).TotalSeconds, result);
但是当运行这个测试时,似乎对.ToInstant()
的调用不遵循ZonedDateTime
版本,而是遵循原始的UTC DateTime
对象。因此,我看到的结果是24小时。
在确定基于utc的时间戳之间的持续时间时,时区是无关的。
UTC是协调世界时。对地球上的每个人来说都是一样的。它没有夏令时,它的偏移量总是零(UTC+00:00)。
既然您已经声明了输入值是UTC,那么您不必在此操作中使用野田时间。只需减去这两个值。
TimeSpan duration = endUtc - startUtc;
如果你使用Noda时间,UTC值最好用Instant
表示,这使得获得Duration
非常容易。
Instant start = Instant.FromDateTimeUtc(startUtc);
Instant end = Instant.FromDateTimeUtc(endUtc);
Duration duration = end - start;
你也可以使用碰巧是"在UTC"的ZonedDateTime
值来表示它们,但是你很快就会发现API要求你将它们转换回Instant
值以获得Duration
。
ZonedDateTime start = LocalDateTime.FromDateTime(startUtc).InUtc();
ZonedDateTime end = LocalDateTime.FromDateTime(endUtc).InUtc();
Duration duration = end.ToInstant() - start.ToInstant();
您可能认为只使用LocalDateTime
是一种选择,但是该结构表示墙时间,没有任何时区信息。你不能在它们之间获得Duration
。您可以通过使用Period.Between
来获得Period
,但是这将表示两个表示之间的日历/时钟值差异—这与实际经过的时间量不同。
作为一个有助于理解区别的思考练习,考虑以下两个值:
2015-11-01 00:30
2015-11-01 01:30
如果我告诉你这些值是UTC的,那么就有一个小时的差。但是,如果我告诉您这些是时钟值,并且它们位于美国东部时区,那么它们可能间隔一个小时,或者它们可能间隔两个小时。这取决于01:30是在夏时制转换之前还是之后-因为这一天有两个。
现在如果我给你这些值:
2015-11-01 00:30
2015-11-01 02:30
同样,如果将它们解释为UTC,它们正好相隔两个小时。但是,如果您在同一个美国东部时区解释它们,那么它们正好间隔三个小时,因为该范围包括DST转换。如果你只是减去本地的时间值,那么你会得到两个小时,这是不正确的。
切换到利用ZonedDateTime
的LocalDateTime
属性允许比较日期/时间相对于时区。这适用于两个主要的测试用例(23小时和25小时):
public static double GetDurationForTimezone(DateTime startUtc, DateTime endUtc, string timezoneId)
{
var timezone = DateTimeZoneProviders.Tzdb.GetZoneOrNull(timezoneId);
// convert UTC to timezone
var startInstantUtc = Instant.FromDateTimeUtc(startUtc);
var startZonedDateTime = startInstantUtc.InZone(timezone);
var startLocalDateTime = startZonedDateTime.LocalDateTime;
var endInstantUtc = Instant.FromDateTimeUtc(endUtc);
var endZonedDateTime = endInstantUtc.InZone(timezone);
var endLocalDateTime = endZonedDateTime.LocalDateTime;
return Period.Between(startLocalDateTime, endLocalDateTime, PeriodUnits.Seconds).Seconds;
}
查看此页:ZonedDateTime。比较器成员
似乎你必须使用属性Local
而不是Instant
来反映当地的夏令时