2014-08-28 4 views
4

Какой правильный и более сжатый способ получить ZonedDateTime (ы), которые представляют начало и конец текущего дня в часовом поясе, установленном в системе, на которой выполняется код ?Noda Time - начало/конец дня с зоной

Не слишком ли сложный код?

ZonedDateTime nowInZone = SystemClock.Instance.Now.InZone(DateTimeZoneProviders.Bcl.GetSystemDefault()); 

ZonedDateTime start = new LocalDateTime(nowInZone.Year, nowInZone.Month, nowInZone.Day, 0, 0, 0).InZoneStrictly(DateTimeZoneProviders.Bcl.GetSystemDefault()); 

ZonedDateTime end = new LocalDateTime(nowInZone.Year, nowInZone.Month, nowInZone.Day, 23, 59, 59).InZoneStrictly(DateTimeZoneProviders.Bcl.GetSystemDefault()); 

Учитывая эти значения, мне нужно проверить, существует ли между ними другое ZonedDateTime.

+0

Вместо этого вы можете использовать 'InZoneLeniently'. Например, Бразилия, по ее переходу на переход на DST, идет с 11:59 вечера до 1 часа ночи. Нет 12:00. 'InZoneStrictly' будет бросать в этом случае. 'InZoneLeniently' перейдет к 1am при преобразовании LocalDateTime 12am (если я правильно читаю документы). Точно так же на обратном переходе он идет с 23:59 до 23:00. Есть два 11:59 вечера. 'InZoneLeniently' выбирает более поздний. –

+0

@mikez - Хороший вопрос о Бразилии. Это уже охвачено методом, который я продемонстрировал. Благодарю. –

ответ

6

Значение AtStartOfDay на объекте DateTimeZone имеет магию, которую вы ищете.

// Get the current time 
IClock systemClock = SystemClock.Instance; 
Instant now = systemClock.Now; 

// Get the local time zone, and the current date 
DateTimeZone tz = DateTimeZoneProviders.Tzdb.GetSystemDefault(); 
LocalDate today = now.InZone(tz).Date; 

// Get the start of the day, and the start of the next day as the end date 
ZonedDateTime dayStart = tz.AtStartOfDay(today); 
ZonedDateTime dayEnd = tz.AtStartOfDay(today.PlusDays(1)); 

// Compare instants using inclusive start and exclusive end 
ZonedDateTime other = new ZonedDateTime(); // some other value 
bool between = dayStart.ToInstant() <= other.ToInstant() && 
       dayEnd.ToInstant() > other.ToInstant(); 

Пару моментов:

  • Это лучше, чтобы получить в привычку разделения экземпляра часов от вызова к теперь. Это упрощает замену часов позже при модульном тестировании.

  • Необходимо только один раз получить местный часовой пояс. Я предпочитаю использовать провайдера Tzdb, но любой из них будет работать для этой цели.

  • В конце дня лучше использовать начало следующего дня. Это мешает вам иметь дело с проблемами детализации, например, следует ли вам принимать 23:59, 23:59:59, 23: 59.999, 23: 59: 59.9999999 и т. Д. Кроме того, упрощается получение целого числа результаты при выполнении математики.

    В целом, даты + интервалы времени (или интервалы только по времени) следует рассматривать как интервалы с половинным интервалом [start,end) - в то время как диапазоны с датой следует рассматривать как полностью закрытые интервалы [start,end].

  • Из-за этого начало сравнивается с <=, но конец сравнивается с >.

  • Если вы точно знаете, что другое значение ZonedDateTime указано в том же часовом поясе и использует тот же календарь, вы можете опустить вызовы на ToInstant и просто сравнить их напрямую.

Update

Как Джон упоминается в комментариях Interval типа может быть полезным удобство для этой цели. Он уже настроен для работы с полуоткрытым диапазоном значений Instant. Следующая функция получит интервал для текущего «дня» в конкретной временной зоне:

public Interval GetTodaysInterval(IClock clock, DateTimeZone timeZone) 
{ 
    LocalDate today = clock.Now.InZone(timeZone).Date; 
    ZonedDateTime dayStart = timeZone.AtStartOfDay(today); 
    ZonedDateTime dayEnd = timeZone.AtStartOfDay(today.PlusDays(1)); 
    return new Interval(dayStart.ToInstant(), dayEnd.ToInstant()); 
} 

Называйте это, как это (используя то же значение, сверху):

Interval day = GetTodaysInterval(systemClock, tz); 

А теперь сравнение может быть сделано с помощью Contains функции:

bool between = day.Contains(other.ToInstant()); 

Обратите внимание, что вам все равно придется преобразовать в Instant, как Interval тип а не часовая зона.

+0

Он был хорошо скрыт на виду;) Спасибо за ответ и за те лучшие практики, которые вы указали. – Jhack

+1

Вы также можете упомянуть «Интервал» - например, вы можете легко написать метод, который возвращает интервал «сегодня» с учетом часов и часового пояса (или ZonedClock от 2.0 ...) –

Смежные вопросы