Решение, к которому мы, наконец, пришли, состояло в том, чтобы использовать 00:00 вместо 24:00, с логикой во всем классе и в остальной части приложения для интерпретации этого локального значения. Это настоящий кладж, но это наименее навязчивая и самая элегантная вещь, которую я мог придумать.
Во-первых, класс LocalTimeInterval хранит внутренний флаг того, является ли конечная точка интервала полночью конца дня (24:00). Этот флаг будет истинным, только если время окончания равно 00:00 (равно LocalTime.MIDNIGHT).
/**
* @return Whether the end of the day is {@link LocalTime#MIDNIGHT} and this should be considered midnight of the
* following day.
*/
public boolean isEndOfDay()
{
return isEndOfDay;
}
По умолчанию конструктор считает 00:00 началом дня, но есть альтернативный конструктор для ручного создания интервала на весь день:
public LocalTimeInterval(final LocalTime start, final LocalTime end, final boolean considerMidnightEndOfDay)
{
...
this.isEndOfDay = considerMidnightEndOfDay && LocalTime.MIDNIGHT.equals(end);
}
Есть причина, по которой этот конструктор не имеет просто времени начала и флага «конец дня»: при использовании с пользовательским интерфейсом с раскрывающимся списком времен мы не знаем, будет ли пользователь выберите 00:00 (что отображается как 24:00), но мы знаем, что, поскольку раскрывающийся список находится в конце диапазона, в нашем случае это означает 24:00. (Хотя LocalTimeInterval допускает пустые интервалы, мы не допускаем их в нашем приложении.)
Проверка перекрытий требует специальной логики для работы с 24:00:
public boolean overlaps(final LocalTimeInterval localInterval)
{
if (localInterval.isEndOfDay())
{
if (isEndOfDay())
{
return true;
}
return getEnd().isAfter(localInterval.getStart());
}
if (isEndOfDay())
{
return localInterval.getEnd().isAfter(getStart());
}
return localInterval.getEnd().isAfter(getStart()) && localInterval.getStart().isBefore(getEnd());
}
Точно так же преобразование в абсолютный интервал требует добавления еще одного дня к результату, если isEndOfDay() возвращает значение true. Важно, чтобы код приложения никогда не создавал Interval вручную из начального и конечного значений LocalTimeInterval, поскольку время окончания может указывать на конец дня:
public Interval toInterval(final ReadableInstant baseInstant)
{
final DateTime start = getStart().toDateTime(baseInstant);
DateTime end = getEnd().toDateTime(baseInstant);
if (isEndOfDay())
{
end = end.plusDays(1);
}
return new Interval(start, end);
}
При сохранении LocalTimeInterval в базе данных мы смогли сделать кладж полностью прозрачным, поскольку Hibernate и SQL не имеют ограничения на 24:00 (и в любом случае не имеют понятия LocalTime). Если isEndOfDay() возвращает true, наша реализация PersistentLocalTimeIntervalAsTime сохраняет и извлекает истинное значение времени 24:00:
...
final Time startTime = (Time) Hibernate.TIME.nullSafeGet(resultSet, names[0]);
final Time endTime = (Time) Hibernate.TIME.nullSafeGet(resultSet, names[1]);
...
final LocalTime start = new LocalTime(startTime, DateTimeZone.UTC);
if (endTime.equals(TIME_2400))
{
return new LocalTimeInterval(start, LocalTime.MIDNIGHT, true);
}
return new LocalTimeInterval(start, new LocalTime(endTime, DateTimeZone.UTC));
а также
final Time startTime = asTime(localTimeInterval.getStart());
final Time endTime = localTimeInterval.isEndOfDay() ? TIME_2400 : asTime(localTimeInterval.getEnd());
Hibernate.TIME.nullSafeSet(statement, startTime, index);
Hibernate.TIME.nullSafeSet(statement, endTime, index + 1);
Печально, что нам пришлось писать обходной путь в первую очередь; это лучшее, что я мог сделать.
person
Garret Wilson
schedule
05.04.2011
LocalTime
, а именноPlainTime
, поддерживает немного более широкий диапазон 00:00/24:00. И я обнаружил, что эта функция не создает никаких проблем (временной порядок по-прежнему довольно ясен), но может помочь решить некоторые другие проблемы более элегантным способом (например, IANA-TZDB также использует значение 24:00). - person Meno Hochschild   schedule 15.06.2015