在计算日期时间之间的持续时间时安全处理夏时制(或任何其他理论上的非恒定偏移量)

即使在过去的24小时内,我也不是第一次提出这个话题,但是令我感到惊讶的是,我还没有找到解决这个问题的清晰/最佳实践的解决方案。这个问题似乎也与我认为毫无保留地将所有日期保存在UTC中的决定相矛盾。我将尝试在此处说明问题:

给出两个DateTime对象,在考虑夏令时的情况下找到它们之间的持续时间。

请考虑以下情形:

  1. UtcDate-LocalDate,其中LocalDate比a早1毫秒 DST切换。

  2. LocalDateA-LocalDateB,其中LocalDateB为1 比DST切换时间早一毫秒。

UtcDate-LocalDate.ToUtc()提供的持续时间不考虑DST开关。 LocalDateA.ToUtc()-LocalDateB.ToUtc()是正确的,但是LocalDateA-LocalDateB也忽略了DST。

现在,显然有 个解决方案。我现在使用的解决方案是这种扩展方法:

public static TimeSpan Subtract(this DateTime minuend,TimeZoneInfo minuendTimeZone,DateTime subtrahend,TimeZoneInfo subtrahendTimeZone)
{
    return TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(minuend,DateTimeKind.Unspecified),minuendTimeZone)
        .Subtract(TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(subtrahend,subtrahendTimeZone));
}

它有效,我想。但是我有一些问题:

  1. 如果在保存日期之前将所有日期都转换为UTC,则此日期 方法将无济于事。时区信息(以及 DST)丢失。我已经习惯于始终将日期保存为UTC,是 DST的问题还不足以使它变得糟糕 决定吗?

  2. 不太可能有人会意识到这种方法,甚至 考虑这个问题时,计算之间的差异 日期。有更安全的解决方案吗?

  3. 如果我们一起努力,也许科技行业可以说服 大会废除了夏令时。

bluemoon88888 回答:在计算日期时间之间的持续时间时安全处理夏时制(或任何其他理论上的非恒定偏移量)

正如您所指出的,这已经在前面讨论过了。 Herehere是两个不错的评论。

此外,DateTime.Subtract上的the documentation这样说:

在执行减法时,Subtract(DateTime)方法不考虑两个Kind值的DateTime属性值。减去DateTime对象之前,请确保这些对象表示相同时区中的时间。否则,结果将包括时区之间的时差。

注意

DateTimeOffset.Subtract(DateTimeOffset)方法 会在进行减法时考虑时区之间的差异。

除了“表示相同时区中的时间”外,请记住,即使对象位于相同时区中,DateTime值的减法仍将不考虑DST或两者之间的其他转换对象。

关键是要确定经过的时间,您应该减去绝对时间点。这些最好用.NET中的DateTimeOffset表示。

如果您已经有DateTimeOffset个值,则可以减去它们。但是,只要您首先将它们正确转换为DateTime,您仍然可以使用DateTimeOffset值。

或者,您可以将所有内容都转换为UTC-但是您必须通过DateTimeOffset或类似的代码来正确执行此操作。

根据您的情况,您可以将代码更改为以下内容:

public static TimeSpan Subtract(this DateTime minuend,TimeZoneInfo minuendTimeZone,DateTime subtrahend,TimeZoneInfo subtrahendTimeZone)
{
    return minuend.ToDateTimeOffset(minuendTimeZone) -
        subtrahend.ToDateTimeOffset(subtrahendTimeZone);
}

您还需要使用ToDateTimeOffset扩展方法(which I've also used on other answers)。

public static DateTimeOffset ToDateTimeOffset(this DateTime dt,TimeZoneInfo tz)
{
    if (dt.Kind != DateTimeKind.Unspecified)
    {
        // Handle UTC or Local kinds (regular and hidden 4th kind)
        DateTimeOffset dto = new DateTimeOffset(dt.ToUniversalTime(),TimeSpan.Zero);
        return TimeZoneInfo.ConvertTime(dto,tz);
    }

    if (tz.IsAmbiguousTime(dt))
    {
        // Prefer the daylight offset,because it comes first sequentially (1:30 ET becomes 1:30 EDT)
        TimeSpan[] offsets = tz.GetAmbiguousTimeOffsets(dt);
        TimeSpan offset = offsets[0] > offsets[1] ? offsets[0] : offsets[1];
        return new DateTimeOffset(dt,offset);
    }

    if (tz.IsInvalidTime(dt))
    {
        // Advance by the gap,and return with the daylight offset  (2:30 ET becomes 3:30 EDT)
        TimeSpan[] offsets = { tz.GetUtcOffset(dt.AddDays(-1)),tz.GetUtcOffset(dt.AddDays(1)) };
        TimeSpan gap = offsets[1] - offsets[0];
        return new DateTimeOffset(dt.Add(gap),offsets[1]);
    }

    // Simple case
    return new DateTimeOffset(dt,tz.GetUtcOffset(dt));
}
本文链接:https://www.f2er.com/2736954.html

大家都在问