Files
baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/appointmentplanning/AppointmentTimePolicy.java
T

43 lines
2.3 KiB
Java

package com.baiyue.lastleg.appointmentplanning;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;
/** Resolves a location's appointment window without using the operator's system time zone. */
public final class AppointmentTimePolicy {
private AppointmentTimePolicy() { }
public record Window(Instant start, Instant end, String timezone) { }
public static Window resolve(LocalDateTime start, LocalDateTime end, String timezone,
ZoneOffset startOffset, ZoneOffset endOffset) {
if (start == null) throw new IllegalArgumentException("请填写预约开始时间");
if (timezone == null || timezone.isBlank()) throw new IllegalArgumentException("请先补齐预约地点时区");
final ZoneId zone;
try { zone = ZoneId.of(timezone); }
catch (DateTimeException error) { throw new IllegalArgumentException("预约地点时区无效", error); }
if (zone instanceof ZoneOffset) throw new IllegalArgumentException("预约地点需要地区时区,不能仅填写固定时差");
Instant first = instant(start, zone, startOffset);
Instant last = end == null ? null : instant(end, zone, endOffset);
if (end == null && endOffset != null) throw new IllegalArgumentException("未填写结束时间,不能指定结束时差");
if (last != null && !last.isAfter(first)) throw new IllegalArgumentException("预约结束时间必须晚于开始时间");
return new Window(first, last, zone.getId());
}
private static Instant instant(LocalDateTime value, ZoneId zone, ZoneOffset selected) {
List<ZoneOffset> offsets = zone.getRules().getValidOffsets(value);
if (offsets.isEmpty()) throw new IllegalArgumentException("该当地时间因夏令时切换不存在,请重新选择");
if (selected == null && offsets.size() > 1) {
throw new IllegalArgumentException("该当地时间在夏令时切换期间出现两次,请明确时差");
}
if (selected != null && !offsets.contains(selected)) {
throw new IllegalArgumentException("所选时差与预约地点及日期不符");
}
return value.toInstant(selected == null ? offsets.get(0) : selected);
}
}