Java 8:如何设计一个 Instant 与 String 互转的工具类?

大家好,我是磊磊落落,目前我在技术上主要关注:Java、Golang、架构设计、云原生和自动化测试。欢迎来我的博客(leileiluoluo.com)获取我的最近更新!

本文首先将介绍在 Java 8 之前,传统的 DateString 相互转换的工具类是怎么实现的;接着再探索在 Java 8 新引入 Instant 后,如何实现 InstantString 的互转,以及新的工具类的实现。

1 传统日期转换工具类设计

在 Java 8 之前,我们常使用 Date 来表示日期与时间。设计 DateString 互转的工具类时,只要借助一下 SimpleDateFormat,即可轻松的实现。

示例代码如下:

java 复制代码
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {

    public static Date str2Date(String dateStr, String pattern) {
        try {
            return new SimpleDateFormat(pattern).parse(dateStr);
        } catch (ParseException e) {
            throw new RuntimeException("date parse failed");
        }
    }

    public static String date2Str(Date date, String pattern) {
        return new SimpleDateFormat(pattern).format(date);
    }

    public static void main(String[] args) {
        // string to date
        Date date = DateUtil.str2Date("2023-11-15", "yyyy-MM-dd");

        // date to string
        String str = DateUtil.date2Str(date, "yyyy/MM");
        System.out.println(str);
    }

}

可以看到,如上 DateUtil 工具类的 str2Datedate2Str 方法可以分别实现 StringDate,以及 DateString 的转换。

2 Java 8:Instant 与 String 转换工具类设计

Java 8 中新引入了一个 Instant 类来表示时间线上的一个点(瞬间)。如何设计一个 InstantString 互转的工具类呢?

先看一个错误示例,然后再看一下修正后的正确示例。

Java 8 中,需要借助 DateTimeFormatter 来实现 InstantString 的互转。

下面即是一个未考虑周全的错误示例。

2.1 错误示例

下面尝试封装一下 InstantString 互转的工具类,因其存在一些问题,所以起名 FatalInstantUtil。该工具类的 str2Instant 方法用于 StringInstant 的转换;instant2Str 方法用于 InstantString 的转换。

java 复制代码
// 错误示例
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class FatalInstantUtil {

    public static Instant str2Instant(String dateTimeStr, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

        return LocalDateTime.parse(dateTimeStr, formatter)
                .atZone(ZoneId.systemDefault())
                .toInstant();
    }

    public static String instant2Str(Instant instant, String pattern) {
        return DateTimeFormatter.ofPattern(pattern)
                .format(instant);
    }

    public static void main(String[] args) {
        // string to instant
        Instant instant = str2Instant("2023-11-15", "yyyy-MM-dd");
        System.out.println(instant);

        // instant to string
        String str = instant2Str(Instant.now(), "yyyy-MM");
        System.out.println(str);
    }

}

使用如上工具类的 str2Instant 方法进行 StringInstant 的转换时,如下写法都是可以正常执行的:

java 复制代码
str2Instant("2023-11-15 17:23:56", "yyyy-MM-dd HH:mm:ss");
str2Instant("2023-11-15 17:23", "yyyy-MM-dd HH:mm");
str2Instant("2023-11-15 17", "yyyy-MM-dd HH");

但如下写法会执行失败:

java 复制代码
str2Instant("2023-11-15", "yyyy-MM-dd");
str2Instant("2023-11", "yyyy-MM");
str2Instant("2023", "yyyy");

执行 str2Instant("2023-11-15", "yyyy-MM-dd"); 时的报错信息如下:

text 复制代码
Exception in thread "main" java.time.format.DateTimeParseException: Text '2023-11-15' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2023-11-15 of type java.time.format.Parsed
	at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2023)
	at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1958)
	at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:494)
	at FatalInstantUtil.str2Instant(FatalInstantUtil.java:11)
	at FatalInstantUtil.main(FatalInstantUtil.java:23)
Caused by: java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2023-11-15 of type java.time.format.Parsed
	at java.base/java.time.LocalDateTime.from(LocalDateTime.java:463)
	at java.base/java.time.format.Parsed.query(Parsed.java:241)
	at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
	... 3 more
Caused by: java.time.DateTimeException: Unable to obtain LocalTime from TemporalAccessor: {},ISO resolved to 2023-11-15 of type java.time.format.Parsed
	at java.base/java.time.LocalTime.from(LocalTime.java:433)
	at java.base/java.time.LocalDateTime.from(LocalDateTime.java:459)
	... 5 more

这个异常信息说的不是很清楚,其实原因出在未给 DateTimeFormatter 设置月、日、时、分、秒的默认值;这样,如果这些值缺省时,DateTimeFormatterparse 的时候不知怎么处理这些值,就会抛出 DateTimeParseException 异常。

此外,使用如上工具类的 instant2Str 方法进行 InstantString 的转换时也会报错。

如,执行 instant2Str(Instant.now(), "yyyy-MM"); 时的报错信息如下:

text 复制代码
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra
	at java.base/java.time.Instant.getLong(Instant.java:604)
	at java.base/java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:308)
	at java.base/java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2763)
	at java.base/java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2402)
	at java.base/java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1849)
	at java.base/java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1823)
	at FatalInstantUtil.instant2Str(FatalInstantUtil.java:18)
	at FatalInstantUtil.main(FatalInstantUtil.java:27)

这个异常信息说的也不是很清楚,其实原因出在未给 DateTimeFormatter 指定时区;这样其在 format 的时候不知道转换到哪个时区的格式就会抛出异常。

知道了异常出现的原因后,下面修正一下,看一下正确的示例。

2.2 正确示例

修正后的正确示例代码如下:

java 复制代码
// 正确示例
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;

public class InstantUtil {

    public static Instant str2Instant(String dateTimeStr, String pattern) {
        DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern(pattern)
                .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                .toFormatter();

        return LocalDateTime.parse(dateTimeStr, formatter)
                .atZone(ZoneId.systemDefault())
                .toInstant();
    }

    public static String instant2Str(Instant instant, String pattern) {
        return DateTimeFormatter.ofPattern(pattern)
                .withZone(ZoneId.systemDefault())
                .format(instant);
    }

    public static void main(String[] args) {
        // string to instant
        Instant instant = str2Instant("2023-11-15", "yyyy-MM-dd");
        System.out.println(instant);

        // instant to string
        String str = instant2Str(Instant.now(), "yyyy-MM");
        System.out.println(str);
    }

}

如上修正后的代码中:str2Instant 方法,使用了 DateTimeFormatterBuilder 来构造 DateTimeFormatter,其使用 parseDefaulting 来指定了月、日、时、分、秒的默认值,这样这些值缺省时,会使用指定的默认值来填充,就不会抛异常了。

改造后的 str2Instant 方法,对于如下各种时间与格式的解析都没有问题了:

java 复制代码
str2Instant("2023-11-15 17:23:56.345", "yyyy-MM-dd HH:mm:ss.SSS");
str2Instant("2023-11-15 17:23:56", "yyyy-MM-dd HH:mm:ss");
str2Instant("2023-11-15 17:23", "yyyy-MM-dd HH:mm");
str2Instant("2023-11-15 17", "yyyy-MM-dd HH");
str2Instant("2023-11-15", "yyyy-MM-dd");
str2Instant("2023-11", "yyyy-MM");
str2Instant("2023", "yyyy");

如上修正后的 instant2Str 方法,为 DateTimeFormatter 指定了时区,这样 InstantString 转换也不会抛异常了。

至此,一个可用的 InstantString 互转的工具类就实现好了。

本文所涉及的全部代码已托管至本人 GitHub,欢迎关注或 Fork。

参考资料

1\] [Java: Unable to obtain LocalDateTime from TemporalAccessor when parsing LocalDateTime \| Stack Overflow - stackoverflow.com](https://link.juejin.cn?target=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F27454025%2Funable-to-obtain-localdatetime-from-temporalaccessor-when-parsing-localdatetime "https://stackoverflow.com/questions/27454025/unable-to-obtain-localdatetime-from-temporalaccessor-when-parsing-localdatetime") \[2\] [Java: UnsupportedTemporalTypeException when formatting Instant to String \| Stack Overflow - stackoverflow.com](https://link.juejin.cn?target=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F25229124%2Funsupportedtemporaltypeexception-when-formatting-instant-to-string "https://stackoverflow.com/questions/25229124/unsupportedtemporaltypeexception-when-formatting-instant-to-string")

相关推荐
Wyc7240917 分钟前
SpringBoot
java·spring boot·spring
Bella_chene19 分钟前
IDEA中无法使用JSP内置对象
java·servlet·intellij-idea·jsp
凯基迪科技1 小时前
exe软件壳的分类----加密保护壳
java
wuxuanok1 小时前
Web后端开发-分层解耦
java·笔记·后端·学习
kyle~2 小时前
C/C++字面量
java·c语言·c++
neoooo2 小时前
别慌,Java只有值传递——一次搞懂“为啥我改了它还不变”!
java·后端·spring
秋难降2 小时前
Python 知识 “八股”:给有 C 和 Java 基础的你😁😁😁
java·python·c
wuxuanok2 小时前
Web后端开发-请求响应
java·开发语言·笔记·学习
livemetee2 小时前
spring-ai 1.0.0 (3)交互增强:Advisor 顾问模块
java
DDDDDouble2 小时前
<二>Sping-AI alibaba 入门-记忆聊天及持久化
java·人工智能