欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java8 新特性之日期时间对象及一些其他特性

程序员文章站 2022-03-28 16:11:47
日期时间对象 关于日期时间的操作可以分为两种: 转换:与字符串的互相转换,与时间戳的互相转换 计算:计算两个时间点之间的间隔、时间点与时间段的计算(计算下周n、下个月...

日期时间对象

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

  • 转换:与字符串的互相转换,与时间戳的互相转换
  • 计算:计算两个时间点之间的间隔、时间点与时间段的计算(计算下周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) 这种反人类历法的操作,当然是会抛出异常的

localtime 和 localdatetime 都有类似于 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.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 文档!!

我在想可能我们被矫枉过正了。各种各样技术群最多的回答都是:去问百度!! 百度全知道吗?!

说实在的,在今天这个时代,是个人都能在网络上发表文章言论,然后大家再互相转载,假的都能成为真的!
没有经过验证就转载的;在当时有效,现在过时了的;随便在文章中一个转载链接的 ... 比比皆是

总结

以上所述是小编给大家介绍的java8 新特性之日期时间对象及一些其他特性,希望对大家有所帮助