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
}