package com.xianhgu.ts.date;
import com.xiaghu.ts.string.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* @author xianghu.wang
* @time 2020/2/20
* @description Date工具包
*/
public class DateUtils {
/**
* 日誌服務.
*/
private static Logger logger = LoggerFactory.getLogger(DateUtils.class);
/**
* 隱藏構造方法.
*/
private DateUtils() {
}
/**
* 時區id.
*/
private static final String ASIA_SHANGHAI = "Asia/Shanghai";
private static final ZoneId ZONE_ID_SHANGHAI = ZoneId.of(ASIA_SHANGHAI);
/**
* 日期格式化樣式:yyyyMMdd
*/
public static final String YYYYMMDD = "yyyyMMdd";
/**
* 日期格式化樣式:yyyyMMddHHmm
*/
public static final String YYYYMMDDHHMM = "yyyyMMddHHmm";
/**
* 日期格式化樣式:yyyyMMddHHmmss
*/
public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
/**
* 日期格式化樣式:yyyyMMddHHmmssSSS
*/
public static final String YYYYMMDDMMSSSSS = "yyyyMMddHHmmssSSS";
/**
* 日期格式化樣式:yyyy-MM-dd
*/
public static final String YYYY_MM_DD = "yyyy-MM-dd";
/**
* 日期格式化樣式:yyyy-MM-dd HH:mm
*/
public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
/**
* 日期格式化樣式:yyyy-MM-dd HH:mm:ss
*/
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
/**
* 日期格式化樣式:yyyy-MM-dd HH:mm:ss SSS
*/
public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss SSS";
/**
* 日期格式化樣式:yyyy-MM-dd HH:mm:ss SSS
*/
public static final String YYYY_MM_DD_HH_MM_SS_SSS1 = "yyyy-MM-dd HH:mm:ss.SSS";
/**
* 日期格式化樣式:yyyyMMdd HH:mm:ss
*/
public static final String YYYYMMDD_HH_MM_SS = "yyyyMMdd HH:mm:ss";
/**
* 時間格式化樣式:HH:mm:ss
*/
public static final String HHMMSS = "HH:mm:ss";
/**
* 鎖對象
*/
private static final byte[] lockObj = new byte[1];
/**
* 存放不同的日期模板格式的dtf的Map.
*/
private static Map<String, ThreadLocal<DateTimeFormatter>> dtfMap = new HashMap<String, ThreadLocal<DateTimeFormatter>>();
/**
* 返回一個ThreadLocal的dtf,每個線程只會new一次dtf.
*
* @return java.time.format.DateTimeFormatter
* @author xianghu.wang
* @date 2020/2/20
* @param: pattern 日期格式
*/
private static DateTimeFormatter getDtf(final String pattern) {
if (dtfMap.containsKey(pattern)) {
return dtfMap.get(pattern).get();
} else {
synchronized (lockObj) {
if (!dtfMap.containsKey(pattern)) {
final ThreadLocal<DateTimeFormatter> dtf = ThreadLocal.withInitial(() -> DateTimeFormatter.ofPattern(pattern));
dtfMap.put(pattern, dtf);
return dtf.get();
} else {
return dtfMap.get(pattern).get();
}
}
}
}
/**
* 按格式格式化成日期字符串,若沒有傳遞format,按照yyyy-MM-dd HH:mm:ss格式化.
* 輸入時間爲 null時返回 null
*
* @author xianghu.wang
* @date 2020/2/20
* @param: date 需要轉換的時間
* @param: pattern 目標日期格式
*/
public static String formatDate(Date date, String pattern) {
if (null == date) {
logger.error(" #business 輸入時間不能爲空");
return null;
}
if (StringUtils.isBlank(pattern)) {
pattern = YYYY_MM_DD_HH_MM_SS;
}
return convertDateType(date).format(getDtf(pattern));
}
/**
* 按格式格式化成日期字符串,若沒有傳遞format,按照yyyy-MM-dd HH:mm:ss格式化.
* 輸入時間爲 null時返回 null
*
* @author xianghu.wang
* @date 2020/2/20
* @param: date 需要轉換的時間
* @param: pattern 目標日期格式
*/
public static String formatLocalDateTime(LocalDateTime date, String pattern) {
if (null == date) {
logger.error(" #business 輸入時間不能爲空");
return null;
}
if (StringUtils.isBlank(pattern)) {
pattern = YYYY_MM_DD_HH_MM_SS;
}
return date.format(getDtf(pattern));
}
/**
* 13位時間戳轉成指定格式字符串.
*
* @param timeMillis
* @param pattern
*/
public static String formatLong(Long timeMillis, String pattern) {
if (null == timeMillis) {
logger.error(" #business 輸入時間不能爲空");
return null;
}
return getDtf(pattern).format(LocalDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), ZONE_ID_SHANGHAI));
}
/**
* 13位時間戳轉成指定格式的字符串.
*
* @param time 13位時間戳
* @param pattern 目標日期格式
*/
public static String formatDate(long time, String pattern) {
return formatDate(new Date(time), pattern);
}
/**
* 解析指定格式的字符串爲Date.
* 案例:
* parse("2019-08-01 12:00:00","yyyy-MM-dd HH:mm:ss") --> Date
* parse("2019-08-01","yyyy-MM-dd") --> Date
*
* @author xianghu.wang
* @date 2020/2/20
* @param: dateStr 需要轉換的時間
* @param: pattern 時間格式
*/
public static Date parseToDate(String dateStr, String pattern) {
LocalDateTime parse = LocalDateTime.parse(dateStr, getDtf(pattern));
return convertDateType(parse);
}
/**
* 將指定格式的字符串時間轉換成13位時間戳格式.
*
* @author xianghu.wang
* @date 2020/2/20
* @param: dateStr 時間戳
* @param: pattern 時間格式
*/
public static Long parseToLong(String dateStr, String pattern) {
Date date = parseToDate(dateStr, pattern);
if (date != null) {
return date.getTime();
} else {
return null;
}
}
/**
* 字符串格式時間戳之間的轉換.
*
* @param timeStr 待轉換時間戳
* @param originPattern 待轉換時間戳格式
* @param targetPattern 目標時間格式
*/
public static String parseToString(String timeStr, String originPattern, String targetPattern) {
if (StringUtils.hasBlank(Arrays.asList(timeStr, originPattern, targetPattern))) {
logger.error(" #business 輸入參數不能爲空");
return null;
}
LocalDateTime time = LocalDateTime.parse(timeStr, getDtf(originPattern));
return getDtf(targetPattern).format(time);
}
/**
* 是否有空字符串.
*
* @param originTime
* @param originPattern
* @param targetTime
* @param targetPattern
* @return
*/
private static boolean containsBlank(String originTime, String originPattern, String targetTime, String targetPattern) {
return StringUtils.isBlank(originTime) || StringUtils.isBlank(originPattern)
|| StringUtils.isBlank(targetTime) || StringUtils.isBlank(targetPattern);
}
/**
* 獲取當日零點時間戳.
*/
public static long getMidnight() {
return LocalDate.now().atStartOfDay(ZONE_ID_SHANGHAI).toInstant().toEpochMilli();
}
/**
* 獲取指定日期零點時間戳.
*/
public static Long getMidnight(Date date) {
if (date == null) {
logger.error(" #business 輸入時間不能爲空");
return null;
}
return convertDateType(date).toLocalDate().atStartOfDay(ZONE_ID_SHANGHAI).toInstant().toEpochMilli();
}
/**
* 判斷是否同一天.
*
* @param first
* @param second
*/
public static boolean sameDay(long first, long second) {
Instant firstInstant = Instant.ofEpochMilli(first);
Instant secondInstant = Instant.ofEpochMilli(second);
final LocalDateTime firstTime = LocalDateTime.ofInstant(firstInstant, ZONE_ID_SHANGHAI);
final LocalDateTime secondTime = LocalDateTime.ofInstant(secondInstant, ZONE_ID_SHANGHAI);
return firstTime.getDayOfYear() == secondTime.getDayOfYear();
}
/**
* 時間加法.
*
* @param time 被加數
* @param unit 時間單位
* @param interval 加數
*/
public static Date TimePlus(Date time, DateUnit unit, long interval) {
if (Objects.isNull(time) || Objects.isNull(unit)) {
logger.error(" #business 輸入時間不能爲空");
return null;
}
LocalDateTime localDateTime = convertDateType(time);
switch (unit) {
case DAY:
localDateTime = localDateTime.plusDays(interval);
break;
case HOUR:
localDateTime = localDateTime.plusHours(interval);
break;
case MINUTE:
localDateTime = localDateTime.plusMinutes(interval);
break;
case WEEK:
localDateTime = localDateTime.plusWeeks(interval);
case MONTH:
localDateTime = localDateTime.plusMonths(interval);
break;
case YEAR:
localDateTime = localDateTime.plusYears(interval);
break;
default:
throw new RuntimeException("unsupported enum exception");
}
return convertDateType(localDateTime);
}
/**
* 時間減法.
*
* @param time 被減數
* @param unit 時間單位
* @param interval 減數
*/
public static Date TimeMinus(Date time, DateUnit unit, long interval) {
return TimePlus(time, unit, -interval);
}
/**
* 查找當前時間所在小時根據每 interval 分鐘劃分區的上邊界.
* interval = 0 時返回 time 的分鐘數
* 示例:
* time = 1582190863000(2020-02-20 17:27:43), interval = 15 -> 17:15
* time = 1582191702132(2020-02-20 17:41:42), interval = 15 -> 17:30
* time = 1582192078463(2020-02-20 17:47:58), interval = 15 -> 17:45
*
* @param time 時間
* @param interval 分區長度
*/
public static String getHeadBoundary(long time, int interval) {
LocalDateTime localDateTime = convertDateType(time);
int minute = localDateTime.getMinute();
if (interval != 0) {
minute = minute - minute % interval;
}
return LocalTime.of(localDateTime.getHour(), minute).toString();
}
/**
* 查找當前時間所在小時根據每 interval 分鐘劃分區的下邊界.
* interval = 0 時返回 time 的分鐘數
* 示例:
* time = 1582190863000(2020-02-20 17:27:43), interval = 15 -> 17:30
* time = 1582191702132(2020-02-20 17:41:42), interval = 15 -> 17:45
* time = 1582192078463(2020-02-20 17:47:58), interval = 15 -> 18:00
*
* @param time 時間
* @param interval 分區長度
*/
public static String getTailBoundary(long time, int interval) {
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZONE_ID_SHANGHAI);
int minute = localDateTime.getMinute();
long lack = 0L;
if (interval != 0) {
lack = interval - minute % interval;
}
LocalDateTime resultLDT = localDateTime.plusMinutes(lack);
return LocalTime.of(resultLDT.getHour(), resultLDT.getMinute()).toString();
}
/**
* 轉換時間格式 long -> LocalDateTime.
*
* @author xianghu.wang
* @date 2020/2/20
* @param: millis
*/
public static LocalDateTime convertDateType(long millis) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZONE_ID_SHANGHAI);
}
/**
* 轉換時間格式 Date -> LocalDateTime.
*
* @author xianghu.wang
* @date 2020/2/20
* @param: date
*/
public static LocalDateTime convertDateType(Date date) {
if (null == date) {
logger.error(" #business 輸入時間不能爲空");
return null;
}
Instant instant = date.toInstant();
return instant.atZone(ZONE_ID_SHANGHAI).toLocalDateTime();
}
/**
* 轉換時間格式 LocalDateTime -> Date.
*
* @author xianghu.wang
* @date 2020/2/20
* @param: date
*/
public static Date convertDateType(LocalDateTime date) {
if (null == date) {
logger.error(" #business 輸入時間不能爲空");
return null;
}
ZonedDateTime zdt = date.atZone(ZONE_ID_SHANGHAI);
return Date.from(zdt.toInstant());
}
}
package com.xianghu.ts.date;
/**
* 時間單位枚舉
*/
public enum DateUnit {
YEAR,
DAY,
MONTH,
WEEK,
HOUR,
MINUTE,
SECOND
}