【问题标题】:Determine the number of hours between two DateTimes in a specific Time Zone, taking DST into account确定特定时区中两个 DateTime 之间的小时数,同时考虑 DST
【发布时间】:2016-11-07 22:29:31
【问题描述】:

我正在尝试编写一个函数,该函数将 (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);

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

https://dotnetfiddle.net/djZ7ME

【问题讨论】:

  • 您还必须记住,纽约将在 11 月 6 日更改 DST,而欧洲则在 10 月 30 日更改。

标签: c# datetime datetimeoffset


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    您可以使用IsDaylightSavingTime 并使用异或运算来增加或减少一个小时。在我的测试中,我使用德国时区进行测试。也许您必须调整测试日期。我正在使用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);
        }
    }
    

    【讨论】:

    • DateTime.IsDaylightSavingTime 只会告诉您提供的值是否在 DST 中针对当前本地时区 - 而不是针对给定的指定时区。
    • 但据我了解@Langdon 的问题,他只是对当前的本地时区感兴趣(1) run on a machine in any time zone,(3) take DST settings for the given time zone into account
    • 对,"for the given time zone" - 不管机器上的哪一套。标题还写着"in a specific time zone"
    • 另外,您的回答做出了一些假设:1) DST 偏差始终为一小时,2) 开始日期和结束日期之间的标准偏移量没有变化。在某些边缘情况下,这两者都可以被证明是错误的。接受的答案使用正确解决此问题的框架方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-12
    • 2010-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多