刘光辉
15 小时以前 34981c30a78e8bbd7791131059a9210f9928b62c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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<LocalDate> executionDates(LocalDate start, LocalDate end, String cycleType, int interval) {
        validate(start, end, cycleType, interval);
        String normalizedCycleType = cycleType.trim().toLowerCase(Locale.ROOT);
        List<LocalDate> 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("取样计划周期间隔不能小于零");
    }
}