2020import java .time .LocalDateTime ;
2121import java .time .LocalTime ;
2222import java .time .OffsetDateTime ;
23+ import java .time .ZoneOffset ;
2324import java .time .ZonedDateTime ;
25+ import java .time .format .DateTimeFormatter ;
26+ import java .time .format .DateTimeFormatterBuilder ;
27+ import java .time .temporal .ChronoField ;
2428import java .util .ArrayList ;
2529import java .util .LinkedHashMap ;
2630import java .util .List ;
4044 * {@code Instant}, etc.).
4145 *
4246 * <p>JSR-310 types ({@code Instant}, {@code OffsetDateTime}, {@code ZonedDateTime}, {@code
43- * LocalDateTime}, {@code LocalDate}, {@code LocalTime}) are round-tripped as their ISO-8601 string
44- * form.
47+ * LocalDateTime}, {@code LocalDate}, {@code LocalTime}) are round-tripped as ISO-8601 strings.
48+ * Types with a sub-second component are serialised with fixed six-digit (microsecond) fractional
49+ * precision; parsing accepts any ISO-8601 fractional precision (or none).
4550 */
4651public final class GsonJsonMapper implements TypedTypeMapper {
4752
@@ -60,17 +65,47 @@ public Gson gson() {
6065 return gson ;
6166 }
6267
68+ private static final DateTimeFormatter LOCAL_TIME_MICROS =
69+ new DateTimeFormatterBuilder ()
70+ .appendPattern ("HH:mm:ss" )
71+ .appendFraction (ChronoField .NANO_OF_SECOND , 6 , 6 , true )
72+ .toFormatter ();
73+
74+ private static final DateTimeFormatter LOCAL_DATE_TIME_MICROS =
75+ new DateTimeFormatterBuilder ()
76+ .append (DateTimeFormatter .ISO_LOCAL_DATE )
77+ .appendLiteral ('T' )
78+ .append (LOCAL_TIME_MICROS )
79+ .toFormatter ();
80+
81+ private static final DateTimeFormatter OFFSET_DATE_TIME_MICROS =
82+ new DateTimeFormatterBuilder ().append (LOCAL_DATE_TIME_MICROS ).appendOffsetId ().toFormatter ();
83+
84+ private static final DateTimeFormatter ZONED_DATE_TIME_MICROS =
85+ new DateTimeFormatterBuilder ()
86+ .append (OFFSET_DATE_TIME_MICROS )
87+ .optionalStart ()
88+ .appendLiteral ('[' )
89+ .parseCaseSensitive ()
90+ .appendZoneRegionId ()
91+ .appendLiteral (']' )
92+ .optionalEnd ()
93+ .toFormatter ();
94+
95+ private static final DateTimeFormatter INSTANT_MICROS =
96+ OFFSET_DATE_TIME_MICROS .withZone (ZoneOffset .UTC );
97+
6398 private static Gson defaultGson () {
6499 return new GsonBuilder ()
65- .registerTypeAdapter (Instant .class , iso (Instant :: toString , Instant ::parse ))
100+ .registerTypeAdapter (Instant .class , iso (INSTANT_MICROS :: format , Instant ::parse ))
66101 .registerTypeAdapter (
67- OffsetDateTime .class , iso (OffsetDateTime :: toString , OffsetDateTime ::parse ))
102+ OffsetDateTime .class , iso (OFFSET_DATE_TIME_MICROS :: format , OffsetDateTime ::parse ))
68103 .registerTypeAdapter (
69- ZonedDateTime .class , iso (ZonedDateTime :: toString , ZonedDateTime ::parse ))
104+ ZonedDateTime .class , iso (ZONED_DATE_TIME_MICROS :: format , ZonedDateTime ::parse ))
70105 .registerTypeAdapter (
71- LocalDateTime .class , iso (LocalDateTime :: toString , LocalDateTime ::parse ))
106+ LocalDateTime .class , iso (LOCAL_DATE_TIME_MICROS :: format , LocalDateTime ::parse ))
72107 .registerTypeAdapter (LocalDate .class , iso (LocalDate ::toString , LocalDate ::parse ))
73- .registerTypeAdapter (LocalTime .class , iso (LocalTime :: toString , LocalTime ::parse ))
108+ .registerTypeAdapter (LocalTime .class , iso (LOCAL_TIME_MICROS :: format , LocalTime ::parse ))
74109 .create ();
75110 }
76111
0 commit comments