Datetime in Java 8

按照之前说的,今天总结一下时间相关的类和方法。

在使用Java 8 之前,大家应该使用过 Joda Time Library 吧。Java 8 中的时间相关类就是源于这个Lib

废话不多说,开始总结一下。

这里说的package为Java 8 及以上中的 java.time package, 讨论其中的相关class和方法

为何引入新的时间包

既然引入新的,那说明老的不好用呗。
其新特点在于:

  • 线程安全,按照源码注解所示:This class is immutable and thread-safe
  • 很容易使用,方法更容易理解,更适合人类常规使用习惯和思维方式
  • 更好的时区处理和支持

LocalDate

名字很直接,当地日期,基于系统时钟获得

1
LocalDate localDate = LocalDate.now();

如何format也是大家经常使用的功能

1
2
localDate.format(DateTimeFormatter.ISO_DATE);
localDate.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));

还是很容易的,可以使用系统已经预置的format格式或者自己自定义,或者可以使用DateTimeFormatterBuilder进行更复杂的自定义

时间解析也是很直观

1
2
3
LocalDate.of(2021, 07, 24);
LocalDate.parse("2021-07-24");
LocalDate.parse("2021/07/24", DateTimeFormatter.ofPattern("yyyy/MM/dd"))

日期的计算

1
2
LocalDate tomorrow = LocalDate.now().plusDays(1);
LocalDate previousMonthSameDay = LocalDate.now().minus(1, ChronoUnit.MONTHS);

常用的方法

1
2
3
4
LocalDate.now().getDayOfMonth();
LocalDate.now().getDayOfWeek();
LocalDate.now().getDayOfYear();
LocalDate.now().isLeapYear();

日期比较

1
2
boolean notBefore = LocalDate.parse("2021-07-12").isBefore(LocalDate.parse("2021-07-11"));
boolean isAfter = LocalDate.parse("2021-07-12").isAfter(LocalDate.parse("2021-07-11"));

其它

1
2
3
LocalDate.now().atStartOfDay();
LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());

LocalTime

和上面用法基本类似

1
2
3
LocalTime.now();
now.format(DateTimeFormatter.ISO_TIME);
now.format(DateTimeFormatter.ofPattern("HH:mm:ss"));

解析时间

1
2
LocalTime.of(6, 30);
LocalTime.parse("06:30");

时间计算

1
2
LocalTime.now().plus(1, ChronoUnit.HOURS);
LocalTime.now().plusHours(1);

获取和比较

1
2
3
LocalTime.now().getHour();
LocalTime.parse("06:30").isBefore(LocalTime.parse("07:30"));
LocalTime.MAX;

LocalDateTime

哈哈,内容基本重复了,同上类似,不再赘述了

1
2
3
4
5
6
LocalDateTime.now();
LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME);
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

LocalDateTime.of(2021, Month.JULY, 20, 06, 30);
LocalDateTime.parse("2021-07-24T06:30:00");

其它的自己看吧,不列了

ZonedDateTime & OffsetDateTime

用于通过时区和时间偏移方式来支持跨时区的时间操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Set<String> allZoneIds = ZoneId.getAvailableZoneIds();
System.out.println(allZoneIds);

// 将新加坡时间转换为巴黎时间
LocalDateTime now = LocalDateTime.now();
ZoneId asiaSingapore = ZoneId.of("Asia/Singapore");
ZoneId europeParis = ZoneId.of("Europe/Paris");
ZonedDateTime zonedDateTimeSingapore = ZonedDateTime.of(now, asiaSingapore);
System.out.println(zonedDateTimeSingapore);
ZonedDateTime zonedDateTimeParis = zonedDateTimeSingapore.withZoneSameInstant(europeParis);
System.out.println(zonedDateTimeParis);

System.out.println(zonedDateTimeSingapore.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
System.out.println(zonedDateTimeSingapore.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSZ zzzz")));

ZonedDateTime parse = ZonedDateTime.parse("2021-07-24T15:15:30+02:00[Europe/Paris]");
System.out.println(parse);
System.out.println(parse.withZoneSameInstant(asiaSingapore));

OffsetDateTime 感觉和使用ZonedDateTime类似,一个用时区,一个用偏移量

1
2
3
4
5
6
7
8
LocalDateTime now = LocalDateTime.now();
ZoneOffset offset = ZoneOffset.of("+08:00");
OffsetDateTime offSetByEight = OffsetDateTime.of(now, offset);
System.out.println(offSetByEight.toLocalDateTime());
System.out.println(offSetByEight.withOffsetSameInstant(ZoneOffset.of("+09:00")));

ZoneId asiaTokyo = ZoneId.of("Asia/Tokyo");
System.out.println(offSetByEight.atZoneSameInstant(asiaTokyo));

关于时间格式化样式字符串,发现还是有个很复杂的标准,目前只知道常用的表达,其它格式详细可以参见Wiki
P.S.之前寻找国家语言等代码也是通过Wiki来找到相应的标准

Period and Duration

用于支持日期和时间的区间计算

例如获得两localDate之间的天数

1
2
Period.between(initialDate, finalDate).getDays();
ChronoUnit.DAYS.between(initialDate, finalDate);

获得两个time时间的秒数或分钟数

1
2
Duration.between(initialTime, finalTime).getSeconds();
ChronoUnit.SECONDS.between(initialTime, finalTime);

EpochSecond

这个值的处理应该是很常用的,我们在传递数据是还是用原点时间戳数值比较多
但是要注意的是,时间戳数值并没有表明时区,但按照Unix系统的定义,我们应该使用UTC时区为基准,计算时间戳,所以都是基于UTC时间转换出的数值
你可以使用此工具进行查看epoch converter

如下,同一个时间戳数值,通过不同的offset获得不同时区的localDateTime

1
2
3
4
LocalDateTime utcDateTime = LocalDateTime.ofEpochSecond(Instant.now().getEpochSecond(), 0, ZoneOffset.UTC);
System.out.println(utcDateTime);
localDateTime = LocalDateTime.ofEpochSecond(Instant.now().getEpochSecond(), 0, ZoneOffset.of("+08:00"));
System.out.println(localDateTime);

或者通过ZonedDateTime

1
2
3
4
5
6
7
8
9
10
11
Instant nowUtc = Instant.now();
ZoneId paris = ZoneId.of("Europe/Paris");
ZoneId asiaSingapore = ZoneId.of("Asia/Singapore");

ZonedDateTime nowParis = ZonedDateTime.ofInstant(nowUtc, paris);
System.out.println(nowParis);

ZonedDateTime nowAsiaSingapore = ZonedDateTime.ofInstant(nowUtc, asiaSingapore);
System.out.println(nowAsiaSingapore);

System.out.println(nowParis.toLocalDateTime());

总结

看似简单的时间类,其实还是有很多细节值得注意的
还一些不错的方法这里没有一一列出.
对于时区和时间戳部分,引发了一些思考,还是有收获的