Java如何获取含季度在内的任意ChronoUnit对应周期首日
实现方案
核心思路是提前把不同时间单位对应的首日计算规则做映射存储,运行时直接匹配规则,完全避免多层if-elif分支判断,纯JDK实现就可以覆盖所有常用周期,不需要强制引入第三方依赖。
纯JDK无依赖实现(覆盖日/周/月/季/年全常用周期)
标准ChronoUnit枚举本身没有定义季度单位,季度逻辑只需要做一处轻量判断即可,其余单位直接用JDK自带的TemporalAdjusters实现,代码可直接复用:
import java.time.DayOfWeek; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjusters; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; public class CycleDateUtil { // 用EnumMap存储各单位的首日调整规则,初始化一次全局复用,性能优于普通HashMap private static final Map<ChronoUnit, TemporalAdjuster> FIRST_DAY_ADJUSTERS = new EnumMap<>(ChronoUnit.class); static { // 日维度:当日就是周期首日 FIRST_DAY_ADJUSTERS.put(ChronoUnit.DAYS, date -> date); // 周维度:默认周一为周首日,需要周日起始的话替换为DayOfWeek.SUNDAY即可 FIRST_DAY_ADJUSTERS.put(ChronoUnit.WEEKS, TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); // 月维度:当月1号 FIRST_DAY_ADJUSTERS.put(ChronoUnit.MONTHS, TemporalAdjusters.firstDayOfMonth()); // 年维度:当年1月1号 FIRST_DAY_ADJUSTERS.put(ChronoUnit.YEARS, TemporalAdjusters.firstDayOfYear()); } /** * 获取指定日期所属周期的首日 * @param date 待计算日期 * @param unit 时间单位,支持DAYS/WEEKS/MONTHS/YEARS * @param isQuarter 是否为季度周期(标准ChronoUnit无季度枚举,用该标记区分) * @return 对应周期的首日 */ private static LocalDate findFirstDateOf(LocalDate date, ChronoUnit unit, boolean isQuarter) { // 季度仅做单处判断,无多层分支 if (isQuarter) { int startMonth = ((date.getMonthValue() - 1) / 3) * 3 + 1; return LocalDate.of(date.getYear(), startMonth, 1); } TemporalAdjuster adjuster = FIRST_DAY_ADJUSTERS.get(unit); if (adjuster == null) { throw new IllegalArgumentException("暂不支持的时间单位: " + unit); } return date.with(adjuster); } // 对应业务里的周期日期计算逻辑示例 public static List<LocalDate> calculateCompoundingDates(LocalDate start, LocalDate end, long duration, ChronoUnit unit, boolean isQuarter) { List<LocalDate> result = new ArrayList<>(); LocalDate current = start; while (current.isBefore(end)) { result.add(current); LocalDate nextCycleDate = current.plus(duration, unit); current = findFirstDateOf(nextCycleDate, unit, isQuarter); } result.add(end); return result; } }
逻辑说明
- 所有常规单位的计算规则都存在Map中,后续如果要加半年、十年等其他周期,只需要往Map里新增一条规则即可,不需要修改核心判断逻辑
- 季度默认按自然季度计算(1-3月为Q1,以此类推),如果是自定义财年季度(比如财年从4月起始),只需要修改季度首月的计算逻辑即可
- 周首日可根据业务需求灵活调整,不需要改动整体结构
ThreeTen-Extra库适配方案
如果项目已经引入ThreeTen-Extra,不需要自己写季度计算逻辑,库中自带的YearQuarter类可以直接拿到季度首日,只需要在规则映射里新增一条对应季度单位的调整规则即可,整体结构和上面的纯JDK实现完全一致,不需要写分支判断。
内容的提问来源于stack exchange,提问作者user3081519
相关产品推荐
相关产品推荐

