/* * Copyright 2024-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package xyz.zhouxy.plusone.commons.time; import java.time.DateTimeException; import java.time.Month; import java.time.MonthDay; import java.time.temporal.ChronoField; import com.google.common.collect.Range; import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod; import xyz.zhouxy.plusone.commons.base.IWithIntCode; import xyz.zhouxy.plusone.commons.util.AssertTools; /** * 季度 * * @author ZhouXY */ public enum Quarter implements IWithIntCode { /** 第一季度 */ Q1(1), /** 第二季度 */ Q2(2), /** 第三季度 */ Q3(3), /** 第四季度 */ Q4(4), ; /** 季度值 (1/2/3/4) */ private final int value; /** 月份范围 */ private final Range monthRange; /** 常量值 */ private static final Quarter[] ENUMS = Quarter.values(); /** * @param value 季度值 (1/2/3/4) */ Quarter(int value) { this.value = value; final int lastMonth = value * 3; final int firstMonth = lastMonth - 2; this.monthRange = Range.closed(firstMonth, lastMonth); } // ================================ // #region - StaticFactoryMethods // ================================ /** * 根据给定的月份值返回对应的季度 * * @param monthValue 月份值,取值范围为1到12 * @return 对应的季度 * @throws IllegalArgumentException 如果月份值不在有效范围内(1到12),将抛出异常 */ @StaticFactoryMethod(Quarter.class) public static Quarter fromMonth(int monthValue) { ChronoField.MONTH_OF_YEAR.checkValidValue(monthValue); return of(computeQuarterValueInternal(monthValue)); } /** * 根据给定的月份返回对应的季度 * * @param month 月份 * @return 对应的季度 */ @StaticFactoryMethod(Quarter.class) public static Quarter fromMonth(Month month) { AssertTools.checkNotNull(month); final int monthValue = month.getValue(); return of(computeQuarterValueInternal(monthValue)); } /** * 根据指定的年份,获取一个新的 YearQuarter 实例 * 此方法允许在保持当前季度信息不变的情况下,更改年份 * * @param year 指定的年份 * @return 返回一个新的 YearQuarter 实例,年份更新为指定的年份 */ public final YearQuarter atYear(int year) { return YearQuarter.of(year, this); } /** * 根据给定的季度值返回对应的季度 * * @param value 季度值 (1/2/3/4) * @return 对应的季度 * @throws IllegalArgumentException 如果季度值不在有效范围内(1到4),将抛出异常 */ @StaticFactoryMethod(Quarter.class) public static Quarter of(int value) { return ENUMS[checkValidIntValue(value) - 1]; } // ================================ // #endregion - StaticFactoryMethods // ================================ // ================================ // #region - computes // ================================ /** * 加上指定数量的季度 * * @param quarters 所添加的季度数量 * @return 计算结果 */ public Quarter plus(long quarters) { final int amount = (int) ((quarters % 4) + 4); return ENUMS[(ordinal() + amount) % 4]; } /** * 减去指定数量的季度 * * @param quarters 所减去的季度数量 * @return 计算结果 */ public Quarter minus(long quarters) { return plus(-(quarters % 4)); } // ================================ // #endregion - computes // ================================ // ================================ // #region - Getters // ================================ /** * 获取季度值 * * @return 季度值 */ public int getValue() { return value; } @Override public int getCode() { return getValue(); } /** * 该季度的第一个月 * * @return {@code Month} 对象 */ public Month firstMonth() { return Month.of(firstMonthValue()); } /** * 该季度的第一个月 * * @return 月份值从 1 开始,1 表示 1月,以此类推。 */ public int firstMonthValue() { return this.monthRange.lowerEndpoint(); } /** * 该季度的最后一个月 * * @return {@code Month} 对象 */ public Month lastMonth() { return Month.of(lastMonthValue()); } /** * 该季度的最后一个月 * * @return 月份值从 1 开始,1 表示 1月,以此类推。 */ public int lastMonthValue() { return this.monthRange.upperEndpoint(); } /** * 该季度的第一天 * * @return {@code MonthDay} 对象 */ public MonthDay firstMonthDay() { return MonthDay.of(firstMonth(), 1); } /** * 该季度的最后一天 * * @return {@code MonthDay} 对象 */ public MonthDay lastMonthDay() { // 季度的最后一个月不可能是 2 月 final Month month = lastMonth(); return MonthDay.of(month, month.maxLength()); } /** * 计算该季度的第一天为当年的第几天 * * @param leapYear 是否为闰年 * @return day of year */ public int firstDayOfYear(boolean leapYear) { return firstMonth().firstDayOfYear(leapYear); } // ================================ // #endregion - Getters // ================================ /** * 检查给定的季度值是否有效 * * @param value 季度值 * @return 如果给定的季度值有效则返回该值 * @throws DateTimeException 如果给定的季度值不在有效范围内(1到4),将抛出异常 */ public static int checkValidIntValue(int value) { AssertTools.checkCondition(value >= 1 && value <= 4, () -> new DateTimeException("Invalid value for Quarter: " + value)); return value; } // ================================ // #region - Internal // ================================ /** * 计算给定月份对应的季度值 * * @param monthValue 月份值,取值范围为1到12 * @return 对应的季度值 */ private static int computeQuarterValueInternal(int monthValue) { return (monthValue - 1) / 3 + 1; } // ================================ // #endregion - Internal // ================================ }