基於jdk8的全新實用時間工具類

基於jdk8的實用時間工具類, 已經經過多個項目的考驗與完善, 包含對"時間"的心得體會

歡迎轉載,轉載請註明網址:https://blog.csdn.net/qq_41910280

版本說明
2020-07-02 初次提交 (寫於2019年)

簡介
  本文提供一個全面的基於jdk8的時間工具類。Talk is cheap, let’s Getting Started.

1. 直接上代碼

 
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.util.Date;
import java.util.TimeZone;

import static java.time.format.ResolverStyle.STRICT;

/**
 * <h3>時間工具類</h3>
 * <p>
 * <ul>
 * <li>u means year(year-proleptic) which can be negative<br/>
 * <li>y means year-of-era which con't be negative
 * </ul>
 *  example:<br/>
 *  1 in "u" equal to 1 AD in "y G"<br/>
 *  0 in "u" equal to 1 BC in "y G"<br/>
 *  -1 in "u" equal to 2 BC in "y G"
 * <p>
 *
 * @author zhouyou
 * @date 2019/10/28 18:18
 * @email [email protected]
 */
public class TimeUtils {
    static {
        /**
         * 中國曾實行過夏令時(1986-1991) Asia/Shanghai會處理夏令時 而GMT+8不會
         * 設置JVM時區 避免docker等採用UTC
         */
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
    }

    /**
     * maybe we can set DEFAULT_ZONE_ID = ZoneOffset.ofHours(8);
     * or we can set DEFAULT_ZONE_ID = ZoneId.of("Asia/Shanghai");
     */
    public static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();
    /**
     * 東八區 並不能完全代表北京時間
     *
     * @see #DEFAULT_ZONE_ID
     * @see #getDefaultZoneOffset(LocalDateTime)
     * @see #getDefaultZoneOffset(Instant)
     */
    @Deprecated
    public static final ZoneOffset DEFAULT_ZONE_OFFSET = ZoneOffset.ofHours(8);

    public static final String DAY_BEGIN_TIME = " 00:00:00";
    public static final String DAY_END_TIME = " 23:59:59";

    public static final DateTimeFormatter ISO_DATE_TIMESTAMP = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS").withResolverStyle(STRICT).withZone(DEFAULT_ZONE_ID);

    public static final DateTimeFormatter ISO_DATE_TIME = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss").withResolverStyle(STRICT).withZone(DEFAULT_ZONE_ID);

    public static final DateTimeFormatter ISO_DATE = DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(STRICT).withZone(DEFAULT_ZONE_ID);

    public static final DateTimeFormatter ISO_TIME = DateTimeFormatter.ofPattern("HH:mm:ss").withResolverStyle(STRICT).withZone(DEFAULT_ZONE_ID);

    public static final DateTimeFormatter ISO_MONTHDAY = DateTimeFormatter.ofPattern("MM-dd").withResolverStyle(STRICT).withZone(DEFAULT_ZONE_ID);

    public static final DateTimeFormatter ISO_DATE_WITHOUT_HYPHEN = DateTimeFormatter.ofPattern("uuuuMMdd").withResolverStyle(STRICT).withZone(DEFAULT_ZONE_ID);

    public static final DateTimeFormatter ISO_DATETIME_WITHOUT_HYPHEN = DateTimeFormatter.ofPattern("uuuuMMddHHmmss").withResolverStyle(STRICT).withZone(DEFAULT_ZONE_ID);


    /**
     * <p>
     * <b>脫離時間談ZoneOffset毫無意義</b><br/>
     * see: 日光節約時間DST(夏令時)
     * example: 在一年中, 洛杉磯一段時間爲"-8", 一段時間爲"-7"
     * </p>
     *
     * @param localDateTime
     * @return
     */
    private static ZoneOffset getDefaultZoneOffset(LocalDateTime localDateTime) {
        return DEFAULT_ZONE_ID.getRules().getOffset(localDateTime);
    }

    /**
     * @param instant
     * @return
     * @see #getDefaultZoneOffset(LocalDateTime)
     */
    private static ZoneOffset getDefaultZoneOffset(Instant instant) {
        return DEFAULT_ZONE_ID.getRules().getOffset(instant);
    }

    /**
     * LocalDateTime 和 Date的相互轉換
     */
    public static Date toDate(LocalDateTime localDateTime) {
        return localDateTime == null ? null : Date.from(localDateTime.atZone(DEFAULT_ZONE_ID).toInstant());
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        return date == null ? null : toInstant(date).atZone(DEFAULT_ZONE_ID).toLocalDateTime();
    }

    /**
     * LocalDate 和 Date的相互轉換
     */
    public static Date toDate(LocalDate localDate) {
        return localDate == null ? null : Date.from(localDate.atStartOfDay(DEFAULT_ZONE_ID).toInstant());
    }

    public static LocalDate toLocalDate(Date date) {
        return date == null ? null : toInstant(date).atZone(DEFAULT_ZONE_ID).toLocalDate();
    }

    /**
     * LocalTime 和 Date的相互轉換
     */
    public static Date toDate(LocalTime localTime) {
        return localTime == null ? null : Date.from(localTime.atDate(LocalDate.now()).atZone(DEFAULT_ZONE_ID).toInstant());
    }

    // 只要h以下單位
    public static Date toTime(LocalTime localTime) {
        return localTime == null ? null : Date.from(localTime.atDate(LocalDate.ofEpochDay(0L)).atZone(DEFAULT_ZONE_ID).toInstant());
    }

    public static LocalTime toLocalTime(Date date) {
        return date == null ? null : toInstant(date).atZone(DEFAULT_ZONE_ID).toLocalTime();
    }

    /**
     * 不直接使用toInstant防止java.sql.Time或java.sql.Date拋出異常
     * Instant instant;
     * if (date instanceof java.sql.Time || date instanceof java.sql.Date) {
     * Calendar calendar = Calendar.getInstance();
     * calendar.setTime(date);
     * instant = calendar.toInstant();
     * } else {
     * instant = date.toInstant();
     * }
     * return instant;
     *
     * @param date
     * @return
     */
    private static Instant toInstant(Date date) {
        return Instant.ofEpochMilli(date.getTime());
    }

    /**
     * @param date
     * @return <b>yyyy-MM-dd HH:mm:ss</b>
     */
    public static String format(Date date) {
        return format(date, null);
    }

    public static String format(Date date, DateTimeFormatter formatter) {
        formatter = defaultDateTimeFormatter(formatter);
        return formatter.format(toLocalDateTime(date));
    }

    /**
     * @param localDateTime
     * @return <b>yyyy-MM-dd HH:mm:ss</b>
     */
    public static String format(LocalDateTime localDateTime) {
        return format(localDateTime, null);
    }

    /**
     * @param localDateTime
     * @param formatter     默認yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static String format(LocalDateTime localDateTime, DateTimeFormatter formatter) {
        formatter = defaultDateTimeFormatter(formatter);
        return formatter.format(localDateTime);
    }

    /**
     * @param localDate
     * @return <b>yyyy-MM-dd</b>
     */
    public static String format(LocalDate localDate) {
        return format(localDate, null);
    }

    /**
     * @param localDate
     * @param formatter 默認yyyy-MM-dd
     * @return
     */
    public static String format(LocalDate localDate, DateTimeFormatter formatter) {
        formatter = defaultDateFormatter(formatter);
        return formatter.format(localDate);
    }

    /**
     * @param text yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static LocalDateTime parse2LocalDateTime(String text) {
        return parse2LocalDateTime(text, null);
    }

    /**
     * @param text
     * @param formatter 默認yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static LocalDateTime parse2LocalDateTime(String text, DateTimeFormatter formatter) {
        formatter = defaultDateTimeFormatter(formatter);
        return LocalDateTime.parse(text, formatter);
    }

    /**
     * @param text yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static Date parse2Date(String text) {
        return toDate(parse2LocalDateTime(text));
    }

    /**
     * @param text yyyy-MM-dd
     * @return
     */
    public static LocalDate parse2LocalDate(String text) {
        return parse2LocalDate(text, null);
    }

    /**
     * @param text
     * @param formatter 默認yyyy-MM-dd
     * @return
     */
    public static LocalDate parse2LocalDate(String text, DateTimeFormatter formatter) {
        formatter = defaultDateFormatter(formatter);
        return LocalDate.parse(text, formatter);
    }

    /**
     * @param text MM-dd
     * @return
     */
    public static MonthDay parseMonthDay(String text) {
        return parseMonthDay(text, null);
    }

    /**
     * @param text MM-dd
     * @return
     */
    public static MonthDay parseMonthDay(String text, DateTimeFormatter formatter) {
        formatter = defaultMonthDayFormatter(formatter);
        return MonthDay.parse(text, formatter);
    }

    /**
     * other way 1: return localDateTime.toInstant(DEFAULT_ZONE_OFFSET).toEpochMilli();
     * other way 2: return Timestamp.valueOf(localDateTime).getTime();
     *
     * @param localDateTime
     * @return
     */
    public static long getTime(LocalDateTime localDateTime) {
        // Instant效率高於Timestamp
        return localDateTime.atZone(DEFAULT_ZONE_ID).toInstant().toEpochMilli();
    }

    /**
     * 根據{@link #DEFAULT_ZONE_ID}進行時間戳解析
     *
     * @param timeMillis ms
     * @return
     */
    public static LocalDateTime parseTime(long timeMillis) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), DEFAULT_ZONE_ID);
    }

    /**
     * <b>固定偏移量</b>
     * 根據DEFAULT_ZONE_OFFSET進行時間戳解析
     *
     * @param timeMillis ms
     * @return
     * @see #parseTime(long)
     */
    @Deprecated
    public static LocalDateTime parseTimeFast(long timeMillis) {
        int nanoOfSecond = (int) Math.floorMod(timeMillis, 1000) * 1000_000;
        long epochSecond = Math.floorDiv(timeMillis, 1000);
        return LocalDateTime.ofEpochSecond(epochSecond, nanoOfSecond, DEFAULT_ZONE_OFFSET);
    }

    /**
     * <b>時間間隔</b>
     * <p>
     * 選擇ChronoUnit是因爲:<br/>
     * Duration和Period會計算各個時間差,但並不會整合,
     * 這意味着:如果相差2個月0天,獲取的相差天數是0
     * <p/>
     *
     * @param temporal1
     * @param temporal2
     * @param unit
     * @return
     */
    public static long between(Temporal temporal1, Temporal temporal2, ChronoUnit unit) {
        return unit.between(temporal1, temporal2);
    }

    /**
     * @return <b>yyyy-MM-dd HH:mm:ss</b>
     */
    public static String now() {
        return now(null);
    }

    /**
     * @return <b>yyyy-MM-dd</b>
     */
    public static String nowDate() {
        return LocalDate.now().format(ISO_DATE);
    }

    /**
     * @return <b>yyyy-MM-dd</b>
     */
    public static String plusDate(long yearsToAdd, long monthsToAdd, long daysToAdd) {
        LocalDate now = LocalDate.now();
        if (yearsToAdd != 0) {
            now = now.plusYears(yearsToAdd);
        }
        if (monthsToAdd != 0) {
            now = now.plusMonths(monthsToAdd);
        }
        if (daysToAdd != 0) {
            now = now.plusDays(daysToAdd);
        }
        return now.format(ISO_DATE);
    }

    /**
     * @param formatter 默認yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static String now(DateTimeFormatter formatter) {
        formatter = defaultDateTimeFormatter(formatter);
        return LocalDateTime.now().format(formatter);
    }


    /**
     * @param text yyyy-MM-dd
     * @return
     */
    public static Date getDayBegin(String text) {
        return toDate(LocalDateTime.of(LocalDate.parse(text, ISO_DATE), LocalTime.MIN));
    }

    /**
     * @param text yyyy-MM-dd
     * @return
     */
    public static Date getDayEnd(String text) {
        return toDate(LocalDateTime.of(LocalDate.parse(text, ISO_DATE), LocalTime.MAX));
    }

    /**
     * @param localDate
     * @return
     */
    public static Date getDayBegin(LocalDate localDate) {
        return toDate(LocalDateTime.of(localDate, LocalTime.MIN));
    }

    /**
     * @param localDate
     * @return
     */
    public static Date getDayEnd(LocalDate localDate) {
        return toDate(LocalDateTime.of(localDate, LocalTime.MAX));
    }

    /**
     * @param localDateTime
     * @return
     */
    public static Date getDayBegin(LocalDateTime localDateTime) {
        return getDayBegin(localDateTime.toLocalDate());
    }

    /**
     * @param localDateTime
     * @return
     */
    public static Date getDayEnd(LocalDateTime localDateTime) {
        return getDayEnd(localDateTime.toLocalDate());
    }

    public static Date getDayBegin(Date date) {
        return toDate(LocalDateTime.of(toLocalDate(date), LocalTime.MIN));
    }

    public static Date getDayEnd(Date date) {
        return toDate(LocalDateTime.of(toLocalDate(date), LocalTime.MAX));
    }

    /**
     * @param text yyyy-MM-dd
     * @return
     */
    @Deprecated
    public static Date[] getDayBeginAndEnd(String text) {
        LocalDate localDate = LocalDate.parse(text, ISO_DATE);
        return getDaysByLocalDate(LocalDateTime.of(localDate, LocalTime.MIN), LocalDateTime.of(localDate, LocalTime.MAX));
    }

    @Deprecated
    public static Date[] getDayBeginAndEnd(Date date) {
        LocalDate localDate = toLocalDate(date);
        return getDaysByLocalDate(LocalDateTime.of(localDate, LocalTime.MIN), LocalDateTime.of(localDate, LocalTime.MAX));
    }

    private static Date[] getDaysByLocalDate(LocalDateTime... localDateTimes) {
        Date[] dates = new Date[localDateTimes.length];
        for (int i = 0; i < dates.length; i++) {
            dates[i] = toDate(localDateTimes[i]);
        }
        return dates;
    }

    private static DateTimeFormatter defaultDateTimeFormatter(DateTimeFormatter formatter) {
        return formatter == null ? ISO_DATE_TIME : formatter;
    }

    private static DateTimeFormatter defaultDateFormatter(DateTimeFormatter formatter) {
        return formatter == null ? ISO_DATE : formatter;
    }

    private static DateTimeFormatter defaultMonthDayFormatter(DateTimeFormatter formatter) {
        return formatter == null ? ISO_MONTHDAY : formatter;
    }

}
神奇的小尾巴:
本人郵箱:[email protected] [email protected]
[email protected] 歡迎交流,共同進步。
歡迎轉載,轉載請註明本網址。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章