package jnpf.limsService.support; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** 水系统 Renwu 周期日期计算。 */ public final class LimsSxtQuyangJihuaRenwuPlanner { private LimsSxtQuyangJihuaRenwuPlanner() { } public static List executionDates(LocalDate start, LocalDate end, String cycleType, int interval) { validate(start, end, cycleType, interval); String normalizedCycleType = cycleType.trim().toLowerCase(Locale.ROOT); List dates = new ArrayList<>(); for (LocalDate candidate = start; !candidate.isAfter(end); candidate = candidate.plusDays(1)) { if (matchesCycle(start, candidate, normalizedCycleType, interval)) { dates.add(candidate); } } return dates; } private static boolean matchesCycle(LocalDate start, LocalDate candidate, String cycleType, int interval) { long daysDiff = ChronoUnit.DAYS.between(start, candidate); long step = executionStep(interval); switch (cycleType) { case "daily": return daysDiff % step == 0; case "weekly": return candidate.getDayOfWeek() == start.getDayOfWeek() && (daysDiff / 7) % step == 0; case "monthly": return isMatchingDayOfMonth(start, candidate) && monthsBetween(start, candidate) % step == 0; case "quarterly": long monthDiff = monthsBetween(start, candidate); return isMatchingDayOfMonth(start, candidate) && monthDiff % 3 == 0 && (monthDiff / 3) % step == 0; case "yearly": return isMatchingYearlyDate(start, candidate) && (candidate.getYear() - start.getYear()) % step == 0; default: throw new IllegalArgumentException("未知的取样计划周期类型:" + cycleType); } } private static long executionStep(int interval) { return (long) interval + 1; } private static long monthsBetween(LocalDate start, LocalDate candidate) { return ChronoUnit.MONTHS.between(start.withDayOfMonth(1), candidate.withDayOfMonth(1)); } private static boolean isMatchingDayOfMonth(LocalDate start, LocalDate candidate) { return start.getDayOfMonth() == start.lengthOfMonth() ? candidate.getDayOfMonth() == candidate.lengthOfMonth() : start.getDayOfMonth() == candidate.getDayOfMonth(); } private static boolean isMatchingYearlyDate(LocalDate start, LocalDate candidate) { if (start.getMonthValue() == 2 && start.getDayOfMonth() == 29) { return candidate.getMonthValue() == 2 && candidate.getDayOfMonth() == (candidate.isLeapYear() ? 29 : 28); } return start.getMonthValue() == candidate.getMonthValue() && start.getDayOfMonth() == candidate.getDayOfMonth(); } private static void validate(LocalDate start, LocalDate end, String cycleType, int interval) { if (start == null || end == null) throw new IllegalArgumentException("取样计划起止日期不能为空"); if (end.isBefore(start)) throw new IllegalArgumentException("取样计划结束日期不能早于开始日期"); if (cycleType == null || cycleType.trim().isEmpty()) throw new IllegalArgumentException("取样计划周期类型不能为空"); if (interval < 0) throw new IllegalArgumentException("取样计划周期间隔不能小于零"); } }