Сравнение LocalDateTime для разных часовых поясов в Nodatime

Я работаю над приложением, которое позволяет пользователю планировать событие. Пользователь предоставляет часовой пояс Олсона с помощью средства выбора часового пояса, а также дату и время для указанного события с помощью средства выбора календаря asp и стороннего средства выбора времени ajax (поэтому DateTime поставляется всегда будет в том же порядке). Я сравниваю время, которое хочет пользователь, и часовой пояс, который пользователь предоставляет, с временем нашего сервера и его часовым поясом, и запускаю событие в тот момент, когда пользователь ожидает его запуска.

Из того, что я понимаю, прочитав эту ссылку в группе Google nodatime, преобразовав один ZonedDateTime в другой часовой пояс (используя WithZone) довольно просто (как только у меня будет отображено событие пользователя из LocalDateTime к ZonedDateTime очевидно). Мне не нужно беспокоиться о смещениях, и разница в летнем времени между, скажем, Pheonix и Chicago будет должным образом учтена.

Я изначально конвертировал время сервера (DateTime.Now) к ZonedDateTime и сравнил таким образом, но после прочтения этой ссылки на SO я перешел на использование IClock,

Пока что в тестировании все работает, но меня беспокоят угловые случаи, которые я не могу тестировать. Согласно документации для NodaTime:

Самая большая "гоча" - это конвертация LocalDateTime в ZonedDateTime - у него есть несколько угловых случаев, которые вы должны рассмотреть.

Я внимательно прочитал документацию и полагаю, что эта ошибка относится к тем временам года, которые либо не происходят, либо происходят дважды. Эти времена никогда не будут установлены как время событий для наших пользователей, но я использую LenientResolver для них. Есть ли другие ошибки - когда я конвертирую из LocalDateTime в ZonedDateTime , я что- то упускаю или летнее время будет преследовать меня?

Кроме того, мне нужно конвертировать пользователя ZonedDateTime перед сравнением с часовым поясом сервера (что я сейчас и делаю) или это ненужный (или даже ошибочный) шаг? Сможет ли NodaTime сравнивать должным образом (без проблем с переходом на летнее время), если бы я сравнивал необращенные события ZonedDateTime (вместо события ZonedDateTime после преобразования в часовой пояс сервера) на текущий сервер 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 ответ

Решение

Похоже, вы на правильном пути.

относительно LenientResolverУбедитесь, что вы знаете о его поведении. Оно использует ReturnStartOfIntervalAfter для пружинного зазора, и ReturnLater для резервного дублирования.

ИМХО, это не лучшая конфигурация для планирования будущих событий. (См. Выпуск № 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,

Другие вопросы по тегам