Skip to content

Commit 883fb8f

Browse files
authored
fix: Long timestamp default decoding (#443)
1 parent 91b9f6f commit 883fb8f

File tree

3 files changed

+73
-13
lines changed

3 files changed

+73
-13
lines changed

codec.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import (
1111
)
1212

1313
var (
14-
timeType = reflect.TypeOf(time.Time{})
15-
ratType = reflect.TypeOf(big.Rat{})
16-
durType = reflect.TypeOf(LogicalDuration{})
14+
timeType = reflect.TypeOf(time.Time{})
15+
timeDurationType = reflect.TypeOf(time.Duration(0))
16+
ratType = reflect.TypeOf(big.Rat{})
17+
durType = reflect.TypeOf(LogicalDuration{})
1718
)
1819

1920
type null struct{}

codec_native.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,17 @@ func createDecoderOfNative(schema *PrimitiveSchema, typ reflect2.Type) ValDecode
8383
convert: createLongConverter(schema.encodedType),
8484
}
8585

86-
case st == Long && lt == "":
86+
case st == Long:
87+
isTimestamp := (lt == TimestampMillis || lt == TimestampMicros)
88+
if isTimestamp && typ.Type1() == timeDurationType {
89+
return &errorDecoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s and logicalType %s",
90+
typ.Type1().String(), schema.Type(), lt)}
91+
}
8792
if resolved {
8893
return &longConvCodec[int64]{convert: createLongConverter(schema.encodedType)}
8994
}
9095
return &longCodec[int64]{}
9196

92-
case lt != "":
93-
return &errorDecoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s and logicalType %s",
94-
typ.String(), schema.Type(), lt)}
95-
9697
default:
9798
break
9899
}
@@ -245,13 +246,14 @@ func createEncoderOfNative(schema Schema, typ reflect2.Type) ValEncoder {
245246
case st == Long && lt == TimeMicros: // time.Duration
246247
return &timeMicrosCodec{}
247248

248-
case st == Long && lt == "":
249+
case st == Long:
250+
isTimestamp := (lt == TimestampMillis || lt == TimestampMicros)
251+
if isTimestamp && typ.Type1() == timeDurationType {
252+
return &errorEncoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s and logicalType %s",
253+
typ.Type1().String(), schema.Type(), lt)}
254+
}
249255
return &longCodec[int64]{}
250256

251-
case lt != "":
252-
return &errorEncoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s and logicalType %s",
253-
typ.String(), schema.Type(), lt)}
254-
255257
default:
256258
break
257259
}

schema_compatibility_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package avro_test
22

33
import (
44
"math/big"
5+
"strconv"
56
"testing"
67
"time"
78

@@ -815,6 +816,62 @@ func TestSchemaCompatibility_Resolve(t *testing.T) {
815816
"b": map[string]any{"a": int64(20)},
816817
},
817818
},
819+
{
820+
name: "Record Writer Field Missing With Long timestamp-millis Default",
821+
reader: `{
822+
"type":"record", "name":"test", "namespace": "org.hamba.avro",
823+
"fields":[
824+
{"name": "a", "type": "string"},
825+
{
826+
"name": "b",
827+
"type": {
828+
"type": "long",
829+
"logicalType": "timestamp-millis"
830+
},
831+
"default": ` + strconv.FormatInt(1725616800000, 10) + `
832+
}
833+
]
834+
}`,
835+
writer: `{
836+
"type":"record", "name":"test", "namespace": "org.hamba.avro",
837+
"fields":[
838+
{"name": "a", "type": "string"}
839+
]
840+
}`,
841+
value: map[string]any{"a": "foo"},
842+
want: map[string]any{
843+
"a": "foo",
844+
"b": time.UnixMilli(1725616800000).UTC(), // 2024-09-06 10:00:00
845+
},
846+
},
847+
{
848+
name: "Record Writer Field Missing With Long timestamp-micros Default",
849+
reader: `{
850+
"type":"record", "name":"test", "namespace": "org.hamba.avro",
851+
"fields":[
852+
{"name": "a", "type": "string"},
853+
{
854+
"name": "b",
855+
"type": {
856+
"type": "long",
857+
"logicalType": "timestamp-micros"
858+
},
859+
"default": ` + strconv.FormatInt(1725616800000000, 10) + `
860+
}
861+
]
862+
}`,
863+
writer: `{
864+
"type":"record", "name":"test", "namespace": "org.hamba.avro",
865+
"fields":[
866+
{"name": "a", "type": "string"}
867+
]
868+
}`,
869+
value: map[string]any{"a": "foo"},
870+
want: map[string]any{
871+
"a": "foo",
872+
"b": time.UnixMicro(1725616800000000).UTC(), // 2024-09-06 10:00:00
873+
},
874+
},
818875
}
819876

820877
for _, test := range tests {

0 commit comments

Comments
 (0)