Java8 DateUtils

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
}

 

發佈了28 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章