为了账号安全,请及时绑定邮箱和手机立即绑定

03-时间日期类

标签:
Java

若想了解java8其它新特性,请到 00-java8常用新特性文章索引 阅读。

Java8 在 java.time 包中增加了时间日期相关的API,弥补了 Java8 以前对日期、时间处理的不足。

在介绍Java8新的时间日期API前,先看看 java8 以前我们操作日期时间常用到的 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat

1 java.util.Date

1.1 java.util.Date常用API介绍

java.util.Date从JDK1.0开始就已经存在了,在开发中经常会接触到,进入 java.util.Date 的源码,我们可以看到好多方法都已经过时了。

下面我们介绍一下Date中未过时的方法。

  • Date() // Date 空参构造器,调用的构造器 Date(long date),入参是 System.currentTimeMillis()
  • Date(long date) // 带时间戳入参的构造器,初始化一个 Date对象,入参 date 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
  • long getTime() // 返回 1970年1月1日00:00:00 GMT 到当前 Date 的毫秒数
  • void setTime(long time) //设置Date的时间,入参 time 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
  • boolean before(Date when) // 测试此日期是否早于入参指定的日期。
  • boolean after(Date when) // 测试此日期是否晚于入参指定的日期。
  • int compareTo(Date anotherDate) // 当前日期与入参指定日期进行比较,当前日期小于入参日期,返回 -1;当前日期等于入参日期,返回0;当前日期大于入参日期,返回 1;
  • static Date from(Instant instant) // 将一个 Instant 实例转为 Date 实例
  • Instant toInstant() // 将一个 Date 实例转为 Instant 实例

Date源码

public class Date implements Serializable, Cloneable, Comparable<Date> {
    // Date 空参构造器,调用的构造器 Date(long date),入参是 System.currentTimeMillis()
    public Date() {
        this(System.currentTimeMillis());
    }

    // 通过时间戳,初始化一个 Date对象,入参 date 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
    public Date(long date) {
        fastTime = date;
    }
    
    // 返回 1970年1月1日00:00:00 GMT 到当前 Date 的毫秒数
    public long getTime() {
        return getTimeImpl();
    }

    //设置Date的时间,入参 time 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
    public void setTime(long time) {
        fastTime = time;
        cdate = null;
    }

    // 测试此日期是否早于入参指定的日期。
    public boolean before(Date when) {
        return getMillisOf(this) < getMillisOf(when);
    }

    // 测试此日期是否晚于入参指定的日期。
    public boolean after(Date when) {
        return getMillisOf(this) > getMillisOf(when);
    }
    
    // 当前日期与入参指定日期进行比较,
    // 当前日期小于入参日期,返回 -1;当前日期等于入参日期,返回0;当前日期大于入参日期,返回 1;
    public int compareTo(Date anotherDate) {
        long thisTime = getMillisOf(this);
        long anotherTime = getMillisOf(anotherDate);
        return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
    }

    // 将一个 Instant 实例转为 Date 实例
    public static Date from(Instant instant) {
        try {
            return new Date(instant.toEpochMilli());
        } catch (ArithmeticException ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    // 将一个 Date 实例转为 Instant 实例
    public Instant toInstant() {
        return Instant.ofEpochMilli(getTime());
    }
}

1.2 java.util.Date简单使用

public class DateCalendarTest {

    /**
     * 测试 java.util.Date
     */
    @Test
    public void testDateAPI() {
        // 1、Date()  Date 空参构造器,调用的构造器 Date(long date),入参是 System.currentTimeMillis()
        Date date1 = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        // 用时间戳入参的构造器
        // 2、Date(long date) 入参 date 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
        Date date2 = new Date(0);
        System.out.println("空参构造器:" + dateFormat.format(date1));
        System.out.println("时间戳入参构造器:" + dateFormat.format(date2));

        // 3、getTime()  // 返回 1970年1月1日00:00:00 GMT 到当前 Date 的毫秒数
        long time = date1.getTime();
        System.out.println("date1的毫秒数:" + time);

        //  setTime(long time) 入参 time 表示自1970年1月1日00:00:00 GMT(格林尼治标准时间)以来的毫秒数
        date2.setTime(System.currentTimeMillis());
        System.out.println("date2设置时间后的毫秒数:" + date2.getTime());

        // before(Date when) 测试此日期是否早于入参指定的日期。
        boolean before = date1.before(date2);
        System.out.println("data1 before date2 ? " + before);

        // after(Date when) 测试此日期是否晚于入参指定的日期。
        boolean after = date1.after(date2);
        System.out.println("data1 after date2 ? " + after);

        Instant instant = date1.toInstant();
        System.out.println("date1.toInstant(): " + instant);

        Date from = Date.from(instant);
        System.out.println("Date.from(instant): " + from.getTime());
    }
}

运行结果:

空参构造器:2020-09-03
时间戳入参构造器:1970-01-01
date1的毫秒数:1599102000943
date2设置时间后的毫秒数:1599102000949
data1 before date2 ? true
data1 after date2 ? false
date1.toInstant(): 2020-09-03T03:00:00.943Z
Date.from(instant): 1599102000943

2 java.util.Calendar

Calendar提供了操作日历相关的API。month是从0开始的,1-12月份的值是 0-11。

2.1 java.util.Calendar 常用API介绍

  • Calendar getInstance() // 使用默认时区和区域设置获取日历
  • void add(int field,int amount) //根据日历的规则,为给定的日历字段添加或减去指定的时间量。
  • Date getTime() // 返回表示当前日历时间的 Date
  • void setTime(Date date) // 通过给定的 date 设置 Calendar 的时间
  • long getTimeInMillis() // 返回 Calendar 时间对应的毫秒数
  • void setTimeInMillis(long millis) // 用给定的毫秒数设置日历的当前时间
  • int get(int field) // 返回指定日历字段的值。
  • void set(int field,int value) // 将给定的日历字段设置为给定值。
  • void set(int year,int month,int date) // 设置Calendar对象的年、月、日3个字段的值。
  • void set(int year,int month,int date,int hour,int minute,int second) // 设置Calendar对象的年、月、日、时、分、秒6个字段的值。

2.2 java.util.Calendar简单使用


    /**
     * 测试 java.util.Calendar
     */
    @Test
    public void testCalendarAPI() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cal = Calendar.getInstance();
        System.out.println("当前日历时间:" + simpleDateFormat.format(cal.getTime()));
        //年
        System.out.println("年:" + cal.get(Calendar.YEAR));
        //月:month从0开始
        System.out.println("月(month从0开始):" + cal.get(Calendar.MONTH));
        //日
        System.out.println("日:" + cal.get(Calendar.DATE));
        //设置年、月、日、时、分、秒
        cal.set(2020, 10, 1, 8, 0, 0);
        System.out.println("设置后的时间:" + simpleDateFormat.format(cal.getTime()));
        //年份+1
        cal.add(Calendar.YEAR, 1);
        System.out.println("将年份+1:" + simpleDateFormat.format(cal.getTime()));
    }

运行结果:

当前日历时间:2020-09-03
当前日历时间的毫秒数:1599103912759:2020(month从0开始):8:3
设置后的时间:2020-11-01
将年份+12021-11-01

3 SimpleDateFormat

SimpleDateFormat是常用的日期格式化类,将日期字符串解析成Date实例,或者Date格式化为某种模式的字符串。

3.1 SimpleDateFormat 常用API

  • SimpleDateFormat(String pattern) // 构造器,入参是时间字符串对应的模式 pattern
  • String format(Date date) // 把给定的 date 按照 pattern 指定的模式,格式化为字符串
  • Date parse(String source) // 把给定的日期字符串 source 解析成Date实例

SimpleDateFormat定义 pattern 时,各个字母分别对应的含义

Date and Time Pattern:pattern定义的样例
Result:pattern 对应时间字符串的样式

3.2 SimpleDateFormat 简单使用

    /**
     * 测试 SimpleDateFormat
     */
    @Test
    public void testSimpleDateFormatAPI() throws ParseException {
        //定义时间字符串的pattern
        String pattern = "yyyy-MM-dd HH:mm:ss";
        SimpleDateFormat format = new SimpleDateFormat(pattern);

        //把字符串解析为Date
        Date date = format.parse("2020-09-03 12:30:29");
        System.out.println("String to Date:"+date);

        //打印 Date 的时间字符串
        Date now = new Date();
        System.out.println("Date to String:"+format.format(now));
    }

运行结果:

String to Date:Thu Sep 03 12:30:29 CST 2020
Date to String:2020-09-03 12:57:16

4 Date、Calendar、SimpleDateFormat不足

  • Calendar的month从0开始计算,开发者很容易忽略;
  • Date 和 Calendar 所提供用于操作时间和日期的API太少了,运算比较麻烦,比如求当前日期的下周六;
  • SimpleDateFormate 也是线程不安全的。

4.1 演示 SimpleDateFormate 线程不安全


    /**
     * 演示SimpleDateFormat类线程不安全
     */
    @Test
    public void testSimpleDateFormatUnSafe() throws InterruptedException {
        // 闭锁:主线程等待所有子线程运行完后,再继续往下运行
        // 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
        CountDownLatch latch = new CountDownLatch(5);

        //定义时间字符串的pattern
        String pattern = "yyyy-MM-dd HH:mm:ss";
        SimpleDateFormat format = new SimpleDateFormat(pattern);

        for (int i = 1; i <= 5; i++) {
            int millis = i * 1000;
            new Thread(() -> {
                Date date = new Date(millis);
                String strDateTime = format.format(date);
                System.out.println("当前线程" + Thread.currentThread().getName() + " 对应的时间字符串:" + strDateTime);
                // 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
                latch.countDown();//闭锁数值减一
            }).start();
        }
        // 主线程阻塞,等待所有子线程运行完后,再继续往下运行
        // 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
        latch.await();
    }

因为 SimpleDateFormat 线程不安全,运行结果:

当前线程Thread-0 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-1 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-3 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-4 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-2 对应的时间字符串:1970-01-01 08:00:03

线程安全的话,正确的结果应该是:

当前线程Thread-2 对应的时间字符串:1970-01-01 08:00:03
当前线程Thread-4 对应的时间字符串:1970-01-01 08:00:05
当前线程Thread-0 对应的时间字符串:1970-01-01 08:00:01
当前线程Thread-1 对应的时间字符串:1970-01-01 08:00:02
当前线程Thread-3 对应的时间字符串:1970-01-01 08:00:04

5 Java8 提供新时间日期API

5.1 时间日期相关API:

LocalDate、LocalTime、LocalDateTime、Instant 的实例,都是不可变的对象,就算进行一些时间调整操作,如 plus/minus操作,返回的实例都是一个新的对象。
所以这些类的操作都是线程安全的

LocalDate、LocalDateTime 获取月份返回的是 Month 的枚举,通过 Month 枚举的getValue 返回 1-12 的数值表示 1-12 月份。

  • LocalDate //日期类,不包含时间
  • LocalTime //时间类,不包含日期
  • LocalDateTime // 日期时间类,包含日期和时间
  • Instant // 某个时间点,可以获取到毫秒数,能够与 LocalDateTime 相互转换

5.2 两个时间或日期之差

  • Duration // 两个时间 Instance 之间的间隔
  • Period // 两个日期之间的间隔

5.3 日期操作类

  • TemporalAdjuster // 调整时间或日期,如:调整到这个月的第一个星期一,调整后生成一个新的时间或日期实例
  • TemporalAdjusters //好多静态方法,用于提供常用的 TemporalAdjuster 实例

5.4 日期或时间解析和格式化

  • DateTimeFormatter // 线程安全的日期或时间解析和格式化

5.5 时区

  • ZonedDateTime // 带时区的日期时间
  • ZoneId // 时区对应的ID,格式为:{区域}/{城市},如:Asia/Shanghai

下面,我们对上门的类详细介绍。

6 LocalDate 日期类

LocalDate 方法 用途
static LocalDate now()
static LocalDate of(int year, int month, int dayOfMonth)
从默认时区的系统时钟获取当前日期。
从给定的年、月和日获取LocalDate的实例。
static LocalDate parse(CharSequence text, DateTimeFormatter formatter) 通过给定的字符串和 DateTimeFormatter 解析成 LocalDate
LocalDateTime atTime(int hour, int minute, int second) 这将返回一个 LocalDateTime ,日期为当前日期,时间为指定的小时、分钟和秒
long getLong(TemporalField field) 返回指定字段的值,
TemporalField接口的实现类 ChronoField 是个枚举类,定义了很多常用的时间字段TemporalField,LocalDate 中只能使用:,
DAY\_OF\_WEEK ,
ALIGNED\_DAY\_OF\_WEEK\_IN\_MONTH,
ALIGNED\_DAY\_OF\_WEEK\_IN\_YEAR,
DAY\_OF\_MONTH,
DAY\_OF\_YEAR,
EPOCH\_DAY,
ALIGNED\_WEEK\_OF\_MONTH,
ALIGNED\_WEEK\_OF\_YEAR,
MONTH\_OF\_YEAR,
PROLEPTIC\_MONTH,
YEAR\_OF\_ERA,
YEAR,
ERA
int getDayOfMonth()
DayOfWeek getDayOfWeek()
int getDayOfYear()
返回月份中第几天
返回 DayOfWeek 枚举,表示周几
返回年份中第几天
Month getMonth()
int getMonthValue()
返回 Month 枚举,表示月份
返回月份对应的数值 1-12
int getYear() 返回年份
LocalDate minusDays(long daysToSubtract)
LocalDate minusMonths(long monthsToSubtract)
LocalDate minusWeeks(long weeksToSubtract)
LocalDate minusYears(long yearsToSubtract)
当前日期减去多少天,返回新的LocalDate实例
当前日期减去多少月,返回新的LocalDate实例
当前日期减去多少周,返回新的LocalDate实例
当前日期减去多少年,返回新的LocalDate实例
LocalDate plusDays(long daysToAdd)
LocalDate plusMonths(long monthsToAdd)
LocalDate plusWeeks(long weeksToAdd)
LocalDate plusYears(long yearsToAdd)
当前日期加上多少天,返回新的LocalDate实例
当前日期加上多少月,返回新的LocalDate实例
当前日期加上多少周,返回新的LocalDate实例
当前日期加上多少年,返回新的LocalDate实例
LocalDate withDayOfMonth(int dayOfMonth)
LocalDate withDayOfYear(int dayOfYear)
LocalDate withMonth(int month)
LocalDate withYear(int year)
调整月份中的天,返回新的LocalDate实例
调整年份中的年,返回新的LocalDate实例
跳整日期中的月份,返回新的LocalDate实例
调整日期中的年份,返回新的LocalDate实例
boolean isLeapYear() 如果年份是闰年,则为true,否则为false
boolean isAfter(ChronoLocalDate other) 如果晚于给定的日期,则为true,否则为false
boolean isBefore(ChronoLocalDate other) 如果早于给定的日期,则为true,否则为false

6.1 LocalDate 简单使用


    /**
     * 测试 LocalDate
     */
    @Test
    public void testLocalDate() {
        // 从给定的年、月和日获取LocalDate的实例。
        LocalDate ldOf = LocalDate.of(2020,9,3);
        System.out.println("从给定的年、月和日获取LocalDate的实例: "+ldOf);
        // 从默认时区的系统时钟获取当前日期
        LocalDate ldNow = LocalDate.now();
        System.out.println("从默认时区的系统时钟获取当前日期: "+ldNow);
        // 返回一个 LocalDateTime ,日期为当前日期,时间为指定的小时、分钟和秒
        LocalDateTime localDateTime = ldNow.atTime(1, 2, 30);
        System.out.println("返回一个 LocalDateTime日期为当前日期,时间为指定的小时、分钟和秒:" + localDateTime);

        // 返回月份中第几天
        System.out.println("返回月份中第几天: "+ldNow.getDayOfMonth());
        // 返回 DayOfWeek 枚举,表示周几
        System.out.println("返回 DayOfWeek 枚举,表示周几: "+ldNow.getDayOfWeek());
        // 返回年份中第几天
        System.out.println("返回年份中第几天: "+ldNow.getDayOfYear());

        // 返回 Month 枚举,表示月份
        System.out.println("返回 Month 枚举,表示月份: " + ldNow.getMonth());
        // 返回月份对应的数值 1-12
        System.out.println("返回月份对应的数值 1-12 : " + ldNow.getMonthValue());

        // 当前日期减去多少天,返回新的LocalDate实例
        System.out.println("当前日期减去5天,返回新的LocalDate实例: " + ldNow.minusDays(5));

        // 当前日期减去多少月,返回新的LocalDate实例
        System.out.println("当前日期减去1月,返回新的LocalDate实例: " + ldNow.minusMonths(1));

        // 当前日期加上多少周,返回新的LocalDate实例
        System.out.println("当前日期加上1周,返回新的LocalDate实例: " + ldNow.plusWeeks(1));
        // 当前日期加上多少年,返回新的LocalDate实例
        System.out.println("当前日期加上2年,返回新的LocalDate实例: " + ldNow.plusYears(2));

        // 如果年份是闰年,则为true,否则为false
        System.out.println("今年是否是闰年?" + ldNow.isLeapYear());

        // 如果早于给定的日期,则为true,否则为false
        System.out.println("当前日期是否早于2020-9-3? " + ldNow.isBefore(ldOf));
        // 如果晚于给定的日期,则为true,否则为false
        System.out.println("当前日期是否晚于2020-9-3? " + ldNow.isAfter(ldOf));
    }

运行结果:

从给定的年、月和日获取LocalDate的实例: 2020-09-03
从默认时区的系统时钟获取当前日期: 2020-09-04
返回一个 LocalDateTime日期为当前日期,时间为指定的小时、分钟和秒:2020-09-04T01:02:30
返回月份中第几天: 4
返回 DayOfWeek 枚举,表示周几: FRIDAY
返回年份中第几天: 248
返回 Month 枚举,表示月份: SEPTEMBER
返回月份对应的数值 1-12 : 9
当前日期减去5天,返回新的LocalDate实例: 2020-08-30
当前日期减去1月,返回新的LocalDate实例: 2020-08-04
当前日期加上1周,返回新的LocalDate实例: 2020-09-11
当前日期加上2年,返回新的LocalDate实例: 2022-09-04
今年是否是闰年?true
当前日期是否早于2020-9-3false
当前日期是否晚于2020-9-3true

7 LocalTime 时间类

因为篇幅问题,只列出常用的API。

LocalTime 方法 用途
static LocalTime now()
static LocalTime of(int hour, int minute, int second)
static LocalTime of(int hour, int minute, int second, int nanoOfSecond)
从默认时区的系统时钟获取当前时间。
从给定的hour, minute 和 second获取LocalTime的实例。
从给定的hour, minute, second 和 nanosecond(纳秒)获取LocalTime的实例。
static LocalTime parse(CharSequence text, DateTimeFormatter formatter) 通过给定的字符串和 DateTimeFormatter 解析成 LocalTime
LocalDateTime atDate(LocalDate date) 这将返回一个 LocalDateTime ,日期为给定的LocalDate,时间为指定的当前时间
long getLong(TemporalField field) 返回指定字段的值,
TemporalField接口的实现类 ChronoField 是个枚举类,定义了很多常用的时间字段TemporalField,LocalTime 中只能使用:
NANO\_OF\_SECOND,
NANO\_OF\_DAY,
MICRO\_OF\_SECOND,
MICRO\_OF\_DAY,
MILLI\_OF\_SECOND,
MILLI\_OF\_DAY,
SECOND\_OF\_MINUTE,
SECOND\_OF\_DAY,
MINUTE\_OF\_HOUR,
MINUTE\_OF\_DAY,
HOUR\_OF\_AMPM,
CLOCK\_HOUR\_OF\_AMPM,
HOUR\_OF\_DAY, hour,
CLOCK\_HOUR\_OF\_DAY,
AMPM\_OF\_DAY
int getHour()
int getMinute()
int getSecond()
getNano()
返回小时
返回分钟
返回秒
返回纳秒
LocalTime minusHours(long hoursToSubtract)
LocalTime minusMinutes(long minutesToSubtract)
LocalTime minusSeconds(longsecondsToSubtract)
LocalTime minusNanos(long nanosToSubtract)
当前时间减去多少小时,返回新的LocalTime实例
当前时间减去多少分钟,返回新的LocalTime实例
当前时间减去多少秒,返回新的LocalTime实例
当前时间减去多少纳秒,返回新的LocalTime实例
LocalTime plusHours(long hoursToAdd)
LocalTime plusMinutes(long minutesToAdd)
LocalTime plusSeconds(long secondstoAdd)
LocalTime plusNanos(long nanosToAdd)
当前时间加上多少小时,返回新的LocalTime实例
当前时间加上多少分钟,返回新的LocalTime实例
当前时间加上多少秒,返回新的LocalTime实例
当前时间加上多少纳秒,返回新的LocalTime实例
LocalTime withHour(int hour)
LocalTime withMinute(int minute)
LocalTime withSecond(int second)
LocalTime withNano(int nanoOfSecond)
调整小时,返回新的LocalTime实例
调整分钟,返回新的LocalTime实例
跳整秒数,返回新的LocalTime实例
调整纳秒,返回新的LocalTime实例
boolean isAfter(LocalTime other) 如果晚于给定的时间,则为true,否则为false
boolean isBefore(LocalTime other) 如果早于给定的时间,则为true,否则为false

7.1 LocalTime的简单使用

    /**
     * 测试 LocalTime
     */
    @Test
    public void testLocalTime() {
        // 从给定的hour, minute 和 second获取LocalTime的实例。
        LocalTime ltOf = LocalTime.of(1,5,23);
        System.out.println("从给定的hour, minute 和 second获取LocalTime的实例: "+ltOf);
        // 从默认时区的系统时钟获取当前时间
        LocalTime ltNow = LocalTime.now();
        System.out.println("从默认时区的系统时钟获取当前时间: "+ltNow);
        // 这将返回一个 LocalDateTime ,日期为给定的LocalDate,时间为指定的当前时间
        LocalDateTime localDateTime = ltNow.atDate(LocalDate.now());
        System.out.println("这将返回一个 LocalDateTime ,日期为今天(2020-09-04),时间为指定的当前时间:" + localDateTime);

        // 返回小时
        System.out.println("返回小时: "+ltNow.getHour());
        // 返回分钟
        System.out.println("返回分钟: "+ltNow.getMinute());
        // 返回秒
        System.out.println("返回秒: "+ltNow.getSecond());

        // 返回纳秒
        System.out.println("返回纳秒: " + ltNow.getNano());

        // 当前时间减去多少小时,返回新的LocalTime实例
        System.out.println("当前时间减去1小时,返回新的LocalTime实例: " + ltNow.minusHours(1));

        // 当前时间减去多少分钟,返回新的LocalTime实例
        System.out.println("当前时间减去20分钟,返回新的LocalTime实例: " + ltNow.minusMinutes(20));

        // 当前时间减去多少秒,返回新的LocalTime实例
        System.out.println("当前时间加上10秒,返回新的LocalTime实例: " + ltNow.plusSeconds(10));
        // 当前时间减去多少纳秒,返回新的LocalTime实例
        System.out.println("当前时间加上999纳秒,返回新的LocalTime实例: " + ltNow.plusNanos(999));

        // 调整小时,返回新的LocalTime实例
        System.out.println("调整小时到3时,返回新的LocalTime实例:" + ltNow.withHour(3));
    }

运行结果:

从给定的hour, minute 和 second获取LocalTime的实例: 01:05:23
从默认时区的系统时钟获取当前时间: 01:24:16.666
这将返回一个 LocalDateTime ,日期为今天(2020-09-04),时间为指定的当前时间:2020-09-04T01:24:16.666
返回小时: 1
返回分钟: 24
返回秒: 16
返回纳秒: 666000000
当前时间减去1小时,返回新的LocalTime实例: 00:24:16.666
当前时间减去20分钟,返回新的LocalTime实例: 01:04:16.666
当前时间加上10秒,返回新的LocalTime实例: 01:24:26.666
当前时间加上999纳秒,返回新的LocalTime实例: 01:24:16.666000999
调整小时到3时,返回新的LocalTime实例:03:24:16.666

8 LocalDateTime 时间日期类

LocalDateTime 表示日期时间,可以理解为包含了 LocalDate 和 LocalTime。

LocalDateTime中的API 与 LocalDate 和 LocalTime的相似,同样包含 :

  • now(),
  • ofXX(xx),
  • minusXX(xx),
  • plusXX(xx),
  • withXX(xx)方法,
  • isAfter(xx),
  • isBefore(xx)
  • static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) // 解析时间字符串成LocalDateTime
  • long getLong(TemporalField field) // 返回指定字段的值

8.1 LocalDateTime简单使用


    /**
     * 测试 LocalDateTime
     */
    @Test
    public void testLocalDateTime() {
        // 从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例。
        LocalDateTime ldtOf = LocalDateTime.of(2020, 9, 3, 1, 5, 23);
        System.out.println("从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: " + ldtOf);
        // 从默认时区的系统时钟获取当前日期时间 LocalDateTime
        LocalDateTime ldtNow = LocalDateTime.now();
        System.out.println("从默认时区的系统时钟获取当前日期时间 LocalDateTime: " + ldtNow);
        // 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区
        ZonedDateTime zonedDateTime = ldtNow.atZone(ZoneId.systemDefault());
        System.out.println("返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:" + zonedDateTime);
        // 通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例
        ZonedDateTime shZonedDateTime = ldtNow.atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println("通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:" + shZonedDateTime);

        //返回周几
        long dayOfWeek = ldtNow.getLong(ChronoField.DAY\\_OF\\_WEEK);
        System.out.println("返回周几:" + dayOfWeek);

        //日期在天中的毫秒数
        long milliOfDay = ldtNow.getLong(ChronoField.MILLI\\_OF\\_DAY);
        System.out.println("日期在天中的毫秒数:" + milliOfDay);
        LocalDateTime endExclusive = ldtNow.withHour(0).withMinute(0).withSecond(0).withNano(0);
        System.out.println("日期在天中的毫秒数 Duration:" + Duration.between(endExclusive, ldtNow).toMillis());

        //当前日期是年份中的第几天
        long dayOfYear = ldtNow.getLong(ChronoField.DAY\\_OF\\_YEAR);
        System.out.println("getLong(TemporalField field)方式获取当前日期是年份中的第几天:" + dayOfYear);


        // 返回月份中第几天
        System.out.println("返回月份中第几天: " + ldtNow.getDayOfMonth());
        // 返回 DayOfWeek 枚举,表示周几
        System.out.println("返回 DayOfWeek 枚举,表示周几: " + ldtNow.getDayOfWeek());
        // 返回年份中第几天
        System.out.println("返回年份中第几天: " + ldtNow.getDayOfYear());

        // 返回 Month 枚举,表示月份
        System.out.println("返回 Month 枚举,表示月份: " + ldtNow.getMonth());
        // 返回月份对应的数值 1-12
        System.out.println("返回月份对应的数值 1-12 : " + ldtNow.getMonthValue());

        // 返回小时
        System.out.println("返回小时: " + ldtNow.getHour());
        // 返回分钟
        System.out.println("返回分钟: " + ldtNow.getMinute());
        // 返回秒
        System.out.println("返回秒: " + ldtNow.getSecond());
        // 返回纳秒
        System.out.println("返回纳秒: " + ldtNow.getNano());

        // 当前日期减去多少天,返回新的LocalDateTime实例
        System.out.println("当前日期减去5天,返回新的LocalDateTime实例: " + ldtNow.minusDays(5));

        // 当前日期减去多少月,返回新的LocalDateTime实例
        System.out.println("当前日期减去1月,返回新的LocalDateTime实例: " + ldtNow.minusMonths(1));

        // 当前日期加上多少周,返回新的LocalDateTime实例
        System.out.println("当前日期加上1周,返回新的LocalDateTime实例: " + ldtNow.plusWeeks(1));
        // 当前日期加上多少年,返回新的LocalDateTime实例
        System.out.println("当前日期加上2年,返回新的LocalDateTime实例: " + ldtNow.plusYears(2));


        // 当前日期时间减去多少小时,返回新的LocalDateTime实例
        System.out.println("当前日期时间减去1小时,返回新的LocalDateTime实例: " + ldtNow.minusHours(1));

        // 当前日期时间减去多少分钟,返回新的LocalDateTime实例
        System.out.println("当前日期时间减去20分钟,返回新的LocalDateTime实例: " + ldtNow.minusMinutes(20));

        // 当前日期时间减去多少秒,返回新的LocalDateTime实例
        System.out.println("当前日期时间加上10秒,返回新的LocalDateTime实例: " + ldtNow.plusSeconds(10));
        // 当前日期时间减去多少纳秒,返回新的LocalDateTime实例
        System.out.println("当前日期时间加上999纳秒,返回新的LocalDateTime实例: " + ldtNow.plusNanos(999));

        // 如果早于给定的日期,则为true,否则为false
        System.out.println("当前日期是否早于2020-9-3? " + ldtNow.isBefore(ldtOf));
        // 如果晚于给定的日期,则为true,否则为false
        System.out.println("当前日期是否晚于2020-9-3? " + ldtNow.isAfter(ldtOf));
        // 调整小时,返回新的LocalDateTime实例
        System.out.println("调整月份到8月,返回新的LocalDateTime实例:" + ldtNow.withMonth(8));

        // with(TemporalAdjuster adjuster) 通过TemporalAdjuster去调整到对应的时间点,返回新的Instant实例
        //  调整当前日期到下个月第一天
        LocalDateTime firstDayOfNextMonth = ldtNow.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("通过TemporalAdjuster去调整到到下个月第一天:" + firstDayOfNextMonth);

        // LocalDateTime 转为 LocalDate
        LocalDate localDate = ldtNow.toLocalDate();
        System.out.println("LocalDateTime 转为 LocalDate: " + localDate);

        // LocalDateTime 转为 LocalTime
        LocalTime localTime = ldtNow.toLocalTime();
        System.out.println("LocalDateTime 转为 LocalTime: " + localTime);

        // LocalDateTime 转为 Instant
        Instant instant = ldtNow.atZone(ZoneId.systemDefault()).toInstant();
        // instant打印的是格林威治时间,会小8小时,但是毫秒数(时间戳)和当前时区的毫秒数是一样的
        System.out.println("LocalDateTime 转为 Instant: " + instant);
    }

运行结果:

从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: 2020-09-03T01:05:23
从默认时区的系统时钟获取当前日期时间 LocalDateTime: 2020-09-04T14:10:08.476
返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:2020-09-04T14:10:08.476+08:00[Asia/Shanghai]
通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:2020-09-04T14:10:08.476+08:00[Asia/Shanghai]
返回周几:5
日期在天中的毫秒数:51008476
日期在天中的毫秒数 Duration:51008476
getLong(TemporalField field)方式获取当前日期是年份中的第几天:248
返回月份中第几天: 4
返回 DayOfWeek 枚举,表示周几: FRIDAY
返回年份中第几天: 248
返回 Month 枚举,表示月份: SEPTEMBER
返回月份对应的数值 1-12 : 9
返回小时: 14
返回分钟: 10
返回秒: 8
返回纳秒: 476000000
当前日期减去5天,返回新的LocalDateTime实例: 2020-08-30T14:10:08.476
当前日期减去1月,返回新的LocalDateTime实例: 2020-08-04T14:10:08.476
当前日期加上1周,返回新的LocalDateTime实例: 2020-09-11T14:10:08.476
当前日期加上2年,返回新的LocalDateTime实例: 2022-09-04T14:10:08.476
当前日期时间减去1小时,返回新的LocalDateTime实例: 2020-09-04T13:10:08.476
当前日期时间减去20分钟,返回新的LocalDateTime实例: 2020-09-04T13:50:08.476
当前日期时间加上10秒,返回新的LocalDateTime实例: 2020-09-04T14:10:18.476
当前日期时间加上999纳秒,返回新的LocalDateTime实例: 2020-09-04T14:10:08.476000999
当前日期是否早于2020-9-3false
当前日期是否晚于2020-9-3true
调整月份到8月,返回新的LocalDateTime实例:2020-08-04T14:10:08.476
通过TemporalAdjuster去调整到到下个月第一天:2020-10-01T14:10:08.476
LocalDateTime 转为 LocalDate: 2020-09-04
LocalDateTime 转为 LocalTime: 14:10:08.476
LocalDateTime 转为 Instant: 2020-09-04T06:10:08.476Z

9 Instant 某个时间点

Instant 某个时间点,可以获取到毫秒数,能够与 LocalDateTime 相互转换

Instant 方法 用途
static Instant now() 返回当前时间对应的 Instant 实例
static Instant ofEpochMilli(long epochMilli) 通过给定的毫秒数(milliseconds from 1970-01-01T00:00:00Z),返回 Instant 实例
ZonedDateTime atZone(ZoneId zone) 将当前 Instant 和给定的时区ID ZoneId 结合返回 ZonedDateTime
int get(TemporalField field) 返回指定字段的值,
TemporalField接口的实现类 ChronoField 是个枚举类,定义了很多常用的时间字段TemporalField,Instant中只能使用:
NANO\_OF\_SECOND,
MICRO\_OF\_SECOND,
MILLI\_OF\_SECOND,
INSTANT\_SECOND,
举例解释常用字段的作用:
MILLI\_OF\_SECOND: 秒内的毫秒数,从0到999
INSTANT\_SECONDS: 1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数
boolean isAfter(Instant otherInstant) 如果当前 Instant 晚于给定的Instant,则为true,否则为false
boolean isBefore(Instant otherInstant) 如果当前 Instant 早于给定的Instant,则为true,否则为false
Instant minus(long amountToSubtract, TemporalUnit unit) 减去给定的时间量,返回新的Instant实例
Instant plus(long amountToAdd, TemporalUnit unit) 增加给定的时间量,返回新的Instant实例
Instant with(TemporalField field, long newValue) 调整给定时间属性的值 ,返回新的Instant实例
Instant with(TemporalAdjuster adjuster) 通过TemporalAdjuster去调整到对应的时间点,返回新的Instant实例
long toEpochMilli() 返回当前时间点对应的毫秒数,也就是时间戳

9.1 Instant 简单使用

   /**
     * 测试 Instant
     * 注意:Instant.toString() 调用的是 DateTimeFormatter.ISO\\_INSTANT.format(this)
     *       instant打印的是格林威治时间,会小8小时,但是毫秒数(时间戳)和当前时区的毫秒数是一样的
     */
    @Test
    public void testInstant() {
        // 返回当前时间对应的 Instant 实例
        Instant instantNow = Instant.now();
        System.out.println("返回当前时间对应的 Instant 实例: " + instantNow);
        // 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区
        ZonedDateTime zonedDateTime = instantNow.atZone(ZoneId.systemDefault());
        System.out.println("返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:" + zonedDateTime);
        // 通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例
        ZonedDateTime shZonedDateTime = instantNow.atZone(ZoneId.of("Asia/Shanghai"));
        System.out.println("通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:" + shZonedDateTime);

        //秒内的毫秒数,从0到999
        long milliOfSecond = instantNow.getLong(ChronoField.MILLI\\_OF\\_SECOND);
        System.out.println("秒内的毫秒数,从0到999:" + milliOfSecond);

        //1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数
        long instantSeconds = instantNow.getLong(ChronoField.INSTANT\\_SECONDS);
        System.out.println("1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数:" + instantSeconds);

        //1970-01-01T00:00Z(ISO)开始到当前Instant实例的毫秒数
        long milli = instantNow.toEpochMilli();
        System.out.println("开始到当前Instant实例的毫秒数:" + milli);

        // 当前日期减去多少天,返回新的Instant实例
        System.out.println("当前日期减去5天,返回新的Instant实例: " + instantNow.minus(5, ChronoUnit.DAYS));

        // 当前日期减去1小时
        System.out.println("当前日期减去1小时,返回新的Instant实例: " + instantNow.minus(1, ChronoUnit.HOURS));

        // 当前日期时间减去多少秒,返回新的Instant实例
        System.out.println("当前日期时间加上10秒,返回新的Instant实例: " + instantNow.plusSeconds(10));
        // 当前日期时间减去多少纳秒,返回新的Instant实例
        System.out.println("当前日期时间加上999纳秒,返回新的Instant实例: " + instantNow.plusNanos(999));

        // 2020-9-3 对应的 Instant
        Instant otherInstant = LocalDateTime.of(2020, 9, 3, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant();
        // 如果早于给定的日期,则为true,否则为false
        System.out.println("当前日期是否早于2020-9-3? " + instantNow.isBefore(otherInstant));
        // 如果晚于给定的日期,则为true,否则为false
        System.out.println("当前日期是否晚于2020-9-3? " + instantNow.isAfter(otherInstant));

        // with(ChronoField.INSTANT\\_SECONDS, 0)):设置1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数
        // 也就是调整到 1970-01-01T00:00Z(ISO)
        System.out.println("调整到 1970-01-01T00:00Z(ISO):" + instantNow.with(ChronoField.INSTANT\\_SECONDS, 0).with(ChronoField.NANO\\_OF\\_SECOND,0));

        // Instant 转为 LocalDateTime
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instantNow, ZoneId.systemDefault());
        System.out.println("Instant 转为 LocalDateTime: "+localDateTime);

    }
 

运行结果:

返回当前时间对应的 Instant 实例: 2020-09-04T06:06:10.851Z
返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区:2020-09-04T14:06:10.851+08:00[Asia/Shanghai]
通过给定 Asia/Shanghai 时区,返回 ZonedDateTime 实例:2020-09-04T14:06:10.851+08:00[Asia/Shanghai]
秒内的毫秒数,从0999851
1970-01-01T00:00Z(ISO)开始到当前Instant实例的秒数:1599199570
开始到当前Instant实例的毫秒数:1599199570851
当前日期减去5天,返回新的Instant实例: 2020-08-30T06:06:10.851Z
当前日期减去1小时,返回新的Instant实例: 2020-09-04T05:06:10.851Z
当前日期时间加上10秒,返回新的Instant实例: 2020-09-04T06:06:20.851Z
当前日期时间加上999纳秒,返回新的Instant实例: 2020-09-04T06:06:10.851000999Z
当前日期是否早于2020-9-3false
当前日期是否晚于2020-9-3true
调整到 1970-01-01T00:00Z(ISO):1970-01-01T00:00:00Z
Instant 转为 LocalDateTime: 2020-09-04T14:06:10.851

10 Duration 时间间隔

Duration:时间间隔,表示两个时间点之间的时间间隔长度。

Duration 方法 用途
static Duration between(Temporal startInclusive, Temporal endExclusive) 返回两个时间点之间的间隔Duration。
参数可以是:LocalTime、LocalDateTime、Instant
注意:LocalDate不可以作为入参
static Instant ofEpochMilli(long epochMilli) 通过给定的毫秒数(milliseconds from 1970-01-01T00:00:00Z),返回 Instant 实例
of(long amount, TemporalUnit unit)
static Duration ofDays(long days)
static Duration ofHours(long hours)
static Duration ofMinutes(long minutes)
static Duration ofSeconds(long seconds)
static Duration ofSeconds(long seconds)
通过给定时间单位 TemporalUnit 的时间长度 amount,返回Duration实例
Duration minus(long amountToSubtract, TemporalUnit unit) 减去给定的时间量,返回新的Duration实例
Duration plus(long amountToAdd, TemporalUnit unit) 增加给定的时间量,返回新的Duration实例
long toDays()
long toHours()
long toMinutes()
long toMillis()
Duration 时间长度转为 天数/小时数/分钟数/毫秒数

10.1 Duration 简单使用

    /**
     * 测试 Duration
     */
    @Test
    public void testDuration() {
        // 从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例。
        LocalDateTime ldtOf = LocalDateTime.of(2020, 9, 4, 10, 0, 0);
        System.out.println("从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: " + ldtOf);
        // 从默认时区的系统时钟获取当前日期时间 LocalDateTime
        LocalDateTime ldtNow = LocalDateTime.now();
        System.out.println("从默认时区的系统时钟获取当前日期时间 LocalDateTime: " + ldtNow);

        // 返回两个时间点之间的间隔Duration
//        参数可以是:LocalTime、LocalDateTime、Instant
//        注意:LocalDate不可以作为入参
        Duration duration = Duration.between(ldtOf, ldtNow);
        // 打印 Duration 时间长度转为 天数/小时数/分钟数/毫秒数
        System.out.println("Duration 时间间隔天数:" + duration.toDays());
        System.out.println("Duration 时间间隔小时数:" + duration.toHours());
        System.out.println("Duration 时间间隔分钟数:" + duration.toMinutes());
        System.out.println("Duration 时间间隔毫秒数:" + duration.toMillis());
    }

运行结果:

从给定的year, month, day, hour, minute 和 second 获取LocalDateTime的实例: 2020-09-04T10:00
从默认时区的系统时钟获取当前日期时间 LocalDateTime: 2020-09-04T14:24:22.692
Duration 时间间隔天数:0
Duration 时间间隔小时数:4
Duration 时间间隔分钟数:264
Duration 时间间隔毫秒数:15862692

11 Period 日期间隔

Period 方法 用途
static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) 返回两个日期之间的间隔 Period,入参是:LocalDate 类型
static Period of(int years, int months, int days) 通过给定的 years, months 和 days 生成Period
Period minusDays(long daysToSubtract)
Period minusMonths(long monthsToSubtract)
Period minusYears(long yearsToSubtract)
period 实例中 days字段- daysToSubtract\ months字段-daysToSubtract\ years字段-yearsToSubtract
Period plusDays(long daysToAdd)
Period plusMonths(long monthsToAdd)
Period plusYears(long yearsToAdd)
period 实例中 days字段+ daysToSubtract\ months字段+daysToSubtract\ years字段+yearsToSubtract
long getDays()
int getMonths()
int getYears()
获 period 实例的 days/months/years 字段值,这里不是间隔的长度,只是字段值,看测试用例会更容易理解
Period withDays(int days)
Period withMonths(int months)
Period withYears(int years)
调整 Period中的 days,months,years 字段为给定值,返回新的 Period 实例
long toTotalMonths() 返回 Period 转换过来总的月数。
如: Period period = Period.of(1, 1, 1);
period.toTotalMonths() //13

11.1 Period 简单使用

    /**
     * 测试 Period
     */
    @Test
    public void testPeriod() {
        // 从给定的年、月和日获取LocalDate的实例。
        LocalDate ldOf = LocalDate.of(2018, 9, 3);
        System.out.println("从给定的年、月和日获取LocalDate的实例: " + ldOf);
        // 从默认时区的系统时钟获取当前日期
        LocalDate ldNow = LocalDate.now();
        System.out.println("从默认时区的系统时钟获取当前日期: " + ldNow);
        // 返回两个时间点之间的间隔Period
//        参数类型是:LocalDate
        Period period = Period.between(ldOf, ldNow);
        // 打印 Period 时间长度转为 天数/月数/年数
        System.out.println("period 实例 days 字段值:" + period.getDays());
        System.out.println("period 实例 months 字段值:" + period.getMonths());
        System.out.println("period 实例 years 字段值:" + period.getYears());

        // Period 日期间隔长度转换为总的月数
        System.out.println("Period 日期间隔长度转换为总的月数:" + period.toTotalMonths());
    }

运行结果:

从给定的年、月和日获取LocalDate的实例: 2018-09-03
从默认时区的系统时钟获取当前日期: 2020-09-04
period 实例 days 字段值:1
period 实例 months 字段值:0
period 实例 years 字段值:2
Period 日期间隔长度转换为总的月数:24

12 日期操作类 TemporalAdjuster 和 TemporalAdjusters

前面介绍过:

  • TemporalAdjuster // 调整时间或日期,如:调整到这个月的第一个星期一,调整后生成一个新的时间或日期实例
  • TemporalAdjusters //好多静态方法,用于提供常用的 TemporalAdjuster

Instant、LocalDate、LocalTime、LocalDateTime 或者其他实现了 Temporal 接口的类中都有一个 with(TemporalAdjuster adjuster) 方法,入参为 TemporalAdjuster,也就是要进行的时间调整操作,返回新的时间日期实例。

TemporalAdjusters 提供了好多静态方法用于生成常用的 TemporalAdjuster 实例

TemporalAdjusters 静态方法 用途
static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) 调整返回新日期实例,调整到当前日期月份的第几个周几,ordinal表示第几个,DayOfWeek是枚举类,表示周几
static TemporalAdjuster firstDayOfMonth() 调整返回新日期实例,调整到当前日期当前月份第一天
static TemporalAdjuster firstDayOfNextMonth() 调整返回新日期实例,调整到当前日期下一月份的第一天
static TemporalAdjuster firstDayOfNextYear() 调整返回新日期实例,调整到当前日期下一年份的第一天
static TemporalAdjuster firstDayOfYear() 调整返回新日期实例,调整到当前日期当前年份的第一天
static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek) 调整返回新日期实例,调整到当前日期当前月份的第一个周几
static TemporalAdjuster lastDayOfMonth() 调整返回新日期实例,调整到当前日期当前月份的最后一天
static TemporalAdjuster lastDayOfYear() 调整返回新日期实例,调整到当前日期当前年份的最后一天
static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) 调整返回新日期实例,调整到当前日期当前月份的最后一个周几
static TemporalAdjuster next(DayOfWeek dayOfWeek) 调整返回新日期实例,调整到下一个周几
static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek) 如果当前日期刚好符合是入参指定周几(DayOfWeek),返回当前日期实例;否则调整到下一个周几,返回新日期实例
static TemporalAdjuster ofDateAdjuster(UnaryOperator dateBasedAdjuster) 通过函数式接口 UnaryOperator ,把当前日期实例作为抽象方法入参,进行日期操作后返回新的 LocalDate
static TemporalAdjuster previous(DayOfWeek dayOfWeek) 调整返回新日期实例,调整到前一个周几
static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek) 如果当前日期刚好符合是入参指定周几(DayOfWeek),返回当前日期实例;否则调整到前一个周几,返回新日期实例

12.1 TemporalAdjusters简单使用

   /**
     * 测试 TemporalAdjuster
     */
    @Test
    public void testTemporalAdjuster() {
        // 从默认时区的系统时钟获取当前日期
        LocalDate ldNow = LocalDate.now();
        System.out.println("当前日期: " + ldNow);

        // 调整到当前日期当前月份第一天
        LocalDate firstDayOfMonth = ldNow.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println("当前月份第一天: " + firstDayOfMonth);
        // 当前日期刚好符合是入参指定周几(DayOfWeek),返回当前日期实例;否则调整到下一个周几,返回新日期实例
        LocalDate nextOrSameFriday = ldNow.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));
        System.out.println("当前日期刚好符合是入参指定周五(DayOfWeek),返回当前日期实例: " + nextOrSameFriday);
        LocalDate nextOrSameMonday = ldNow.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
        System.out.println("当前日期不符合是入参指定周一(DayOfWeek),调整到下一个周一,返回新日期实例: " + nextOrSameMonday);
        // 下一个周五
        LocalDate nextFriday = ldNow.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
        System.out.println("下一个周五: " + nextFriday);
        // 当前月份的最后一天
        LocalDate lastDayOfMonth = ldNow.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("当前月份的最后一天: " + lastDayOfMonth);
        // 下一月份的第一天
        LocalDate firstDayOfNextMonth = ldNow.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("下一月份的第一天: " + firstDayOfNextMonth);

    }

运行结果:

当前日期: 2020-09-04
当前月份第一天: 2020-09-01
当前日期刚好符合是入参指定周五(DayOfWeek),返回当前日期实例: 2020-09-04
当前日期不符合是入参指定周一(DayOfWeek),调整到下一个周一,返回新日期实例: 2020-09-07
下一个周五: 2020-09-11
当前月份的最后一天: 2020-09-30
下一月份的第一天: 2020-10-01

13 DateTimeFormatter 日期或时间解析和格式化

DateTimeFormatter 用途
static DateTimeFormatter ofPattern(String pattern) 通过日期时间格式 pattern 构建一个 DateTimeFormatter
String format(TemporalAccessor temporal) 将 LocalTime、LocalDate、LocalDateTime 作为入参,格式化为日期时间字符串

LocalDate、LocalTime、LcoalDateTime中都有 static LocalDate/LocalTime/LcoalDateTime parse(CharSequence text, DateTimeFormatter formatter)方法,可以将时间字符串,解析为 LocalDate/LocalTime/LcoalDateTime 实例。

定义 Pattern 相关字母和符号的含义

13.1 DateTimeFormatter 简单使用


    /**
     * 测试 DateTimeFormatter
     */
    @Test
    public void testDateTimeFormatter() {
        // 通过日期时间格式 pattern 构建一个 DateTimeFormatter
        DateTimeFormatter ldtFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");
        LocalDateTime ldt = LocalDateTime.now();
        String strLdt = ldtFormat.format(ldt);// 格式化 LocalDateTime
        System.out.println("format LocalDateTime : " + strLdt);
        System.out.println("parse to LocalDateTime : " + LocalDateTime.parse("2020-10-01 08:00:06 666", ldtFormat));//解析为LocalDateTime

        DateTimeFormatter ldFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate ld = LocalDate.now();
        String strLd = ldFormat.format(ld);// 格式化 LocalDateTime
        System.out.println("format LocalDate : " + strLd);
        System.out.println("parse to LocalDate : " + LocalDate.parse("2020-10-01", ldFormat));//解析为LocalDate

        DateTimeFormatter ltFormat = DateTimeFormatter.ofPattern("HH:mm:ss SSS");
        LocalTime lt = LocalTime.now();
        String strLt = ltFormat.format(lt);// 格式化 LocalDateTime
        System.out.println("format LocalDate : " + strLt);
        System.out.println("parse to LocalTime : " + LocalTime.parse("08:00:06 666", ltFormat));//解析为LocalTime

    }

运行结果:

format LocalDateTime : 2020-09-04 14:57:57 294
parse to LocalDateTime : 2020-10-01T08:00:06.666
format LocalDate : 2020-09-04
parse to LocalDate : 2020-10-01
format LocalDate : 14:57:57 294
parse to LocalTime : 08:00:06.666

13.2 DateTimeFormatter 线程安全,解决 SimpleDateFormate 线程不安全问题

4.1小节已经演示了 SimpleDateFormate 线程不安全问题,但是 DateTimeFormatter 时线程安全的,看下面代码:

    /**
     * 演示 DateTimeFormatter 类线程安全
     */
    @Test
    public void testDateTimeFormatterSafe() throws InterruptedException {
        // 闭锁:主线程等待所有子线程运行完后,再继续往下运行
        // 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
        CountDownLatch latch = new CountDownLatch(5);

        //定义时间字符串的pattern
        String pattern = "yyyy-MM-dd HH:mm:ss";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

        for (int i = 1; i <= 5; i++) {
            int seconds = i;
            new Thread(() -> {
                LocalDateTime of = LocalDateTime.of(2020, 9, 3, 12, 14, 30);
                of = of.plusSeconds(seconds);//每个线程分别增加传进来的秒数
                String strDateTime = formatter.format(of);
                System.out.println("当前线程" + Thread.currentThread().getName() + " 对应的时间字符串:" + strDateTime);
                // 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
                latch.countDown();//闭锁数值减一
            }).start();
        }
        // 主线程阻塞,等待所有子线程运行完后,再继续往下运行
        // 不了解 CountDownLatch 可以忽略 CountDownLatch 的相关代码,后面会有系列文章介绍
        latch.await();
    }

运行结果:

当前线程Thread-0 对应的时间字符串:2020-09-03 12:14:31
当前线程Thread-3 对应的时间字符串:2020-09-03 12:14:34
当前线程Thread-1 对应的时间字符串:2020-09-03 12:14:32
当前线程Thread-4 对应的时间字符串:2020-09-03 12:14:35
当前线程Thread-2 对应的时间字符串:2020-09-03 12:14:33

14 时区 ZonedDateTime 和 ZoneId

  • ZonedDateTime // 带时区的日期时间
  • ZoneId // 时区对应的ID,格式为:{区域}/{城市},如:Asia/Shanghai

在 Instant 和 LocalDateTime 中有实例方法 ZonedDateTime atZone(ZoneId zone),返回一个带时区的日期时间实例 ZonedDateTime

ZoneId 中的两个常用方法:

  • getAvailableZoneIds() : 可以获取所有时区信息
  • of(id) : 用指定的时区信息获取ZoneId 对象

14.1 ZonedDateTime 和 ZoneId 简单使用

    /**
     * 测试 时区
     */
    @Test
    public void testZone() {
        ZoneId systemDefaultZoneId = ZoneId.systemDefault();// 系统默认时区
        ZoneId shZoneId = ZoneId.of("Asia/Shanghai");// 上海时区

        System.out.println("系统默认时区:" + systemDefaultZoneId);
        System.out.println("上海时区:" + shZoneId);

        LocalDateTime ldt = LocalDateTime.now();
        // 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是系统默认时区
        ZonedDateTime zdtFromLdt = ldt.atZone(systemDefaultZoneId);
        System.out.println(" ZonedDateTime from LocalDateTime:" + zdtFromLdt);

        Instant instant = Instant.now();
        // 这将返回一个带时区的日期时间实例 ZonedDateTime,入参是上海时区
        ZonedDateTime zdtFromInstant = instant.atZone(shZoneId);
        System.out.println(" ZonedDateTime from Instant In Asia/Shanghai :" + zdtFromInstant);


        // 获取所有时时区信息
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();

        // 打印所有时区信息
        System.out.println("所有时区信息: ");
        availableZoneIds.forEach(System.out::println);
    }

运行结果:

系统默认时区:Asia/Shanghai
上海时区:Asia/Shanghai
 ZonedDateTime from LocalDateTime:2020-09-04T15:11:04.685+08:00[Asia/Shanghai]
 ZonedDateTime from Instant In Asia/Shanghai :2020-09-04T15:11:04.685+08:00[Asia/Shanghai]
所有时区信息: 
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El\\_Salvador
Asia/Pontianak
...

15 结语

这篇文章写之前,Java8相关的时间日期API 在项目中经常使用到,以为写起来会很简单,但是真正整理思路时,才发现好多细节繁琐的事项。整理编辑思路,各个章节需要循序渐进,收集资料,看 Oracle 官网的 JAVA API docs,看源码进一步理解各个API 的使用,写DEMO,测试,开始编辑码字。

硬肝: 3个深夜搞到1点半,3个早晨7点到8点半,3个中午 12点半到13点半。
致敬自己!

这篇文章已经把实战中经常用到的大部分API进行介绍,还有一些特殊的API,有兴趣的朋友,自行研究,也欢迎留言讨论。


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
11
获赞与收藏
8

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消