比较Nodatime中不同时区的LocalDateTime

时间:2014-08-04 17:02:51

标签: .net vb.net datetime nodatime

我正在开发一个允许用户安排活动的应用程序。用户使用Time Zone Picker提供Olson时区,并通过asp日历选择器和第三方ajax时间选择器提供所述事件的日期和时间(因此提供的DateTime将始终位于相同的模式)。我将用户想要的时间和用户提供的时区与我们服务器的时间和时区进行比较,并在用户期望被触发的瞬间触发事件。

根据我的理解,阅读this link at the nodatime google group,将一个ZonedDateTime转换为另一个时区(使用WithZone)非常简单(一旦我将用户的事件映射到从LocalDateTimeZonedDateTime,很明显。我不需要担心抵消,比如Pheonix和芝加哥之间的夏令时差异将被恰当地考虑在内。

我最初将服务器的时间(DateTime.Now)转换为ZonedDateTime并进行了比较,但在阅读this link on SO后,我转而使用IClock

到目前为止,在测试中一切正在进行,但我担心我可能没有测试的极端情况。根据NodaTime的文档:

  

最大的"陷阱"正在将LocalDateTime转换为ZonedDateTime - 它有一些您需要考虑的极端情况。

我已经仔细阅读了文档,并且我认为这个问题涉及一年中那些不会发生或发生两次的时间。这些时间永远不会被设置为我们用户的活动时间,但我会使用LenientResolver。还有其他问题 - 当我从LocalDateTime转换为ZonedDateTime时,我错过了什么或者夏令时最终会困扰我吗?

此外,我是否需要在比较之前将用户的ZonedDateTime转换为服务器时区(我现在正在做)或者这是不必要的(甚至是错误的一步? NodaTime是否能够正确比较(没有夏令时问题),如果我要比较事件未转换的ZonedDateTime(而不是事件' s ZonedDateTime转换为服务器时间后zone)到当前服务器ZonedDateTime(参见下面的代码,第三行到最后一行)?单步执行代码时,我可以看到时间和偏移量,但我担心这样做可能过于简单化了,引发了问题。

Protected Function EventIsReady(ByVal insTimeZone As String, ByVal eventDate As DateTime) As Boolean
        Dim clock As IClock = SystemClock.Instance
        Dim now As Instant = clock.Now

        'server time zone (America/Chicago), unfortunately not UTC
        Dim zone = DateTimeZoneProviders.Tzdb("America/Chicago")
        Dim serverZonedDateTime = now.InZone(zone)

        'user time zone
        Dim userTimeZone As NodaTime.DateTimeZone = NodaTime.DateTimeZoneProviders.Tzdb.GetZoneOrNull(insTimeZone)
        Dim userEventLocalDateTime = LocalDateTime.FromDateTime(eventDate)
        Dim eventZonedDateTime = userTimeZone.ResolveLocal(userEventLocalDateTime, Resolvers.LenientResolver)
        Dim eventTimeInServerTimeZone = eventZonedDateTime.WithZone(zone)

        Dim isReady As Boolean = False
        If eventTimeInServerTimeZone >= serverZonedDateTime Then
            isReady = True
        End If
        Return isReady
    End Function

1 个答案:

答案 0 :(得分:1)

听起来你走在正确的轨道上。

关于LenientResolver,请确保您了解其行为。它使用ReturnStartOfIntervalAfter作为弹跳间隙,ReturnLater作为后退重叠。

恕我直言,这不是安排未来事件的最佳配置。 (参见Issue #295),然后尝试这样做:

<强> VB.NET

Public Shared ReadOnly SchedulingResolver As ZoneLocalMappingResolver = _
  Resolvers.CreateMappingResolver(Resolvers.ReturnEarlier, _
  AddressOf ReturnForwardShifted)

Public Shared Function ReturnForwardShifted(local As LocalDateTime, _
  zone As DateTimeZone, before As ZoneInterval, after As ZoneInterval) _
  As ZonedDateTime
    Dim newLocal As LocalDateTime = local.PlusTicks(after.Savings.Ticks)
    Return New ZonedDateTime(newLocal, zone, after.WallOffset)
End Function

<强> C#

public static readonly ZoneLocalMappingResolver SchedulingResolver =
  Resolvers.CreateMappingResolver(Resolvers.ReturnEarlier, ReturnForwardShifted);

public static ZonedDateTime ReturnForwardShifted(LocalDateTime local,
  DateTimeZone zone, ZoneInterval before, ZoneInterval after)
{
    LocalDateTime newLocal = local.PlusTicks(after.Savings.Ticks);
    return new ZonedDateTime(newLocal, zone, after.WallOffset);
}

关于服务器的时区 - 您应该将其从代码中删除。您的代码不应该关心服务器的时区是什么。而是在ToInstant()(您的ZonedDateTime变量)上调用eventZonedDateTime,然后将其与Instant返回的clock.Now进行比较。