2 天以前 4f55d3cb4bc644e4534106336f2047af1a4db5df
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package com.ruoyi.inspectiontask.service.impl;
 
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.HashSet;
import java.util.Set;
 
final class TimingTaskScheduleUtils {
 
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
 
    private TimingTaskScheduleUtils() {
    }
 
    static LocalDateTime calculateFirstExecutionTime(String frequencyType, String frequencyDetail) {
        return calculateNextExecutionTime(frequencyType, frequencyDetail, LocalDateTime.now().minusSeconds(1));
    }
 
    static LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) {
        if (frequencyType == null || frequencyDetail == null) {
            throw new IllegalArgumentException("任务参数不能为空");
        }
        switch (frequencyType.toUpperCase()) {
            case "DAILY":
                return calculateDailyNextTime(frequencyDetail, currentTime);
            case "WEEKLY":
                return calculateWeeklyNextTime(frequencyDetail, currentTime);
            case "MONTHLY":
                return calculateMonthlyNextTime(frequencyDetail, currentTime);
            case "QUARTERLY":
                return calculateQuarterlyNextTime(frequencyDetail, currentTime);
            default:
                throw new IllegalArgumentException("不支持的频率类型: " + frequencyType);
        }
    }
 
    private static LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) {
        LocalTime executionTime = parseTime(timeStr);
        LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime);
        return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1);
    }
 
    private static LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) {
        String[] parts = validateAndSplit(detail, ",", 2);
        Set<DayOfWeek> targetDays = parseDayOfWeeks(parts[0]);
        LocalTime time = parseTime(parts[1]);
 
        LocalDateTime nextTime = current;
        for (int i = 0; i < 366; i++) {
            nextTime = nextTime.plusDays(1);
            if (targetDays.contains(nextTime.getDayOfWeek())) {
                return LocalDateTime.of(nextTime.toLocalDate(), time);
            }
        }
        throw new IllegalArgumentException("无法找到下一次执行时间");
    }
 
    private static LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) {
        String[] parts = validateAndSplit(detail, ",", 2);
        int dayOfMonth = validateDayOfMonth(parts[0]);
        LocalTime time = parseTime(parts[1]);
 
        for (int i = 0; i < 24; i++) {
            YearMonth targetYearMonth = YearMonth.from(current).plusMonths(i);
            int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth());
            LocalDateTime target = LocalDateTime.of(
                    targetYearMonth.getYear(),
                    targetYearMonth.getMonthValue(),
                    adjustedDay,
                    time.getHour(),
                    time.getMinute()
            );
            if (target.isAfter(current)) {
                return target;
            }
        }
        throw new IllegalArgumentException("无法找到下一次执行时间");
    }
 
    private static LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) {
        String[] parts = validateAndSplit(detail, ",", 3);
        int startMonth = validateMonth(parts[0]);
        int dayOfMonth = validateDayOfMonth(parts[1]);
        LocalTime time = parseTime(parts[2]);
 
        YearMonth anchor = YearMonth.of(current.getYear(), startMonth);
        for (int i = 0; i < 12; i++) {
            YearMonth targetYearMonth = anchor.plusMonths(3L * i);
            int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth());
            LocalDateTime target = LocalDateTime.of(
                    targetYearMonth.getYear(),
                    targetYearMonth.getMonthValue(),
                    adjustedDay,
                    time.getHour(),
                    time.getMinute()
            );
            if (target.isAfter(current)) {
                return target;
            }
        }
        throw new IllegalArgumentException("无法找到下一次执行时间");
    }
 
    private static LocalTime parseTime(String timeStr) {
        try {
            return LocalTime.parse(timeStr, TIME_FORMATTER);
        } catch (DateTimeParseException e) {
            throw new IllegalArgumentException("时间格式必须为HH:mm", e);
        }
    }
 
    private static String[] validateAndSplit(String input, String delimiter, int expectedParts) {
        String[] parts = input.split(delimiter);
        if (parts.length != expectedParts) {
            throw new IllegalArgumentException("参数格式错误");
        }
        return parts;
    }
 
    private static int validateDayOfMonth(String dayStr) {
        int day = Integer.parseInt(dayStr.trim());
        if (day < 1 || day > 31) {
            throw new IllegalArgumentException("日期必须在1-31之间");
        }
        return day;
    }
 
    private static int validateMonth(String monthStr) {
        int month = Integer.parseInt(monthStr.trim());
        if (month < 1 || month > 12) {
            throw new IllegalArgumentException("月份必须在1-12之间");
        }
        return month;
    }
 
    private static Set<DayOfWeek> parseDayOfWeeks(String dayOfWeekStr) {
        Set<DayOfWeek> days = new HashSet<>();
        for (String dayStr : dayOfWeekStr.split("\\|")) {
            switch (dayStr.trim().toUpperCase()) {
                case "MON":
                    days.add(DayOfWeek.MONDAY);
                    break;
                case "TUE":
                    days.add(DayOfWeek.TUESDAY);
                    break;
                case "WED":
                    days.add(DayOfWeek.WEDNESDAY);
                    break;
                case "THU":
                    days.add(DayOfWeek.THURSDAY);
                    break;
                case "FRI":
                    days.add(DayOfWeek.FRIDAY);
                    break;
                case "SAT":
                    days.add(DayOfWeek.SATURDAY);
                    break;
                case "SUN":
                    days.add(DayOfWeek.SUNDAY);
                    break;
                default:
                    throw new IllegalArgumentException("无效的星期几: " + dayStr);
            }
        }
        return days;
    }
}