确定特定时区中两个日期时间之间的小时数,并考虑DST

时间:2016-11-07 22:29:32

标签: c# datetime datetimeoffset

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

看起来很容易,但我似乎无法弄明白。

我正在其他时区生成工作时间表。因此,例如,昨天(2016年11月6日)在纽约,我需要产生25个小时的时间表。今天(2016年11月7日)我只需要生成24小时的时间表。明年3月,将有一天需要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);

一些代码的小提示......如果您在本地时区为“东部标准时间”的机器上运行此代码,则只有2个代码失败。

https://dotnetfiddle.net/djZ7ME

2 个答案:

答案 0 :(得分:2)

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

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

在这种情况下,正如参考答案指出的那样,你应该注意创造不存在的时间!

答案 1 :(得分:-1)

您可以使用IsDaylightSavingTime并使用XOR操作来添加或减去一小时。在我的测试中,我使用德国时区进行测试。也许你必须适应测试日期。我正在使用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);
    }
}