计算DateTimes之间的持续时间时,安全处理夏时制(或任何其他理论上的非恒定偏移量) [英] Safe handling of daylight savings (or any other theoretical non-constant offset) while calculating durations between DateTimes

查看:57
本文介绍了计算DateTimes之间的持续时间时,安全处理夏时制(或任何其他理论上的非恒定偏移量)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I know this isn't the first time this topic has been brought up even in the past 24 hours, but I'm surprised that I have not come across one clear / best practices solution to this problem. The problem also seems to contradict what I thought was a no-brainer design decision to save all dates in UTC. I'll try to state the problem here:

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

Given two DateTime objects, find the duration between them while accounting for daylight savings.

请考虑以下情形:

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

  1. UtcDate - LocalDate where LocalDate is 1 millisecond earlier than a DST switchover.

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

LocalDateA - LocalDateB where LocalDateB is 1 millisecond earlier than a DST switchover.

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

UtcDate - LocalDate.ToUtc() provides a duration that did not consider the DST switch. LocalDateA.ToUtc() - LocalDateB.ToUtc() is correct, but LocalDateA - LocalDateB also disregards DST.

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

Now, there obviously are solutions to this problem. The solution that I'm using now is this extension method:

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, 
            DateTimeKind.Unspecified), subtrahendTimeZone));
}

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

It works, I guess. I have some problems with it though:

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

  1. If dates are all converted to UTC before being saved, then this method won't help. The timezone information (and any handling of DST) is lost. I've been conditioned to always save dates in UTC, is the issue of DST just not impactful enough to make that a bad decision?

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

It's unlikely that someone will be aware of this method, or even thinking about this problem, when calculating the difference between dates. Is there a safer solution?

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

If we all work together, maybe the tech industry can convince congress to abolish daylight savings.

推荐答案

正如您所指出的,这已经在前面讨论过了.此处此处是两个不错的帖子.

As you pointed out, this has been discussed before. Here and here are two good posts to review.

此外, DateTime上的文档.Subtract 这样说:

Also, the documentation on DateTime.Subtract has this to say:

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

The Subtract(DateTime) method does not consider the value of the Kind property of the two DateTime values when performing the subtraction. Before subtracting DateTime objects, ensure that the objects represent times in the same time zone. Otherwise, the result will include the difference between time zones.

注意

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

The DateTimeOffset.Subtract(DateTimeOffset) method does consider the difference between time zones when performing the subtraction.

除了表示同一时区中的时间"外,请记住,即使对象位于同一时区中, DateTime 值的减法仍将不考虑DST或其他转换在两个对象之间.

Beyond just "represent times in the same time zone", keep in mind that even if the objects are in the same time zone, the subtraction of DateTime values will still not consider DST or other transitions between the two objects.

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

The key point is that to determine the time elapsed, you should be subtracting absolute points in time. These are best represented by a DateTimeOffset in .NET.

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

If you already have DateTimeOffset values, you can just subtract them. However, you can still work with DateTime values as long as you first convert them to a DateTimeOffset properly.

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

Alternatively, you could convert everything to UTC - but you'd have to go through DateTimeOffset or similar code to do that properly anyway.

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

In your case, you can change your code to the following:

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

您还需要 ToDateTimeOffset 扩展方法(我也用在其他答案上).

You will also need the ToDateTimeOffset extension method (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));
}

这篇关于计算DateTimes之间的持续时间时,安全处理夏时制(或任何其他理论上的非恒定偏移量)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆