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

C# 考虑DST,确定特定时区中两个日期时间之间的小时数,c#,datetime,datetimeoffset,C#,Datetime,Datetimeoffset,我正在尝试编写一个函数,它将(1)在任何时区的机器上运行,(2)计算两个日期之间的小时数,(3)考虑给定时区的DST设置 这似乎很容易,但我似乎想不出来 我正在其他时区生成工作时间表。例如,昨天(2016年11月6日)在纽约,我需要制定25小时的时间表。今天(2016年11月7日),我只需要生成24小时的时间表。明年3月,将有一天需要23个小时 我想这是我的签名,但我想我可能需要一个字符串TimeZoneId参数 public double GetScheduledHours(DateTime

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

这似乎很容易,但我似乎想不出来

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

我想这是我的签名,但我想我可能需要一个
字符串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个失败

您可以使用和使用异或运算来加减一小时。在我的测试中,我使用德国时区进行测试。也许你必须调整测试日期。我习惯于断言

[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
然而,如果你感兴趣的时区不是本地时区,事情就会变得复杂一些。显示了在特定时区中查找午夜的解决方案,这应该会有所帮助

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

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

您还必须记住,纽约将在11月6日更改DST,但在10月30日的欧洲,
DateTime.IsDaylightSavingTime
只会告诉您提供的值是否为当前本地时区的DST,而不是给定的命名时区。但据我从@Langdon了解的问题,他只对当前本地时区感兴趣
(1)在任何时区的机器上运行,
(3) 考虑给定时区的DST设置
对,
“给定时区”
-不管机器上设置的是哪一个。标题上还写着
“特定时区”
。此外,您的答案还做出了一些假设:1)DST偏差始终为一小时,2)开始日期和结束日期之间的标准偏移量没有变化。在某些边缘情况下,这两种情况都可能被证明是错误的。公认的答案使用了适当解释这一点的框架方法。