2015-02-02 3 views
8

Извините, если это кажется простым вопросом, но я в основном просто хочу проверить, что решение, которое у меня есть, является наиболее разумным/выполняется для всех случаев.Каков наилучший способ найти последний возможный момент LocalDate в конкретном часовом поясе?

Предварительно существующая база данных SQL Server, с которой мы работаем, сохраняет значения «период» как включительно start -> inclusive end UTC DateTime. (Оба начала & концевые столбцы: datetime2(7), автоматически преобразуются в System.DateTime экземпляров DateTimeKind.UTC, прежде чем мы начнем с ними работать).

Так что, если мне нужно хранить «весь день/месяц/год, с учетом часового пояса пользователя» Мне нужно найти «последний возможный момент, заданного LocalDate, в частности DateTimeZone».

Методов У меня есть следующие:

public static LocalDateTime AtEndOfDay(this LocalDate localDate) 
{ 
    return localDate 
     .PlusDays(1) 
     .AtMidnight() 
     .PlusTicks(-1); 
} 

public static ZonedDateTime AtEndOfDay(this DateTimeZone zone, LocalDate localDate) 
{ 
    return zone 
     .AtStartOfDay(localDate.PlusDays(1)) 
     .Plus(Duration.FromTicks(-1)); 
} 

Я думаю, что также нужно избегать (в другом месте) отображение «конца дня» LocalDateTime с помощью .AtLeniently(..), так как если 23: 59: 59,9999999 является «пропущено »и не существует в целевом DateTimeZone, тогда он будет отображаться в следующий доступный момент на« внешней »стороне зазора 00: 00: 00.000000, что даст мне значение ZonedDateTime.

Измененный Методы:

public static LocalDateTime AtEndOfDay(this LocalDate localDate) 
{ 
    // TODO: Replace with localDate.At(LocalTime.MaxValue) when NodaTime 2.0 is released. 
    return localDate 
     .PlusDays(1) 
     .AtMidnight() 
     .PlusTicks(-1); 
} 

public static ZonedDateTime AtEndOfDay(this DateTimeZone zone, LocalDate localDate) 
{ 
    return zone 
     .AtStartOfDay(localDate.PlusDays(1)) 
     .Plus(-Duration.Epsilon); 
} 

public static ZonedDateTime AtEndOfDayInZone(this LocalDate localDate, DateTimeZone zone) 
{ 
    return zone.AtEndOfDay(localDate); 
} 
+2

Я бы почти всегда рекомендовал вместо этого использовать полуоткрытый интервал (включительно начало, эксклюзивный конец). Значения, которые вам нужны, намного проще вычислять. То естьвам не нужно компенсировать разрешение/точность, в которой хранятся значения даты и времени (на протяжении всего их путешествия через ваш код), чтобы гарантировать, что вы всегда получили «последний возможный момент» –

+0

Я знаю, что существуют противоречивые мнения о том, как хранить интервалы, но, как я уже упоминал в своем вопросе, я работаю с существующей системой и изменяю все, что нужно, это не вариант. –

+0

Извините, я не имею в виду, что я не вижу выгоды или не хочу _want_ изменять структуру БД, у меня просто нет этого в качестве опции прямо сейчас :) –

ответ

5

Во-первых, как было отмечено в комментариях, в любое время вы можете работа с эксклюзивной верхней границей, которая была бы хорошей идеей :)

Ваш AtEndOfDay метод выглядит разумным для меня, за исключением того, что я бы использовал Duration.Epsilon вместо Duration.FromTicks. В частности, в Noda Time 2.0 мы собираемся перейти к точности наносекунд вместо тиков; Duration.Epsilon будет делать то, что вы хотите в обоих случаях.

Для вашего LocalDate решения, мне кажется, что мы упускаем значение LocalTime.MaxValue (или EndOfDay), крупнейший представима LocalTime. Если бы это было доступно, вы могли бы просто написать:

return date.At(LocalTime.MaxValue); 

, который устранил бы те же проблемы «клещей», что и раньше. Я постараюсь не забывать добавить, что для 2.0 - хотя это будет документировано с комментарием к эффекту «Использовать это только если вы вынуждены устаревшими системами» :)

Один недостаток добавления дня а затем вычитание галочки (или наносекунды) заключается в том, что она не сработает на LocalDate.MaxValue. Вероятно, это не проблема , но для кода внутри самого времени Noda мы стараемся избегать подобных вещей. Я бы не попытался избежать этого для версии ZonedDateTime, хотя это довольно сложный сценарий. (Возможно, есть способы сделать это, но это не стоило бы.)

+0

Я попытался добавить чек, но похоже, что это свойство 'LocalDate.MaxValue', это' internal' или функция NodaTime 2.0? –

+0

@BenJenkinson: Похоже, мы тоже не получили его в версии 2.0. Как я уже сказал, это, вероятно, не проблема * реальная * для вас - или вы могли бы просто добавить чек, чтобы узнать, больше ли год на данный момент 9000 :) –

+0

@BenJenkinson: Собственно, подумав об этом, он, вероятно, будет должен быть «MaxIsoValue» или что-то в этом роде ... учитывая, что разные системы календаря имеют разные максимальные значения. Однако есть «CalendarSystem.MaxYear». –