java代码之美(17) ---Java8 LocalDateTime

Java8 LocalDateTime

在java8之前我们在处理时间的时候都是用的Date,但它其实有很明显的缺点。

1.我们也会对日期做一些操作,比如加几天、加几分,当月的最后一天等等。有些计算实现比较复杂。
2.也会用SimpleDateFormat来格式化日期。但SimpleDateFormat是线程不安全的。

所以现在一般都推荐使用LocalDateTime 它是线程安全的,并且性能更好,代码更简洁。

一、示例

新时间日期API常用、重要对象主要有下面三个:

LocalDate : 只含年月日的日期对象
LocalTime :只含时分秒的时间对象
LocalDateTime : 同时含有年月日时分秒的日期对象

下面会通过示例来一一理解它们。

1、创建实例

    public static void main(String[] args) {
        //1、获取当前日期
        LocalDate now = LocalDate.now();
        System.out.println("当前时间 = " + now);
        //输出: 当前时间 = 2020-07-06

        //2、获取指定日期(参数依次 年、月、日)
        LocalDate localDate = LocalDate.of(2020, 6, 30);
        System.out.println("年月日 = " + localDate);
        //输出: 年月日 = 2020-06-30

        //3、获取当前时间
        LocalTime localTime = LocalTime.now();
        System.out.println("localTime = " + localTime);
        //输出: localTime = 22:32:45.994

        //4、获取指定时间(参数依次 时、分、秒、纳秒
        LocalTime localTimeOf = LocalTime.of(12, 24, 12, 4444);
        System.out.println("localTimeOf = " + localTimeOf);
        //输出: localTimeOf = 12:24:12.000004444

        //5、获取当前年月日,时分秒都有的日期
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("localDateTime = " + localDateTime);
        //输出: localDateTime = 2020-07-06T22:32:45.994

        //6、获取指定年月日,时分秒都有的日期(参数依次 年、月、日、时、分)
        LocalDateTime localDateTimeOf = LocalDateTime.of(2020, 7, 30, 12, 12);
        System.out.println("localDateTimeOf = " + localDateTimeOf);
        //输出: localDateTimeOf = 2020-07-30T12:12

         //7、日期+时间 组成 包含年月日,时分秒都有的日期
        LocalDateTime of = LocalDateTime.of(LocalDate.now(), LocalTime.now());
        System.out.println("of = " + of);
        //输出: of = 2020-07-06T22:32:45.995
    }

2、计算日期和时间

日期时间的加减
  • 对于LocalDate,只有精度大于或等于日的加减,如年、月、日;
  • 对于LocalTime,只有精度小于或等于时的加减,如时、分、秒、纳秒;
  • 对于LocalDateTime,则可以进行任意精度的时间相加减;

加法操作

public static void main(String[] args) {
            
        //获取当前时间
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("当前时间 = " + localDateTime);

        //1、加1年
        LocalDateTime plusYears = localDateTime.plusYears(1L);
        System.out.println("plusYears = " + plusYears);
        //输出: plusYears = 2021-07-06T22:46:49.196
        
        //2、加1个月
        LocalDateTime plusMonths = localDateTime.plusMonths(1L);
        System.out.println("plusMonths = " + plusMonths);
        //输出: plusMonths = 2020-08-06T22:46:49.196
        
        //3、加一天
        LocalDateTime plusDays = localDateTime.plusDays(1L);
        System.out.println("plusDays = " + plusDays);
        //输出: plusDays = 2020-07-07T22:46:49.196
        
        //4、加1个小时
        LocalDateTime plusHours = localDateTime.plusHours(1L);
        System.out.println("plusHours = " + plusHours);
        //输出: plusHours = 2020-07-06T23:46:49.196
        
        //5、加10分
        LocalDateTime plusMinutes = localDateTime.plusMinutes(10L);
        System.out.println("plusMinutes = " + plusMinutes);
        //输出: plusMinutes = 2020-07-06T22:56:49.196
        
        //6、加200毫秒
        LocalDateTime plusSeconds = localDateTime.plusSeconds(200L);
        System.out.println("plusSeconds = " + plusSeconds);
        //输出: plusSeconds = 2020-07-06T22:50:09.196
    }

也可以用另外一种方式

     LocalDateTime nextMonth = localDateTime.plus(1, ChronoUnit.MONTHS);
     LocalDateTime nextYear = localDateTime.plus(1, ChronoUnit.YEARS);
     LocalDateTime nextWeek = localDateTime.plus(1, ChronoUnit.WEEKS);

减法操作

    public static void main(String[] args) {
        //获取当前时间
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("当前时间 = " + localDateTime);
        //输出: 当前时间 = 2020-07-06T22:53:38.264

        //1、减1年
        LocalDateTime minusYears = localDateTime.minusYears(1L);
        System.out.println("minusYears = " + minusYears);
        //输出: minusYears = 2019-07-06T22:53:38.264

        //2、减1个月
        LocalDateTime minusMonths = localDateTime.minusMonths(1L);
        System.out.println("minusMonths = " + minusMonths);
        //输出: minusMonths = 2020-06-06T22:53:38.264

        //3、减一天
        LocalDateTime minusDays = localDateTime.minusDays(1L);
        System.out.println("minusDays = " + minusDays);
        //输出: minusDays = 2020-07-05T22:53:38.264

        //4、减1个小时
        LocalDateTime minusHours = localDateTime.minusHours(1L);
        System.out.println("minusHours = " + minusHours);
        //输出: minusHours = 2020-07-06T21:53:38.264

        //5、减10分
        LocalDateTime minusMinutes = localDateTime.minusMinutes(10L);
        System.out.println("minusMinutes = " + minusMinutes);
        //输出: minusMinutes = 2020-07-06T22:43:38.264

        //6、减200毫秒
        LocalDateTime minusSeconds = localDateTime.minusSeconds(200L);
        System.out.println("minusSeconds = " + minusSeconds);
        //输出: minusSeconds = 2020-07-06T22:50:18.264
    }

也可以用另外一种方式

        LocalDateTime lastMonth = localDateTime.minus(1, ChronoUnit.MONTHS);
        LocalDateTime lastYear = localDateTime.minus(1, ChronoUnit.YEARS);
        LocalDateTime lastWeek = localDateTime.minus(1, ChronoUnit.WEEKS);

注意从代码中可以看到,这些 plus()minus() 方法,是不会改变原date和time的实例的,返回的是新的实例。

3、比较日期和时间

当我们想知道给定的时间或日期是在另一个时间/日期之前还是之后,我们就可以用到isBefore()isAfter()方法,如下所示:

 public static void main(String[] args) {
       public static void main(String[] args) {
        LocalDate ld1 = LocalDate.of(2020, 7, 6);
        LocalDate ld2 = LocalDate.of(2020, 7, 7);

        boolean after = ld1.isAfter(ld2);
        System.out.println("ld1是否在ld2之后 = " + after);
        //输出:  ld1是否在ld2之后 = false

        boolean before = ld1.isBefore(ld2);
        System.out.println("ld1是否在ld2之前 = " + before);
        //输出:  ld1是否在ld2之前 = true

        LocalDateTime ldt1 = LocalDateTime.of(2020, 7, 7, 12, 12);
        LocalDateTime ldt2 = LocalDateTime.of(2020, 7, 7, 14, 12);

        boolean after1 = ldt1.isAfter(ldt2);
        System.out.println("ldt1是否在ldt2之后 = " + after1);
        //输出:  ldt1是否在ldt2之后 = false

        boolean before1 = ldt1.isBefore(ldt2);
        System.out.println("ldt1是否在ldt2之后 = " + before1);
        //输出:  ldt1是否在ldt2之后 = true

        //时间相减
        Duration duration = Duration.between(ldt1, ldt2);
        //两个时间差的天数
        long days = duration.toDays();
        System.out.println("days = " + days);
        //输出: days = 0

        //小时数差
        long hours = duration.toHours();
        System.out.println("hours = " + hours);
        //输出: hours = 2

        //分钟数差
        long minutes = duration.toMinutes();
        System.out.println("minutes = " + minutes);
        //输出: minutes = 120

        //毫秒数差
        long millis = duration.toMillis();
        System.out.println("millis = " + millis);
        //输出: millis = 7200000

        //纳秒数差
        long nanos = duration.toNanos();
        System.out.println("nanos = " + nanos);
        //输出: nanos = 7200000000000
    }

4、在String和日期之间转换

在以前使用java.util.Date的时候,我们一般使用 SimpleDateFormat 去完成日期/时间和字符串的转换,在新的日期时间API中,我们使用全新的 DateTimeFormatter

如果你遵循ISO标准在日期/时间和字符串之间进行转换,那么这个事情会变得很容易,因为在 DateTimeFormatter 中,已经内置了ISO标准的格式。我们来看看代码

日期转时间

public static void main(String[] args) {
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println("ldt = " + ldt);
        //输出: ldt = 2020-07-07T18:32:34.757

        String format1 = ldt.format(DateTimeFormatter.ISO_DATE);
        System.out.println("format1 = " + format1);
        //输出: format1 = 2020-07-07

        String format2 = ldt.format(DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println("format2 = " + format2);
        //输出:  format2 = 20200707
        
        String format3 = ldt.format(DateTimeFormatter.ISO_DATE_TIME);
        System.out.println("format3 = " + format3);
        //输出: format3 = 2020-07-07T18:32:34.757

        String format4 = ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        System.out.println("format4 = " + format4);
        //输出: format4 = 2020-07-07T18:32:34.757

        String format = ldt.format(DateTimeFormatter.ofPattern("d-M-y"));
        System.out.println("format = " + format);
        //输出: format = 7-7-2020

        String format5 = ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println("format5 = " + format5);
        //输出: format5 = 2020-07-07 18:32:34
        
        String format6 = ldt.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒"));
        System.out.println("format6 = " + format6);
        //输出: format6 = 2020年07月07日18时32分34秒
      }

String转日期

public static void main(String[] args) {
        LocalDate ld = LocalDate.parse("2020-07-07");
        System.out.println("ld = " + ld);
        //输出: ld = 2020-07-07

        String str = "2020-07-07 22:24:33";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt = LocalDateTime.parse(str,dateTimeFormatter);
        System.out.println("ldt = " + ldt);
        //输出: ldt = 2020-07-07T22:24:33
      }

5、其它

有的时候,你需要进行一些更加复杂的操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,你可以使用重载版本的with方法,向其传递一个提供了更多定制化选择的TemporalAdjuster对象,更 加 灵 活 地 处 理 日 期。

日期处理

  public static void main(String[] args) {
        LocalDate date = LocalDate.parse("2020-07-07");
        //获取这个月的第一个周末的时间
        System.out.println(date.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.SUNDAY)));
        //输出: 2020-07-05

        //获取上个月的最后一周末的时间
        System.out.println(date.with(TemporalAdjusters.dayOfWeekInMonth(0, DayOfWeek.SUNDAY)));
        //输出: 2020-06-28

        //获取这个月的倒数第一个周末的时间
        System.out.println(date.with(TemporalAdjusters.dayOfWeekInMonth(-1, DayOfWeek.SUNDAY)));
        //输出: 2020-07-26

        //获取这个月的第一个周末的时间,上面的dayOfWeekInMonth更灵活,可以定义第几周
        System.out.println(date.with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY)));
        //输出: 2020-07-05

        //明年的第一天
        System.out.println(date.with(TemporalAdjusters.firstDayOfNextYear()));
        //输出: 2021-01-01
        
        //获取下个周5的时间
        System.out.println(date.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)));
        //输出: 2020-07-10
        
        //获取本月最后一天
        System.out.println(date.with(TemporalAdjusters.lastDayOfMonth()));
        //输出: 2020-07-31

        //获取本月第一天
        System.out.println(date.with(TemporalAdjusters.firstDayOfMonth()));
        //输出: 2020-07-01
      }

其它还有许多,具体查看api

时间处理

public static void main(String[] args) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //一天开始时间
        LocalDateTime todayStart = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        String format = todayStart.format(dateTimeFormatter);
        System.out.println("format = " + format);
        //输出: format = 2020-07-07 00:00:00
        
        //一天结束时间
        LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
        String format1 = todayEnd.format(dateTimeFormatter);
        System.out.println("format1 = " + format1);
        //输出: format1 = 2020-07-07 23:59:59

        //一天中午时间
        LocalDateTime todayMid = LocalDateTime.of(LocalDate.now(), LocalTime.NOON);
        String format2 = todayMid.format(dateTimeFormatter);
        System.out.println("format2 = " + format2);
        //输出: format2 = 2020-07-07 12:00:00
      }

举了这么多例子,在实际开发中应该足够用了。


二、完整示例

这里整理一个完整的时间工具类,可以在实际工作中进行运用它

public class DateTimeUtils {

    public static final String DATETIME_FORMATTER = "yyyy-MM-dd HH:mm:ss";

    public static final String DATE_FORMATTER = "yyyy-MM-dd";

    public static final String DATE_FORM = "yyyy-MM";


    //1、 ==================1、获取当天,当月最早时间和最晚时间 ========================

    /**
     * 获取当天的开始时间
     * 示例:2020-08-21T00:00
     */
    public static LocalDateTime getDayStart(LocalDateTime time) {
        return time.withHour(0).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 获取当天的结束时间
     * 示例:2020-08-21T23:59:59.999999999
     */
    public static LocalDateTime getDayEnd(LocalDateTime time) {
        return time.withHour(23).withMinute(59).withSecond(59).withNano(999999999);
    }


    /**
     * 获取一个月的开始时间
     * 示例: 2020-08-01T00:00
     */
    public static LocalDateTime getMonthStart(LocalDateTime time) {
        return time.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
    }

    /**
     * 获取一个月的结束时间
     * 示例: 2020-08-31T23:59:59.999999999
     */
    public static LocalDateTime getMonthDayEnd(LocalDateTime time) {
        return time.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
    }


    /**
     * 获取当天的开始时间
     * 示例:2020-08-21T00:00
     */
    public static LocalDateTime getDayStart(LocalDate time) {
        return LocalDateTime.of(time, LocalTime.MIN);
    }

    /**
     * 获取一天的结束时间
     * 示例:2020-08-21T23:59:59.999999999
     */
    public static LocalDateTime getDayEnd(LocalDate time) {
        return LocalDateTime.of(time, LocalTime.MAX);
    }


    /**
     * 获取当月的开始时间
     * 示例: 2020-08-01T00:00
     */
    public static LocalDateTime getMonthStart(LocalDate time) {
        return LocalDateTime.of(time.with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
    }

    /**
     * 获取当月的结束时间
     * 示例: 2020-08-31T23:59:59.999999999
     */
    public static LocalDateTime getMonthDayEnd(LocalDate time) {
        return LocalDateTime.of(time.with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MAX);
    }


    // =============================== 2、时间转字符串 =====================================

    /**
     * 获取 当前 日期时间字符串(yyyy-MM-dd HH:mm:ss)
     *
     * @return
     */
    public static String getCurrentDateTimeStr() {
        return DateTimeFormatter.ofPattern(DATETIME_FORMATTER).format(LocalDateTime.now());
    }

    /**
     * 获取 当前 日期字符串(yyyy-MM-dd)
     *
     * @return
     */
    public static String getCurrentDateStr() {
        return DateTimeFormatter.ofPattern(DATE_FORMATTER).format(LocalDateTime.now());
    }

    /**
     * 获取 当前 时间字符串(yyyy-MM)
     *
     * @return
     */
    public static String getCurrentTimeStr() {
        return DateTimeFormatter.ofPattern(DATE_FORM).format(LocalDateTime.now());
    }

    /**
     * 获取 指定 日期时间字符串(yyyy-MM-dd HH:mm:ss)
     *
     * @return
     */
    public static String getCurrentDateTimeStr(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return StringUtils.EMPTY;
        }
        return DateTimeFormatter.ofPattern(DATETIME_FORMATTER).format(localDateTime);
    }

    /**
     * 获取 指定 日期字符串(yyyy-MM-dd)
     *
     * @return
     */
    public static String getCurrentDateStr(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return StringUtils.EMPTY;
        }
        return DateTimeFormatter.ofPattern(DATE_FORMATTER).format(localDateTime);
    }

    /**
     * 获取 指定 时间字符串(yyyy-MM)
     *
     * @return
     */
    public static String getCurrentTimeStr(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return StringUtils.EMPTY;
        }
        return DateTimeFormatter.ofPattern(DATE_FORM).format(localDateTime);
    }


    /**
     * 获取 指定 日期字符串(yyyy-MM-dd)
     *
     * @return
     */
    public static String getCurrentDateStr(LocalDate localDate) {
        if (localDate == null) {
            return StringUtils.EMPTY;
        }
        return DateTimeFormatter.ofPattern(DATE_FORMATTER).format(localDate);
    }


    // =============================== 3、字符串转时间 =====================================


    /**
     * 将时间字符串转为自定义时间格式的LocalDateTime
     * 字符串格式: yyyy-MM-dd HH:mm:ss
     */
    public static LocalDateTime convertStringToLocalDateTime(String time) {
        return LocalDateTime.parse(time, DateTimeFormatter.ofPattern(DATETIME_FORMATTER));
    }

    /**
     * 字符串格式: yyyy-MM-dd
     */
    public static LocalDate convertStringToLocalDate(String time) {
        return LocalDate.parse(time, DateTimeFormatter.ofPattern(DATE_FORMATTER));
    }


    /**
     * 将时间字符串转为自定义时间格式的LocalDateTime
     *
     * @param time   需要转化的时间字符串
     * @param format 自定义的时间格式
     * @return
     */
    public static LocalDateTime convertStringToLocalDateTime(String time, String format) {
        return LocalDateTime.parse(time, DateTimeFormatter.ofPattern(format));
    }


    // ============================== 4、求两个时间的时间差 ==============================

    /**
     * 获取两个日期的 天数 差
     */
    public static long betweenLessDay(LocalDateTime startTime, LocalDateTime endTime) {
        //时间相减
        Duration duration = Duration.between(startTime, endTime);
        //两个时间差的天数
        return duration.toDays();
    }

    /**
     * 获取两个日期的 小时 差
     */
    public static long betweenLessHour(LocalDateTime startTime, LocalDateTime endTime) {
        Duration duration = Duration.between(startTime, endTime);
        return duration.toHours();
    }


    /**
     * 获取两个日期的 分钟 差
     */
    public static long betweenLessMinutes(LocalDateTime startTime, LocalDateTime endTime) {
        Duration duration = Duration.between(startTime, endTime);
        return duration.toMinutes();
    }

    /**
     * 获取两个日期的 秒 差
     */
    public static long betweenLessMillis(LocalDateTime startTime, LocalDateTime endTime) {
        Duration duration = Duration.between(startTime, endTime);
        return duration.toMillis();
    }


    // ================================ 5. long和LocalDateTime互转 ============================

    /**
     * 将long类型的timestamp转为LocalDateTime
     *
     * @param timestamp
     * @return
     */
    public static LocalDateTime convertTimestampToLocalDateTime(long timestamp) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
    }

    /**
     * 将LocalDateTime转为long类型的timestamp
     *
     * @param localDateTime
     * @return
     */
    public static long convertLocalDateTimeToTimestamp(LocalDateTime localDateTime) {
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }
}

参考

1、JDK8新特性之:Optional

2、Optional类包含的方法介绍及其示例


别人骂我胖,我会生气,因为我心里承认了我胖。别人说我矮,我就会觉得好笑,因为我心里知道我不可能矮。这就是我们为什么会对别人的攻击生气。
攻我盾者,乃我内心之矛(23)
posted on 2020-07-21 22:06  雨点的名字  阅读(3219)  评论(0编辑  收藏  举报