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条评论
点击登录参与评论