确定特定时区中两个 DateTime 之间的小时数,同时考虑 DST

Determine the number of hours between two DateTimes in a specific Time Zone, taking DST into account

我正在尝试编写一个函数,它将 (1) 运行 在任何时区的机器上,(2) 计算两个日期之间的小时数,以及 (3) 采用 DST 设置对于给定的时区考虑...

看起来很容易,但我似乎想不通。

我正在生成其他时区的工作时间表。因此,例如,昨天(2016 年 11 月 6 日)在纽约,我需要生成 25 小时的时间表。今天(2016 年 11 月 7 日)我只需要生成 24 小时的时间表。明年三月,会有一天需要23小时

我正在看这是我的签名,但我想我可能需要一个 string TimeZoneId 参数。

public double GetScheduledHours(DateTime begin, DateTime end) {}

我从这里开始:

// incorrectly outputs 24
Console.WriteLine((new DateTime(2016, 11, 7) - new DateTime(2016, 11, 6)).TotalHours);

接下来我尝试了这个,但只取得了短暂的成功:

// correctly outputs 25 on a machine whose local time zone is "Eastern Standard Time"
// incorrectly outputs 24 on dotnetfiddle whose local time zone is "Coordinated Universal Time"
var ticks = (new DateTime(2016, 11, 7).ToUniversalTime().Ticks - new DateTime(2016, 11, 6).ToUniversalTime().Ticks;
Console.WriteLine(ticks / 10000 / 1000 / 60 / 60 + " hours");

我尝试使用 DateTimeOffset 无济于事,还发现了一些奇怪的行为...

// on a machine whose local time zone is Easter Standard Time,
// if you store the DateTime inside of a DateTimeOffset, simple subtraction works fine
var pacificTz = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");

DateTime pst6thAsDateTime = pst6th.DateTime;
DateTime pst7thAsDateTime = pst7th.DateTime;

DateTimeOffset pst6thAsDateTimeOffset = pst6th.DateTime;
DateTimeOffset pst7thAsDateTimeOffset = pst7th.DateTime;

// incorrectly outputs 24
Console.WriteLine((pst7thAsDateTime - pst6thAsDateTime).TotalHours);

// correctly outputs 25
// also incorrectly outputs 24 on a machine whose local time zone is not "Eastern Standard Time"
Console.WriteLine((pst7thAsDateTimeOffset - pst6thAsDateTimeOffset).TotalHours);

带有一些代码的 fiddle...如果您 运行 在本地时区为 "Eastern Standard Time" 的机器上使用此代码,只有 2 个失败。

https://dotnetfiddle.net/djZ7ME

你可以使用IsDaylightSavingTime and use XOR operation to add or subtract an hour. In my test I'm using german time zone to test. Maybe you have to adapt the testdates. I'm using fluentassertions断言。

[TestFixture]
public class DateTimeScheduleTests
{
    public double GetScheduledHours(DateTime begin, DateTime end)
    {
        var beginIsDaylight = begin.IsDaylightSavingTime();
        var endIsDayLight = end.IsDaylightSavingTime();

        var diff = 0;
        if (beginIsDaylight ^ endIsDayLight)
            diff = beginIsDaylight ? -1 : 1;

        return (begin - end).TotalHours + diff;
    }

    [Test]
    public void TestDate()
    {
        var begin = new DateTime(2016, 10, 30);
        var end = new DateTime(2016, 10, 29);

        GetScheduledHours(begin, end).Should().Be(24);

        begin = new DateTime(2016, 10, 31);
        end = new DateTime(2016, 10, 30);

        GetScheduledHours(begin, end).Should().Be(25);

        begin = new DateTime(2017, 3, 26);
        end = new DateTime(2017, 3, 25);

        GetScheduledHours(begin, end).Should().Be(24);

        begin = new DateTime(2017, 3, 27);
        end = new DateTime(2017, 3, 26);

        GetScheduledHours(begin, end).Should().Be(23);
    }
}

如果你只想使用本机的本地时区,那么答案很简单:

var start = new DateTimeOffset(new DateTime(2016, 11, 6));
var end = new DateTimeOffset(new DateTime( 2016, 11, 7));
var hours = (end - start).TotalHours; // 25

但是,如果您感兴趣的时区不是本地时区,事情就会变得有点复杂。 Jon Skeet's answer here 显示了在特定时区查找午夜的解决方案,这应该有所帮助。

var pacificTz = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
var startLocal = new DateTime(2016, 11, 6);
var startOffset = new DateTimeOffset(startLocal, pacificTz.GetUtcOffset(startLocal));
var endLocal = new DateTime(2016, 11, 7);
var endOffset = new DateTimeOffset(endLocal, pacificTz.GetUtcOffset(endLocal));
var hours = (endOffset - startOffset).TotalHours; // 25

在这种情况下,正如参考答案所指出的,您应该注意创建不存在的时间!