Java 8 日期、时间与格式化

本文目录

1 Instant

2 ZoneId

3 LocalDateTime

4 ZonedDateTime

5 关系图解

6 时间格式化

7 相关文章


 

1 Instant

Instant 表示基于世界标准时间(UTC)的时间线上的某一个时间点,通常用作事件的时间戳。

Instant 取值为 基于世界标准时间 从 1970/01/01 00:00:00.000 到 某一个时间点 经历的时长(秒 + 纳秒),例如:

instantValue = 0s:表示 1970/01/01 00:00:00.000 (UTC) 或 1970/01/01 08:00:00.000 (UTC+8)

instantValue = 3600s:表示 1970/01/01 01:00:00.000 (UTC) 或 1970/01/01 09:00:00.000 (UTC+8)

// 获取当前时间点
Instant instant = Instant.now();

// 获取指定时间点(毫秒)
Instant instant = Instant.ofEpochMilli(long epochMilli);

// 获取指定时间点(秒)
Instant instant = Instant.ofEpochSecond(long epochSecond);

// 获取时间点值(毫秒)
long instantValue = instant.toEpochMilli();

注意:当通过指定时间点的方式创建Instant对象时,参数值应该是基于世界标准时间的值。

 

2 ZoneId

ZoneId 表示时区,例如:北京时区为东八区,即 UTC+8 。

// 获取当前系统默认时区
ZoneId zoneId = ZoneId.systemDefault();

// 获取指定时区:东八区
ZoneId zoneId = ZoneId.of("+08:00");

 

3 LocalDateTime

LocalDateTime 表示一个不含时区信息的时间,其组成包含两个部分:LocalDate 和 LocalTime

LocalDate 仅包含日期部分(即 年 月 日)

LocalTime 仅包含时间部分(即 时 分 秒)

 

通过1、2两节内容可知,要表示一个时间,需要 Instant 和 ZoneId 两个信息,即 基于世界标准时间的时间点 和 要表示的时间的时区 信息,这样,才可以计算得到一个相应的时间信息,例如:

instantValue = 3600s:表示 1970/01/01 01:00:00.000 (UTC)

zoneId = UTC+8:表示 东八区

localDateTime = 1970/01/01 01:00:00.000(UTC) + 时区偏移量(+8小时) = 1970/01/01 09:00:00.000 (UTC+8)

// 获取当前时间点
Instant instant = Instant.now();

// 获取当前系统默认时区
ZoneId zoneId = ZoneId.systemDefault();

// 获取当前系统默认时区的本地时间
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId);

// 快捷方式:获取当前系统默认时区的本地时间
LocalDateTime nowLocalDateTime = LocalDateTime.now();

 

通过上述说明可知,LocalDateTime仅保存一个时间信息,在构造其对象时,提供的 Instant 和 ZoneId 信息,只是为了计算对应时区的一个时间信息,LocalDateTime 对象构造完成后,即表示一个时间,但并不保存时区信息。因此,也可以通过直接指定时间的方式构造 LocalDateTime 对象。

// 直接指定时间
LocalDateTime localDateTime = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);


由于 LocalDateTime 对象不保存时区信息,因此,就无法从其中获取 Instant 信息。因为没有时区信息,无法确定 LocalDateTime 表示的是哪里的时间,因此,无法确定对应的世界标准时间的时间点。例如:

1970年01月01日 16:00:00

只有一个时间信息,没有时区信息,无法知道这个时间是北京时间,还是纽约时间。

 

在实际应用过程中,LocalDateTime 对象通常用于时间格式化(显示时间),而不用于不同时区的时间转换。

 

4 ZonedDateTime

LocalDateTime 表示一个无时区信息的时间,而 ZonedDateTime 表示一个包含时区信息的时间。

当为 LocalDateTime 附上时区信息时,即可转化为 ZonedDateTime 对象。

// 表示 localDateTime 为 UTC+8 时区的时间  
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("+08:00"));

 

Instant 也是一个仅包含时间信息的对象,因此,为 Instant 附上时区信息时,同样,可转化为 ZonedDateTime 对象。

// 基于 UTC+8 时区,将 instant 转化为 ZonedDateTime 对象。
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("+08:00"));

// 等同于如下代码
ZonedDateTime zonedDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("+08:00")).atZone(ZoneId.of("+08:00"));

 

由于 ZonedDateTime 包含时区信息,因此,可以从其中获取 Instant 对象信息,例如:

1970年01月01日 16:00:00 UTC+8

明确是东八区的时间,则对应的世界标准时间为 1970年01月01日 08:00:00 ,其 instantValue = 28800秒。

// 时区
ZoneId zoneId = zonedDateTime.getZone();

// 时间点
Instant instant = zonedDateTime.toInstant();

// 世界标准时间的时间点值
long timestamp = zonedDateTime.toInstant().toEpochMilli();

 

5 关系图解

关系图解

上图描述了 Instant、ZoneId、LocalDateTime 和 ZonedDateTime 之间的关系。

需要说明的是,Instant 和 ZoneId 结合得到 LocalDateTime 步骤中,ZoneId 仅用于确定时间偏移量,从而计算得到该时区对应的时间,并由 LocalDateTime 对象保存时间信息,而时区信息并没有在 LocalDateTime 对象保存。

 

常见的时间异常情况,大部分是由于时区问题没有处理正确导致。这种情况,在网络传递时间数据的时候,时常发生,其根本原因是两端基于不同的时区对时间进行处理,例如:

服务端:基于UTC,返回时间字符串信息,如 timestamp = 1970/01/01 08:00:00,对应北京时间为 1970/01/01 16:00:00。

客户端:基于本地时区,处理服务端返回的时间字符串信息,即 将 1970/01/01 08:00:00 当成北京时间,导致时间差8小时。

时间异常图示

 

解决方案:以Instant值作为时间值进行存储和网络传输,各端以Instant值为基础,各自构造其所需时区的时间对象。

解决方案图示

 

6 时间格式化

在 Java 8 之前,通常使用 SimpleDateFormat 对时间进行格式化,但 SimpleDateFormat 不是线程安全的类,即 在多线程环境下使用共享的 SimpleDateFormat 对象(例如 static 对象),则可能发生异常情况。

因此,为了保证线程安全,在每个线程中,需要创建一个 SimpleDateFormat 对象,保证不同线程之间不会共享一个 SimpleDateFormat 对象。或者,通过 ThreadLocal 方式,达到线程隔离效果。

在 Java 8 之后,JDK 提供了 DateTimeFormatter 类,用于提供时间格式化方法。

// 初始化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

// 获取 当前系统默认时区 的 本地时间
LocalDateTime nowDateTime = LocalDateTime.now();

// 格式化时间
String dateTimeString = formatter.format(nowDateTime);

// 解析 时间字符串 得到 LocalDateTime 对象
LocalDateTime dateTimeObj = LocalDateTime.from(formatter.parse(dateTimeString));

 

7 相关文章

《字符串格式化:Formatter类》

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值