diff --git a/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/gson/InstantTypeAdapter.java b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/gson/InstantTypeAdapter.java
new file mode 100644
index 0000000..24e6fb3
--- /dev/null
+++ b/plusone-commons/src/main/java/xyz/zhouxy/plusone/commons/gson/InstantTypeAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package xyz.zhouxy.plusone.commons.gson;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * {@code Instant} 的 {@code TypeAdapter},
+ * 用于 Gson 对 {@code Instant} 进行相互转换。
+ *
+ *
+ * 使用 {@link DateTimeFormatter#ISO_INSTANT} 进行 {@link Instant} 的序列化与反序列化。
+ *
+ *
+ * @author ZhouXY
+ * @since 1.1.0
+ * @see TypeAdapter
+ * @see com.google.gson.GsonBuilder
+ */
+public final class InstantTypeAdapter extends TypeAdapter {
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(JsonWriter out, Instant value) throws IOException {
+ out.value(DateTimeFormatter.ISO_INSTANT.format(value));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Instant read(JsonReader in) throws IOException {
+ return Instant.parse(in.nextString());
+ }
+}
diff --git a/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/gson/GsonTypeAdapterTests.java b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/gson/GsonTypeAdapterTests.java
index b1d2022..b00f5da 100644
--- a/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/gson/GsonTypeAdapterTests.java
+++ b/plusone-commons/src/test/java/xyz/zhouxy/plusone/commons/gson/GsonTypeAdapterTests.java
@@ -17,6 +17,7 @@ package xyz.zhouxy.plusone.commons.gson;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -25,6 +26,7 @@ import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
+import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.google.gson.Gson;
@@ -39,6 +41,7 @@ public class GsonTypeAdapterTests {
.registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter().nullSafe())
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTypeAdapter().nullSafe())
.registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeTypeAdapter().nullSafe())
+ .registerTypeAdapter(Instant.class, new InstantTypeAdapter().nullSafe())
.create();
final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
@@ -60,24 +63,29 @@ public class GsonTypeAdapterTests {
final LocalDate date = LocalDate.of(2025, 6, 6);
final LocalDateTime localDateTime = date.atTime(6, 6, 6, 666000000);
final ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("+08:00"));
+ final Instant instant = zonedDateTime.toInstant();
+ @DisplayName("测试使用 TypeAdapter 中默认的 formatter 进行序列化")
@Test
void test_serialize_defaultFormatter() {
Foo foo = new Foo();
foo.localDate = date;
foo.localDateTime = localDateTime;
foo.zonedDateTime = zonedDateTime;
+ foo.instant = instant;
String json = String.format(
- "{\"localDate\":\"%s\",\"localDateTime\":\"%s\",\"zonedDateTime\":\"%s\"}",
+ "{\"localDate\":\"%s\",\"localDateTime\":\"%s\",\"zonedDateTime\":\"%s\",\"instant\":\"%s\"}",
DateTimeFormatter.ISO_LOCAL_DATE.format(date),
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(localDateTime),
- DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime)
+ DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime),
+ DateTimeFormatter.ISO_INSTANT.format(instant)
);
assertEquals(json, gsonWithDefaultFormatter.toJson(foo));
}
+ @DisplayName("测试指定 formatter 进行序列化")
@Test
void test_serialize_specifiedFormatter() {
Foo foo = new Foo();
@@ -95,20 +103,24 @@ public class GsonTypeAdapterTests {
assertEquals(json, gsonWithSpecifiedFormatter.toJson(foo));
}
+ @DisplayName("测试使用 TypeAdapter 中默认的 formatter 进行反序列化")
@Test
void test_deserialize_defaultFormatter() {
String json = String.format(
- "{\"localDate\":\"%s\",\"localDateTime\":\"%s\",\"zonedDateTime\":\"%s\"}",
+ "{\"localDate\":\"%s\",\"localDateTime\":\"%s\",\"zonedDateTime\":\"%s\",\"instant\":\"%s\"}",
DateTimeFormatter.ISO_LOCAL_DATE.format(date),
DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(localDateTime),
- DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime)
+ DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime),
+ DateTimeFormatter.ISO_INSTANT.format(instant)
);
Foo foo = gsonWithDefaultFormatter.fromJson(json, Foo.class);
assertEquals(date, foo.localDate);
assertEquals(localDateTime, foo.localDateTime);
assertEquals(zonedDateTime, foo.zonedDateTime);
+ assertEquals(instant, foo.instant);
}
+ @DisplayName("测试指定 formatter 进行反序列化")
@Test
void test_deserialize_specifiedFormatter() {
String json = String.format(
@@ -127,5 +139,6 @@ public class GsonTypeAdapterTests {
LocalDate localDate;
LocalDateTime localDateTime;
ZonedDateTime zonedDateTime;
+ Instant instant;
}
}