计算日期范围匹配 Java
Calculate date range match into Java
我想实现此代码以检查用户是否订阅了服务:
public String calculateSubscription(String email) {
Optional<Subscription> subscriptionsByUserEmail = subscriptionService.findSubscriptionsByUserEmail(email);
if (subscriptionsByUserEmail.isPresent()) {
Subscription subscription = subscriptionsByUserEmail.get();
LocalDateTime startAt = subscription.getStartAt();
LocalDateTime endAt = subscription.getEndAt();
LocalDateTime now = LocalDateTime.now();
if(/* do here comparison */){
return "subscribed";
}
}
return "unsubscribed";
}
在数据库中我存储了 2 个字段:
@Column(name = "start_at")
@Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime startAt;
@Column(name = "end_at")
@Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime endAt;
我需要检查 now
变量是否在时间间隔 startAt
和 endAt
之间匹配。
如何将此检查实施到 if 语句中?
编辑:
@Converter(autoApply = true)
public class LocalDateTimeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(LocalDateTime localDateTime) {
return Optional.ofNullable(localDateTime)
.map(Timestamp::valueOf)
.orElse(null);
}
@Override
public LocalDateTime convertToEntityAttribute(Timestamp timestamp) {
return Optional.ofNullable(timestamp)
.map(Timestamp::toLocalDateTime)
.orElse(null);
}
}
您可以像下面这样使用 LocalDateTime#isAfter
and LocalDateTime#isBefore
:
if (now.isAfter(startAt) && now.isBefore(endAt)) { .... }
这取决于要求。 @dariosicily 有一点,但是如果 Chronology 或 Calendar 系统对您的应用程序很重要,那么建议使用 compareTo
LocalDateTime startAt = subscription.getStartAt();
LocalDateTime endAt = subscription.getEndAt();
LocalDateTime now = LocalDateTime.now();
if(now.compareTo(startAt) == 1 && now.compareTo(endAt) == -1){
return "subscribed";
}
您必须检查 now
是否在 startDate
和 endDate
之间:
public String calculateSubscription(String email) {
Optional<Subscription> subscriptionsByUserEmail = subscriptionService.findSubscriptionsByUserEmail(email);
String result = "unsubscribed";
if(subscriptionsByUserEmail.isPresent()){
Subscription subscription = subscriptionsByUserEmail.get();
LocalDateTime startAt = subscription.getStartAt();
LocalDateTime endAt = subscription.getEndAt();
LocalDateTime now = LocalDateTime.now();
if (startAt.isBefore(now) && endAt.isAfter(now)) {
result = "subscribed";
}
}
return result;
}
两点:
- 不要使用
LocalDateTime
作为时间点。任何阅读它的人都可以在他们能想到的任何时区自由解释它,24 到 26 小时的解释变化。 Instant
是用于某个时间点的 class。它和LocalDateTime
一样有isBefore
和isAfter
方法,所以代码不会有什么不同。也定义时间点的替代方案包括 ZonedDateTime
和 OffsetDateTime
.
- 对于
Optional
不要使用其 isPresent
和 get
方法(极少数情况除外)。它们是低级的,到目前为止,我们通常可以使用更高级别的方法获得更优雅的代码。
我更喜欢使用 Optional
的方法链接,所以我的代码看起来像:
public String calculateSubscription(String email) {
Instant now = Instant.now();
return subscriptionService.findSubscriptionsByUserEmail(email)
.filter(sub -> ! sub.getStartAt().isAfter(now) && sub.getEndAt().isAfter(now))
.map(sub -> "subscribed")
.orElse("unsubscribed");
}
这适用于 getStartAt()
和 getEndAt()
return Instant
。为了在极端情况下获得一致的结果,我只读取了一次时钟(在过滤条件下不是两次)。时间间隔的标准解释是半开的,从开始到结束不包括,所以我的代码使用这个。我使用“not after”来表示“on or before”。在您的情况下,您可能不需要这种精度。
要将时间点存储到数据库中,对于大多数数据库引擎,您需要 timestamp with time zone
。
如果可以并且仅当可以时建立时间点
编辑:详细说明一下,LocalDateTime
给我们的日期和时间,不建立时间点。例如,东京的 6 月 30 日 11:45 比洛杉矶的 6 月 30 日 11:45 早 16 小时。因此,对于某个时间点,我们还需要一个时区或一个 UTC 偏移量。
如果您的数据库可以提供一个时间点,通常通过上述 timestamp with time zone
,我们很高兴。具体如何将其检索到 Java 取决于您的 JPA 版本和实现,因此我不想尝试在这一点上具体说明。您可能需要也可能不需要在 Java 或 中使用 OffsetDateTime
(甚至 ZonedDateTime
)而不是 Instant
来提供不同的来自您现在使用的自定义转换器。后者可能使用 Timestamp::toInstant
而不是 Timestamp::toLocalDateTime
.
一样容易
如果您的数据库仅提供日期和时间,例如,如果它使用其 datetime
数据类型并且无法更改,请不要假装不是这样。在这种情况下,坚持转换为 Instant
可能弊大于利。无论如何,没有一条链比它最弱的 link 强。所以在这种情况下,在 Java 中始终坚持 LocalDateTime
。然后以这种方式读取时钟:
ZoneId timeZoneThatTheDatabaseAssumes = ZoneId.of("Europe/Gibraltar");
LocalDateTime now = LocalDateTime.now(timeZoneThatTheDatabaseAssumes);
当然要为您的数据库使用的时区指定时区 ID。
我想实现此代码以检查用户是否订阅了服务:
public String calculateSubscription(String email) {
Optional<Subscription> subscriptionsByUserEmail = subscriptionService.findSubscriptionsByUserEmail(email);
if (subscriptionsByUserEmail.isPresent()) {
Subscription subscription = subscriptionsByUserEmail.get();
LocalDateTime startAt = subscription.getStartAt();
LocalDateTime endAt = subscription.getEndAt();
LocalDateTime now = LocalDateTime.now();
if(/* do here comparison */){
return "subscribed";
}
}
return "unsubscribed";
}
在数据库中我存储了 2 个字段:
@Column(name = "start_at")
@Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime startAt;
@Column(name = "end_at")
@Convert(converter = LocalDateTimeConverter.class)
private LocalDateTime endAt;
我需要检查 now
变量是否在时间间隔 startAt
和 endAt
之间匹配。
如何将此检查实施到 if 语句中?
编辑:
@Converter(autoApply = true)
public class LocalDateTimeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(LocalDateTime localDateTime) {
return Optional.ofNullable(localDateTime)
.map(Timestamp::valueOf)
.orElse(null);
}
@Override
public LocalDateTime convertToEntityAttribute(Timestamp timestamp) {
return Optional.ofNullable(timestamp)
.map(Timestamp::toLocalDateTime)
.orElse(null);
}
}
您可以像下面这样使用 LocalDateTime#isAfter
and LocalDateTime#isBefore
:
if (now.isAfter(startAt) && now.isBefore(endAt)) { .... }
这取决于要求。 @dariosicily 有一点,但是如果 Chronology 或 Calendar 系统对您的应用程序很重要,那么建议使用 compareTo
LocalDateTime startAt = subscription.getStartAt();
LocalDateTime endAt = subscription.getEndAt();
LocalDateTime now = LocalDateTime.now();
if(now.compareTo(startAt) == 1 && now.compareTo(endAt) == -1){
return "subscribed";
}
您必须检查 now
是否在 startDate
和 endDate
之间:
public String calculateSubscription(String email) {
Optional<Subscription> subscriptionsByUserEmail = subscriptionService.findSubscriptionsByUserEmail(email);
String result = "unsubscribed";
if(subscriptionsByUserEmail.isPresent()){
Subscription subscription = subscriptionsByUserEmail.get();
LocalDateTime startAt = subscription.getStartAt();
LocalDateTime endAt = subscription.getEndAt();
LocalDateTime now = LocalDateTime.now();
if (startAt.isBefore(now) && endAt.isAfter(now)) {
result = "subscribed";
}
}
return result;
}
两点:
- 不要使用
LocalDateTime
作为时间点。任何阅读它的人都可以在他们能想到的任何时区自由解释它,24 到 26 小时的解释变化。Instant
是用于某个时间点的 class。它和LocalDateTime
一样有isBefore
和isAfter
方法,所以代码不会有什么不同。也定义时间点的替代方案包括ZonedDateTime
和OffsetDateTime
. - 对于
Optional
不要使用其isPresent
和get
方法(极少数情况除外)。它们是低级的,到目前为止,我们通常可以使用更高级别的方法获得更优雅的代码。
我更喜欢使用 Optional
的方法链接,所以我的代码看起来像:
public String calculateSubscription(String email) {
Instant now = Instant.now();
return subscriptionService.findSubscriptionsByUserEmail(email)
.filter(sub -> ! sub.getStartAt().isAfter(now) && sub.getEndAt().isAfter(now))
.map(sub -> "subscribed")
.orElse("unsubscribed");
}
这适用于 getStartAt()
和 getEndAt()
return Instant
。为了在极端情况下获得一致的结果,我只读取了一次时钟(在过滤条件下不是两次)。时间间隔的标准解释是半开的,从开始到结束不包括,所以我的代码使用这个。我使用“not after”来表示“on or before”。在您的情况下,您可能不需要这种精度。
要将时间点存储到数据库中,对于大多数数据库引擎,您需要 timestamp with time zone
。
如果可以并且仅当可以时建立时间点
编辑:详细说明一下,LocalDateTime
给我们的日期和时间,不建立时间点。例如,东京的 6 月 30 日 11:45 比洛杉矶的 6 月 30 日 11:45 早 16 小时。因此,对于某个时间点,我们还需要一个时区或一个 UTC 偏移量。
如果您的数据库可以提供一个时间点,通常通过上述 timestamp with time zone
,我们很高兴。具体如何将其检索到 Java 取决于您的 JPA 版本和实现,因此我不想尝试在这一点上具体说明。您可能需要也可能不需要在 Java 或 中使用 OffsetDateTime
(甚至 ZonedDateTime
)而不是 Instant
来提供不同的来自您现在使用的自定义转换器。后者可能使用 Timestamp::toInstant
而不是 Timestamp::toLocalDateTime
.
如果您的数据库仅提供日期和时间,例如,如果它使用其 datetime
数据类型并且无法更改,请不要假装不是这样。在这种情况下,坚持转换为 Instant
可能弊大于利。无论如何,没有一条链比它最弱的 link 强。所以在这种情况下,在 Java 中始终坚持 LocalDateTime
。然后以这种方式读取时钟:
ZoneId timeZoneThatTheDatabaseAssumes = ZoneId.of("Europe/Gibraltar");
LocalDateTime now = LocalDateTime.now(timeZoneThatTheDatabaseAssumes);
当然要为您的数据库使用的时区指定时区 ID。