Skip to content

Commit 6207e32

Browse files
committed
Merge branch 'LeonPoon-more-logical-types'
2 parents 4846c9b + 96e18f3 commit 6207e32

File tree

15 files changed

+241
-19
lines changed

15 files changed

+241
-19
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ runtime, Java classes provided separately (see [Scavro Plugin](https://github.co
7171
|RECORD|case class<br>case class + schema|case class extending `SpecificRecordBase`| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
7272
|PROTOCOL|_No Type_<br>Scala ADT|RPC trait<br>Scala ADT| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
7373
|Date|java.time.LocalDate<br>java.sql.Date|java.time.LocalDate<br>java.sql.Date| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
74+
|TimeMillis|java.time.LocalTime|java.time.LocalTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
75+
|TimeMicros|java.time.LocalTime|java.time.LocalTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
7476
|TimestampMillis|java.time.Instant<br>java.sql.Timestamp|java.time.Instant<br>java.sql.Timestamp| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
77+
|TimestampMicros|java.time.Instant<br>java.sql.Timestamp|java.time.Instant<br>java.sql.Timestamp| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
78+
|LocalTimestampMillis|java.time.LocalDateTime|java.time.LocalDateTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
79+
|LocalTimestampMicros|java.time.LocalDateTime|java.time.LocalDateTime| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
7580
|UUID|java.util.UUID|java.util.UUID| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
7681
|Decimal|BigDecimal|BigDecimal| See [Customizable Type Mapping](https://github.com/julianpeeters/avrohugger#customizable-type-mapping)|
7782

avrohugger-core/src/main/scala/format/specific/converters/JavaConverter.scala

+12
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,18 @@ object JavaConverter {
155155
case JavaSqlTimestamp => BLOCK(tree.DOT("getTime").APPLY())
156156
case JavaTimeInstant => BLOCK(tree.DOT("toEpochMilli"))
157157
}
158+
case _: LogicalTypes.TimestampMicros => (typeMatcher.avroScalaTypes.timestampMicros match {
159+
case JavaTimeZonedDateTime => BLOCK(tree.DOT("toEpochSecond").INFIX("*", LIT(1000000L)).INFIX("+", tree.DOT("getNano").INFIX("/", LIT(1000L))))
160+
}) withComment "avro timestamp-micros long stores the number of microseconds from the unix epoch, 1 January 1970 00:00:00.000000 UTC"
161+
case _: LogicalTypes.LocalTimestampMillis => (typeMatcher.avroScalaTypes.localTimestampMillis match {
162+
case JavaTimeLocalDateTime => BLOCK(tree.DOT("toEpochSecond").APPLY(RootClass.newClass("java.time.ZoneOffset").DOT("UTC")).INFIX("*", LIT(1000L)).INFIX("+", tree.DOT("getNano").INFIX("/", LIT(1000000L))))
163+
}) withComment "avro local-timestamp-millis long stores the number of millis, from 1 January 1970 00:00:00.000000"
164+
case _: LogicalTypes.LocalTimestampMicros => (typeMatcher.avroScalaTypes.localTimestampMicros match {
165+
case JavaTimeLocalDateTime => BLOCK(tree.DOT("toEpochSecond").APPLY(RootClass.newClass("java.time.ZoneOffset").DOT("UTC")).INFIX("*", LIT(1000000L)).INFIX("+", tree.DOT("getNano").INFIX("/", LIT(1000L))))
166+
}) withComment "avro local-timestamp-micros long stores the number of microseconds, from 1 January 1970 00:00:00.000000"
167+
case _: LogicalTypes.TimeMicros => (typeMatcher.avroScalaTypes.timeMicros match {
168+
case JavaTimeLocalTime => BLOCK(tree.DOT("toNanoOfDay").INFIX("/", LIT(1000L)))
169+
}) withComment "avro time-micros long stores the number of microseconds after midnight, 00:00:00.000000"
158170
case _ => tree
159171
}
160172
case Schema.Type.INT => schema.getLogicalType match {

avrohugger-core/src/main/scala/format/specific/converters/ScalaConverter.scala

+47-1
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,17 @@ object ScalaConverter {
197197
}
198198
}
199199
case Schema.Type.LONG => {
200+
val caseLWithTypeLong = CASE(ID("l") withType (LongClass))
200201
Option(schema.getLogicalType()) match {
201202
case Some(logicalType) => {
202-
if (logicalType.getName == "timestamp-millis") {
203+
if (logicalType.getName == "time-micros") {
204+
(typeMatcher.avroScalaTypes.timeMicros match {
205+
case JavaTimeLocalTime =>
206+
val LocalTimeClass = RootClass.newClass("java.time.LocalTime")
207+
val resultExpr = BLOCK(LocalTimeClass.DOT("ofNanoOfDay").APPLY(REF("l").INFIX("*", LIT(1000L))))
208+
tree MATCH caseLWithTypeLong ==> resultExpr
209+
}) withComment "avro time-micros long stores the number of microseconds after midnight, 00:00:00.000000"
210+
} else if (logicalType.getName == "timestamp-millis") {
203211
typeMatcher.avroScalaTypes.timestampMillis match {
204212
case JavaSqlTimestamp => {
205213
val TimestampClass = RootClass.newClass("java.sql.Timestamp")
@@ -214,6 +222,44 @@ object ScalaConverter {
214222
tree MATCH longConversion
215223
}
216224
}
225+
} else if (logicalType.getName == "timestamp-micros") {
226+
(typeMatcher.avroScalaTypes.timestampMicros match {
227+
case JavaTimeZonedDateTime =>
228+
val ZonedDateTime = RootClass.newClass("java.time.ZonedDateTime")
229+
val LocalDateTime = RootClass.newClass("java.time.LocalDateTime")
230+
val ZoneOffset = RootClass.newClass("java.time.ZoneOffset")
231+
val ZoneId = RootClass.newClass("java.time.ZoneId")
232+
val resultExpr = BLOCK(ZonedDateTime.DOT("of").APPLY(LocalDateTime DOT "ofEpochSecond" APPLY(
233+
REF("l").INFIX("/", LIT(1000000L)),
234+
PAREN(REF("l").INFIX("%", LIT(1000000L))) DOT "toInt" INFIX("*", LIT(1000)),
235+
ZoneOffset DOT "UTC"
236+
), ZoneId DOT "of" APPLY LIT("UTC")))
237+
tree MATCH CASE(ID("l") withType (LongClass)) ==> resultExpr
238+
}) withComment "avro timestamp-micros long stores the number of microseconds from the unix epoch, 1 January 1970 00:00:00.000000 UTC"
239+
} else if (logicalType.getName == "local-timestamp-millis") {
240+
(typeMatcher.avroScalaTypes.localTimestampMillis match {
241+
case JavaTimeLocalDateTime =>
242+
val LocalDateTime = RootClass.newClass("java.time.LocalDateTime")
243+
val ZoneOffset = RootClass.newClass("java.time.ZoneOffset")
244+
val resultExpr = BLOCK(LocalDateTime DOT "ofEpochSecond" APPLY(
245+
REF("l").INFIX("/", LIT(1000L)),
246+
PAREN(REF("l").INFIX("%", LIT(1000L))) DOT "toInt" INFIX("*", LIT(1000000)),
247+
ZoneOffset DOT "UTC"
248+
))
249+
tree MATCH CASE(ID("l") withType (LongClass)) ==> resultExpr
250+
}) withComment "avro local-timestamp-millis long stores the number of millis, from 1 January 1970 00:00:00.000000"
251+
} else if (logicalType.getName == "local-timestamp-micros") {
252+
(typeMatcher.avroScalaTypes.localTimestampMicros match {
253+
case JavaTimeLocalDateTime =>
254+
val LocalDateTime = RootClass.newClass("java.time.LocalDateTime")
255+
val ZoneOffset = RootClass.newClass("java.time.ZoneOffset")
256+
val resultExpr = BLOCK(LocalDateTime DOT "ofEpochSecond" APPLY(
257+
REF("l").INFIX("/", LIT(1000000L)),
258+
PAREN(REF("l").INFIX("%", LIT(1000000L))) DOT "toInt" INFIX("*", LIT(1000)),
259+
ZoneOffset DOT "UTC"
260+
))
261+
tree MATCH CASE(ID("l") withType (LongClass)) ==> resultExpr
262+
}) withComment "avro local-timestamp-micros long stores the number of microseconds, from 1 January 1970 00:00:00.000000"
217263
}
218264
else tree
219265
}

avrohugger-core/src/main/scala/matchers/DefaultParamMatcher.scala

+12
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ object DefaultParamMatcher {
4242
case TimestampMillis =>
4343
CustomDefaultParamMatcher.checkCustomTimestampMillisType(
4444
typeMatcher.avroScalaTypes.timestampMillis)
45+
case TimestampMicros =>
46+
CustomDefaultParamMatcher.checkCustomTimestampMicrosType(
47+
typeMatcher.avroScalaTypes.timestampMicros)
48+
case LocalTimestampMillis =>
49+
CustomDefaultParamMatcher.checkCustomLocalTimestampMillisType(
50+
typeMatcher.avroScalaTypes.localTimestampMillis)
51+
case LocalTimestampMicros =>
52+
CustomDefaultParamMatcher.checkCustomLocalTimestampMicrosType(
53+
typeMatcher.avroScalaTypes.localTimestampMicros)
54+
case TimeMicros =>
55+
CustomDefaultParamMatcher.checkCustomTimeMicrosType(
56+
typeMatcher.avroScalaTypes.timeMicros)
4557
}
4658
case Schema.Type.FLOAT => LIT(0F)
4759
case Schema.Type.DOUBLE => LIT(0D)

avrohugger-core/src/main/scala/matchers/TypeMatcher.scala

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ class TypeMatcher(
4949
schema = schema,
5050
default = CustomTypeMatcher.checkCustomNumberType(avroScalaTypes.long)) {
5151
case TimestampMillis => CustomTypeMatcher.checkCustomTimestampMillisType(avroScalaTypes.timestampMillis)
52+
case TimestampMicros => CustomTypeMatcher.checkCustomTimestampMicrosType(avroScalaTypes.timestampMicros)
53+
case LocalTimestampMicros => CustomTypeMatcher.checkCustomLocalTimestampMicrosType(avroScalaTypes.localTimestampMicros)
54+
case LocalTimestampMillis => CustomTypeMatcher.checkCustomLocalTimestampMillisType(avroScalaTypes.localTimestampMillis)
55+
case TimeMicros => CustomTypeMatcher.checkCustomTimeMicrosType(avroScalaTypes.timeMicros)
5256
}
5357
case Schema.Type.INT =>
5458
LogicalType.foldLogicalTypes(

avrohugger-core/src/main/scala/matchers/custom/CustomDefaultParamMatcher.scala

+20
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,24 @@ object CustomDefaultParamMatcher {
5858
case JavaSqlTime => NEW(REF("java.sql.Time"), LIT(0L))
5959
case JavaTimeLocalTime => REF("java.time.LocalTime.now")
6060
}
61+
62+
def checkCustomTimeMicrosType(timeMillisType: AvroScalaTimeType): Tree =
63+
timeMillisType match {
64+
case JavaTimeLocalTime => REF("java.time.LocalTime.MIDNIGHT")
65+
}
66+
67+
def checkCustomTimestampMicrosType(timeMillisType: AvroScalaTimestampType): Tree =
68+
timeMillisType match {
69+
case JavaTimeZonedDateTime => REF("java.time.ZonedDateTime.of").APPLY(REF("java.time.LocalDateTime") DOT "MIN", REF("java.time.ZoneId") DOT "of" APPLY LIT("UTC"))
70+
}
71+
72+
def checkCustomLocalTimestampMillisType(timeMillisType: AvroScalaLocalTimestampType): Tree =
73+
timeMillisType match {
74+
case JavaTimeLocalDateTime => REF("java.time.LocalDateTime") DOT "MIN"
75+
}
76+
77+
def checkCustomLocalTimestampMicrosType(timeMillisType: AvroScalaLocalTimestampType): Tree =
78+
timeMillisType match {
79+
case JavaTimeLocalDateTime => REF("java.time.LocalDateTime") DOT "MIN"
80+
}
6181
}

avrohugger-core/src/main/scala/matchers/custom/CustomTypeMatcher.scala

+16
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,20 @@ object CustomTypeMatcher {
6262
case ScalaBigDecimalWithPrecision(_) => decimalTaggedType(precision, scale)
6363
}
6464
}
65+
66+
def checkCustomTimeMicrosType(timeType: AvroScalaTimeType) = timeType match {
67+
case JavaTimeLocalTime => RootClass.newClass(nme.createNameType("java.time.LocalTime"))
68+
}
69+
70+
def checkCustomTimestampMicrosType(timeType: AvroScalaTimestampType) = timeType match {
71+
case JavaTimeZonedDateTime => RootClass.newClass(nme.createNameType("java.time.ZonedDateTime"))
72+
}
73+
74+
def checkCustomLocalTimestampMicrosType(timeType: AvroScalaLocalTimestampType) = timeType match {
75+
case JavaTimeLocalDateTime => RootClass.newClass(nme.createNameType("java.time.LocalDateTime"))
76+
}
77+
78+
def checkCustomLocalTimestampMillisType(timeType: AvroScalaLocalTimestampType) = timeType match {
79+
case JavaTimeLocalDateTime => RootClass.newClass(nme.createNameType("java.time.LocalDateTime"))
80+
}
6581
}

avrohugger-core/src/main/scala/types/AvroScalaTypes.scala

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ case class AvroScalaTypes(
2424
date: AvroScalaDateType = JavaTimeLocalDate,
2525
timestampMillis: AvroScalaTimestampMillisType = JavaTimeInstant,
2626
timeMillis: AvroScalaTimeMillisType = JavaTimeLocalTime,
27+
timeMicros: AvroScalaTimeType = JavaTimeLocalTime,
28+
timestampMicros: AvroScalaTimestampType = JavaTimeZonedDateTime,
29+
localTimestampMillis: AvroScalaLocalTimestampType = JavaTimeLocalDateTime,
30+
localTimestampMicros: AvroScalaLocalTimestampType = JavaTimeLocalDateTime,
2731
uuid: AvroUuidType = JavaUuid
2832
)
2933

avrohugger-core/src/main/scala/types/LogicalAvroScalaTypes.scala

+16-1
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,25 @@ case object JavaTimeInstant extends AvroScalaTimestampMillisType
1818
sealed trait AvroUuidType extends Product with Serializable
1919
case object JavaUuid extends AvroUuidType
2020

21+
sealed trait AvroScalaTimeType extends Serializable
2122
sealed trait AvroScalaTimeMillisType extends Product with Serializable
2223
case object JavaSqlTime extends AvroScalaTimeMillisType
23-
case object JavaTimeLocalTime extends AvroScalaTimeMillisType
24+
case object JavaTimeLocalTime extends AvroScalaTimeType with AvroScalaTimeMillisType
25+
26+
sealed trait AvroScalaTimestampType extends Serializable
27+
case object JavaTimeZonedDateTime extends AvroScalaTimestampType
28+
29+
sealed trait AvroScalaLocalTimestampType extends Serializable
30+
case object JavaTimeLocalDateTime extends AvroScalaLocalTimestampType
2431

2532
sealed abstract class LogicalType(name: String)
2633
case class Decimal(precision: Int, scale: Int) extends LogicalType("decimal")
2734
case object Date extends LogicalType("date")
2835
case object TimestampMillis extends LogicalType("timestamp-millis")
36+
case object TimestampMicros extends LogicalType("timestamp-micros")
37+
case object LocalTimestampMicros extends LogicalType("local-timestamp-micros")
38+
case object LocalTimestampMillis extends LogicalType("local-timestamp-millis")
39+
case object TimeMicros extends LogicalType("timestamp-millis")
2940
case object TimeMillis extends LogicalType("time-millis")
3041
case object UUID extends LogicalType("uuid")
3142

@@ -35,6 +46,10 @@ object LogicalType {
3546
case d: org.apache.avro.LogicalTypes.Decimal => Some(Decimal(d.getPrecision, d.getScale))
3647
case _: org.apache.avro.LogicalTypes.Date => Some(Date)
3748
case _: org.apache.avro.LogicalTypes.TimestampMillis => Some(TimestampMillis)
49+
case _: org.apache.avro.LogicalTypes.TimestampMicros => Some(TimestampMicros)
50+
case _: org.apache.avro.LogicalTypes.LocalTimestampMillis => Some(LocalTimestampMillis)
51+
case _: org.apache.avro.LogicalTypes.LocalTimestampMicros => Some(LocalTimestampMicros)
52+
case _: org.apache.avro.LogicalTypes.TimeMicros => Some(TimeMicros)
3853
case _: org.apache.avro.LogicalTypes.TimeMillis => Some(TimeMillis)
3954
case _ if logicalType.getName == "uuid" => Some(UUID)
4055
case _ => None

avrohugger-core/src/test/avro/logical.avsc

+28
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,34 @@
5959
"type": "int",
6060
"logicalType": "time-millis"
6161
}
62+
},
63+
{
64+
"name": "timeMicros",
65+
"type": {
66+
"type": "long",
67+
"logicalType": "time-micros"
68+
}
69+
},
70+
{
71+
"name": "timestampMicros",
72+
"type": {
73+
"type": "long",
74+
"logicalType": "timestamp-micros"
75+
}
76+
},
77+
{
78+
"name": "localTimestampMicros",
79+
"type": {
80+
"type": "long",
81+
"logicalType": "local-timestamp-micros"
82+
}
83+
},
84+
{
85+
"name": "localTimestampMillis",
86+
"type": {
87+
"type": "long",
88+
"logicalType": "local-timestamp-millis"
89+
}
6290
}
6391
]
6492
}

0 commit comments

Comments
 (0)