Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
594 views
in Technique[技术] by (71.8m points)

.net - Comparing LocalDateTime for Different Time Zones in Nodatime

I am working on an application that allows a user to schedule an event. The user supplies an Olson time zone by using a Time Zone Picker, and a date and time for said event through an asp calendar picker and third-party ajax time picker (so the DateTime supplied will always be in the same pattern). I compare the time the user wants and the time zone the user supplies with our server's time and its time zone, and fire the event the instant the user expects it to be fired.

From what I understand, having read this link at the nodatime google group, converting one ZonedDateTime to another time zone (using WithZone) is fairly straightforward (once I have the user's event mapped from LocalDateTime to a ZonedDateTime, obviously). I don't need to worry about offsets, and daylight savings time differences between, say, Pheonix and Chicago will be properly accounted for.

I had originally converted the server's time (DateTime.Now) to a ZonedDateTime and compared this way, but after reading this link on SO I switched to using IClock.

So far in testing everything is working out, but I am worried about corner cases that I might not be testing for. According to the documentation for NodaTime:

The biggest "gotcha" is converting LocalDateTime to ZonedDateTime - it has some corner cases you need to consider.

I have read through the documentation thoroughly and I assume that this gotcha is in reference to those times of the year that either do not occur or that occur twice. These times will never be set as event times for our users but I do use the LenientResolver for them. Are there any other gotchas - when I convert from LocalDateTime to ZonedDateTime, am I missing anything or will daylight savings time end up haunting me?

Also, do I need to convert the user's ZonedDateTime to the server time zone before comparison (which I am doing now) or is this an unnecessary (or even erroneous) step? Will NodaTime be able to compare properly (without daylight savings problems) if I were to compare the event's unconverted ZonedDateTime (instead of the event's ZonedDateTime after conversion to the server time zone) to the current server ZonedDateTime (see code below, third to last line)? When stepping through the code I can see the times and offsets, but I'm worried that doing this might be an oversimplification that introduces problems.

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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It sounds like you're on the right track.

Regarding LenientResolver, make sure you are aware of its behavior. It uses ReturnStartOfIntervalAfter for the spring-forward gap, and ReturnLater for the fall-back overlap.

IMHO, that isn't the best configuration for scheduling of future events. (See Issue #295), and try this instead:

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);
}

Regarding the server's time zone - you should leave that out of your code. Your code should not care what the time zone of the server is. Instead, call ToInstant() on the ZonedDateTime (your eventZonedDateTime variable), then compare that with the Instant returned from clock.Now.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...