时间查询
java.time 框架包括一个用于询问日期时间值的架构:Temporal Query。 TemporalQuery 接口的一些实现可以在复数命名的TemporalQueries 类中找到。
您也可以编写自己的实现。 TemporalQuery 是 functional interface,这意味着它声明了一个方法。方法是queryFrom。
这是我实施TemporalQuery 的第一次尝试,所以请谨慎对待。这是完整的课程。免费使用 (ISC License),但风险自负。
棘手的部分是问题的要求是周末由 UTC 定义,而不是时区或传递的日期时间值的偏移量。所以我们需要将传递的日期时间值调整为UTC。虽然Instant 在逻辑上是等价的,但我使用OffsetDateTime 和offset of UTC,因为它更灵活。具体来说,OffsetDateTime 提供了一个getDayOfWeek 方法。
CAVEAT:我不知道我是否以正统的方法做事,因为我没有完全理解 java.time 设计的基础,正如它的创建者所期望的那样。具体来说,我不确定我将TemporalAccessor ta 转换为java.time.chrono.ChronoZonedDateTime 是否正确。但它似乎运作良好。
如果这个类与Instant 实例以及ChronoZonedDateTime/ZonedDateTime 一起使用会更好。
package com.example.javatimestuff;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
/**
* Answers whether a given temporal value is between Friday 22:00 UTC
* (inclusive) and Sunday 23:00 UTC (exclusive).
*
* @author Basil Bourque.
*
* © 2016 Basil Bourque
* This source code may be used according to the terms of the ISC License (ISC). (Basically, do anything but sue me.)
* https://opensource.org/licenses/ISC
*
*/
public class WeekendFri2200ToSun2300UtcQuery implements TemporalQuery<Boolean> {
static private final EnumSet<DayOfWeek> WEEKEND_DAYS = EnumSet.of ( DayOfWeek.FRIDAY , DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
static private final OffsetTime START_OFFSET_TIME = OffsetTime.of ( LocalTime.of ( 22 , 0 ) , ZoneOffset.UTC );
static private final OffsetTime STOP_OFFSET_TIME = OffsetTime.of ( LocalTime.of ( 23 , 0 ) , ZoneOffset.UTC );
@Override
public Boolean queryFrom ( TemporalAccessor ta ) {
if ( ! ( ta instanceof java.time.chrono.ChronoZonedDateTime ) ) {
throw new IllegalArgumentException ( "Expected a java.time.chrono.ChronoZonedDateTime such as `ZonedDateTime`. Message # b4a9d0f1-7dea-4125-b68a-509b32bf8d2d." );
}
java.time.chrono.ChronoZonedDateTime czdt = ( java.time.chrono.ChronoZonedDateTime ) ta;
Instant instant = czdt.toInstant ();
OffsetDateTime odt = OffsetDateTime.ofInstant ( instant , ZoneOffset.UTC );
DayOfWeek dayOfWeek = odt.getDayOfWeek ();
if ( ! WeekendFri2200ToSun2300UtcQuery.WEEKEND_DAYS.contains ( dayOfWeek ) ) {
// If day is not one of our weekend days (Fri-Sat-Sun), then we know this moment is not within our weekend definition.
return Boolean.FALSE;
}
// This moment may or may not be within our weekend. Very early Friday or very late Sunday is not a hit.
OffsetDateTime weekendStart = odt.with ( DayOfWeek.FRIDAY ).toLocalDate ().atTime ( START_OFFSET_TIME ); // TODO: Soft-code with first element of WEEKEND_DAYS.
OffsetDateTime weekendStop = odt.with ( DayOfWeek.SUNDAY ).toLocalDate ().atTime ( STOP_OFFSET_TIME ); // TODO: Soft-code with last element of WEEKEND_DAYS.
// Half-Open -> Is equal to or is after the beginning, AND is before the ending.
// Not Before -> Is equal to or is after the beginning.
Boolean isWithinWeekend = ( ! odt.isBefore ( weekendStart ) ) && ( odt.isBefore ( weekendStop ) );
return isWithinWeekend;
}
static public String description () {
return "WeekendFri2200ToSun2300UtcQuery{ " + START_OFFSET_TIME + " | " + WEEKEND_DAYS + " | " + STOP_OFFSET_TIME + " }";
}
}
让我们使用TemporalQuery。虽然定义TemporalQuery 需要一些工作,但使用它非常简单:
- 实例化
TemporalQuery 对象。
- 应用于我们的日期时间对象。
(在我们的例子中,java.time.chrono.ChronoZonedDateTime 的任何实例,例如 ZonedDateTime)
正在使用中。
WeekendFri2200ToSun2300UtcQuery query = new WeekendFri2200ToSun2300UtcQuery ();
我添加了一个用于调试和记录的静态 description 方法,以验证查询的设置。这是我自己发明的方法,TemporalQuery 接口不需要。
System.out.println ( "Weekend is: " + WeekendFri2200ToSun2300UtcQuery.description () );
今天,星期二。不应该在周末。
ZonedDateTime now = ZonedDateTime.now ( ZoneId.of ( "America/Montreal" ) );
Boolean nowIsWithinWeekend = now.query ( query );
System.out.println ( "now: " + now + " is in weekend: " + nowIsWithinWeekend );
现在是这个星期五早上。 不应该在周末。
ZonedDateTime friday1000 = ZonedDateTime.of ( LocalDate.of ( 2016 , 4 , 29 ) , LocalTime.of ( 10 , 0 ) , ZoneId.of ( "America/Montreal" ) );
Boolean friday1000IsWithinWeekend = friday1000.query ( query );
System.out.println ( "friday1000: " + friday1000 + " is in weekend: " + friday1000IsWithinWeekend );
在这个星期五晚些时候。应该是 TRUE,在周末内。
ZonedDateTime friday2330 = ZonedDateTime.of ( LocalDate.of ( 2016 , 4 , 29 ) , LocalTime.of ( 23 , 30 ) , ZoneId.of ( "America/Montreal" ) );
Boolean friday2330IsWithinWeekend = friday2330.query ( query );
System.out.println ( "friday2330: " + friday2330 + " is in weekend: " + friday2330IsWithinWeekend );
运行时。
周末是:WeekendFri2200ToSun2300UtcQuery{ 22:00Z | [周五、周六、周日] | 23:00Z }
现在:2016-04-26T20:35:01.014-04:00[America/Montreal] 在周末:false
friday1000: 2016-04-29T10:00-04:00[America/Montreal] 在周末:false
friday2330: 2016-04-29T23:30-04:00[America/Montreal] 在周末:是的
Local… 不代表本地
提到问题...说您想将 LocalDateTime 与 UTC(周末开始/停止)中的值进行比较是没有意义的。 LocalDateTime 没有从 UTC 偏移的时区。虽然命名可能违反直觉,但Local… 类意味着它们可以应用于任何地方,而没有特别的地方。所以它们没有任何意义,它们不是时间线上的一个点,除非你应用一个指定的偏移量或时区。
整个答案假设您对此术语感到困惑,并且确实打算比较时间轴上的实际时刻。