package cn.teammodel.utils; import cn.teammodel.common.CommonConstant; import cn.teammodel.config.exception.ServiceException; import cn.teammodel.model.entity.school.School; import lombok.AllArgsConstructor; import lombok.Data; import java.time.*; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAdjusters; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 学校中年级,学年,日期相关的工具类 * @author winter * @create 2023-11-28 10:15 */ public class SchoolDateUtil { /** * 通过当前时间与学校的学期安排获取当前的学年 * 学校学期: *
     * "semesters": [
     *         {
     *           "name": "下学期",
     *           "start": 0,
     *           "month": 3,
     *           "day": 1,
     *           "id": "2"
     *         },
     *         {
     *           "name": "上学期",
     *           "start": 1,
     *           "month": 9,
     *           "day": 1,
     *           "id": "1"
     *         }
     *         
     * 

* 示例: 传参 2023.11.28 返回 2023学年上学期 * 注意: 处理跨年问题 -> 2024.1.1, 应该也是 2023学年上学期,同时你需要注意,semesters 可能不止两个学期 *

*/ public static semesterModel getSemesterByNow(List semesters, LocalDate date) { if (semesters == null || semesters.isEmpty() || date == null) { throw new ServiceException("semesters and date must not be null or empty."); } // 将 semester 根据大小排序 semesters.sort(Comparator.comparing(s -> LocalDate.of(date.getYear(), s.getMonth(), s.getDay()))); // i = 0 的日期是开学最早, i = size - 1 是开学最晚 int size = semesters.size(); // 拿到最晚开学的日期,将年份 - 1后,与头节点组成可跨年区间 School.Semester lastSemester = semesters.get(size - 1); School.Semester firstSemester = semesters.get(0); // 动态替换 LocalDate lastSemesterStart = LocalDate.of(date.getYear() - 1, lastSemester.getMonth(), lastSemester.getDay()); String lastSemesterId = lastSemester.getId(); for (School.Semester semester : semesters) { // 特判头尾的提起,分别将学期区间提前和延后一年(不用延后),例如 2023.3.1 与 2023.9.1 我们分别向添加一个节点,划分其为 3 个区间: // 2022.9.1 - 2023.3.1, 2023.3.1 - 2023.9.1, 2023.9.1 - 2024.3.1 LocalDate curSemesterStart = LocalDate.of(date.getYear(), semester.getMonth(), semester.getDay()); if (date.isEqual(lastSemesterStart) || date.isAfter(lastSemesterStart) && date.isBefore(curSemesterStart)) { String academicYearId = generateCurAcademicId(lastSemesterStart.getYear(), lastSemesterId); return new semesterModel(academicYearId, lastSemesterId, lastSemesterStart.atStartOfDay(), semester.getId(), curSemesterStart.atStartOfDay()); } lastSemesterStart = curSemesterStart; lastSemesterId = semester.getId(); } // 剩下的时间段, 学年应该就是最后一个学年. 学期末就应该是下一年(year + 1)的第一个节点 String academicYearId = generateCurAcademicId(date.getYear(), lastSemester.getId()); return new semesterModel( academicYearId, lastSemester.getId(), LocalDate.of(date.getYear(), lastSemester.getMonth(), lastSemester.getDay()).atStartOfDay(), firstSemester.getId(), LocalDate.of(date.getYear() + 1, firstSemester.getMonth(), firstSemester.getDay()).atStartOfDay() ); } /** * 返回 ID -> 学年(组合 id: 学年-semesterId -> 2023-{semesterId}) */ private static String generateCurAcademicId(int academicYear, String semesterId) { return academicYear + CommonConstant.DASH + semesterId; } public static String calculateAcademicYearId(List semesters, LocalDate date) { return getSemesterByNow(semesters, date).getAcademicYearId(); } /** * 计算一学期相对的周数
* 如果 timeStamp 为 null, 则返回当前学期的总周数 */ public static long calculateWeekNum(LocalDateTime startDateTime, LocalDateTime endDateTime, Long timeStamp) { if (timeStamp == null) { return ChronoUnit.WEEKS.between(startDateTime, endDateTime) + 1; } else { LocalDateTime curDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timeStamp), ZoneOffset.of("+08:00")); startDateTime = startDateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) .withHour(0) .withMinute(0) .withSecond(0) .withNano(0); // 如果传参没在学期范围内, 返回 -1 if (curDateTime.isBefore(startDateTime) || curDateTime.isAfter(endDateTime)) { return -1; } return ChronoUnit.WEEKS.between(startDateTime, curDateTime) + 1; } } /** * 创建初始化的 Map */ public static Map createEmptyWeekMap(long totalWeek) { Map initialMap = new HashMap<>(); for (long i = 1; i <= totalWeek; i++) { initialMap.put(i, 0); } return initialMap; } @Data @AllArgsConstructor public static class semesterModel { private String academicYearId; private String startSemesterId; private LocalDateTime startDatetime; private String endSemesterId; private LocalDateTime endDatetime; } }