java8新特性之time包深度解析

Owen Jia 2019年01月29日 658次浏览

Java8新特性java.time.*包学习。 自从java发布模式变更就发现自己有些跟不上他们的速度,java8还有不少没有用透而9、10、11相继出来,长江后浪推前浪一浪胜过一浪。之前date的使用还不敢自信说多透彻,后续都是泪...(欢迎酱油...)

以jdk1.8.0_111为例

新的设计思路

  • 引入final定义支持时间点不可变和线程安全,长久来的Date的设计一直遭人诟病着;
  • 设计LocalDate、LocalDateTime、LocalTime、instant、Clock、Duration等类,format\zoo\temporal等包规范时间的定义划分;
  • 时间统一使用 ISO-8601 日历系统,也就是yyyy-MM-dd'T'HH:mm:ss:SSSZZ格式,输出2012-04-13T10:53:43:119+08:00样子,要是用过jota-time包估计你什么都懂了;
  • 规范并提供更加好用的时间操作方法,plus\minus\with\to\get\of\now等方法命名规则;

jdk1.8包目录简介:

java.time

  • time:父级基础包,常用的时间相关类都在这里,如LocalDate\LocalDateTime\Instant等
  • chrono:日历系统包,日历相关的接口(类似Calendar)也包括提供对其他日历系统的API
  • format:格式化和解析包,主要类是DateTimeFormatter
  • temporal:扩展功能包,提供细粒度的时间控制field、unit,如weeks、months、month-of-year等
  • zone:时区包,时区规则、本地时区等

chrono

format

temporal

zone

围绕常用点和大多数人经常用到做些用例

计算、格式化、字符串转换这三快可以说是很基础的功能,大多数人主要围绕这几块进行开发和封装,基础掌握后面的高级用法慢慢就可以积累。一般使用都是围绕time基础包中LocalDate\LocalDateTime\LocalTime类展开,先介绍一下这几个类及相关的类。

LocalDate: 一个ISO-8601日历系统下的data对象,如2007-12-03
LocalDateTime: 一个ISO-8601日历系统下的data-time对象,如2007-12-03T10:15:30
LocalTime: 一个ISO-8601日历系统下的time对象,如10:15:30
Instant: 一个瞬时的时间点值对象,从1970-01-01T00:00:00Z点毫秒计算的
Clock: 基于instant生成的时钟对象,遵守UTC时区规则可以去生成date和time
Duration: 一个time范围值对象,单位也可以是分钟或小时
Period: 一个date范围值对象,单位可以是年、月、日,和duration正好是两个粒度对象
OffsetDateTime: 从UTC/Greenwich格式时间偏移成ISO-8601的date-time对象,如2007-12-03T10:15:30+01:00
OffSetTime: 和OffsetDateTime类似,粒度是到time的UTC时间格式对象,如10:15:30+01:00
ZonedDateTime: 带时区的ISO-8601日历系统的date-time对象,如2007-12-03T10:15:30+01:00 Europe/Paris
ZonedOffset: 一个带时区的从Greenwich/UTC的偏移量范围中对象,如+02:00

网上找的新旧类的比较图挺好的,贴在这里:

timeVSutil

日期与时间转换

//从date转向time
LocalDateTime localDateTime = LocalDateTime.now();
printTest(localDateTime); 
//2019-02-01 14:52:26
LocalDate date = localDateTime.toLocalDate();
printTest(date); 
//2019-02-01
LocalTime time = localDateTime.toLocalTime();
printTest(time); 
//14:52:26.706

//从time转向datetime
LocalTime localTime = LocalTime.now();
printTest(localTime); 
//14:52:26.727
LocalDateTime dateTime = localTime.atDate(LocalDate.now());
printTest(dateTime); 
//2019-02-01 14:52:26

//从date转向datetime
LocalDate localDate = LocalDate.now();
printTest(localDate); 
//2019-02-01
LocalDateTime dateTime1 = localDate.atTime(LocalTime.of(8,32,45));
printTest(dateTime1); 
//2019-02-01 08:32:45

时间加减计算与比较

LocalDateTime dateTime = LocalDateTime.now(Clock.system(ZoneId.systemDefault()));
LocalDateTime datetime2 =dateTime.minusDays(2);

printTest(dateTime);
//2019-02-01 15:01:12
printTest(datetime2);
//2019-01-30 15:01:12
printTest(datetime2.plusHours(3));
//2019-01-30 18:01:12
printTest(datetime2.minusWeeks(1));
//2019-01-23 15:01:12
printTest(datetime2.plus(1,ChronoUnit.MONTHS));
//2019-02-28 15:01:12

printTest(datetime2.compareTo(dateTime));
//-1
printTest(datetime2.withYear(2));
//0002-01-30 15:01:12
printTest(datetime2.isBefore(dateTime));
//true

Duration duration = Duration.ofDays(5);
printTest(duration);
//PT120H
printTest(duration.plusHours(2).toMinutes());
//7320

时间输出格式化

LocalDateTime dateTime = LocalDateTime.now(Clock.systemDefaultZone());

printTest(dateTime);
//2019-02-01 14:59:45
printTest(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
//20190201
printTest(dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
//2019-02-01T14:59:45.997
printTest(dateTime.format(DateTimeFormatter.ISO_WEEK_DATE));
//2019-W05-5
printTest(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
//2019-02-01 14:59:45.997

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern("yyyy-MM-dd");
builder.parseStrict().toFormatter();
printTest(dateTime.format(builder.parseStrict().toFormatter()));
//2019-02-01

时间对象和string相互转换

LocalDateTime dateTime = LocalDateTime.now(Clock.system(ZoneId.systemDefault()));
printTest(dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE));
//2019-02-01
printTest(dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
//2019-02-01T14:57:57.744
printTest(dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME));
//14:57:57.744

printTest(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
//20190201
printTest(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//2019-02-01 14:57:57
printTest(dateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)));
//19-2-1 下午2:57
printTest(dateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
//2019-2-1 14:57:57
printTest(dateTime.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.valueOf("MEDIUM"))));
//2019-2-1 14:57:57

printTest(LocalDateTime.parse("2019-12-03T10:15:30").toString());
//2019-12-03T10:15:30
printTest(LocalDate.parse("2019-12-03",DateTimeFormatter.ISO_LOCAL_DATE));
//2019-12-03
printTest(LocalTime.parse("10:15:30",DateTimeFormatter.ISO_LOCAL_TIME));
//10:15:30

新API和旧Date转换策略

LocalDateTime localDateTime = LocalDateTime.now();
localDateTime.minusHours(2);
printTest(localDateTime);
//2019-02-01 14:55:06
Date localDateTime2 = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
printTest(localDateTime2.toString());
//Fri Feb 01 14:55:06 CST 2019

LocalDate localDate = LocalDate.now();
printTest(localDate);
//2019-02-01
Date localDate2 = Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
printTest(localDate2);
//Fri Feb 01 00:00:00 CST 2019

Date date = new Date();
printTest(date);
//Fri Feb 01 14:55:06 CST 2019
LocalDateTime date2 = LocalDateTime.ofInstant(date.toInstant(),ZoneId.systemDefault());
printTest(date2);
//2019-02-01 14:55:06

LocalTime localTime = LocalDateTime.ofInstant(new Date().toInstant(),ZoneId.systemDefault()).toLocalTime();
printTest(localTime);
//14:55:06.360

时区的添加和去除以及相互转换

//加时区
LocalDateTime localDateTime = LocalDateTime.now();
printTest(localDateTime);
//2019-02-01 16:00:53
printTest(localDateTime.atZone(ZoneId.of("Europe/Paris")));
//2019-02-01T16:00:53.451+01:00[Europe/Paris]
printTest(localDateTime.atZone(ZoneId.of("Asia/Shanghai")));
//2019-02-01T16:00:53.451+08:00[Asia/Shanghai]

//去除时区
ZonedDateTime zonedDateTime = ZonedDateTime.now();
printTest(zonedDateTime);
//2019-02-01T16:00:53.477+08:00[Asia/Shanghai]
printTest(zonedDateTime.toLocalDateTime());
//2019-02-01 16:00:53
printTest(zonedDateTime.toLocalDate());
//2019-02-01

//其他时区转到本地时区
ZonedDateTime zonedDateTime1 = ZonedDateTime.of(LocalDate.now(),LocalTime.now(),ZoneId.of("Europe/Paris"));
printTest(zonedDateTime1);
//2019-02-01T16:00:53.477+01:00[Europe/Paris]
ZonedDateTime zonedLocal = zonedDateTime1.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
printTest(zonedLocal);
//2019-02-01T23:00:53.477+08:00[Asia/Shanghai]

//本地时区转到其他时区
LocalDateTime localDateTime2 = LocalDateTime.now(ZoneId.systemDefault());
printTest(localDateTime2);
//2019-02-01 16:00:53
ZonedDateTime zonedDateTime13 = localDateTime2.atZone(ZoneId.systemDefault());
printTest(zonedDateTime13);
//2019-02-01T16:00:53.477+08:00[Asia/Shanghai]
ZonedDateTime zonedDateTime2 = zonedDateTime13.withZoneSameInstant(ZoneId.of("Europe/Paris"));
printTest(zonedDateTime2);
//2019-02-01T09:00:53.477+01:00[Europe/Paris]

想学好一件东西,强烈建议你买本书看看

不要听信那些“网上找找文档看看就能掌握”的鬼话,过来人都明白知识体系的分量有多重要。自己找的资料是非常零碎的,同时需要你很强大的知识构建能力去把这些都串起来。很不幸,这样的能力绝大多数人都不具备。

每一本技术书的完成都不是一朝一夕随随便便就写出来的,作者用心良苦的在思考如何帮助你去理解。写书和技术能力有时不一定成正比,但逻辑思维能力应该不弱。反正写技术书的人在我心里都是神一般的存在,虽然垃圾书比较多但不影响我对他们的仰慕。

推荐

常用java处理时间包:joda-time

java8-api文档:jdk8-doc


作者:Owen Jia
推荐关注他的博客:Owen Blog

知识的海洋里,当奋起直追,芳不负年华