java8的日期API总结(JSR310)

简介: java8 datetime api有什么 LocalDate LocalDate 与 Date 转换 LocalTime LocalDateTime LocalDateTime 与 Date Instant ZoneId 日期计算 Java的日期时间API,一直有一些令人头疼的问题。

时间API回顾

Java的日期时间API,一直有一些令人头疼的问题。

有了 java.util.Date 又搞出个java.sql.Date,改个名字不行?

好好的new Date(year,month,day) 早早的就被标记为 @Deprecated,那时候还是jdk1.1,如今都java9了,还没删除。

BTW,如果你还不知道为什么该方法被标记为弃用,请执行一下如下代码:

Date date =new Date(2017,5,1);
System.out.println(date);//Fri Jun 01 00:00:00 CST 3917

好吧,有一天又给了一个Calendar,每次一个操作,都感觉,IDE一行设置80个字符,是真不够。

我想搞个纯日期,那叫一个麻烦,相信很多人都这么写:

Calendar cal=Calendar.getInstance();
cal.set(Calendar.HOUR,0);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);
Date date=cal.getTime();

心塞~

貌似看起来Calendar比Date好一点,但他继承了Date一个很恶心的特性,月份问题。

System.out.println(cal.getTime());//Fri Nov 17 12:00:00 CST 2017
System.out.println(cal.get(Calendar.MONTH));//10

JSR310

java8终于有了个改观,这就是JSR310.

JSR-310规范提供一个新的和改进的Java日期与时间API,该规范领导者Stephen Colebourne就是joda-time作者,因此很多环节很像joda-time。

java8 datetime api 带来了什么

旧时代的时间api,在java.util 包中,而JSR310的类都在 java.time 包中。

日期时间API将日期和时间分开为LocalDate和LocalTime;

既有日期又有时间,则使用 LocalDateTime;

改善了jdk的TimeZone,使用更优雅的ZoneId;

增加了 Instant 的概念,可以理解为时间线上的一个瞬间;

很重要的,jsr310类都是线程安全的。

瞬时时间

Temporal,这是一个接口,所有的时间点都实现了该接口。

LocalDate

//获取当前日期
LocalDate date1=LocalDate.now();
System.out.println(date1);

//获取指定日期
LocalDate date2=LocalDate.of(2017, 12, 24);
System.out.println(date2);

//获取当月第一天日期
LocalDate minus = date1.minus(date1.getDayOfMonth()-1,ChronoUnit.DAYS);
System.out.println(minus);

//将日期的月份修改为指定值
date2.withMonfh(2);//2017-02-24

//格式化
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM");
System.out.println(date2.format(formatter));//2017-12

//获取某个时间单位
DayOfWeek weekday = date2.getDayOfWeek();
System.out.println(weekday.getValue());//SUNDAY
System.out.println(weekday);//7
System.out.println(date2.getDayOfMonth());//24
//也可以用通用的方法
System.out.println(date2.get(ChronoField.YEAR));//2017

LocalDate 与 Date 转换

//LocalDate -> Date
LocalDate localDate1=LocalDate.now();
ZoneId zoneId=ZoneId.systemDefault();
Instant instant=localDate1.atStartOfDay(zoneId).toInstant();
Date from = Date.from(instant);
System.out.println(from);

//Date 转 LocalDate
Date date2=new Date();
Instant instant = date2.toInstant();
ZoneId zoneId=ZoneId.systemDefault();
LocalDate localDate2 = instant.atZone(zoneId).toLocalDate();
System.out.println(localDate);

LocalTime

实际单纯使用时间的场景,并没有单纯使用日期的时间多。而且,LocalDate的思路捋清楚,LocalTime的使用大同小异。

LocalDateTime

结合了LocalDate 和 LocalTime,也就是传统意义上类似Date的时间。

LocalDate localDate = dt.toLocalDate();
LocalTime localTime = dt.toLocalTime();

很多方法和LocalDate是类似的,不再浪费篇幅。

LocalDateTime 与 Date

Date 转 LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);

# LocalDateTime 转 Date
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zone = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zone).toInstant();
Date date = Date.from(instant);

Instant

这是一个时间点,它是1970-01-01 以来的偏移量,它包含秒和纳秒。

Instant now = Instant.now();
//完整的时间戳
date.getEpochSecond()+"."+date.getNano()/1000/1000;

与之对应的旧的api类似于 System.currentTimeMillis()和 System.nanoTime()。或者可以参考其他语言的时间戳。

从前面的格式转换可以看出。

Date -> Instant -(ZoneId)-> LocalDate

用文字描述,就是 Date 可以转换为 Instant;Instant 可以借助ZoneId 转换为LocalDate。反之亦然。

Instant 和 LocalDateTime

Instant 和 LocalDateTime的区别在于,前者是时间线上的一个绝对时刻,而LocalDateTime是Instant在不同时区的一个时间表示,但它并没有保存时区。

其他几个不常用的瞬时时间

瞬时时间的实现很多,很多并不常用:

ZonedDateTime: 就是带了时区的LocalDateTime。 Year: 年。 YearMonth:只记录月。 MonthDay: 只记录月日。

说明一点,怎么获得这些类型,一般我们使用的多是完整的时间或日期,如LocalDateTime,而这些不常用的类型,大部分都是从LocalDateTime转换而来。

LocalDateTime d1=LocalDateTime.now();
YearMonth ym=YearMonth.from(d1);

// 当然也可以直接创建
ym=YearMonth.now();

格式化

作为SimpleDateFormat的替代品,使用 DateTimeFormatter。

创建 DateTimeFormatter

//使用內建的模式
DateTimeFormatter formater=DateTimeFormatter.ISO_DATE_TIME;

// 自定义模式
DateTimeFormatter formater=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

//使用国际化样式,它的具体表现取决于默认的Locale
FormatStyle dateTimeStyle=FormatStyle.MEDIUM;
DateTimeFormatter formater=DateTimeFormatter.ofLocalizedDateTime(dateTimeStyle);

格式化

直接调用瞬时时间的format方法。

String format = d1.format(formater);

反过来,直接调用静态工厂方法parse解析时间:

// 标准的ISO8601时间格式
LocalDateTime.parse("2017-11-30T16:10:32");

// 非标准的时间格式
LocalDateTime parse = LocalDateTime.parse("2017-11-30 16:10:32",formater);

国际化测试

LocalDateTime d1=LocalDateTime.now();
FormatStyle dateTimeStyle=FormatStyle.LONG;

Locale.setDefault(Locale.CHINA);
DateTimeFormatter formater=DateTimeFormatter.ofLocalizedDateTime(dateTimeStyle);
String format = d1.format(formater);
System.out.println(format);//20171130日 下午041032

dateTimeStyle=FormatStyle.MEDIUM;

Locale.setDefault(Locale.CHINA);
formater=DateTimeFormatter.ofLocalizedDateTime(dateTimeStyle);
format = d1.format(formater);
System.out.println(format);//2017-11-30 16:10:32

Locale.setDefault(Locale.UK);
formater=DateTimeFormatter.ofLocalizedDateTime(dateTimeStyle);
format = d1.format(formater);
System.out.println(format);//30-Nov-2017 16:10:32

时区

ZoneId

它是根据名称来记忆时区的方式,如:Asia/Shanghai

ZoneId zoneId=ZoneId.systemDefault();
System.out.println(zoneId);
TimeZone tz=TimeZone.getDefault();
System.out.println(tz);

输出:

Asia/Shanghai
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]

ZoneId忽略了哪些用处不大的信息,更清晰。如果必要的时候,可以在属性 zoneId.getRules() 中获取。

ZoneOffset

时区偏移,它是根据偏移量来记忆时区的方式,如:GMT+08:00

方法

jsr的api提供了很多很多的方法,但从字面上很容易知道他的作用。下面对方法分分类,他们都具有如下的方法前缀:

now: 获取当前时间,每一个瞬时时间都有此方法。

of:静态工厂方法。

parse:静态工厂方法,从字符串解析。

from: 静态工厂转换方法,从另一个时间取值创建对象。

until: 计算时间差

get*:获取某些东西的值。

is*:检查某些东西的是否是truewith*:不可变的setter等价物。

plus*:加一些量到某个对象。

minus*:从某个对象减去一些量。

to*:转换到另一个类型。

at*:把这个对象与另一个对象组合起来,例如:instant.atZone(zoneId)。

时间单位

很多方法都会 TemporalUnit 类型的参数,而它只是一个接口,我们通常使用它的实现类 ChronoUnit 来枚举可用的单位。如:

ChronoUnit days = ChronoUnit.DAYS;

Chronology

历法,实现了常见一些历法。

Chronology c=IsoChronology.INSTANCE;
ChronoLocalDateTime d = c.localDateTime(LocalDateTime.now());  
System.out.println(d);
//2017-11-29T21:40:05.902

其他实现的历法有:

//回历,伊斯兰历法
HijrahChronology.INSTANCE;
//日本历
JapaneseChronology.INSTANCE;
//民国历
MinguoChronology.INSTANCE;
//泰历,佛历
ThaiBuddhistChronology.INSTANCE;

Duration

表示一个时间段,时间粒度精确到秒。

它继承了 TemporalAmount 接口。

LocalDateTime d1 = LocalDateTime.of(2000, 1, 1, 7, 30, 10);
LocalDateTime d2 = LocalDateTime.of(2000, 1, 1, 7, 32, 0);
Duration duration = Duration.between(d1, d2);
System.out.println(duration);//PT1M50S 表示两个时间过去了1分钟50

除了两个时间计算,也可以使用时间量创建。

Duration duration=Duration.ofDays(3);

时间计算

在日期计算上,引入的joda-time特性,实在太方便了。

时间与量的计算

一个时间类型,与一个量的计算,计算结果仍是个时间。

如,计算一个时间对应的几天前,几小时后的时间。

如果要得到本月的第一天的日期,那么以前我们需要这么做。

Calendar cal=Calendar.getInstance();
cal.set(Calendar.HOUR,0);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);

cal.set(Calendar.DAY_OF_MONTH,1);
System.out.println(cal.getTime());

现在呢?

LocalDate date=LocalDate.now().withDayOfMonth(1);
System.out.println(date);

如果我们希望计算当前时间的前一天的时刻,以前需要这么做:

Calendar cal=Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, -1);

现在呢?

LocalDateTime date=LocalDateTime.now().minusDays(1);

既简单,语义也更佳。支持链式语法,编码方便。

除此之外还可以通过一个时间量来计算,方法为:minus(TemporalAmount amountToSubtract),典型的 TemporalAmount的实现就是 Duration.

LocalDateTime d1=LocalDateTime.now();
Duration duration=Duration.ofDays(3);

LocalDateTime d2=d1.minus(duration);
System.out.println(d2);

时间与时间的计算

两个时间之间的计算,结果为一个量。如,计算两个时间相隔多少天。

举例,计算2016年元旦到五一劳动节相隔多少天?

LocalDateTime d1=LocalDateTime.of(2016, 1, 1, 0, 0);
LocalDateTime d2=LocalDateTime.of(2016, 5, 1, 0, 0);

long interval = d1.until(d2, ChronoUnit.DAYS);
System.out.println(interval);

也可以使用时间量的计算:

Duration d=Duration.between(d1, d2);
System.out.println(d.toDays());

Clock

这是个好有意思的api,它代表一个时钟,什么意思?

举个栗子,如果我们每秒打印一下当前时间,你可能这样做:

while(true) {
  Date date = new Date();
  System.out.println(date);
  Thread.sleep(1000);
}

就是说,每次都创建一个当前时间的对象,然后打印输出(或者渲染一个时钟)。

现在我们来看 Clock吧,它就是一个时钟,只是我们看不见。它的值不是一成不变的,而像一个真正的时钟,一直在 tick...tick... 的走。

Clock c=Clock.systemDefaultZone();
while(true) {
  System.out.println(c.instant());
  Thread.sleep(1000);
}

这就好比你戴了一块表,需要时间的时候,看一眼,就知道现在是什么时候了。而传统的方式,就好比你想知道几点了,问CPU,哥们,几点了?

当然,一般情况下,Clock我们就当做一个看不见的钟表,需要对应的数据时:

LocalDateTime now = LocalDateTime.now(clock);

每一个瞬时时间类都有该方法。

总结

  • 关键知识点:瞬时时间、时间量、ZoneId、Clock,其他知识点,调方法就可以了。

  • JSR310 标准中的每一个时间对象,都是线程安全的。

  • 每一个对象都没有公开的构造器,如果需要一个对象你首先想的不是 new,而是静态工厂方法: now、of、from

  • 请习惯链式语法。

  • 月份不需要再减一了,切记。

  • 如果你喜欢敲全类名,请在使用时间api时忘记java.utiljava.text,记住java.time

目录
相关文章
|
9天前
|
Java API C++
Java 8 Stream Api 中的 peek 操作
本文介绍了Java中`Stream`的`peek`操作,该操作通过`Consumer<T>`函数消费流中的每个元素,但不改变元素类型。文章详细解释了`Consumer<T>`接口及其使用场景,并通过示例代码展示了`peek`操作的应用。此外,还对比了`peek`与`map`的区别,帮助读者更好地理解这两种操作的不同用途。作者为码农小胖哥,原文发布于稀土掘金。
Java 8 Stream Api 中的 peek 操作
|
15天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
38 11
|
12天前
|
监控 Java 大数据
【Java内存管理新突破】JDK 22:细粒度内存管理API,精准控制每一块内存!
【9月更文挑战第9天】虽然目前JDK 22的确切内容尚未公布,但我们可以根据Java语言的发展趋势和社区的需求,预测细粒度内存管理API可能成为未来Java内存管理领域的新突破。这套API将为开发者提供前所未有的内存控制能力,助力Java应用在更多领域发挥更大作用。我们期待JDK 22的发布,期待Java语言在内存管理领域的持续创新和发展。
|
13天前
|
Java API 数据处理
【Java的SIMD革命】JDK 22向量API:释放硬件潜能,让Java应用性能飙升!
【9月更文挑战第7天】 JDK 22向量API的发布标志着Java编程语言在SIMD技术领域的重大突破。这一新特性不仅释放了现代硬件的潜能,更让Java应用性能实现了飙升。我们有理由相信,在未来的发展中,Java将继续引领编程语言的潮流,为开发者们带来更加高效、更加强大的编程体验。让我们共同期待Java在SIMD技术的推动下开启一个全新的性能提升时代!
|
15天前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
12天前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
1月前
|
机器人 API Python
智能对话机器人(通义版)会话接口API使用Quick Start
本文主要演示了如何使用python脚本快速调用智能对话机器人API接口,在参数获取的部分给出了具体的获取位置截图,这部分容易出错,第一次使用务必仔细参考接入参数获取的位置。
100 1
|
22天前
|
存储 JSON API
淘系API接口(解析返回的json数据)商品详情数据解析助力开发者
——在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦! 淘宝API接口(如淘宝开放平台提供的API)允许开发者获取淘宝商品的各种信息,包括商品详情。然而,需要注意的是,直接访问淘宝的商品数据API通常需要商家身份或开发者权限,并且需要遵循淘宝的API使用协议。
淘系API接口(解析返回的json数据)商品详情数据解析助力开发者
|
1月前
|
SQL 存储 数据处理
|
1月前
|
XML JSON API
RESTful API设计最佳实践:构建高效、可扩展的接口
【8月更文挑战第17天】RESTful API设计是一个涉及多方面因素的复杂过程。通过遵循上述最佳实践,开发者可以构建出更加高效、可扩展、易于维护的API。然而,值得注意的是,最佳实践并非一成不变,随着技术的发展和业务需求的变化,可能需要不断调整和优化API设计。因此,保持对新技术和最佳实践的关注,是成为一名优秀API设计师的关键。