牛叔叔 的笔记

好好学习

2022-09-07 15:29

JDK8最新的时间和日期相关API使用详解

牛叔叔

JavaEE

(573)

(0)

收藏

1. JDK8时间日期API

在Java8以前,Java处理时间基本都是使用Date和Calendar这两个类。但是Date大部分方法已被废弃,Calendar又有很多不太友好的设计(月份从0开始)。Java8中提供了一套全新的时间处理库,源码中的目录为java.time,该包中的类都是不可变且线程安全。

主要API如下:

ZoneId: 时区ID,用来确定Instant和LocalDateTime互相转换的规则

Instant: 用来表示时间线上的一个点(瞬时)

LocalDate: 表示没有时区的只含年月日的日期, LocalDate是不可变并且线程安全的

LocalTime: 表示没有时区的只含时分秒的时间, LocalTime是不可变并且线程安全的

LocalDateTime: 表示没有时区的日期时间,同时含有年月日时分秒的日期对象, LocalDateTime是不可变并且线程安全的

Clock: 表示真实世界的时钟,可通过时钟访问当前时刻、日期、时间,用到时区。

Duration: 用秒和纳秒表示时间的数量(长短),用于计算两个日期的“时间”间隔

Period: 用于计算两个“日期”间隔

其中,LocalDate、LocalTime、LocalDateTime是新API里的基础对象,绝大多数操作都是围绕这几个对象来进行的。

2. 创建对象

2.1. 获取当前时间

@Test
public void testCurrentTime(){
    LocalDate localDate = LocalDate.now();
    System.out.println(localDate);
    LocalTime localTime = LocalTime.now();
    System.out.println(localTime);
    LocalDateTime localDateTime = LocalDateTime.now();
    System.out.println(localDateTime);
}

结果如下:

2022-09-05
17:23:32.168633800
2022-09-05T17:23:32.168633800

2.2. 指定具体日期时间

@Test
public void testCustomTime(){
    LocalDate localDate = LocalDate.of(2022,9,5);
    LocalTime localTime = LocalTime.of(17,25,52);
    LocalDateTime localDateTime = LocalDateTime.of(localDate,localTime);
    LocalDateTime localDateTime2 = LocalDateTime.of(2022,9,5,17,25,52);    
}

结果如下:

2022-09-05
17:25:52
2022-09-05T17:25:52
2022-09-05T17:25:52

3. 修改日期

3.1. 加减日期

对于LocalDate,可以加减年、月、日、周;

对于LocalTime,可以加减时、分、秒、毫秒、纳秒;

对于LocalDateTime,则可以进行任意精度的时间相加减。

@Test
public void testPlusTime(){
    LocalDateTime localDateTime = LocalDateTime.now();
    LocalDateTime plusYears = localDateTime.plusYears(3L);
    LocalDateTime plusMonths = localDateTime.plusMonths(3L);
    LocalDateTime plusDays = localDateTime.plusDays(3L);
    LocalDateTime plusHours = localDateTime.plusHours(3L);
    LocalDateTime plusMinutes = localDateTime.plusMinutes(3L);
    LocalDateTime plusSeconds = localDateTime.plusSeconds(3L);
}

结果如下:

2022-09-05T17:37:43.311506500
2025-09-05T17:37:43.311506500
2022-12-05T17:37:43.311506500
2022-09-08T17:37:43.311506500
2022-09-05T20:37:43.311506500
2022-09-05T17:40:43.311506500
2022-09-05T17:37:46.311506500

3.2. 修改为指定值

@Test
public void testUpdateTime(){
    LocalDate localDate = LocalDate.now();
    //指定本年的第300天
    LocalDate withDayOfYear = localDate.withDayOfYear(300);
    //指定本月的第20天
    LocalDate withDayOfMonth = localDate.withDayOfMonth(20);
    //当前时间指定月份
    LocalDate withMonth = localDate.withMonth(10);
    //当前时间指定年份
    LocalDate withYear = localDate.withYear(2023);
}

结果如下:

2022-09-07
2022-10-27
2022-09-20
2022-10-07
2023-09-07

4. 获取日期的年月日时分秒

@Test
public void testGetDateTime(){
    LocalDateTime localDateTime = LocalDateTime.now();

    int year = localDateTime.getYear();
    Month month = localDateTime.getMonth();
    int day = localDateTime.getDayOfMonth();
    int hour = localDateTime.getHour();
    int minute = localDateTime.getMinute();
    int second = localDateTime.getSecond();
    int naco = localDateTime.getNano();

    int dayOfYear = localDateTime.getDayOfYear();
    DayOfWeek dayOfWeek = localDateTime.getDayOfWeek();
    
    System.out.println(year);
    System.out.println(month.getValue()+":"+month);
    System.out.println(day);
    System.out.println(hour);
    System.out.println(minute);
    System.out.println(second);
    System.out.println(naco);
    System.out.println(dayOfYear);
    System.out.println(dayOfWeek.getValue()+":"+dayOfWeek);        
}

结果如下:

2022
9:SEPTEMBER
7
9
14
27
226310400
250
3:WEDNESDAY

5. 比较前后

@Test
public void testBeforeAfter(){
    LocalDate localDate1 = LocalDate.of(2022,9,9);
    LocalDate localDate2 = LocalDate.of(2023,10,10);
    System.out.println(localDate1.isEqual(localDate2));
    System.out.println(localDate1.isAfter(localDate2));
    System.out.println(localDate1.isBefore(localDate2));
}

结果如下:

false
false
true

6. Period日期间隔

Period是由年月日为单位的日期间隔。

6.1. 初始化Period

ofXXX()系列方法: 根据年月日来构造持续时间

from(TemporalAmount amount):根据TemporalAmount实例创建Period对象

parse(CharSequence text):根据ISO-8601持续时间格式字符串创建Period对象

between(LocalDate startDateInclusive, LocalDate endDateExclusive):获取两个日期对象之间的持续时间。

public void testCreatePeriod(){
    //根据年月日构造Period
    System.out.println(Period.of(1, 2, 3));
    System.out.println(Period.ofDays(1));
    System.out.println(Period.ofMonths(2));
    //根据周数构造
    System.out.println(Period.ofWeeks(3));
    System.out.println(Period.ofYears(1));
    System.out.println(Period.from(Period.ofMonths(1)));
    //根据ISO-8601时间格式字符串进行构造
    System.out.println(Period.parse("P20Y10M5D"));
    //计算两个日期对象之间的持续时间
    System.out.println(Period.between(LocalDate.now().minusYears(1).minusDays(1),LocalDate.now() ));
}

结果如下:

P1Y2M3D
P1D
P2M
P21D
P1Y
P1M
P20Y10M5D
P1Y1D

6.2. 日期间隔

@Test
public void testPeriod(){
    LocalDate localDate1 = LocalDate.of(2022,9,9);
    LocalDate localDate2 = LocalDate.of(2023,12,15);
    Period period = Period.between(localDate1,localDate2);
    System.out.println("年份差:"+period.getYears());
    System.out.println("月份差:"+period.getMonths());
    System.out.println("日数差:"+period.getDays());
    System.out.println("年份差:"+period.get(ChronoUnit.YEARS));
    System.out.println("月份差:"+period.get(ChronoUnit.MONTHS));
    System.out.println("日数差:"+period.get(ChronoUnit.DAYS));
    System.out.println();
}

结果如下:

年份差:1
月份差:3
日数差:6
年份差:1
月份差:3
日数差:6

6.3. 常用方法

getXXX(): 获取持续时间对象具体的年、月、日

plusXXX(): 给Period对象加上指定精度的值

minusXXX(): 给Period对象减去指定精度的值

withXXX(): 修改Period对象的某一精度值

@Test
public void testPeriodMethods() {
    Period p = Period.of(1, 2, 3);
    //重设Period的年月日
    System.out.println(p.withYears(3).withMonths(2).withDays(1));
    //加上1天
    System.out.println(p.plusDays(1));
    //减去1天
    System.out.println(p.minusDays(1));
    //判断是否为0
    System.out.println(p.isZero());
    //判断是否为负
    System.out.println(p.isNegative());
    //取负
    System.out.println(p.negated());
}

结果如下:

P3Y2M1D
P1Y2M4D
P1Y2M2D
false
false
P-1Y-2M-3D

7. Duration时间间隔

Duration通常用秒或者纳秒相结合来表示一个时间量,最高精度为纳秒
通常用作表示两个时间之间的间隔,也称作持续时间。

7.1. 创建Duration实例

ofXXX()系列方法: 根据纳秒、毫秒、秒、分、时、天等时间来构造持续时间

from(TemporalAmount amount):根据TemporalAmount实例创建Duration对象

parse(CharSequence text):根据ISO-8601持续时间格式字符串创建Duration对象

between(Temporal startInclusive, Temporal endExclusive):获取两个时间对象之间的持续时间。

@Test
public void testCreateDuration(){
    System.out.println(Duration.ofNanos(1000));
    System.out.println(Duration.ofMillis(1000));
    System.out.println(Duration.ofSeconds(30));
    System.out.println(Duration.ofSeconds(30,12345));
    System.out.println(Duration.ofMinutes(1));
    System.out.println(Duration.ofHours(1));
    System.out.println(Duration.ofDays(1));
    System.out.println(Duration.of(1000, ChronoUnit.MILLIS));
    System.out.println(Duration.from(ChronoUnit.MINUTES.getDuration()));
    System.out.println(Duration.parse("PT20.345S"));
    System.out.println(Duration.between(Instant.parse("2022-09-07T10:15:30.00Z"), Instant.now()));
}

结果如下:

PT0.000001S
PT1S
PT30S
PT30.000012345S
PT1M
PT1H
PT24H
PT1S
PT1M
PT20.345S
PT-3H-23M-58.3512544S

7.2. 时间间隔

@Test
public void testDuration(){
    LocalDateTime localDateTime1 = LocalDateTime.of(2008,4,3,8,30,0);
    LocalDateTime localDateTime2 = LocalDateTime.now();
    Duration duration = Duration.between(localDateTime1,localDateTime2);
    System.out.println("天:"+duration.toDays());
    System.out.println("时:"+duration.toHours());
    System.out.println("分:"+duration.toMinutes());
    System.out.println("秒:"+duration.toSeconds());
    System.out.println("毫秒:"+duration.toMillis());
    System.out.println("纳秒:"+duration.toNanos());
}

结果如下:

天:5270
时:126481
分:7588894
秒:455333657
毫秒:455333657609
纳秒:455333657609897200

7.3. 常用方法

getXXX(): 获取持续时间对象具体的秒数或者毫秒数

plusXXX(): 给Duration对象加上指定精度的值

minusXXX(): 给Duration对象减去指定精度的值

withXXX(): 修改Duration对象的秒数or毫秒数

@Test
public void testDurationMethod(){Duration d = Duration.parse("PT20.345S");

    System.out.println(d.getSeconds());
    System.out.println(d.getNano());

    System.out.println(d.withNanos(3456789));//修改纳秒值,返回一个新的Duration
    System.out.println(d.withSeconds(22));//修改秒值,返回一个新的Duration
    System.out.println(d.plusNanos(1));//加1纳秒,返回一个新的Duration
    System.out.println(d.plusMillis(100));//加100毫秒,返回一个新的Duration
    System.out.println(d.plusSeconds(1));
    System.out.println(d.minusNanos(1));//减去1纳秒,返回一个新的Duration
    System.out.println(d.minusMillis(100));//减去10毫秒,返回一个新的Duration
    System.out.println(d.minusSeconds(1));

    System.out.println(d.isZero());//是否为0
    System.out.println(Duration.ZERO.isZero());//是否为0
    System.out.println(d.isNegative());//是否为负
    System.out.println(d.negated());//求负
    System.out.println(d.negated().abs());//求绝对值
}

结果如下:

20
345000000
PT20.003456789S
PT22.345S
PT20.345000001S
PT20.445S
PT21.345S
PT20.344999999S
PT20.245S
PT19.345S
false
true
false
PT-20.345S
PT20.345S

8. 时间戳Instant

8.1. 简介

Instant用于记录时间线上某一瞬间的时间点,顾名思义就是时间戳,但它不同于System.currentTimeMillis()精度为秒,Instant可以精确到纳秒,它的取值范围为:-1000000000-01-01T00:00Z到1000000000-12-31T23:59:59.999999999Z。

常用方法:

now(): 获取基于UTC时间的Instant

ofEpochMilli(long milli):根据时间戳(毫秒)创建一个Instant实例

ofEpochSecond(long second): 根据时间戳(秒)创建一个Instant实例

parse(): 根据时间字符串转换为Instant实例

8.2. Instant和Date关系

Instant就是java8以前的java.util.Date,两者可以互相转换。Instant表示的是时间线上的一个点,也就是时刻。Instant获取的是UTC的时间,而Date是根据当前服务器所处的环境的默认时区来获取的当前时间。

@Test
public void testInstantDate(){
    System.out.println(Instant.now());
    System.out.println(new Date());
}

结果如下:

2022-09-07T03:27:36.049386Z
Wed Sep 07 11:27:36 CST 2022

8.3. Instant和Date互相转换

相互转换过程中的时区会自动+08:00或者-08:00。 java8没有针对Instant提供一个可供自定义的格式化类,可以先转换成LocalDateTime,再使用DateTimeFormatter来完成格式化。

@Test
public void testInstance(){
    Instant instant1 = Instant.now();
    Date date = Date.from(instant1);
    Instant instant2 = date.toInstant();
    System.out.println(instant1);
    System.out.println(date);
    System.out.println(instant2);
}

结果如下:

2022-09-07T02:19:30.821952100Z
Wed Sep 07 10:19:30 CST 2022
2022-09-07T02:19:30.821Z

8.4. 计算运行时间

@Test
public void testRunTime(){
    Instant instant1 = Instant.now();
    for (int i = 0; i < 100000000; i++) {

    }
    Instant instant2 = Instant.now();
    Duration duration = Duration.between(instant1,instant2);
    System.out.println(duration.toNanos());
}

9. 日期格式化及解析

9.1. DateTimeFormatter

@Test
public void testFormatParse(){
    LocalDateTime localDateTime1 = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");
    String dateTimeStr = formatter.format(localDateTime1);
    LocalDateTime localDateTime2 = LocalDateTime.parse(dateTimeStr,formatter);
    System.out.println(localDateTime1);
    System.out.println(dateTimeStr);
    System.out.println(localDateTime2);
}

结果如下:

2022-09-07T10:32:31.947375600
2022-09-07 10:32:31 947
2022-09-07T10:32:31.947

9.2. long毫秒数转为日期

@Test
public void testLongFormat(){
    long timeMillis = System.currentTimeMillis();
    System.out.println(timeMillis);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    //String dateTime = formatter.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(timeMillis),ZoneId.of("Asia/Shanghai")));
    String dateTime = formatter.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(timeMillis),ZoneId.of("GMT+8")));
    System.out.println(dateTime);
}

结果如下:

1662521033726
2022-09-07 11:23:53

10. Clock

Clock类提供了访问当前日期和时间的方法,某一个特定的时间点也可以使用Instant类来表示。

Clock表示一个时钟,Clock的实例用于查找当前时刻,可以使用存储的时区来解释当前时刻以查找当前日期和时间。某种程度上可以使用时钟代替System.currentTimeMillis()和TimeZone.getDefault()。

我们可以自定义创建一个指定滴答间隔的时钟,用来获取需要的时间日期。

钟表的滴答间隔(tickDuration):规定了提供下一个读数的时间间隔。比如,滴答间隔为 1 秒的钟表,读数的分辨率就到 1 秒。滴答间隔为 5 秒的钟表,读数的"分辨率" 就到 5 秒。这里,5 秒的"分辨率"是指,当实际时间数据是 0 或 1、2、3、4 秒时,从它那里得到的读数都是 0 秒。当实际时间数据是 5 或 6、7、8、9 秒时,从它那里得到的读数都是 5 秒。

@Test
public void testClock() {
    Consumer consumer = System.out::println;

    Clock clock = Clock.systemDefaultZone();
    consumer.accept("当前时区:" + clock);

    long millis = clock.millis();
    consumer.accept("当前毫秒:" + millis);

    Instant instant = clock.instant();
    Date from = Date.from(instant);
    consumer.accept("当前时间:" + from);

    Supplier supplier = System::currentTimeMillis;
    System.out.println(supplier.get());;        
}

结果如下:

当前时区:SystemClock[Asia/Shanghai]
当前毫秒:1662535519180
当前时间:Wed Sep 07 15:25:19 CST 2022
1662535519186


0条评论

点击登录参与评论