Skip to content

Commit 0d8b24d

Browse files
committed
BE: Serde: Impl Avro type for timestamp-nanos and local-timestamp-nanos logical types
1 parent 3121341 commit 0d8b24d

File tree

4 files changed

+70
-4
lines changed

4 files changed

+70
-4
lines changed

api/src/main/java/io/kafbat/ui/util/jsonschema/JsonAvroConversion.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,24 @@ enum LogicalTypeConversion {
462462
Map.of(FORMAT, new TextNode(DATE_TIME))))
463463
),
464464

465+
TIMESTAMP_NANOS("timestamp-nanos",
466+
(node, schema) -> {
467+
if (node.isIntegralNumber()) {
468+
long nanosFromEpoch = node.longValue();
469+
long epochSeconds = nanosFromEpoch / 1_000_000_000L;
470+
long nanoAdjustment = nanosFromEpoch % 1_000_000_000L;
471+
return Instant.ofEpochSecond(epochSeconds, nanoAdjustment);
472+
} else if (node.isTextual()) {
473+
return Instant.parse(node.asText());
474+
} else {
475+
throw new JsonAvroConversionException("node '%s' can't be converted to timestamp-nanos logical type"
476+
.formatted(node));
477+
}
478+
},
479+
(obj, schema) -> new TextNode(obj.toString()),
480+
new SimpleFieldSchema(new SimpleJsonType(JsonType.Type.STRING, Map.of(FORMAT, new TextNode(DATE_TIME))))
481+
),
482+
465483
LOCAL_TIMESTAMP_MILLIS("local-timestamp-millis",
466484
(node, schema) -> {
467485
if (node.isTextual()) {
@@ -491,6 +509,18 @@ enum LogicalTypeConversion {
491509
new SimpleJsonType(
492510
JsonType.Type.STRING,
493511
Map.of(FORMAT, new TextNode(DATE_TIME))))
512+
),
513+
514+
LOCAL_TIMESTAMP_NANOS("local-timestamp-nanos",
515+
(node, schema) -> {
516+
if (node.isTextual()) {
517+
return LocalDateTime.parse(node.asText());
518+
}
519+
Instant instant = (Instant) TIMESTAMP_NANOS.jsonToAvroConversion.apply(node, schema);
520+
return LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
521+
},
522+
(obj, schema) -> new TextNode(obj.toString()),
523+
new SimpleFieldSchema(new SimpleJsonType(JsonType.Type.STRING, Map.of(FORMAT, new TextNode(DATE_TIME))))
494524
);
495525

496526
private final String name;

api/src/test/java/io/kafbat/ui/serdes/builtin/sr/SchemaRegistrySerdeTest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,13 +341,21 @@ void avroLogicalTypesRepresentationIsConsistentForSerializationAndDeserializatio
341341
"name": "lt_timestamp_micros",
342342
"type": { "type": "long", "logicalType": "timestamp-micros" }
343343
},
344+
{
345+
"name": "lt_timestamp_nanos",
346+
"type": { "type": "long", "logicalType": "timestamp-nanos" }
347+
},
344348
{
345349
"name": "lt_local_timestamp_millis",
346350
"type": { "type": "long", "logicalType": "local-timestamp-millis" }
347351
},
348352
{
349353
"name": "lt_local_timestamp_micros",
350354
"type": { "type": "long", "logicalType": "local-timestamp-micros" }
355+
},
356+
{
357+
"name": "lt_local_timestamp_nanos",
358+
"type": { "type": "long", "logicalType": "local-timestamp-nanos" }
351359
}
352360
]
353361
}"""
@@ -362,8 +370,10 @@ void avroLogicalTypesRepresentationIsConsistentForSerializationAndDeserializatio
362370
"lt_uuid": "a37b75ca-097c-5d46-6119-f0637922e908",
363371
"lt_timestamp_millis": "2007-12-03T10:15:30.123Z",
364372
"lt_timestamp_micros": "2007-12-03T10:15:30.123456Z",
373+
"lt_timestamp_nanos": "2007-12-03T10:15:30.123456789Z",
365374
"lt_local_timestamp_millis": "2017-12-03T10:15:30.123",
366-
"lt_local_timestamp_micros": "2017-12-03T10:15:30.123456"
375+
"lt_local_timestamp_micros": "2017-12-03T10:15:30.123456",
376+
"lt_local_timestamp_nanos": "2017-12-03T10:15:30.123456789"
367377
}
368378
""";
369379

api/src/test/java/io/kafbat/ui/util/jsonschema/JsonAvroConversionTest.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,13 +384,21 @@ void logicalTypesField() {
384384
"name": "lt_timestamp_micros",
385385
"type": { "type": "long", "logicalType": "timestamp-micros" }
386386
},
387+
{
388+
"name": "lt_timestamp_nanos",
389+
"type": { "type": "long", "logicalType": "timestamp-nanos" }
390+
},
387391
{
388392
"name": "lt_local_timestamp_millis",
389393
"type": { "type": "long", "logicalType": "local-timestamp-millis" }
390394
},
391395
{
392396
"name": "lt_local_timestamp_micros",
393397
"type": { "type": "long", "logicalType": "local-timestamp-micros" }
398+
},
399+
{
400+
"name": "lt_local_timestamp_nanos",
401+
"type": { "type": "long", "logicalType": "local-timestamp-nanos" }
394402
}
395403
]
396404
}"""
@@ -405,8 +413,10 @@ void logicalTypesField() {
405413
"lt_uuid": "a37b75ca-097c-5d46-6119-f0637922e908",
406414
"lt_timestamp_millis": "2007-12-03T10:15:30.123Z",
407415
"lt_timestamp_micros": "2007-12-13T10:15:30.123456Z",
416+
"lt_timestamp_nanos": "2007-12-13T10:15:30.123456789Z",
408417
"lt_local_timestamp_millis": "2017-12-03T10:15:30.123",
409-
"lt_local_timestamp_micros": "2017-12-13T10:15:30.123456"
418+
"lt_local_timestamp_micros": "2017-12-13T10:15:30.123456",
419+
"lt_local_timestamp_nanos": "2017-12-13T10:15:30.123456789"
410420
}
411421
""";
412422

@@ -427,10 +437,14 @@ var record = (GenericData.Record) converted;
427437
.isEqualTo(Instant.parse("2007-12-03T10:15:30.123Z"));
428438
assertThat(record.get("lt_timestamp_micros"))
429439
.isEqualTo(Instant.parse("2007-12-13T10:15:30.123456Z"));
440+
assertThat(record.get("lt_timestamp_nanos"))
441+
.isEqualTo(Instant.parse("2007-12-13T10:15:30.123456789Z"));
430442
assertThat(record.get("lt_local_timestamp_millis"))
431443
.isEqualTo(LocalDateTime.parse("2017-12-03T10:15:30.123"));
432444
assertThat(record.get("lt_local_timestamp_micros"))
433445
.isEqualTo(LocalDateTime.parse("2017-12-13T10:15:30.123456"));
446+
assertThat(record.get("lt_local_timestamp_nanos"))
447+
.isEqualTo(LocalDateTime.parse("2017-12-13T10:15:30.123456789"));
434448
}
435449
}
436450

@@ -582,13 +596,21 @@ void logicalTypesField() {
582596
"name": "lt_timestamp_micros",
583597
"type": { "type": "long", "logicalType": "timestamp-micros" }
584598
},
599+
{
600+
"name": "lt_timestamp_nanos",
601+
"type": { "type": "long", "logicalType": "timestamp-nanos" }
602+
},
585603
{
586604
"name": "lt_local_timestamp_millis",
587605
"type": { "type": "long", "logicalType": "local-timestamp-millis" }
588606
},
589607
{
590608
"name": "lt_local_timestamp_micros",
591609
"type": { "type": "long", "logicalType": "local-timestamp-micros" }
610+
},
611+
{
612+
"name": "lt_local_timestamp_nanos",
613+
"type": { "type": "long", "logicalType": "local-timestamp-nanos" }
592614
}
593615
]
594616
}"""
@@ -602,8 +624,10 @@ void logicalTypesField() {
602624
inputRecord.put("lt_time_micros", LocalTime.parse("10:15:30.123456"));
603625
inputRecord.put("lt_timestamp_millis", Instant.parse("2007-12-03T10:15:30.123Z"));
604626
inputRecord.put("lt_timestamp_micros", Instant.parse("2007-12-13T10:15:30.123456Z"));
627+
inputRecord.put("lt_timestamp_nanos", Instant.parse("2007-12-13T10:15:30.123456789Z"));
605628
inputRecord.put("lt_local_timestamp_millis", LocalDateTime.parse("2017-12-03T10:15:30.123"));
606629
inputRecord.put("lt_local_timestamp_micros", LocalDateTime.parse("2017-12-13T10:15:30.123456"));
630+
inputRecord.put("lt_local_timestamp_nanos", LocalDateTime.parse("2017-12-13T10:15:30.123456789"));
607631

608632
String expectedJson = """
609633
{
@@ -614,8 +638,10 @@ void logicalTypesField() {
614638
"lt_time_micros": "10:15:30.123456",
615639
"lt_timestamp_millis": "2007-12-03T10:15:30.123Z",
616640
"lt_timestamp_micros": "2007-12-13T10:15:30.123456Z",
641+
"lt_timestamp_nanos": "2007-12-13T10:15:30.123456789Z",
617642
"lt_local_timestamp_millis": "2017-12-03T10:15:30.123",
618-
"lt_local_timestamp_micros": "2017-12-13T10:15:30.123456"
643+
"lt_local_timestamp_micros": "2017-12-13T10:15:30.123456",
644+
"lt_local_timestamp_nanos": "2017-12-13T10:15:30.123456789"
619645
}
620646
""";
621647

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
<antlr4-maven-plugin.version>4.12.0</antlr4-maven-plugin.version>
3434
<apache.commons.version>2.12.0</apache.commons.version>
3535
<assertj.version>3.26.3</assertj.version>
36-
<avro.version>1.11.4</avro.version>
36+
<avro.version>1.12.0</avro.version>
3737
<byte-buddy.version>1.15.11</byte-buddy.version>
3838
<confluent.version>7.8.0</confluent.version>
3939
<datasketches-java.version>3.1.0</datasketches-java.version>

0 commit comments

Comments
 (0)