
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