基于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] 欢迎交流,共同进步。
欢迎转载,转载请注明本网址。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章