我们最终采用的解决方案是使用 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);
}
这个构造函数不仅有开始时间和“结束日”标志是有原因的:当与带有时间下拉列表的 UI 一起使用时,我们不知道用户是否会选择 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,则转换为绝对间隔需要在结果中添加另一天。重要的是,应用程序代码永远不要从 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 时,我们能够使 kludge 完全透明,因为 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);
很遗憾,我们不得不首先编写一个解决方法。这是我能做的最好的。