From 5a6b4e4f19f54e60a7a3ce56bb5b4fdbed41373b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 29 Jul 2021 23:05:39 +0200 Subject: [PATCH] fix(topicdata): parse microseconds at any precision between 0 and 6 (#762) partial fix for #761 --- .gitignore | 4 +- .../java/org/akhq/utils/AvroSerializer.java | 17 +-- .../org/akhq/utils/AvroSerializerTest.java | 106 ++++++++++++++++++ 3 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 src/test/java/org/akhq/utils/AvroSerializerTest.java diff --git a/.gitignore b/.gitignore index b5920519c..a6f451a83 100644 --- a/.gitignore +++ b/.gitignore @@ -25,9 +25,11 @@ client/build ### Ide ### .idea/* .vscode/* -.project/* +.classpath* +.project* .settings/* out/* +bin/* ### Log ### .*.log diff --git a/src/main/java/org/akhq/utils/AvroSerializer.java b/src/main/java/org/akhq/utils/AvroSerializer.java index 89f1cef98..78f45bd42 100644 --- a/src/main/java/org/akhq/utils/AvroSerializer.java +++ b/src/main/java/org/akhq/utils/AvroSerializer.java @@ -15,7 +15,9 @@ import java.nio.ByteBuffer; import java.time.*; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; import java.util.AbstractMap; import java.util.Collection; import java.util.Map; @@ -41,7 +43,12 @@ public class AvroSerializer { protected static final String DATE_FORMAT = "yyyy-MM-dd[XXX]"; protected static final String TIME_FORMAT = "HH:mm[:ss][.SSSSSS][XXX]"; - protected static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm[:ss][.SSSSSS][XXX]"; + protected static final DateTimeFormatter DATETIME_FORMAT = new DateTimeFormatterBuilder() + .append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm")) + .appendOptional(DateTimeFormatter.ofPattern(":ss")) + .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true) + .appendOptional(DateTimeFormatter.ofPattern("XXX")) + .toFormatter(); public static GenericRecord recordSerializer(Map record, Schema schema) { GenericRecord returnValue = new GenericData.Record(schema); @@ -199,13 +206,9 @@ private static Long timestampMillisSerializer(Object data, Schema schema, Schema protected static Instant parseDateTime(String data) { try { - return ZonedDateTime.parse(data, DateTimeFormatter.ofPattern(AvroSerializer.DATETIME_FORMAT)).toInstant(); + return ZonedDateTime.parse(data, DATETIME_FORMAT).toInstant(); } catch (DateTimeParseException e) { - LocalDateTime localDateTime = LocalDateTime.parse( - data, - DateTimeFormatter.ofPattern(AvroSerializer.DATETIME_FORMAT) - ); - + LocalDateTime localDateTime = LocalDateTime.parse(data, DATETIME_FORMAT); return localDateTime.atZone(ZoneId.systemDefault()).toInstant(); } } diff --git a/src/test/java/org/akhq/utils/AvroSerializerTest.java b/src/test/java/org/akhq/utils/AvroSerializerTest.java new file mode 100644 index 000000000..2c779c77f --- /dev/null +++ b/src/test/java/org/akhq/utils/AvroSerializerTest.java @@ -0,0 +1,106 @@ +package org.akhq.utils; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AvroSerializerTest { + + @Nested + static class ParseDateTime { + + @Nested + static class Utc { + + @Test + void testParseDateTime_micros_utc() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12.345678Z"), + Instant.parse("2021-07-16T21:30:12.345678Z")); + } + + @Test + void testParseDateTime_millis_utc() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12.345Z"), + Instant.parse("2021-07-16T21:30:12.345Z")); + } + + @Test + void testParseDateTime_seconds_utc() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12Z"), + Instant.parse("2021-07-16T21:30:12Z")); + } + + @Test + void testParseDateTime_minutes_utc() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30Z"), + Instant.parse("2021-07-16T21:30:00Z")); + } + + } + + @Nested + static class Offset { + + @Test + void testParseDateTime_micros_offset() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12.345678+08:00"), + Instant.parse("2021-07-16T13:30:12.345678Z")); + } + + @Test + void testParseDateTime_millis_offset() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12.345+08:00"), + Instant.parse("2021-07-16T13:30:12.345Z")); + } + + @Test + void testParseDateTime_seconds_offset() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12+08:00"), + Instant.parse("2021-07-16T13:30:12Z")); + } + + @Test + void testParseDateTime_minutes_offset() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30+08:00"), + Instant.parse("2021-07-16T13:30:00Z")); + } + + } + + @Nested + static class Local { + + @Test + void testParseDateTime_micros_local() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12.345678"), + LocalDateTime.parse("2021-07-16T21:30:12.345678").atZone(ZoneId.systemDefault()).toInstant()); + } + + @Test + void testParseDateTime_millis_local() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12.345"), + LocalDateTime.parse("2021-07-16T21:30:12.345").atZone(ZoneId.systemDefault()).toInstant()); + } + + @Test + void testParseDateTime_seconds_local() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30:12"), + LocalDateTime.parse("2021-07-16T21:30:12").atZone(ZoneId.systemDefault()).toInstant()); + } + + @Test + void testParseDateTime_minutes_local() { + assertEquals(AvroSerializer.parseDateTime("2021-07-16T21:30"), + LocalDateTime.parse("2021-07-16T21:30").atZone(ZoneId.systemDefault()).toInstant()); + } + + } + + } + +}