43 lines
2.3 KiB
Java
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);
|
|
}
|
|
}
|