基于本地时区的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小时。

基于本地时区的DateTime (UTC)差异

在确定基于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转换。如果你只是减去本地的时间值,那么你会得到两个小时,这是不正确的。

切换到利用ZonedDateTimeLocalDateTime属性允许比较日期/时间相对于时区。这适用于两个主要的测试用例(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来反映当地的夏令时