java時間工具類;獲取當(dāng)前,過去N個單位,前N個單位(不包含當(dāng)前)年月日時分秒毫秒的開始與結(jié)束時間

time-counter.jpg

在項目開發(fā)中經(jīng)常會遇到時間參數(shù),比如開始時間結(jié)束時間;但是往往會出現(xiàn)一個問題那就是時間的格式問題,當(dāng)前后端格式不一致的時候就導(dǎo)致參數(shù)無法接收。這就要寫一個兼容很多格式的轉(zhuǎn)換器。那有沒有更簡便的方式呢,例如我們通過間隔來計算開始與結(jié)束時間,只要傳一個簡單的代碼碼就可以呢。

  • 修飾符
代碼(大寫) 含義 描述
B before 前N個單位 (不包含當(dāng)前)
P past 過去N個單位(包含當(dāng)前)
C current 當(dāng)前
  • 時間精度
單位 代碼 大小寫
毫秒 S 大寫
s 小寫
m 小寫
H 大寫
d 小寫
M 大寫
y 小寫
W 大寫
  • 傳值方式

    修飾符和時間精度配合再加上單位間隔數(shù)量

描述 表達式
過去2個月 PM2
前7天 Bd7
當(dāng)年 Cy

時間傳參DTO類

import java.util.Date;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

@Schema(name = "時間區(qū)段參數(shù)", description = "時間區(qū)段參數(shù)DTO")
@Getter
@Setter
@Accessors(chain = true)
public class DateSegmentDTO {

    @Schema(description = "開始時間")
    private Date start;

    @Schema(description = "結(jié)束時間")
    private Date end;

    @Schema(description = "時間區(qū)段代碼")
    private String stepCode;
}

時間區(qū)間工具類

時間區(qū)間是[start,end) 包含開始不包含結(jié)束,條件應(yīng)為 >= start , < end

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.Validate;
import org.springframework.util.Assert;

import com.qjwy.system.dto.DateSegmentDTO;

/**
 * 時間區(qū)間工具
 * 
 * 本工具旨在降低時間區(qū)間傳參的復(fù)雜度,通過三個參數(shù)控制開始時間和結(jié)束時間的參數(shù)
 * 
 * Date start; Date end; String stepCode;
 * 
 * 
 * @author:三石
 * @since:1.0
 */
public class DurationUtils {

    private DurationUtils() {
    }

    // 所有修飾和精度的組合
    private static final List<String> COMBINATION = new ArrayList<String>();

    static {
        for (MODIFY_TYPE modifyType : MODIFY_TYPE.values()) {
            for (DURATION_TYPE durationType : DURATION_TYPE.values()) {
                COMBINATION.add(modifyType.name() + durationType.name());
            }
        }
    }

    /**
     * 注入時間
     * 
     * @param date    指定的時間
     * @param segment 時間參數(shù)封裝
     */
    public static void injectDateTime(Date date, DateSegmentDTO segment) {

        Date start = segment.getStart();
        Date end = segment.getEnd();
        String stepCode = segment.getStepCode();

        if (start == null && end == null) {
            Assert.notNull(stepCode, "時間區(qū)間參數(shù)不能同時為null");
            String expression = stepCode.substring(0, 2);
            Assert.isTrue(COMBINATION.contains(expression), "不支持的組合:" + expression + ",目前可支持的組合" + COMBINATION);

            String[] codes = expression.split("");

            MODIFY_TYPE modifyType = MODIFY_TYPE.valueOf(codes[0]);
            DURATION_TYPE durationType = DURATION_TYPE.valueOf(codes[1]);

            Date[] se = modifyType.getRuleFunction().calc(//
                    date, //
                    durationType.getField(), //
                    stepCode.length() >= 3 ? Integer.parseInt(stepCode.substring(2)) : 0, //
                    durationType.getSmallerFields()//
            );

            segment.setStart(se[0]).setEnd(se[1]);
        }

    }

    /**
     * 時間區(qū)間類型
     */
    enum DURATION_TYPE {
        S("毫秒", Calendar.MILLISECOND, new int[] {}), //
        s("秒", Calendar.SECOND, new int[] { Calendar.MILLISECOND }), //
        m("分", Calendar.MINUTE, new int[] { Calendar.MILLISECOND, Calendar.SECOND }), //
        H("小時", Calendar.HOUR_OF_DAY, new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE }), //
        d("日", Calendar.DAY_OF_MONTH, new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY }), //
        M("月", Calendar.MONTH, new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH }), //
        y("年", Calendar.YEAR, new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH }), //

        W("周", Calendar.WEEK_OF_YEAR, new int[] { Calendar.MILLISECOND, Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK });

        // 精度
        private String accuracy;

        // 當(dāng)前時間域
        private int field;

        // 更小的單位的時間域
        private int[] smallerFields;

        private DURATION_TYPE(String accuracy, int field, int[] smallerFields) {
            this.accuracy = accuracy;
            this.field = field;
            this.smallerFields = smallerFields;
        }

        public String getAccuracy() {
            return accuracy;
        }

        public int getField() {
            return field;
        }

        public int[] getSmallerFields() {
            return smallerFields;
        }

    }

    /**
     * 時間區(qū)間修飾類型
     */
    enum MODIFY_TYPE {

        B("before", "前N個,不包含當(dāng)前", //
                (date, field, step, smallerunits) -> {
                    Date start = date;
                    Date end = date;

                    start = add(start, field, -step);

                    Calendar instance = Calendar.getInstance();
                    for (int unit : smallerunits) {
                        int minimum = instance.getMinimum(unit);

                        // 設(shè)置周一為每周中第一天
                        if (unit == Calendar.DAY_OF_WEEK) {
                            minimum = Calendar.MONDAY;
                        }

                        start = set(start, unit, minimum);
                        end = set(end, unit, minimum);
                    }

                    return new Date[] { start, end };
                }//
        ), //
        P("past", "過去N個,包含當(dāng)前", //
                (date, field, step, smallerunits) -> {
                    Date start = date;
                    Date end = date;
                    start = add(start, field, -step);
                    return new Date[] { start, end };
                }//
        ), //

        C("current", "當(dāng)前", //
                (date, field, step, smallerunits) -> {
                    Date start = date;
                    Date end = date;

                    Calendar instance = Calendar.getInstance();
                    for (int unit : smallerunits) {
                        int minimum = instance.getMinimum(unit);

                        // 設(shè)置周一為每周中第一天
                        if (unit == Calendar.DAY_OF_WEEK) {
                            minimum = Calendar.MONDAY;
                        }

                        start = set(start, unit, minimum);
                    }
                    return new Date[] { start, end };
                }//
        );

        // 含義
        private String meaning;
        // 描述
        private String desc;

        // 計算規(guī)則
        private RuleFunction ruleFunction;

        private MODIFY_TYPE(String meaning, String desc, RuleFunction ruleFunction) {
            this.meaning = meaning;
            this.desc = desc;
            this.ruleFunction = ruleFunction;
        }

        public String getMeaning() {
            return meaning;
        }

        public String getDesc() {
            return desc;
        }

        public RuleFunction getRuleFunction() {
            return ruleFunction;
        }

    }

    /**
     * 時間規(guī)則函數(shù)
     */

    @FunctionalInterface
    private interface RuleFunction {
        Date[] calc(Date date, int field, int step, int[] smallerunits);
    }

    /**
     * 驗證date不為null
     * 
     * @param date 時間
     */
    private static void validateDateNotNull(final Date date) {
        Validate.notNull(date, "date");
    }

    /**
     * 將指定字段設(shè)置為返回新對象的日期。使用非寬松解釋
     * 
     * 
     * <p>
     * 不會改變原來的 {@code Date}
     * </p>
     *
     * @param date          日期,不為空
     * @param calendarField 要添加到其中的日歷字段
     * @param amount        要加的量
     * @return 新的{@code Date}與添加的時間量
     * @throws IllegalArgumentException 當(dāng)date為null時
     */
    private static Date set(final Date date, final int calendarField, final int amount) {
        validateDateNotNull(date);
        // getInstance()返回一個新對象,因此這個方法是線程安全的
        final Calendar c = Calendar.getInstance();
        // 設(shè)置一周的開始為周一
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setLenient(false);
        c.setTime(date);
        c.set(calendarField, amount);
        return c.getTime();
    }

    /**
     * 新創(chuàng)建一個日期對象,并設(shè)定日期
     * <p>
     * 不會改變原來的 {@code Date}
     * </p>
     * 
     * @param date          日期,不為空
     * @param calendarField 要添加到其中的日歷字段
     * @param amount        要加的量,可以是負的
     * @return 新的{@code Date}與添加的時間量
     * @throws NullPointerException 當(dāng)date為null時
     */
    private static Date add(final Date date, final int calendarField, final int amount) {
        validateDateNotNull(date);
        final Calendar c = Calendar.getInstance();
        // 設(shè)置一周的開始為周一
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setTime(date);
        c.add(calendarField, amount);
        return c.getTime();
    }
}

測試

    public static void main(String[] args) throws ParseException {
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");

        Date date = format.parse("2022-07-16 09:30:25 321");

        DateSegmentDTO dto1 = new DateSegmentDTO().setStepCode("Py1");
        DurationUtils.injectDateTime(date, dto1);

        System.out.println("過去1年:Py1");
        System.out.println(format.format(dto1.getStart()));
        System.out.println(format.format(dto1.getEnd()));

        DateSegmentDTO dto2 = new DateSegmentDTO().setStepCode("By2");
        DurationUtils.injectDateTime(date, dto2);

        System.out.println("前兩年:By2");
        System.out.println(format.format(dto2.getStart()));
        System.out.println(format.format(dto2.getEnd()));

        DateSegmentDTO dto3 = new DateSegmentDTO().setStepCode("Cy");
        DurationUtils.injectDateTime(date, dto3);
        System.out.println("當(dāng)年:Cy");
        System.out.println(format.format(dto3.getStart()));
        System.out.println(format.format(dto3.getEnd()));
        
        DateSegmentDTO dto4 = new DateSegmentDTO().setStepCode("PW1");
        DurationUtils.injectDateTime(date, dto4);
        System.out.println("過去1周:PW1");
        System.out.println(format.format(dto4.getStart()));
        System.out.println(format.format(dto4.getEnd()));
        
        DateSegmentDTO dto5 = new DateSegmentDTO().setStepCode("Bd7");
        DurationUtils.injectDateTime(date, dto5);
        System.out.println("前7天:Bd7");
        System.out.println(format.format(dto5.getStart()));
        System.out.println(format.format(dto5.getEnd()));
    }

測試結(jié)果

過去1年:Py1
2021-07-16 09:30:25 321
2022-07-16 09:30:25 321
前兩年:By2
2020-01-01 00:00:00 000
2022-01-01 00:00:00 000
當(dāng)年:Cy
2022-01-01 00:00:00 000
2022-07-16 09:30:25 321
過去1周:PW1
2022-07-09 09:30:25 321
2022-07-16 09:30:25 321
前7天:Bd7
2022-07-09 00:00:00 000
2022-07-16 00:00:00 000
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容