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

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

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

Первоначально я преобразовал время сервера (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

person JackArbiter    schedule 04.08.2014    source источник


Ответы (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

С#

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.

person Matt Johnson-Pint    schedule 04.08.2014
comment
Обратите внимание, что мягкий преобразователь был изменен на указанное выше поведение для Noda Time 2.x. См. изменения в резолвере Lenient в руководстве по миграции. - person Matt Johnson-Pint; 03.11.2015