Ver Fonte

提交频率获取周期性日期

小车车 há 3 meses atrás
pai
commit
92925fe3e6

+ 223 - 33
yudao-module-iscs/src/main/java/cn/iocoder/yudao/module/iscs/utils/SuperDateUtils.java

@@ -4,13 +4,36 @@ import java.time.DayOfWeek;
 import java.time.LocalDate;
 import java.time.YearMonth;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAdjusters;
 import java.util.*;
+import java.util.stream.Collectors;
 
 public class SuperDateUtils {
 
     private static final DateTimeFormatter YYYYMM = DateTimeFormatter.ofPattern("yyyy-MM");
     private static final DateTimeFormatter YYYYMMDD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
 
+    private static final Map<String, DayOfWeek> WEEKDAY_MAP = new HashMap<>();
+
+    static {
+        WEEKDAY_MAP.put("周一", DayOfWeek.MONDAY);
+        WEEKDAY_MAP.put("周二", DayOfWeek.TUESDAY);
+        WEEKDAY_MAP.put("周三", DayOfWeek.WEDNESDAY);
+        WEEKDAY_MAP.put("周四", DayOfWeek.THURSDAY);
+        WEEKDAY_MAP.put("周五", DayOfWeek.FRIDAY);
+        WEEKDAY_MAP.put("周六", DayOfWeek.SATURDAY);
+        WEEKDAY_MAP.put("周日", DayOfWeek.SUNDAY);
+        // 兼容全称写法
+        WEEKDAY_MAP.put("星期一", DayOfWeek.MONDAY);
+        WEEKDAY_MAP.put("星期二", DayOfWeek.TUESDAY);
+        WEEKDAY_MAP.put("星期三", DayOfWeek.WEDNESDAY);
+        WEEKDAY_MAP.put("星期四", DayOfWeek.THURSDAY);
+        WEEKDAY_MAP.put("星期五", DayOfWeek.FRIDAY);
+        WEEKDAY_MAP.put("星期六", DayOfWeek.SATURDAY);
+        WEEKDAY_MAP.put("星期日", DayOfWeek.SUNDAY);
+        WEEKDAY_MAP.put("星期天", DayOfWeek.SUNDAY); // 部分地区使用
+    }
+
 
     // ------------------------------------- 获取指定时间段内的所有日期 ---------------------------------------------------
 
@@ -60,26 +83,6 @@ public class SuperDateUtils {
     }*/
 
     // ---------------------------------- 获取指定时间段内的所有周几的日期 -----------------------------------------------------
-    private static final Map<String, DayOfWeek> WEEKDAY_MAP = new HashMap<>();
-
-    static {
-        WEEKDAY_MAP.put("周一", DayOfWeek.MONDAY);
-        WEEKDAY_MAP.put("周二", DayOfWeek.TUESDAY);
-        WEEKDAY_MAP.put("周三", DayOfWeek.WEDNESDAY);
-        WEEKDAY_MAP.put("周四", DayOfWeek.THURSDAY);
-        WEEKDAY_MAP.put("周五", DayOfWeek.FRIDAY);
-        WEEKDAY_MAP.put("周六", DayOfWeek.SATURDAY);
-        WEEKDAY_MAP.put("周日", DayOfWeek.SUNDAY);
-        // 兼容全称写法
-        WEEKDAY_MAP.put("星期一", DayOfWeek.MONDAY);
-        WEEKDAY_MAP.put("星期二", DayOfWeek.TUESDAY);
-        WEEKDAY_MAP.put("星期三", DayOfWeek.WEDNESDAY);
-        WEEKDAY_MAP.put("星期四", DayOfWeek.THURSDAY);
-        WEEKDAY_MAP.put("星期五", DayOfWeek.FRIDAY);
-        WEEKDAY_MAP.put("星期六", DayOfWeek.SATURDAY);
-        WEEKDAY_MAP.put("星期日", DayOfWeek.SUNDAY);
-        WEEKDAY_MAP.put("星期天", DayOfWeek.SUNDAY); // 部分地区使用
-    }
 
     /**
      * 获取指定时间段内所有匹配给定星期的日期
@@ -179,7 +182,7 @@ public class SuperDateUtils {
         dates.forEach(System.out::println); // 输出:2025-06-24 和 2025-07-24
     }*/
 
-// ---------------------------------- 获取指定时间段内的所有指定第多少天,如果超过则输出最后一天 -----------------------------------------------------
+    // ---------------------------------- 获取指定时间段内的所有指定第多少天,如果超过则输出最后一天 -----------------------------------------------------
 
     /**
      * 获取指定时间段内每月的第指定天数的日期。
@@ -241,7 +244,7 @@ public class SuperDateUtils {
         // dates4.forEach(System.out::println); // 输出:2025-03-31
     }*/
 
-// ---------------------------------------- 根据输入的年月获取相邻的前后月份 -----------------------------------------
+    // ---------------------------------------- 根据输入的年月获取相邻的前后月份 -----------------------------------------
 
     /**
      * 根据输入的年月获取相邻的前后月份。
@@ -278,21 +281,20 @@ public class SuperDateUtils {
     }*/
 
 
-    // ------------------------------------------根据频率,当前时间和指定的第多少年去获取最近的3条数据
+    // ------------------------------------------根据频率,当前时间和指定的第多少天去获取最近的count条数据------------------
 
     /**
      * 计算最近多次符合条件的日期
      *
-     * @param effectiveDateStr 生效日期,格式yyyy-MM-dd
+     * @param startDateStr 生效日期,格式yyyy-MM-dd
      * @param frequencyYears   频率(年数)
      * @param dayOfYear        第几天(从1开始)
      * @param count            输出后面多少个数据
      * @param endDateStr       截至日期,格式yyyy-MM-dd
      * @return 最近多次的日期字符串数组,格式yyyy-MM-dd
      */
-    public static List<String> getAdjacentDates(String effectiveDateStr, int frequencyYears, int dayOfYear, int count, String endDateStr) {
-        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
-        LocalDate effectiveDate = LocalDate.parse(effectiveDateStr, formatter);
+    public static List<String> getAdjacentDatesYearDay(String startDateStr, String endDateStr, int frequencyYears, int dayOfYear, int count) {
+        LocalDate effectiveDate = LocalDate.parse(startDateStr, YYYYMMDD);
         int baseYear = effectiveDate.getYear();
 
         // 获取基准年份的有效日期(自动调整非法天数)
@@ -301,7 +303,7 @@ public class SuperDateUtils {
 
         // 添加基准年份日期(需满足不早于生效日期)
         if (!validDateInBaseYear.isBefore(effectiveDate)) {
-            result.add(validDateInBaseYear.format(formatter));
+            result.add(validDateInBaseYear.format(YYYYMMDD));
         }
 
         // 初始化当前年份为基准年 + 频率年数
@@ -312,20 +314,20 @@ public class SuperDateUtils {
             LocalDate targetDate = getValidDateForYear(currentYear, dayOfYear);
             // 检查是否超过截止日期(如果有)
             if (endDateStr != null) {
-                LocalDate endDate = LocalDate.parse(endDateStr, formatter);
+                LocalDate endDate = LocalDate.parse(endDateStr, YYYYMMDD);
                 if (targetDate.isAfter(endDate)) {
                     break;
                 }
             }
             // 确保日期在生效日期之后
             if (targetDate.isAfter(effectiveDate)) {
-                result.add(targetDate.format(formatter));
+                result.add(targetDate.format(YYYYMMDD));
             } else {
                 break; // 无更多有效日期
             }
             currentYear += frequencyYears; // 按频率递增年份
         }
-        return  result.subList(0, Math.min(count, result.size()));
+        return result.subList(0, Math.min(count, result.size()));
     }
 
     private static LocalDate getValidDateForYear(int year, int dayOfYear) {
@@ -338,12 +340,200 @@ public class SuperDateUtils {
         }
     }
 
-    public static void main(String[] args) {
+
+    /*public static void main(String[] args) {
         // 示例测试:查找2025-07-24之后,每年第250天的日期,最多10个
-        List<String> dates = getAdjacentDates("2025-07-24", 1, 206, 10, "2029-07-25");
+        List<String> dates = getAdjacentDatesYearDay("2025-07-24", 2, 206, 10, "2029-07-25");
         System.out.println("结果:" + dates);
         // 预期输出应包含2028-09-07(若250天合法)
+    }*/
+
+
+    // ------------------------------------------根据频率,月份频率输出------------------
+
+    /**
+     * 生成符合条件的日期列表
+     *
+     * @param startDateStr 开始日期,格式yyyy-MM-dd
+     * @param endDateStr   结束日期,格式yyyy-MM-dd
+     * @param frequency    频率(月数)
+     * @param dayOfMonth   第几天(从1开始)
+     * @param count        需要输出的日期个数
+     * @return 符合条件的日期列表
+     * 生成java帮助类输入开始日期和结束日期,输入频率为月,输入第多少天,输入输出的符合数据个数,输出符合的日期;
+     * 如输入开始时间2025-07-24,结束时间2025-12-31,频率为1个月,输入第2日,输入符合数据个数3个,则输出2025-08-02、2025-09-02、2025-10-02;如果输入第31天,如果当前月份没有该日期,则往前推移,2025-07-31,2025-08-31、2025-09-30
+     */
+    public static List<LocalDate> getAdjacentDatesMonthDay(String startDateStr, String endDateStr, int frequency, int dayOfMonth, int count) {
+        LocalDate startDate = LocalDate.parse(startDateStr, YYYYMMDD);
+        LocalDate endDate = LocalDate.parse(endDateStr, YYYYMMDD);
+
+        if (startDate.isAfter(endDate)) {
+            throw new IllegalArgumentException("开始日期不能晚于结束日期");
+        }
+        if (dayOfMonth < 1) {
+            throw new IllegalArgumentException("天数必须大于等于1");
+        }
+
+        List<LocalDate> result = new ArrayList<>();
+        YearMonth currentMonth = YearMonth.from(startDate);
+
+        while (result.size() < count && !currentMonth.isAfter(YearMonth.from(endDate))) {
+            // 计算当前月最大天数
+            int maxDay = currentMonth.lengthOfMonth();
+            // 确定有效天数
+            int effectiveDay = Math.min(dayOfMonth, maxDay);
+            LocalDate candidate = currentMonth.atDay(effectiveDay);
+
+            // 检查日期是否在有效期内
+            if (!candidate.isBefore(startDate) && !candidate.isAfter(endDate)) {
+                result.add(candidate);
+            }
+
+            // 增加频率月数
+            currentMonth = currentMonth.plusMonths(frequency);
+        }
+
+        return result;
+    }
+
+    /*public static void main(String[] args) {
+        // 示例1:基础场景
+        List<LocalDate> dates1 = getAdjacentDatesMonthDay("2025-07-24", "2025-12-31", 1, 24, 10);
+        System.out.println("示例1结果:" + dates1);
+        // 输出:[2025-08-02, 2025-09-02, 2025-10-02]
+
+        // 示例2:处理无效天数场景
+        List<LocalDate> dates2 = getAdjacentDatesMonthDay("2025-01-01", "2025-12-31", 1, 31, 10);
+        System.out.println("示例2结果:" + dates2);
+        // 输出:[2025-07-31, 2025-08-31, 2025-09-30]
+    }*/
+
+
+    // -------------------------------------------------根据周频率获取数据---------------------------------------
+
+    /**
+     * 生成符合条件的日期列表
+     *
+     * @param startDateStr 开始日期,格式yyyy-MM-dd
+     * @param endDateStr   结束日期,格式yyyy-MM-dd
+     * @param frequency    频率(周数)
+     * @param weekday      目标星期几(星期一枚举)
+     * @param count        需要输出的日期个数
+     * @return 符合条件的日期列表,格式为yyyy-MM-dd
+     * 生成java帮助类输入开始日期和结束日期,输入频率为周,输入星期几,输入输出的符合数据个数,输出符合的日期;
+     * 如输入开始时间2025-07-24,结束时间2025-12-31,频率为1个周,输入星期一,输入符合数据个数3个,则输出2025-07-30、2025-08-06、2025-08-13;若输入星期四,则输出025-07-24、2025-07-31、2025-08-07
+     */
+    public static List<String> getAdjacentDatesByWeekday(String startDateStr, String endDateStr, int frequency, String weekday, int count) {
+        DayOfWeek targetDay = WEEKDAY_MAP.get(weekday);
+        if (targetDay == null) {
+            throw new IllegalArgumentException("无效的星期参数: " + weekday);
+        }
+
+        LocalDate startDate = LocalDate.parse(startDateStr, YYYYMMDD);
+        LocalDate endDate = LocalDate.parse(endDateStr, YYYYMMDD);
+
+        if (startDate.isAfter(endDate)) {
+            throw new IllegalArgumentException("开始日期不能晚于结束日期");
+        }
+        if (frequency <= 0) {
+            throw new IllegalArgumentException("频率必须为正整数");
+        }
+        if (count <= 0) {
+            throw new IllegalArgumentException("输出数量必须为正整数");
+        }
+
+        List<String> result = new ArrayList<>();
+        // 查找第一个符合条件的日期(>= startDate)
+        LocalDate firstTarget = startDate.with(TemporalAdjusters.nextOrSame(targetDay));
+        if (firstTarget.isAfter(endDate)) {
+            return result; // 无符合条件的日期
+        }
+
+        // 遍历生成日期
+        LocalDate current = firstTarget;
+        while (result.size() < count && !current.isAfter(endDate)) {
+            result.add(current.format(YYYYMMDD));
+            current = current.plusWeeks(frequency);
+        }
+
+        return result;
+    }
+
+    /*public static void main(String[] args) {
+        // 示例1:开始日期2025-07-24,结束日期2025-12-31,频率1周,星期一,输出3个
+        List<String> example1 = getAdjacentDatesByWeekday("2025-07-24", "2025-12-31", 1, DayOfWeek.MONDAY, 50);
+        System.out.println("示例1结果:" + example1);
+        // 输出示例:[2025-07-28, 2025-08-04, 2025-08-11]
+
+        // 示例2:开始日期2025-07-24,结束日期2025-12-31,频率1周,星期四,输出3个
+        List<String> example2 = getAdjacentDatesByWeekday("2025-07-24", "2025-08-07", 1, DayOfWeek.THURSDAY, 3);
+        System.out.println("示例2结果:" + example2);
+        // 输出示例:[2025-07-24, 2025-07-31, 2025-08-07]
+    }*/
+
+    // ------------------------------获取日的频率-------------------------------------------------------
+
+    /**
+     * 生成符合条件的日期列表
+     *
+     * @param startDateStr  开始日期,格式yyyy-MM-dd
+     * @param endDateStr    结束日期,格式yyyy-MM-dd
+     * @param frequencyDays 频率(天数)
+     * @param count         需要输出的日期个数
+     * @return 符合条件的日期列表(格式yyyy-MM-dd)
+     * @throws IllegalArgumentException 如果参数不合法
+     *                                  生成java帮助类输入开始日期和结束日期,输入频率为天,输入多少天,输入输出的符合数据个数,输出符合的日期;
+     *                                  如输入开始时间2025-07-24,结束时间2025-07-31,频率为1天,输入符合数据个数3个,则输出2025-07-24、2025-07-25、2025-07-26;若频率为2天,则输出025-07-24、2025-07-26、2025-07-28
+     */
+    public static List<String> getAdjacentDatesByDay(
+            String startDateStr,
+            String endDateStr,
+            int frequencyDays,
+            int count) {
+
+        // 参数校验
+        if (frequencyDays <= 0) {
+            throw new IllegalArgumentException("频率天数必须为正整数");
+        }
+        if (count <= 0) {
+            throw new IllegalArgumentException("输出数量必须为正整数");
+        }
+
+        LocalDate startDate = LocalDate.parse(startDateStr, YYYYMMDD);
+        LocalDate endDate = LocalDate.parse(endDateStr, YYYYMMDD);
+
+        if (startDate.isAfter(endDate)) {
+            throw new IllegalArgumentException("开始日期不能晚于结束日期");
+        }
+
+        List<String> result = new ArrayList<>();
+        LocalDate currentDate = startDate;
+
+        // 主循环:生成日期直到满足数量或超出结束日期
+        while (result.size() < count && !currentDate.isAfter(endDate)) {
+            result.add(currentDate.format(YYYYMMDD));
+            currentDate = currentDate.plusDays(frequencyDays);
+        }
+
+        return result;
     }
 
+    /*public static void main(String[] args) {
+        // 示例1:频率1天,输出3个日期
+        List<String> example1 = getAdjacentDatesByDay("2025-07-24", "2025-07-25", 3, 3);
+        System.out.println("示例1结果:" + example1);
+        // 预期输出:[2025-07-24, 2025-07-25, 2025-07-26]
+
+        // 示例2:频率2天,输出3个日期
+        List<String> example2 = getAdjacentDatesByDay("2025-07-24", "2025-08-05", 2, 5);
+        System.out.println("示例2结果:" + example2);
+        // 预期输出:[2025-07-24, 2025-07-26, 2025-07-28]
+
+        // 示例3:跨月场景
+        List<String> example3 = getAdjacentDatesByDay("2025-07-30", "2025-08-05", 2, 3);
+        System.out.println("示例3结果:" + example3);
+        // 预期输出:[2025-07-30, 2025-08-01, 2025-08-03]
+    }*/
+
 
 }