Java8 新特性(三) - 日期时间对象以及一些其他特性
日期时间对象
关于日期时间的操作可以分为两种:
- 转换:与字符串的互相转换,与时间戳的互相转换
- 计算:计算两个时间点之间的间隔、时间点与时间段的计算(计算下周N、下个月D日、去年M月D日等等)
Java8 提供了三个类:LocalDate
、LocalTime
、LocalDateTime
,它们的形式如 2020-01-01
、12:30:00
、2020-01-01 12:30:00
创建对象
获取类对象的方法非常非常简单
LocalDate now = LocalDate.now();
LocalDate ld = LocalDate.of(2019, 1, 1);
// 获取年月日
now.getYear();
now.getMonthValue(); // 如果你调用了 now.getMonth() ,那么它将返回给你一个大写的英文月份单词
now.getDayOfMonth();
// 顾名应该思义
getDayOfWeek();
getDayOfYear();
// 设置年月日
LocalDate ld1 = ld.withYear(2021); // 2021-01-01
LocalDate ld2 = ld.withMonth(12); // 2019-12-01
LocalDate ld3 = ld.withDayOfMonth(12); // 2019-12-12
// 你可能会纳闷,既然是设置,为什么不用单词 set 呢,而用 with
// 因为,set 操作一般是改变调用对象本身,没有返回值;
// 而 with 是在调用对象基础上另外创建一个新对象,设置好值后返回,没有改变调用对象
// 如果你是那个打破砂锅的孩子,你可能会问:为什么不能改变调用对象?
// 因为 LocalDate 是 final 修饰的(final 人称 Java 界的自宫之刀)
// 从物理的角度来讲,目前人类无法改变时间(穿越)
// 如果你有 ld.withMonth(13) 这种反人类历法的操作,当然是会抛出异常的
LocalTime
和LocalDateTime
都有类似于LocalDate
的方法,这里就不一一列举了(因为我感觉自己越来越像 api 文档了)
转换
日期时间对象 和 字符串 之间的互相转换:
// LocalDateTime 对象 -> 字符串
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
String dateTimeStr = now.format(dtf);
System.out.println(dateTimeStr);
// 字符串 -> LocalDateTime 对象
String str = "2022-01-30 12:15:20";
LocalDateTime dateTime = LocalDateTime.parse(str, dtf);
System.out.println(dateTime);
DateTimeFormatter 类还提供一些现成的 formatter ,比如
DateTimeFormatter.BASIC_ISO_DATE ==> DateTimeFormatter.ofPattern("yyyyMMdd")
DateTimeFormatter.ISO_LOCAL_DATE ==> DateTimeFormatter.ofPattern("yyyy-MM-dd")
// 更多 formatter 可以 api 文档中查询
学习的本质,不在于记住哪些知识,而在于它触发了你的思考。—— 迈克尔·桑德尔
日期时间 和 时间戳 之间的互相转换:
// LocalDateTime 对象 -> 时间戳
LocalDateTime now = LocalDateTime.now();
// 获取系统默认时区
ZoneId systemDefaultZoneId = ZoneId.systemDefault();
Instant instant = now.atZone(systemDefaultZoneId).toInstant();
long timestamp = instant.toEpochMilli();
System.out.println(timestamp);
// 时间戳 -> LocalDateTime 对象
long timestamp2 = 1578919583784L;
Instant instant2 = Instant.ofEpochMilli(timestamp2);
LocalDateTime dateTime2 = LocalDateTime.ofInstant(instant2, systemDefaultZoneId);
System.out.println(dateTime2);
“我不明白为什么要把时间戳搞得这么麻烦!”
另外:java.util.Date
与 java.time.LocalDateTime
之间的转换需要通过 Instant
实现,它俩都没有提供直接的转换方法
// 获取系统默认时区
ZoneId systemDefaultZoneId = ZoneId.systemDefault();
// Date 转为 LocalDateTime
Date date3 = new Date();
Instant instant3 = date3.toInstant();
LocalDateTime localDateTime3 = LocalDateTime.ofInstant(instant3, systemDefaultZoneId);
// LocalDateTime 转为 Date
Instant instant4 = now.atZone(systemDefaultZoneId).toInstant();
Date date4 = Date.from(instant4);
还有:LocalDateTime
可以由 LocalDate
和 LocalTime
组成,也可以拆分成它俩
LocalDate nowLocalDate = LocalDate.now();
LocalTime nowLocalTime = LocalTime.now();
LocalDateTime nowLocalDateTime = LocalDateTime.of(nowLocalDate, nowLocalTime);
nowLocalDateTime.toLocalDate();
nowLocalDateTime.toLocalTime();
计算
计算时间点与时间点之间的间隔:
// 计算日期时间之间的间隔
LocalTime startTime = LocalTime.now();
LocalTime endTime = startTime.plusHours(1).plusMinutes(50);
Duration duration = Duration.between(startTime, endTime);
// 间隔秒数
duration.getSeconds();
// 间隔天数
duration.toDays();
// 间隔小时数
duration.toHours();
// 间隔分钟数
duration.toMinutes();
Duration.between(start, end)
的参数可以是 LocalDateTime
、LocalTime
它只会返回一个整数(舍掉小数后的整数,等同于 floor()),不会返回 1小时50分钟
这样的形式
如果你想要 y年M个月d天 H小时m分钟s秒
这种形式,或许你自己动手组装一下了
// 计算日期之间的间隔
LocalDate startDate = LocalDate.now();
LocalDate endDate = LocalDate.of(2031, 1, 1);
Period pe = Period.between(startDate, endDate);
pe.getYears();
pe.getMonths();
pe.getDays();
时间点与时间段的计算:
public LocalDateTime plusYears(long years) {
LocalDate newDate = date.plusYears(years);
return with(newDate, time);
}
public LocalDateTime plusMonths(long months) {
LocalDate newDate = date.plusMonths(months);
return with(newDate, time);
}
public LocalDateTime plusWeeks(long weeks) {
LocalDate newDate = date.plusWeeks(weeks);
return with(newDate, time);
}
public LocalDateTime plusDays(long days) {
LocalDate newDate = date.plusDays(days);
return with(newDate, time);
}
public LocalDateTime plusHours(long hours) {
return plusWithOverflow(date, hours, 0, 0, 0, 1);
}
public LocalDateTime plusMinutes(long minutes) {
return plusWithOverflow(date, 0, minutes, 0, 0, 1);
}
public LocalDateTime plusSeconds(long seconds) {
return plusWithOverflow(date, 0, 0, seconds, 0, 1);
}
public LocalDateTime plusNanos(long nanos) {
return plusWithOverflow(date, 0, 0, 0, nanos, 1);
}
public LocalDateTime minusYears(long years) {
return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years));
}
public LocalDateTime minusMonths(long months) {
return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months));
}
public LocalDateTime minusWeeks(long weeks) {
return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks));
}
public LocalDateTime minusDays(long days) {
return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days));
}
public LocalDateTime minusHours(long hours) {
return plusWithOverflow(date, hours, 0, 0, 0, -1);
}
public LocalDateTime minusMinutes(long minutes) {
return plusWithOverflow(date, 0, minutes, 0, 0, -1);
}
public LocalDateTime minusSeconds(long seconds) {
return plusWithOverflow(date, 0, 0, seconds, 0, -1);
}
public LocalDateTime minusNanos(long nanos) {
return plusWithOverflow(date, 0, 0, 0, nanos, -1);
}
看吧,加减年数、月数、天数、小时数、分钟数、秒数、毫秒数都有
想怎么用就怎么用,举个小例子:
LocalDateTime now = LocalDateTime.now();
// 30年后的今天(我还要上班,还没退休)
LocalDateTime after30Years = now.plusYears(30L);
System.out.println(after30Years);
// 347个月后(我就能还清贷款了 ╥﹏╥)
LocalDateTime after348Months = now.plusMonths(347L);
System.out.println(after348Months);
// 11天后(就是除夕了)
LocalDateTime after10Days = now.plusDays(10L);
System.out.println(after10Days);
// 8小时前(我在上班)
LocalDateTime before8Hours = now.minusHours(8L);
System.out.println(before8Hours);
// 3分钟前(我开始听 Let it go 这首歌)
LocalDateTime before3Before = now.minusMinutes(3L);
System.out.println(before3Before);
// 10秒前(写下下面这条代码)
LocalDateTime before10Second = now.minusSeconds(10L);
System.out.println(before10Second);
其实不用区分什么加减的,也可以用 plusXxx 做减法,只要传入负数参数就行了
另外这里还有一类需求,比如:
明年的感恩节是哪天?(每年11月的第四个星期四为感恩节)
下周五是哪天?
这就要用到时间校正器 TemporalAdjuster
这里容我先介绍一下 TemporalAdjuster
,它是一个函数式接口,只有一个方法 adjustInto
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
Temporal
是一个接口,LocalDateTime
、LocalDate
、LocalTime
都是它的实现类
在 LocalDateTime
、LocalDate
、 LocalTime
中都有 with(TemporalAdjuster adjuster)
这个方法用来实现上面提到的另类需求。
// 下周五
LocalDateTime now = LocalDateTime.now();
LocalDateTime nextFriday = now.with(dt -> {
// dt 是 `Temporal` 对象,但实质上是调用对象的类型
LocalDateTime dateTime = (LocalDateTime) dt;
// 非常可惜,没有 withDayOfWeek() 这个方法,要不然就会非常方便了
int dayOfWeekValue = dateTime.getDayOfWeek().getValue();
int fridayValue = DayOfWeek.FRIDAY.getValue();
return dateTime.plusWeeks(1L)
.plusDays(fridayValue - dayOfWeekValue);
});
System.out.println(nextFriday);
// 明年的感恩节(明年11月第四个星期四)
LocalDate thanksGivingDay = LocalDate.now().with( t -> {
LocalDate d = (LocalDate) t;
// 明年11月1日
LocalDate newDate = d.plusYears(1L).withMonth(11).withDayOfMonth(1);
int dayOfWeekValue = newDate.getDayOfWeek().getValue();
int thursdayValue = DayOfWeek.THURSDAY.getValue();
long plusWeeks = dayOfWeekValue > thursdayValue ? 4L : 3L;
return newDate.plusWeeks(plusWeeks)
.plusDays(thursdayValue - dayOfWeekValue);
});
System.out.println(thanksGivingDay);
其实 TemporalAdjusters
提供了许多 TemporalAdjuster
对象,就像上一节 Stream 中 Collectors
之于 Collector
一样 。
使用 TemporalAdjusters
能够十分方便的实现上面的需求
// 下个周五
LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println(nextFriday);
// 明年的感恩节(明年11月第四个星期四)
LocalDate date = LocalDate.now().plusYears(1L).withMonth(11);
LocalDate thanksGivingDay = date.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));
System.out.println(thanksGivingDay);
注意:
TemporalAdjusters.next(DayOfWeek day)
方法返回的是 接下来第一个周五,并不是我们一般理解的 下周五,比如说:今天 2020-01-13(周一),那么返回的就是 2020-01-17 四天后的周五。
另外 TemporalAdjusters
并不止提供了上面这2个方法,还有很多其他方法, API 文档 中给出了足够多的例子,一看就明白了。
建议有兴趣的同学,去阅读一些源码,参考 Java8 代码逻辑,然后用其他编程语言实现相同的日期时间操作,因为在其他编程中(比如 javaScript)也会经常用到日期时间的操作
其他特性
罗列出来表示我知道他们,但不表示我理解他们,所以 Let It Go!
接口中的默认方法
接口中的静态方法
Optional 类
Optional<String> op = Optional.of(str);
它是用来标识这个变量有可能为空。
如果一个变量有可能为空,Java8 之前我们每次使用这个变量时,都必须判断它是否为空。现在也是!!
Optional
并不能避免空指针异常,仅仅是表示标识变量可能为空。打个比方:
前面一条路上埋了地雷,但从表面上完全看不出来,除非我们走一步都扔石头试一下,否则说不准哪一步就炸了。而 Optional
就是用来标识地雷位置的,我们知道了哪个位置有雷,就会绕着走,从而能够安全通过
另外 Optional 还提供了一个设置默认值的功能,挺好玩的。
Integer pageSize = null;
// 以前我们设置默认值
//pageSize = pageSize == null ? 10 : pageSize;
//System.out.println(pageSize);
// 使用 Optional 设置默认值
pageSize = Optional.ofNullable(pageSize)
.orElse(20);
System.out.println(pageSize);
// 自定义默认值
Integer defaultPageSize = Optional.ofNullable(pageSize)
.orElseGet(() -> {
return new Integer(50);
});
结语
Java8 新特性系列随便到此就结束了。
最最关键的,还是多看 Java 官方 API 文档!!
我在想可能我们被矫枉过正了。各种各样技术群最多的回答都是:去问百度!! 百度全知道吗?!
说实在的,在今天这个时代,是个人都能在网络上发表文章言论,然后大家再互相转载,假的都能成为真的!
没有经过验证就转载的;在当时有效,现在过时了的;随便在文章中一个转载链接的 ... 比比皆是
这里有一个开眼的短视频,介绍 虚假新闻是如何在传播
最恼人的是,百度搜索一个关键词 XXX,点进去一篇博客文章,结果文章内容根本就没有这个关键词,那么关键词到底在哪呢?
关键词在网站的推荐文章列表标题里,所以百度爬取到了,显示在搜索结果里了。真是日了狗了!!
百度搜到的博客能别看就别看,看也要看那些经常更新,比较靠谱的。
靠谱的学习途径:
- 官方最新 API 文档
- 书籍/视频(要注意权威性和时效性)
- 经常更新靠谱的博客
不要过于信任搜索引擎!!
轻易不要使用搜索引擎!!
Java8 新特性(三) - 日期时间对象以及一些其他特性的更多相关文章
- 08JavaScript数学与日期时间对象
JavaScript数学与日期时间对象 5.1.3数学(Math)对象 <script> //欧拉常量,自然对数的底(约等于2.718); document.write(Math.E+&q ...
- java 数据结构(三):java常用类 三 日期时间API
JDK 8之前日期时间API 1.获取系统当前时间:System类中的currentTimeMillis()long time = System.currentTimeMillis();//返回当前时 ...
- jdk8-》日期时间及其格式处理类特性
一.JDK8之时间⽇期处理类 核⼼类: LocalDate:不包含具体时间的⽇期. LocalTime:不含⽇期的时间. LocalDateTime:包含了⽇期及时间. LocalDate 常⽤API ...
- Java8新特性_日期时间新类 LocalDate、LocalTime、LocalDateTime
import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.format.DateTimeForma ...
- Java实现日期时间对象的使用
利用类对象计算日期 在利用Java语言进行信息系统开发中,经常需要对日期进行计算和转换,比如,设置了某活动的开始日期和结束日期,系统需要判断当前是否是该活动时间,在Java开发的信息系统中,通常日期以 ...
- 定时器 & 日期时间对象 & 正则
1 JavaScript 计时事件 通过使用 JavaScript,有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行,这称之为计时事件. 两个关键方法是: setInterv ...
- Java8 新的日期和时间API(笔记)
LocalDate LocalTime Instant duration以及Period 使用LocalDate和LocalTime //2017-03-20 LocalDate date = Loc ...
- 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API
https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...
- 干货 | Java8 新特性教程
本教程翻译整理自 https://github.com/winterbe/java8-tutorial 本教程首发自个人网站: https://www.exception.site/java8/jav ...
随机推荐
- linux scull 的内存使用
在介绍读写操作前, 我们最好看看如何以及为什么 scull 进行内存分配. "如何"是需要全 面理解代码, "为什么"演示了驱动编写者需要做的选择, 尽管 sc ...
- Fast Stone截图工具使用教程
下面是Fast Stone的显示面板,很小巧,但功能强大 一.特殊功能 1.1 添加水印 (1)选择功能栏的"Edge" (2)将要水印的图片选中,选择水印图片的放置位置,应用即可 ...
- 类(class)和继承
.继承之前的写法 ↓ ----------------------------------------------------------------------------------------- ...
- centos7 teamviewer
Step 1: Install the prerequisites. # yum install glibc alsa-lib freetype libICE libSM libX11 libXau ...
- read、write 与recv、send区别 gethostname
recv相对于read有什么区别呢? 其实它跟read函数功能一样,都可以从套接口缓冲区sockfd中取数据到buf,但是recv仅仅只能够用于套接口IO,并不能用于文件IO以及其它的IO,而read ...
- slim的简单使用
1.在命令行进入项目根目录,然后用composer下载slim composer require slim/slim "^3.0" 2.下载slim完成后,在php文件中引入req ...
- 关于启动php-fpm失败的解决办法
当我执行 sudo lnmp php-fpm restart会出现如下错误 Starting php-fpm /usr/local/php/sbin/php-fpm: error while load ...
- Java泛型类特性
在2004年末Java推出了Java5,其中提供了对泛型方法和类的支持,也围绕着泛型推出了一下特性,本章将对Java泛型进行综合的概括 1.泛型特性构件pre-Java 5 1.使用Object表示泛 ...
- 7.13 Python基础语法
Python基础语法 编码: 默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串. 当然你也可以为源码文件指定不同的编码 python2.7中有两个函数 ...
- 【题解】ARC101F Robots and Exits(DP转格路+树状数组优化DP)
[题解]ARC101F Robots and Exits(DP转格路+树状数组优化DP) 先删去所有只能进入一个洞的机器人,这对答案没有贡献 考虑一个机器人只能进入两个洞,且真正的限制条件是操作的前缀 ...