Java8 掌握Date与Java.time转换的核心思路,轻松解决各种时间转换问题



Java8时区时间运用详解,2万字助你通关java.time包




转换核心思路

各种时间类的转换核心只有一个流程:时间类 -> 时间戳 -> 时间类

  • Date和Instant它们存储的都是时间戳信息
  • LocalDateTime、LocalDate、LocalTime、ZonedDateTime、OffsetDateTime、OffsetTime、Year等其它Java8时间类则是年、月、日、时、分、秒、纳秒中的部分值 + 时区或偏移量的组合

从转换角度来说,Date可以直接转为Instant,也可以根据时间戳直接转为其它时间类;Java8的其它时间类之间可以相互转换,也可以转为时间戳再转为Date或Instant。

所以本文有5种思路进行时间转换:

  1. Date转Instant
  2. Instant转Date
  3. Date转java8其它时间类
  4. Java8其它时间类转Date
  5. Java8的时间类互转




时间转换

1. Date —》 Instant

Date内部使用long fastTime存储整个时间戳System.currentTimeMillis(),而Instant则是使用秒long seconds和纳秒int nanos存储整个时间戳。所以它们可以直接互转。这里有一点要注意,时间戳记录的是UTC+0或GMT+0的时间,直接输出Date.toString()时,实际上会自动将GMT+0时间转为系统默认时区时间。

Date date = new Date();
// 1. Date兼容了Instant,可以直接转
Instant instant = date.toInstant(); 
// 2. 或通过时间戳初始化
Instant.ofEpochMilli(date.getTime());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// SimpleDateFormat中使用当前系统的默认时区输出时间,所以需要把时区转为UTC时区才能保证两者区时一致
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(simpleDateFormat.format(date));
System.out.println(instant);

输出时间:

2023-04-25 05:52:43
2023-04-25T05:52:43.246Z



2. Instant —》 Date

Instant与Date的互转方法类似,都是通过时间戳来创建实例:

Instant instant = Instant.now();
// 1. 构造方法转换
Date date = Date.from(instant);
// 2. 通过时间戳初始化
new Date(instant.toEpochMilli());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(simpleDateFormat.format(date));
System.out.println(instant);

输出时间:

2023-04-25 06:05:00
2023-04-25T06:05:00.882Z



3. Date —》 java8时间类

A. 通过转Instant的方式实现转换时间类
下面的时区ZoneId和时间偏移量ZoneOffset可以修改为系统的默认时区ZoneId.systemDefault()以显示和new Date().toString()一样的本地时间。

Date date = new Date();
Instant instant = date.toInstant();
// 1. 通过添加时区、偏移量信息直接转换
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("UTC"));
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC);

// 2. 通过构造方法
ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
OffsetDateTime.ofInstant(instant, ZoneOffset.UTC);
LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

B. 通过时间戳转时间类
正如一开始提到的核心转换思路,我们可以提供Date的时间戳(Instant同理),再将时间戳转为其它时间类:

long timestamp = date.getTime();
// 这里的计算就是Date转Instant,Instant转LocalDateTime的源代码实现方式
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(Math.floorDiv(timestamp, 1000), 
	(int) Math.floorMod(timestamp, 1000), ZoneOffset.UTC);
// 然后通过LocalDateTime转其它类
LocalDate localDate = localDateTime.toLocalDate();
LocalTime localTime = localDateTime.toLocalTime();
ZonedDateTime zonedDateTime1 = localDateTime.atZone(ZoneId.of("UTC"));
OffsetDateTime offsetDateTime1 = localDateTime.atOffset(ZoneOffset.UTC);

C. 通过字符串格式化转换
这种方式比较取巧,不过却很有用,需要注意的点是:LocalDateTime、ZonedDateTime和OffsetDateTime必须提供偏移量信息(或时区信息)。

String pattern = "yyyy-MM-dd HH:mm:ss X";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
String format = simpleDateFormat.format(date);
LocalDateTime.parse(format, dateTimeFormatter);
OffsetDateTime.parse(format, dateTimeFormatter);
ZonedDateTime.parse(format, dateTimeFormatter);

DateTimeFormatter中关于时间格式化中字母代表的匹配含义(部分):

  • V:时区ID,America/Los_Angeles; Z; -08:30
  • z:时区名称或简称, Pacific Standard Time; PST
  • Z:时间偏移量,无指定时区,仅代表偏移量范围,+0000; -0800; -08:00;
  • x:时间偏移量,无指定时区,仅代表偏移量范围,+0000; -08; -0830; -08:30; -083015; -08:30:15;
  • X:世界时(UTC-GMT-UT,取决于时间类支持哪一个世界时)的时间偏移量,Z; -08; -0830; -08:30; -083015; -08:30:15;
  • O:带时区的时间偏移量,GMT+8; GMT+08:00; UTC-08:00;

SimpleDateFormat中关于时间格式化中字母代表的匹配含义(部分):

  • V:无
  • z:时区名称或简称或时区偏移量, Pacific Standard Time; PST;GMT-08:00
  • Z:时间偏移量,无指定时区,仅代表偏移量范围,+0000; -0800; -08:00;
  • x:无
  • X:时间偏移量,无指定时区,仅代表偏移量范围,-08; -0800; -08:00
  • O:无

对比上述三种方法可以看到,Date要转其它时间类,还是通过先转Instant -> 再转其它时间类的方式更方便。



4. Java8时间类 —》 Date

使用Date转java8其它时间类的逆方向转换即可。

初始化时间:

LocalDateTime localDateTime = LocalDateTime.now();
OffsetDateTime offsetDateTime = OffsetDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.now();

A. 通过转Instant的方式实现转换Date

转Instant,再转Date

Instant instant = localDateTime.toInstant(ZoneOffset.ofHours(8));
offsetDateTime.toInstant();
zonedDateTime.toInstant();
Date date = Date.from(instant);
// 输出
System.out.println(localDateTime);
System.out.println(offsetDateTime);
System.out.println(zonedDateTime);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));

输出时间:

2023-04-25T15:18:53.585
2023-04-25T15:18:53.586+08:00
2023-04-25T15:18:53.586+08:00[Asia/Shanghai]
2023-04-25 15:18:53

B. 通过时间戳转Date
localDateTime等时间类可以获取时间的秒级时间戳,所以可以根据这个时间戳直接转为Date。这种转换会损失纳毫秒精度,如果对精度有要求的话,还是得通过转Instant然后调用方法toEpochMilli()来实现。

long epochSecond = localDateTime.toEpochSecond(ZoneOffset.ofHours(8));
offsetDateTime.toEpochSecond();
zonedDateTime.toEpochSecond();
Date date1 = new Date(epochSecond * 1000L);

C. 通过字符串格式化转换
由于Java8中的时间类具备不同的属性(时区、偏移量等),所以通过时间字符串格式化时,LocalDateTime、OffsetDateTime和ZonedDateTime的格式化字符串有所不同。这一点与Date转这些时间类不同,需要在实际转换时注意。

String local = "yyyy-MM-dd HH:mm:ss";
String offset = "yyyy-MM-dd HH:mm:ss X";
String zone = "yyyy-MM-dd HH:mm:ss z";
System.out.println(new SimpleDateFormat(local).parse(DateTimeFormatter.ofPattern(local).format(localDateTime)));
System.out.println(new SimpleDateFormat(offset).parse(DateTimeFormatter.ofPattern(offset).format(offsetDateTime)));
System.out.println(new SimpleDateFormat(zone).parse(DateTimeFormatter.ofPattern(zone).format(zonedDateTime)));

输出时间如下:

Tue Apr 25 16:18:37 CST 2023
Tue Apr 25 16:18:37 CST 2023
Tue Apr 25 16:18:37 CST 2023



5. Java8时间类互转

下面的转换将会提供每一种时间类支持的所有转换情况,包括构造方法、实例对象方法。

A. Instant

Instant instant = Instant.now();
// 通过实例对象转换
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("UTC"));
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC);
// 通过构造方法转换
ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
OffsetDateTime.ofInstant(instant, ZoneOffset.UTC);
LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

B. LocalDateTime
转换OffsetDateTime和ZonedDateTime时,没有toXX方法、且不能使用from(TemporalAccessor temporal)构造方法实例化,因为LocalDateTime没有时区和偏移量信息。

LocalDateTime localDateTime = LocalDateTime.now();
ZoneOffset zoneOffset = ZoneOffset.ofHours(8);
// 通过实例对象转换
Instant instant = localDateTime.toInstant(zoneOffset);
LocalDate localDate = localDateTime.toLocalDate();
LocalTime localTime = localDateTime.toLocalTime();
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneOffset);
OffsetDateTime offsetDateTime = localDateTime.atOffset(zoneOffset);
// 通过构造方法转换
Instant.ofEpochSecond(localDateTime.toEpochSecond(zoneOffset));
LocalDate.from(localDateTime);
LocalTime.from(localDateTime);
OffsetDateTime.of(localDateTime, zoneOffset);
ZonedDateTime.of(localDateTime, zoneOffset);

C. OffsetDateTime
OffsetDateTime和ZonedDateTime的转换相同,使用toXXX直接转为其它时间类,使用其它时间类的from方法复制其时间信息。

OffsetDateTime offsetDateTime = OffsetDateTime.now();
// 通过实例对象转换
Instant instant = offsetDateTime.toInstant();
OffsetTime offsetTime = offsetDateTime.toOffsetTime();
LocalDate localDate = offsetDateTime.toLocalDate();
LocalTime localTime = offsetDateTime.toLocalTime();
LocalDateTime localDateTime = offsetDateTime.toLocalDateTime();
ZonedDateTime zonedDateTime = offsetDateTime.toZonedDateTime();
// 通过构造方法转换
Instant.from(offsetDateTime);
OffsetTime.from(offsetDateTime);
LocalDate.from(offsetDateTime);
LocalTime.from(offsetDateTime);
LocalDateTime.from(offsetDateTime);
ZonedDateTime.from(offsetDateTime);

D.ZonedDateTime

ZonedDateTime zonedDateTime = ZonedDateTime.now();
// 通过实例对象转换
Instant instant = zonedDateTime.toInstant();
LocalDate localDate = zonedDateTime.toLocalDate();
LocalTime localTime = zonedDateTime.toLocalTime();
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
OffsetDateTime offsetDateTime = zonedDateTime.toOffsetDateTime();
// 通过构造方法转换
Instant.from(zonedDateTime);
OffsetTime.from(zonedDateTime);
OffsetDateTime.from(zonedDateTime);
LocalDate.from(zonedDateTime);
LocalTime.from(zonedDateTime);
LocalDateTime.from(zonedDateTime);

猜你喜欢

转载自blog.csdn.net/HO1_K/article/details/130361520