日期时间对象

关于日期时间的操作可以分为两种:

  • 转换:与字符串的互相转换,与时间戳的互相转换
  • 计算:计算两个时间点之间的间隔、时间点与时间段的计算(计算下周N、下个月D日、去年M月D日等等)

Java8 提供了三个类:LocalDateLocalTimeLocalDateTime,它们的形式如 2020-01-0112:30:002020-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) 这种反人类历法的操作,当然是会抛出异常的

LocalTimeLocalDateTime 都有类似于 LocalDate 的方法,这里就不一一列举了(因为我感觉自己越来越像 api 文档了)

Java8 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.Datejava.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 可以由 LocalDateLocalTime 组成,也可以拆分成它俩

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) 的参数可以是 LocalDateTimeLocalTime

它只会返回一个整数(舍掉小数后的整数,等同于 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 是一个接口,LocalDateTimeLocalDateLocalTime 都是它的实现类

LocalDateTimeLocalDateLocalTime 中都有 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,点进去一篇博客文章,结果文章内容根本就没有这个关键词,那么关键词到底在哪呢?

关键词在网站的推荐文章列表标题里,所以百度爬取到了,显示在搜索结果里了。真是日了狗了!!

百度搜到的博客能别看就别看,看也要看那些经常更新,比较靠谱的。

靠谱的学习途径:

  1. 官方最新 API 文档
  2. 书籍/视频(要注意权威性和时效性)
  3. 经常更新靠谱的博客

不要过于信任搜索引擎!!

轻易不要使用搜索引擎!!

Java8 新特性(三) - 日期时间对象以及一些其他特性的更多相关文章

  1. 08JavaScript数学与日期时间对象

    JavaScript数学与日期时间对象 5.1.3数学(Math)对象 <script> //欧拉常量,自然对数的底(约等于2.718); document.write(Math.E+&q ...

  2. java 数据结构(三):java常用类 三 日期时间API

    JDK 8之前日期时间API 1.获取系统当前时间:System类中的currentTimeMillis()long time = System.currentTimeMillis();//返回当前时 ...

  3. jdk8-》日期时间及其格式处理类特性

    一.JDK8之时间⽇期处理类 核⼼类: LocalDate:不包含具体时间的⽇期. LocalTime:不含⽇期的时间. LocalDateTime:包含了⽇期及时间. LocalDate 常⽤API ...

  4. Java8新特性_日期时间新类 LocalDate、LocalTime、LocalDateTime

    import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.format.DateTimeForma ...

  5. Java实现日期时间对象的使用

    利用类对象计算日期 在利用Java语言进行信息系统开发中,经常需要对日期进行计算和转换,比如,设置了某活动的开始日期和结束日期,系统需要判断当前是否是该活动时间,在Java开发的信息系统中,通常日期以 ...

  6. 定时器 & 日期时间对象 & 正则

    1 JavaScript 计时事件 通过使用 JavaScript,有能力做到在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行,这称之为计时事件. 两个关键方法是: setInterv ...

  7. Java8 新的日期和时间API(笔记)

    LocalDate LocalTime Instant duration以及Period 使用LocalDate和LocalTime //2017-03-20 LocalDate date = Loc ...

  8. 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API

    https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...

  9. 干货 | Java8 新特性教程

    本教程翻译整理自 https://github.com/winterbe/java8-tutorial 本教程首发自个人网站: https://www.exception.site/java8/jav ...

随机推荐

  1. linux scull 的内存使用

    在介绍读写操作前, 我们最好看看如何以及为什么 scull 进行内存分配. "如何"是需要全 面理解代码, "为什么"演示了驱动编写者需要做的选择, 尽管 sc ...

  2. Fast Stone截图工具使用教程

    下面是Fast Stone的显示面板,很小巧,但功能强大 一.特殊功能 1.1 添加水印 (1)选择功能栏的"Edge" (2)将要水印的图片选中,选择水印图片的放置位置,应用即可 ...

  3. 类(class)和继承

    .继承之前的写法 ↓ ----------------------------------------------------------------------------------------- ...

  4. centos7 teamviewer

    Step 1: Install the prerequisites. # yum install glibc alsa-lib freetype libICE libSM libX11 libXau ...

  5. read、write 与recv、send区别 gethostname

    recv相对于read有什么区别呢? 其实它跟read函数功能一样,都可以从套接口缓冲区sockfd中取数据到buf,但是recv仅仅只能够用于套接口IO,并不能用于文件IO以及其它的IO,而read ...

  6. slim的简单使用

    1.在命令行进入项目根目录,然后用composer下载slim composer require slim/slim "^3.0" 2.下载slim完成后,在php文件中引入req ...

  7. 关于启动php-fpm失败的解决办法

    当我执行 sudo lnmp php-fpm restart会出现如下错误 Starting php-fpm /usr/local/php/sbin/php-fpm: error while load ...

  8. Java泛型类特性

    在2004年末Java推出了Java5,其中提供了对泛型方法和类的支持,也围绕着泛型推出了一下特性,本章将对Java泛型进行综合的概括 1.泛型特性构件pre-Java 5 1.使用Object表示泛 ...

  9. 7.13 Python基础语法

    Python基础语法 编码: 默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串. 当然你也可以为源码文件指定不同的编码 python2.7中有两个函数 ...

  10. 【题解】ARC101F Robots and Exits(DP转格路+树状数组优化DP)

    [题解]ARC101F Robots and Exits(DP转格路+树状数组优化DP) 先删去所有只能进入一个洞的机器人,这对答案没有贡献 考虑一个机器人只能进入两个洞,且真正的限制条件是操作的前缀 ...