Java8日期时间:LocalDateTime / LocalDate / LocalTime

一、简介

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。

在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

  1. 非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

  2. 设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

  3. 时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

Java8引入了一套全新的日期时间处理API,新的API基于ISO标准日历系统。
Java8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

  • Local(本地) − 简化了日期时间的处理,没有时区的问题。

  • Zoned(时区) − 通过制定的时区处理日期时间。

新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

二、日期/时间初识

示例1:获取当前日期/时间
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class DateTimeAPI {
    public static void main(String[] args) {
        LocalDate.now();       // 获取当前日期 format: yyyy-MM-dd
        LocalTime.now();       // 获取当前时间 format: HH:mm:ss
        LocalDateTime.now();   // 获取当前日期时间 format: yyyy-MM-dd HH:mm:ss
    }
}

以上是对当前日期的获取,比较三者的输出(print console:)有什么区别:

2020-04-20
11:27:01.570
2020-04-20T11:27:01.570
示例2: 构造指定日期/时间

调用工厂方法LocalDate.of()创建任意日期, 该方法需要传入年、月、日做参数,返回对应的LocalDate实例。这个方法的好处是没再犯老API的设计错误,比如年度起始于1900,月份是从0开始等等.

    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2000, 1, 1);
        LocalTime time = LocalTime.of(0,0,0);
        LocalDateTime dateTime = LocalDateTime.of(date, time);
        System.out.println("千禧年=" + dateTime);

        LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0);
        System.out.println("千禧年=" + dateTime1);
    }

输出结果如下:

千禧年=2000-01-01T00:00
千禧年=2000-01-01T00:00
示例3: 获取年月日信息
    public static void main(String[] args) {
        LocalDate date = LocalDate.now();
        System.out.printf("年=%d, 月=%d, 日=%d", date.getYear(), date.getMonthValue(), date.getDayOfMonth());
        System.out.println();

        LocalTime time = LocalTime.now();
        System.out.printf("时=%d, 分=%d, 秒=%d, 毫秒=%d", time.getHour(), time.getMinute(), time.getSecond(), time.getNano());
        System.out.println();

        LocalDateTime dateTime = LocalDateTime.now();
        System.out.printf("年=%d, 月=%d, 日=%d, 时=%d, 分=%d, 秒=%d, 毫秒=%d", dateTime.getYear(), dateTime.getMonthValue(),
                dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), dateTime.getNano());
    }

输出结果如下:

=2020, 月=4, 日=20=11,=58,=1, 毫秒=800000000=2020, 月=4, 日=20,=11,=58,=1, 毫秒=801000000
示例4: 获取当前日期时间戳
    public static void main(String[] args) {

        // 获取秒数
        Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        System.out.println("时间戳 秒数: " + second);

        // 获取毫秒
        Long millisecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        System.out.println("时间戳 毫秒: " + millisecond);
    }

输出结果如下:

时间戳 秒数: 1587367802
时间戳 毫秒: 1587367802085

三、比较与计算

示例1: 日期时间计算

Java8提供了新的plusXxx() 、minsXxx()方法用于计算日期时间增量值 / 减量值,替代了原来的add()方法。新的API将返回一个全新的日期时间示例,需要使用新的对象进行接收。

    public static void main(String[] args) {

        // 时间增量
        LocalTime time = LocalTime.now();
        System.out.println("当前时间 time: " + time);
        System.out.println("增加 2小时: " + time.plusHours(2));
        System.out.println("增加 2分钟: " + time.plusMinutes(2));
        System.out.println("增加 2秒钟: " + time.plusSeconds(2));
        System.out.println("增加 2毫秒: " + time.plusNanos(2));

        // 日期增量
        LocalDate date = LocalDate.now();
        System.out.println("当前日期 date: " + date);
        System.out.println("增加 2年: " + date.plusYears(2));
        System.out.println("增加 2月: " + date.plusMonths(2));
        System.out.println("增加 2周: " + date.plusWeeks(2));
        System.out.println("增加 2天: " + date.plusDays(2));
    }

输出结果如下:

当前时间 time: 14:31:54.785
增加 2小时: 16:31:54.785
增加 2分钟: 14:33:54.785
增加 2秒钟: 14:31:56.785
增加 2毫秒: 14:31:54.785000002

当前日期 date: 2020-04-20
增加 2: 2022-04-20
增加 2: 2020-06-20
增加 2: 2020-05-04
增加 2: 2020-04-22
示例2: 日期时间比较

Java8提供了isAfter()、isBefore()用于判断当前日期时间和指定日期时间的比较。

    public static void main(String[] args) {

        LocalDateTime dateTime = LocalDateTime.now();

        LocalDateTime dateTime1 = LocalDateTime.of(2000,1,1,0,0,0,0);
        if (dateTime.isAfter(dateTime1)) {
            System.out.println("千禧年已经过去了。。。。");
        }

        LocalDateTime dateTime2 = LocalDateTime.of(2050, 1,1,0,0,0,0);
        if (dateTime.isBefore(dateTime2)) {
            System.out.println("2050年还未到来!!!!!");
        }
    }

输出结果如下:

千禧年已经过去了。。。。

2050年还未到来!!!!!
示例3: 比较两个日期是否相等
    public static void main(String[] args) {
        LocalDate now = LocalDate.now();
        LocalDate date = LocalDate.of(2018, 9, 24);
        System.out.println("日期是否相等=" + now.equals(date));
    }

输出结果如下:

日期是否相等=false
示例4: 计算时间点之间的间隔
    public static void main(String[] args) {

        // 计算时间点之间的间隔
        LocalDateTime startTime = LocalDateTime.now();
        LocalDateTime endTime = startTime.plusHours(1).plusMinutes(30);
        Duration duration = Duration.between(startTime, endTime);

        Long day = duration.toDays();
        System.out.println("间隔天数: " + day);

        Long hour = duration.toHours();
        System.out.println("间隔小时: " + hour);

        Long minute = duration.toMinutes();
        System.out.println("间隔分钟: " + minute);

        Long millis = duration.toMillis();
        System.out.println("间隔毫秒: " + millis);
    }

输出结果如下:

间隔天数: 0
间隔小时: 1
间隔分钟: 90
间隔毫秒: 5400000
示例5: 计算日期之间的间隔
    public static void main(String[] args) {

        // 计算日期之间的间隔
        LocalDate startDate = LocalDate.now();
        LocalDate endDate = startDate.plusMonths(1).plusDays(5);
        Period period = Period.between(startDate, endDate);

        int year = period.getYears();
        System.out.println("间隔年数: " + year);

        int month = period.getMonths();
        System.out.println("间隔月数: " + month);

        int day = period.getDays();
        System.out.println("间隔天数: " + day);
    }

输出结果如下:

间隔年数: 0
间隔月数: 1
间隔天数: 5

四、时区

示例1: 创建带有时区的日期时间(ZoneId)

Java 8不仅分离了日期和时间,也把时区分离出来了。现在有一系列单独的类如ZoneId来处理特定时区,ZoneDateTime类来表示某时区下的时间。

    public static void main(String[] args) {

        // 上海时间
        ZoneId shanghaiZoneId = ZoneId.of("Asia/Shanghai");
        ZonedDateTime shanghaiZonedDateTime = ZonedDateTime.now(shanghaiZoneId);

        // 东京时间
        ZoneId tokyoZoneId = ZoneId.of("Asia/Tokyo");
        ZonedDateTime tokyoZonedDateTime = ZonedDateTime.now(tokyoZoneId);

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println("上海时间: " + shanghaiZonedDateTime.format(formatter));
        System.out.println("东京时间: " + tokyoZonedDateTime.format(formatter));
    }

输出结果如下:

上海时间: 2020-04-20 15:03:18
东京时间: 2020-04-20 16:03:18
示例2: 创建带有时区的日期时间(UTC)
    public static void main(String[] args) {

        // 北京时间
        ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime zonedDateTime1 = LocalDateTime.now().atZone(ZoneId.of("UTC+8"));
        System.out.println("上海时间    : " + zonedDateTime);
        System.out.println("上海时间 UTC: " + zonedDateTime1);

        // 东京时间
        ZonedDateTime zonedDateTime2 = LocalDateTime.now().atZone(ZoneId.of("Asia/Tokyo"));
        ZonedDateTime zonedDateTime3 = LocalDateTime.now().atZone(ZoneId.of("UTC+9"));
        System.out.println("东京时间    : " + zonedDateTime2);
        System.out.println("东京时间 UTC: " + zonedDateTime3);
    }

输出结果如下:

上海时间    : 2020-04-20T15:15:58.378+08:00[Asia/Shanghai]
上海时间 UTC: 2020-04-20T15:15:58.380+08:00[UTC+08:00]
东京时间    : 2020-04-20T15:15:58.380+09:00[Asia/Tokyo]
东京时间 UTC: 2020-04-20T15:15:58.381+09:00[UTC+09:00]

五、格式化

示例1: 使用预定义格式解析与格式化日期
    public static void main(String[] args) {

        // 解析日期
        String dateText = "20200420";
        LocalDate date = LocalDate.parse(dateText, DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println("格式化后的日期: " + date);

        // 格式化日期
        dateText = date.format(DateTimeFormatter.BASIC_ISO_DATE);
        System.out.println("dateText: " + dateText);
    }

输出结果如下:

格式化后的日期: 2020-04-20
dateText: 20200420
示例2: 日期和字符串的相互转换
    public static void main(String[] args) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 日期时间转字符串
        LocalDateTime now = LocalDateTime.now();
        String dateToStr = now.format(formatter);
        System.out.println("日期转换成字符串: " + dateToStr);

        // 字符串转日期时间
        String datetimeText = "1999-12-31 23:59:59";
        LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
        System.out.println("字符串转换成日期: " + strToDate);
    }

输出结果如下:

日期转换成字符串: 2020-04-20 15:50:45
字符串转换成日期: 1999-12-31T23:59:59
示例3: 日期和时间戳的相互转换
    public static void main(String[] args) {

        // 日期转时间戳
        System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        System.out.println(LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        System.out.println(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());

        // 时间戳转成日期
        Long instantL = 1587367830000L;
        Instant instant = Instant.ofEpochMilli(instantL);
        LocalDateTime instantToDate = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
        System.out.println("时间戳转换成日期: " + instantToDate);

    }

输出结果如下:

1587371801887
1587371801888
1587371801888
时间戳转换成日期: 2020-04-20T15:30:30
示例4: 字符串和时间戳的相互转换
    public static void main(String[] args) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 字符串转时间戳
        String datetimeText = "2020-04-20 15:30:30";
        LocalDateTime strToDate = LocalDateTime.parse(datetimeText, formatter);
        System.out.println("字符串转换成日期: " + strToDate);
        Long dateToInstant = strToDate.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        System.out.println("日期转换成毫秒时间戳: " + dateToInstant);

        // 时间戳转换成字符串
        Long instant = 1587367830000L;
        LocalDateTime instantToDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(instant), ZoneId.of("+8"));
        System.out.println("时间戳转换成日期: " + instantToDate);
        String dateToStr = formatter.format(instantToDate);
        System.out.println("日期转换成字符串: " + dateToStr);

    }

输出结果如下:

字符串转换成日期: 2020-04-20T15:30:30
日期转换成毫秒时间戳: 1587367830000

时间戳转换成日期: 2020-04-20T15:30:30
日期转换成字符串: 2020-04-20 15:30:30

六、时间矫正器

TemporalAdjuster : 时间校正器。它是一个函数式接口,只有一个方法 adjustInto。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster 的实现。

Temporal 是一个接口,LocalDateTime 、LocalDate 、 LocalTime 都是它的实现类,有实现方法 with(TemporalAdjuster adjuster)。

@FunctionalInterface
public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}
示例1: 时间矫正
    public static void main(String[] args) {

        // 接下来第一个周五
        LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println("接下来第一个周五: " + nextFriday);

        // 明年的感恩节(明年11月第四个星期四)
        LocalDate date = LocalDate.now().plusYears(1).withMonth(11)
                .with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));
        System.out.println("明年感恩节: " + date);
    }

输出结果如下:

接下来第一个周五: 2020-04-24T18:55:50.841
明年感恩节: 2021-11-25

注意: TemporalAdjusters.next(DayOfWeek day) 方法返回的是 接下来第一个周五,并不是我们一般理解的 下周五,比如说:今天 2020-01-13(周一),那么返回的就是 2020-01-17 四天后的周五。

七、相关类说明

Instant 时间戳
Duration 持续时间、时间差
LocalDate 只包含日期,比如:2018-09-24
LocalTime 只包含时间,比如:10:32:10
LocalDateTime 包含日期和时间,比如:2018-09-24 10:32:10
Peroid 时间段
ZoneOffset 时区偏移量,比如:+8:00
ZonedDateTime 带时区的日期时间
Clock 时钟,可用于获取当前时间戳
java.time.format.DateTimeFormatter 时间格式化类

发布了52 篇原创文章 · 获赞 23 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/anndy_/article/details/105630440