【问题标题】:Noda Time Countdown野田时间倒计时
【发布时间】:2014-12-18 08:37:06
【问题描述】:

我已经写了一个应用程序来显示距离下一次大型行业贸易展的剩余时间。
(在撰写本文时大约是两年后)

我开始使用标准的 DateTime 类,但很快就遇到了处理每个月不同天数的问题,2016 年是闰年,包含 Leap Day、夏令时等。

幸好我发现了NodaTime。 (感谢@JonSkeet)

不太庆幸的是,我习惯于使用 DateTime 的方式并不适用,而且我很难弄清楚如何获得剩余的时间。 (周围没有很多例子)

例如,以下代码不起作用,因为您不能从 LocalDateTime 中减去 instant

void example()
{
    DateTime DT = Convert.ToDateTime("09/12/2016 10:00AM");

    LocalDateTime NodaLocalDateTime = new LocalDateTime(
        DT.Year, DT.Month, DT.Day, DT.Hour, DT.Minute, 0);

        Period P = NodaLocalDateTime - SystemClock.Instance.Now;
}

所以问题变成了:

您如何获得从现在到某个日期的剩余时间?

【问题讨论】:

    标签: c# countdown nodatime


    【解决方案1】:

    要确定两个事件之间的“日历”时间量,您需要Period,正如您已经发现的那样。但是,处理本地日期和时间。

    要确定两个事件之间的“日历中性”时间量,您可以使用 InstantDuration - 但您无法显示剩余的月数。

    这两种方法都有缺点,但基本上它们是时间运作方式的基础。如果您使用本地时间方法,那么您会发现当您进行 DST 转换时,时间量会向后或向前跳一个小时。如果您使用即时方法,则仅限于天/月/小时/分钟等 - 而不是几个月。

    两者之间的一个选择是使用LocalDateTimePeriod,但将事件和当前时间都锚定为UTC。这样就永远不会有不连续性,因为 UTC 是一个不变的基线,有效地。这也意味着无论您在世界的哪个地方查看计数器(或托管代码,具体取决于您打算做什么),您都将始终显示相同的“剩余时间”。

    如果您想了解为什么无法在两个ZonedDateTime 值之间获得Period 的更多详细信息,我可以想到一些根本上存在问题的示例。底线是日历算术和时区不能很好地结合在一起......

    只是给出一些实际的代码,我会有类似的东西:

    public sealed class EventCountdown
    {
         private readonly LocalDateTime eventTimeUtc;
         private readonly IClock clock;
    
         // It's probably most convenient to express the event time with the time zone
         // in which it occurs. You could easily change this though.
         public EventCountdown(ZonedDateTime zonedEventTime, IClock clock)
         {
             this.eventTimeUtc = zonedEventTime.WithZone(DateTimeZone.Utc).LocalDateTime;
             this.clock = clock;
         }
    
         public Period GetPeriodRemaining()
         {
             return Period.Between(clock.Now.InUtc().LocalDateTime, eventTimeUtc);
         }
    }
    

    请注意,在 Noda Time 2.0 中,IClock.Now 属性已更改为 GetCurrentInstant 方法...但在这种情况下,您可能会在 UTC 中使用 ZonedClock 并在其上调用 GetCurrentLocalDateTime

    【讨论】:

    • 果然有效。当这种妥协实质上是在剥离偏移量和时区(仅比较 UTC 值)时,搞乱OffsetDateTimeZonedDateTime 只是感觉很奇怪。是否没有可以将时区 UTC 偏移量的差异应用于 Period(.Between) 的解决方案?
    • @Okuma.Scott:我不确定你的意思,但这个解决方案只是比较 UTC 值......而且相当容易做。请注意,此代码不使用OffsetDateTime - 它直接从ZonedDateTimeLocalDateTime
    【解决方案2】:

    我最终使用的解决方案是Period.Between(),这似乎可以解决问题。

    using NodaTime;
    
        DateTime EventDT;
        LocalDateTime LocalizedEventDT;
        Period TimeLeft; 
    
        public EventCountdown()
        {
            // Start with a date and time
            EventDT = Convert.ToDateTime("09/12/2016 10:00AM");
    
            // Localize it
            LocalizedEventDT = new LocalDateTime(
                EventDT.Year, EventDT.Month, 
                EventDT.Day, EventDT.Hour, 
                EventDT.Minute, 0);
        }
    
        // find out how much time is between now and the future date
        public Period GetPeriodRemaining()
        {
            DateTime dt_Now = DateTime.Now;
    
            return Period.Between(new LocalDateTime(
                dt_Now.Year, dt_Now.Month, dt_Now.Day, dt_Now.Hour, 
                dt_Now.Minute, dt_Now.Second), LocalizedEventDT);
        }
    


    如果有人在考虑时区(本地与事件发生地)的同时执行此操作,那就太棒了。 (我尝试使用ZonedDateTime 以类似的方式执行此操作,但遇到了一堵砖墙)。

    此外,我不清楚这种方法是否考虑了一个月中的不同日子、夏令时、闰年等。有谁知道吗?
    如果不是,显然我会欢迎任何优雅的解决方案。

    【讨论】:

    • 一旦开始包含时区,答案应该是什么并不明显。算术基本上是糊涂的。您可以找到剩余的“绝对”时间,您可以用固定长度单位表示,例如小时或天(如果您假设一天是 24 小时),但 月数 并不总是明显的。请注意,LocalDateTime.FromDateTime 是你的朋友,顺便说一句...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-28
    • 1970-01-01
    • 2014-09-20
    • 2014-10-22
    • 2019-06-04
    • 2011-01-14
    • 1970-01-01
    相关资源
    最近更新 更多