import lombok.Getter;
import java.time.format.DateTimeFormatter;
/**
* 描述 : 时间格式化类型枚举类.
*
* 说明:
* 1. TimeFormatEnum.LONG_DATE_PATTERN_LINE.formatter; // 获取默认时间格式: yyyy-MM-dd HH:mm:ss
* 2. 对于LocalDate,只能指定短时间格式化类型,否则会出现UnsupportedTemporalTypeException异常
* 3. 当日期字符串格式与指定的{@link TimeFormatEnum}格式无法对应时,则会出现DateTimeParseException异常
*
* @author : MoCha
* @version : v1
* @date : 2021-02-14 09:04
*/
@Getter
public enum TimeFormatEnum {
/* ---------------- 短时间格式 -------------- */
/**
* 短时间格式 yyyy-MM-dd.
*/
SHORT_DATE_PATTERN_LINE("yyyy-MM-dd"),
/**
* 短时间格式 yyyy/MM/dd.
*/
SHORT_DATE_PATTERN_SLASH("yyyy/MM/dd"),
/**
* 短时间格式 yyyy\MM\dd.
*/
SHORT_DATE_PATTERN_BACK_SLASH("yyyy\\MM\\dd"),
/**
* 短时间格式 yyyyMMdd.
*/
SHORT_DATE_PATTERN_NONE("yyyyMMdd"),
/* ---------------- 长时间格式 -------------- */
/**
* 长时间格式 yyyy-MM-dd HH:mm:ss.
*/
LONG_DATE_PATTERN_LINE("yyyy-MM-dd HH:mm:ss"),
/**
* 长时间格式 yyyy/MM/dd HH:mm:ss.
*/
LONG_DATE_PATTERN_SLASH("yyyy/MM/dd HH:mm:ss"),
/**
* 长时间格式 yyyy\MM\dd HH:mm:ss.
*/
LONG_DATE_PATTERN_BACK_SLASH("yyyy\\MM\\dd HH:mm:ss"),
/**
* 长时间格式 yyyyMMdd HH:mm:ss.
*/
LONG_DATE_PATTERN_NONE("yyyyMMdd HH:mm:ss"),
/**
* 长时间格式 yyyyMMddHHmmss.
*/
LONG_DATE_PATTERN_WITH_NONE("yyyyMMddHHmmss"),
/* ---------------- 长时间格式 带毫秒 -------------- */
/**
* 长时间格式 带毫秒 yyyy-MM-dd HH:mm:ss.SSS .
*/
LONG_DATE_PATTERN_WITH_MISC_LINE("yyyy-MM-dd HH:mm:ss.SSS"),
/**
* 长时间格式 带毫秒 yyyy/MM/dd HH:mm:ss.SSS .
*/
LONG_DATE_PATTERN_WITH_MISC_SLASH("yyyy/MM/dd HH:mm:ss.SSS"),
/**
* 长时间格式 带毫秒 yyyy\MM\dd HH:mm:ss.SSS .
*/
LONG_DATE_PATTERN_WITH_MISC_BACK_SLASH("yyyy\\MM\\dd HH:mm:ss.SSS"),
/**
* 长时间格式 带毫秒 yyyyMMdd HH:mm:ss.SSS .
*/
LONG_DATE_PATTERN_WITH_MISC_NONE("yyyyMMdd HH:mm:ss.SSS");
/**
* 时间格式化器.
*/
private final DateTimeFormatter formatter;
TimeFormatEnum(String pattern) {
formatter = DateTimeFormatter.ofPattern(pattern);
}
/**
* 判断是否为短时间格式类型
*
* 说明:
* 1. 判断的界限以{@link TimeFormatEnum#SHORT_DATE_PATTERN_NONE}为参考
*
* @param timeFormatEnum 时间格式化类型
* @return true表示为短时间格式化类型
*/
public static boolean isShortDatePattern(TimeFormatEnum timeFormatEnum) {
return timeFormatEnum.ordinal() <= SHORT_DATE_PATTERN_NONE.ordinal();
}
}
import org.springframework.util.Assert;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* 描述 : LocalDate工具类.
*
* @author : MoCha
* @version : v1
* @date : 2021-02-13 17:00
*/
public final class LocalDateUtils {
/**
* Prevents instantiation.
*/
private LocalDateUtils() {
}
/* ---------------- 时间戳相关方法 -------------- */
/**
* 当前LocalDate 转 时间戳(秒级别)
*
* @return 时间戳(秒级别)
*/
public static Long toNowSecond() {
return toSecond(LocalDate.now());
}
/**
* 当前LocalDate 转 时间戳(毫秒级别)
*
* @return 时间戳(毫秒级别)
*/
public static Long toNowMilliseconds() {
return toMilliseconds(LocalDate.now());
}
/**
* LocalDate 转 时间戳(秒级别).
*
* @param localDate 日期对象
* @return 时间戳(秒级别)
*/
public static Long toSecond(LocalDate localDate) {
return localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().getEpochSecond();
}
/**
* LocalDate 转 时间戳(毫秒级别).
*
* @param localDate 日期对象
* @return 时间戳(毫秒级别)
*/
public static Long toMilliseconds(LocalDate localDate) {
return localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().toEpochMilli();
}
/**
* 时间戳(秒级别) 转 LocalDate.
*
* @param timestamp 时间戳(秒级别)
* @return 日期对象
*/
public static LocalDate ofSecond(Long timestamp) {
return Instant.ofEpochSecond(timestamp).atZone(ZoneOffset.ofHours(8)).toLocalDate();
}
/**
* 时间戳(毫秒级别) 转 LocalDate.
*
* @param timestamp 时间戳(毫秒级别)
* @return 日期对象
*/
public static LocalDate ofMilliseconds(Long timestamp) {
return Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.ofHours(8)).toLocalDate();
}
/**
* 获取指定日期开始时间点(凌晨零点)的时间戳(秒级别).
*
* @return 指定日期开始时间点的时间戳(秒级别)
*/
public static long getTodayStartTimeStamp(LocalDate localDate) {
return LocalDateTime.of(localDate, LocalTime.MIN).toEpochSecond(ZoneOffset.ofHours(8));
}
/**
* 获取指定日期结束时间点(夜晚23点59分59秒)的时间戳(秒级别).
*
* @return 指定日期结束时间点的时间戳(秒级别)
*/
public static long getTodayEndTimeStamp(LocalDate localDate) {
return LocalDateTime.of(localDate, LocalTime.MAX).toEpochSecond(ZoneOffset.ofHours(8));
}
/* ---------------- 格式化日期对象相关方法 -------------- */
/**
* 根据时间戳返回短时间格式化日期对象 .
*
* @param timestamp 日期时间戳
* @return 日期对象格式化值
*/
public static String formatShortDatePatternLine(Long timestamp) {
return formatShortDatePatternLine(ofMilliseconds(timestamp));
}
/**
* 短时间格式化日期对象 格式:yyyy-MM-dd {@link TimeFormatEnum#SHORT_DATE_PATTERN_LINE}.
*
* @param localDate 日期对象
* @return 日期对象格式化值
*/
public static String formatShortDatePatternLine(LocalDate localDate) {
return TimeFormatEnum.SHORT_DATE_PATTERN_LINE.getFormatter().format(localDate);
}
/**
* 短时间格式化日期对象 格式:yyyyMMdd.
*
* @param localDate 日期对象
* @return 日期对象格式化值
*/
public static String formatShortDatePatternNone(LocalDate localDate) {
return TimeFormatEnum.SHORT_DATE_PATTERN_NONE.getFormatter().format(localDate);
}
/**
* 指定格式化类型进行日期对象格式化.
*
* 说明:
* 1. 对于LocalDate,只能指定短时间格式化类型,否则会出现UnsupportedTemporalTypeException异常
*
* @param localDate 日期对象
* @param timeFormatEnum 格式化类型枚举 {@link TimeFormatEnum}
* @return 日期对象格式化值
*/
public static String format(LocalDate localDate, TimeFormatEnum timeFormatEnum) {
Assert.isTrue(TimeFormatEnum.isShortDatePattern(timeFormatEnum), "对于LocalDate,只能格式化短时间格式的LocalDate");
return timeFormatEnum.getFormatter().format(localDate);
}
/* ---------------- 解析时间字符串相关方法 -------------- */
/**
* 解析日期字符串为日期对象 格式:yyyy-MM-dd.
*
* @param text 日期字符串 格式:yyyy-MM-dd {@link TimeFormatEnum#SHORT_DATE_PATTERN_LINE}
* @return 日期对象
*/
public static LocalDate parseShortDatePatternLine(CharSequence text) {
return LocalDate.parse(text, TimeFormatEnum.SHORT_DATE_PATTERN_LINE.getFormatter());
}
/**
* 解析日期字符串为日期对象 格式:yyyyMMdd.
*
* @param text 日期字符串 格式:yyyyMMdd
* @return 日期对象
*/
public static LocalDate parseShortDatePatternNone(CharSequence text) {
return LocalDate.parse(text, TimeFormatEnum.SHORT_DATE_PATTERN_NONE.getFormatter());
}
/**
* 指定解析类型进行日期字符串解析
*
* 说明:
* 1. 当日期字符串格式与指定的{@link TimeFormatEnum}格式无法对应时,则会出现DateTimeParseException异常
* 2. 对于LocalDate,只能解析短时间格式的日期字符串
*
* @param text 日期字符串
* @param timeFormatEnum 格式化类型枚举 {@link TimeFormatEnum}
* @return 日期对象
*/
public static LocalDate parse(CharSequence text, TimeFormatEnum timeFormatEnum) {
Assert.isTrue(TimeFormatEnum.isShortDatePattern(timeFormatEnum), "对于LocalDate,只能解析短时间格式的日期字符串");
return LocalDate.parse(text, timeFormatEnum.getFormatter());
}
/* ---------------- 与业务场景相关的方法 -------------- */
/**
* 获取指定日期上一个月的最后一天.
*
* @return 日期对象
*/
public static LocalDate getPreviousMonthLastDay(LocalDate localDate) {
return localDate.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
}
/**
* 获取指定日期所在月份的第一个周几.
*
* 说明:
* 1. 如果想要获取当前日期所在月份的第一个周几,第一个入参设置为LocalDate.now()
* 2. DayOfWeek可选值,通常使用MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY
*
*
例如:
* LocalDateUtils.getFirstDayOfWeek(LocalDate.now(), DayOfWeek.THURSDAY)
*
* @param localDate 指定日期对象
* @param dayOfWeek 周几(周一、周二、周三...、周天)
* @return 第一个周几的LocalDate日期对象
*/
public static LocalDate getFirstDayOfWeek(LocalDate localDate, DayOfWeek dayOfWeek) {
return localDate.with(TemporalAdjusters.firstInMonth(dayOfWeek));
}
/**
* 获取当前月份的第一天.
*
* @return 当前月份的第一天日期对象
*/
public static LocalDate getFirstDayOfCurrentMonth() {
return LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
}
/**
* 获取当前月份的最后一天.
*
* @return 当前月份的最后一天日期对象
*/
public static LocalDate getLastDayOfCurrentMonth() {
return LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
}
/**
* 获取两个日期字符串之间的日期时间戳集.
*
* 说明:
* 1. 时间戳仅考虑年月日,不带时分秒
*
*
示例:
* getBetweenTimeStampShortDatePatternNone("20210626", "20210712")
*
* @param startText 开始日期字符串;须为短时间格式,即不带时分秒
* @param endText 结束日期字符串;须为短时间格式,即不带时分秒
* @return 两个日期字符串之间的日期时间戳集
*/
public static List<Long> getBetweenTimeStampShortDatePatternNone(CharSequence startText, CharSequence endText) {
List<Long> betweenTimeStampList = new ArrayList<>();
LocalDate startDate = parse(startText, TimeFormatEnum.SHORT_DATE_PATTERN_NONE);
LocalDate endDate = parse(endText, TimeFormatEnum.SHORT_DATE_PATTERN_NONE);
long distance = getBetweenDays(startDate, endDate);
if (distance < 1) {
return betweenTimeStampList;
}
Stream.iterate(startDate, date -> date.plusDays(1))
.limit(distance + 1)
.forEach(localDate -> betweenTimeStampList.add(toMilliseconds(localDate)));
return betweenTimeStampList;
}
/**
* 获取两个日期字符串之间的日期字符串集.
*
* 说明:
* 1. 日期列表为短时间格式 yyyyMMdd
*
*
示例:
* getBetweenDateTextShortDatePatternNone("20210626", "20210712")
*
* @param startText 开始日期字符串;须为短时间格式,即不带时分秒
* @param endText 结束日期字符串;须为短时间格式,即不带时分秒
* @return 两个日期字符串之间的日期字符串集
*/
public static List<String> getBetweenDateTextShortDatePatternNone(CharSequence startText, CharSequence endText) {
return getBetweenDateText(startText, endText, TimeFormatEnum.SHORT_DATE_PATTERN_NONE);
}
/**
* 获取两个日期字符串之间的日期字符串集.
*
* 示例:
* getBetweenDateText("20210626", "20210712", TimeFormatEnum.SHORT_DATE_PATTERN_NONE)
*
* @param startText 开始日期字符串;须为短时间格式,即不带时分秒
* @param endText 结束日期字符串;须为短时间格式,即不带时分秒
* @param timeFormatEnum 时间格式化类型枚举类;须为短时间格式枚举类
* @return 两个日期字符串之间的日期字符串集
*/
public static List<String> getBetweenDateText(CharSequence startText, CharSequence endText, TimeFormatEnum timeFormatEnum) {
List<String> betweenDateTextList = new ArrayList<>();
LocalDate startDate = parse(startText, timeFormatEnum);
LocalDate endDate = parse(endText, timeFormatEnum);
long distance = ChronoUnit.DAYS.between(startDate, endDate);
if (distance < 1) {
return betweenDateTextList;
}
Stream.iterate(startDate, date -> date.plusDays(1))
.limit(distance + 1)
.forEach(localDate -> betweenDateTextList.add(format(localDate, timeFormatEnum)));
return betweenDateTextList;
}
/**
* 计算两个时间戳间隔的天数.
*
* 说明:时间戳仅考虑年月日,不带时分秒
*
* @param startTimeStamp 开始时间戳
* @param endTimeStamp 结束时间戳
* @return 间隔天数
*/
public static Long getBetweenDays(Long startTimeStamp, Long endTimeStamp) {
LocalDate startLocalDate = ofMilliseconds(startTimeStamp);
LocalDate endLocalDate = ofMilliseconds(endTimeStamp);
return getBetweenDays(startLocalDate, endLocalDate);
}
/**
* 计算两个日期对象间隔的天数.
*
* @param startDate 起始日期对象
* @param endDate 结束日期对象
* @return 两个日期对象间隔的天数
*/
public static Long getBetweenDays(LocalDate startDate, LocalDate endDate) {
return startDate.until(endDate, ChronoUnit.DAYS);
}
/* ---------------- 季度相关的方法 -------------- */
/**
* 获取当前年份季度中文描述.
*
* @return 返回示例:2021年第一季度
*/
public static String getNowYearQuarterlyDesc() {
switch (getNowQuarterEnum()) {
case FIRST:
return LocalDate.now().getYear() + "年第一季度";
case SECOND:
return LocalDate.now().getYear() + "年第二季度";
case THIRD:
return LocalDate.now().getYear() + "年第三季度";
case FOURTH:
return LocalDate.now().getYear() + "年第四季度";
default:
return "";
}
}
/**
* 获取当前季度值.
*
* @return 当前季度值
*/
public static int getNowQuarterly() {
return getNowQuarterEnum().getValue();
}
/**
* 查询当前季度枚举类.
*
* @return 季度枚举类
*/
public static QuarterEnum getNowQuarterEnum() {
return LocalDate.now().query(new QuarterOfYearQuery());
}
/**
* 获取当前时间季度开始时间戳(毫秒级别).
*
* 说明:时间戳仅考虑年月日,不带时分秒
*
* @return 返回当前时间季度开始时间戳
*/
public static Long getNowQuarterStartTimeStamp() {
LocalDate nowLocalDate = LocalDate.now();
Month firstMonthOfQuarter = nowLocalDate.getMonth().firstMonthOfQuarter();
nowLocalDate = LocalDate.of(nowLocalDate.getYear(), firstMonthOfQuarter, 1);
return toMilliseconds(nowLocalDate);
}
}