Java8学习笔记:LocalDateTime、Instant 和 OffsetDateTime 相互转换

it2022-05-05  115

环境

Java 1.8+ IDEA:2019.2.4

前言

最近在写接口 由遇到了LocalDate或者LocalDateTime转OffsetDatetime的问题; 遇到这个时,总是有点懵;今天花时间总结下

Java8中时间api

推荐使用的是:

LocalDate LocalTime Insant Duration Period

OffsetDatetime 转 字符串

String DATE_TIME_SECOND_STRING = "yyyy-MM-dd HH:mm:ss"; OffsetDateTime offsetDateTime = 2019-10-10T00:00+08:00 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_SECOND_STRING); String result = dateTimeFormatter.format(offsetDateTime);

offsetDateTime 在程序中的值,如下图:

字符串 转 OffsetDatetime

String DATE_TIME_SECOND_STRING = "yyyy-MM-dd HH:mm:ss"; String value = "2019-10-10 00:00:00"; // 方法一 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_SECOND_STRING); LocalDateTime parse1 = LocalDateTime.parse(value, dateTimeFormatter); // 结果 OffsetDateTime offsetDateTime = ZonedDateTime.of(parse1, ZoneId.systemDefault()).toOffsetDateTime(); // 方法二 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_SECOND_STRING) .withZone(ZoneId.systemDefault()); // 结果 OffsetDateTime offsetDateTime = ZonedDateTime.parse(value, dateTimeFormatter).toOffsetDateTime();

只有年份 yyyy-MM-dd的情况

String value = "2019-10-10"; // 解析的pattern也要做出相应调整 String DATE_SECOND_STRING = "yyyy-MM-dd"; DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_SECOND_STRING); // 这个地方特别注意,使用LocalDateTime会报错的 LocalDate parse = LocalDate.parse(value, dateTimeFormatter); OffsetDateTime offsetDateTime = ZonedDateTime.of(LocalDateTime.of(parse, LocalTime.MIN), ZoneId.systemDefault()).toOffsetDateTime();

LocalDatetime 转 OffsetDatetime

假设我们有:

LocalDateTime localDateTime = LocalDateTime.now();

按照OffsetDateTime提供的方法:

OffsetDateTime of(LocalDateTime dateTime, ZoneOffset offset)

即我们还需要一个ZoneOffset;

而ZoneOffset表示的是格林威治/UTC的时区偏移量,例如+02:00。

所以我们可以这么用:

OffsetDateTime.of(localDateTime, ZoneOffset.of("+8"))) // 或者 OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(8))

LocalDate 转 OffsetDatetime

由上面可知,我们知道LocalDatetime转OffsetDatetime的方法

那么我们可以先将LocalDate转成LocalDatetime;

LocalDate parse = LocalDate.parse(value, dateTimeFormatter); // 先将LocalDate转为LocalDateTime LocalDateTime localDateTime = LocalDateTime.of(parse, LocalTime.MIN); OffsetDateTime offsetDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault()).toOffsetDateTime();

LocalDateTime 转 Instant

根据LocalDateTime自带的方法:

localDateTime.toInstant(ZoneOffset.of(+8)); // 或者 localDateTime.toInstant(ZoneOffset.ofHours(8));

但如果使用如下方式:

Instant.ofEpochSecond(localDateTime.toEpochSecond(ZoneOffset.of("+8")));

上面这句的结果:年月日是对滴,时间的话就是伦敦时间了。

因为toEpochSecond只控制到了秒,而不是毫秒;

Instant 转 OffsetDateTime

由于Instant类中没有提供,所以就去OffsetDateTime里面找:

OffsetDateTime.ofInstant(instant, ZoneId.systemDefault())

出于这样的思路,我们LocalDateTime转OffsetDatetime,就多了一条路,

我们先LocalDateTime转Instant,然后再Instant转LocalDateTime,当然这有点多此一举。

LocalDateTime、Instant 转 毫秒

看了LocalDateTime后才知道,其只提供了转秒的方法,并没有提供转毫秒的方法。

转秒的:

localDateTime.toEpochSecond(ZoneOffset.of("+8"))

如何转毫秒呢?

得借用Instant类中的toEpochMilli方法:

localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()

这样就得到了毫秒;

关系图

这幅图来自于Java8实战这本书籍

从上面的图中我们可以看出,LocalDateTime,并不能表示我们人类世界中完整的时间,而ZonedDateTime可以。

而且上面的转换中我们可以知道,LocalDateTime转Instant或者OffsetDatetime都是需要加上偏移时区的(ZoneOffset)。

所以可以得出 OffsetDatetime和Instant也是可以表示人类世界中完整的时间的,和ZoneDateTime是等效的。

那么区别呢?

OffsetDatetime、Instant和 ZoneDateTime区别

先看下网上的答案:

OffsetDateTime, ZonedDateTime and Instant all store an instant on the time-line to nanosecond precision. Instant is the simplest, simply representing the instant. OffsetDateTime adds to the instant the offset from UTC/Greenwich, which allows the local date-time to be obtained. ZonedDateTime adds full time-zone rules.

OffsetDateTime ,ZonedDateTime 和 Instant 都会在时间线上存储一个纳秒级精度。 Instant 是最简单的,只需代表instant。 OffsetDateTime 添加到UTC / Greenwich的偏移瞬间,这允许获得本地日期时间。 ZonedDateTime 添加完整的时区规则。

Thus the difference between OffsetDateTime and ZonedDateTime is that the latter includes the rules that cover daylight saving time adjustments.

因此 OffsetDateTime 和之间的区别ZonedDateTime 是后者包括涵盖夏令时调整的规则。


个人以为:

首先Instant是给机器看的类,所以我觉得,他肯定能表示世界完整时间。 重点就是OffsetDatetime和ZoneDateTime。 就我上面的例子而言,我觉得没什么区别。 但根据老外的言论,那就是是否包含夏令营规则的区别

最后顺便记录一个老外的提问:

OffsetDateTime should be used when writing date to database, but I don’t get why.

当将日期写入数据库时,为什么要使用OffsetDatetime

答复:

One reason is that dates with local time offsets always represent the same instants in time, and therefore have a stable ordering. By contrast, the meaning of dates with full timezone information is unstable in the face of adjustments to the rules for the respective timezones. (And these do happen…) Dates whose meaning / ordering is unstable are problematic if (for example) you create a database index on a field the date.

一个原因是具有局部时间偏移的日期总是代表相同的时刻,因此具有稳定的排序。 相比之下,面对对各个时区的规则进行调整,具有全时区信息的日期的含义是不稳定的。 (这些确实发生了…)

如果(例如)在日期的字段上创建数据库索引,则其含义/排序不稳定的日期会出现问题。

设置23:59:59 和 00:00:00

// 设置 23:59:59 OffsetDateTime endDateTime = endDateTime.with(LocalTime.MAX); // 设置 00:00:00 OffsetDateTime endDateTime = endDateTime.with(LocalTime.MIN);

参考地址:

https://stackoverflow.com/questions/30234594/whats-the-difference-between-java-8-zoneddatetime-and-offsetdatetime

https://www.yiibai.com/javatime/javatime_zoneoffset.html

https://blog.csdn.net/hspingcc/article/details/73332252


最新回复(0)