diff --git a/lib/debezium/converters/time.go b/lib/debezium/converters/time.go index 6f276e6f8..6c2bba4ab 100644 --- a/lib/debezium/converters/time.go +++ b/lib/debezium/converters/time.go @@ -72,7 +72,7 @@ func (m MicroTime) Convert(value any) (any, error) { type ZonedTimestamp struct{} func (ZonedTimestamp) layout() string { - return "2006-01-02T15:04:05.999999999Z" + return time.RFC3339Nano } func (z ZonedTimestamp) ToKindDetails() (typing.KindDetails, error) { @@ -108,7 +108,7 @@ func (z ZonedTimestamp) Convert(value any) (any, error) { type TimeWithTimezone struct{} func (t TimeWithTimezone) layout() string { - return "15:04:05.999999Z" + return "15:04:05.999999" + ext.TimezoneOffsetFormat } func (t TimeWithTimezone) ToKindDetails() (typing.KindDetails, error) { diff --git a/lib/debezium/converters/time_test.go b/lib/debezium/converters/time_test.go index d8d4e66a8..7ec983f96 100644 --- a/lib/debezium/converters/time_test.go +++ b/lib/debezium/converters/time_test.go @@ -35,7 +35,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:12Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 12, 000000000, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 12, 000000000, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:12Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -44,7 +44,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.1Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 100000000, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 100000000, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.1Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -53,7 +53,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.12Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 120000000, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 120000000, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.12Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -62,7 +62,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.123Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123000000, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123000000, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.123Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -71,7 +71,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.1234Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123400000, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123400000, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.1234Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -80,7 +80,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.12345Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123450000, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123450000, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.12345Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -89,7 +89,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.123456Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456000, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456000, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.123456Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -98,7 +98,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.1234567Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456700, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456700, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.1234567Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -107,7 +107,7 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.12345678Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456780, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456780, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.12345678Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } @@ -116,10 +116,20 @@ func TestZonedTimestamp_Convert(t *testing.T) { val, err := ZonedTimestamp{}.Convert("2025-09-13T00:00:00.123456789Z") assert.NoError(t, err) - expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456789, time.UTC), ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z") + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456789, time.UTC), ext.TimestampTZKindType, time.RFC3339Nano) assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) assert.Equal(t, "2025-09-13T00:00:00.123456789Z", val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) } + { + // Testing timezone offset + ts := "2025-09-13T00:00:00.123456789+07:00" + val, err := ZonedTimestamp{}.Convert(ts) + assert.NoError(t, err) + + expectedExtTime := ext.NewExtendedTime(time.Date(2025, time.September, 13, 0, 0, 0, 123456789, time.FixedZone("", 7*60*60)), ext.TimestampTZKindType, time.RFC3339Nano) + assert.Equal(t, expectedExtTime, val.(*ext.ExtendedTime)) + assert.Equal(t, ts, val.(*ext.ExtendedTime).GetTime().Format(ZonedTimestamp{}.layout())) + } } } diff --git a/lib/debezium/schema_test.go b/lib/debezium/schema_test.go index 367bca237..97a8007e6 100644 --- a/lib/debezium/schema_test.go +++ b/lib/debezium/schema_test.go @@ -2,6 +2,7 @@ package debezium import ( "testing" + "time" "github.com/stretchr/testify/assert" @@ -230,11 +231,9 @@ func TestField_ToKindDetails(t *testing.T) { } { // Timestamp with timezone - for _, dbzType := range []SupportedDebeziumType{ZonedTimestamp} { - kd, err := Field{DebeziumType: dbzType}.ToKindDetails() - assert.NoError(t, err) - assert.Equal(t, typing.MustNewExtendedTimeDetails(typing.ETime, ext.TimestampTZKindType, "2006-01-02T15:04:05.999999999Z"), kd) - } + kd, err := Field{DebeziumType: ZonedTimestamp}.ToKindDetails() + assert.NoError(t, err) + assert.Equal(t, typing.MustNewExtendedTimeDetails(typing.ETime, ext.TimestampTZKindType, time.RFC3339Nano), kd) } { // Timestamp without timezone @@ -259,7 +258,7 @@ func TestField_ToKindDetails(t *testing.T) { for _, dbzType := range []SupportedDebeziumType{TimeWithTimezone} { kd, err := Field{DebeziumType: dbzType}.ToKindDetails() assert.NoError(t, err) - assert.Equal(t, typing.MustNewExtendedTimeDetails(typing.ETime, ext.TimeKindType, "15:04:05.999999Z"), kd, dbzType) + assert.Equal(t, typing.MustNewExtendedTimeDetails(typing.ETime, ext.TimeKindType, "15:04:05.999999"+ext.TimezoneOffsetFormat), kd, dbzType) } } { diff --git a/lib/typing/ext/parse_test.go b/lib/typing/ext/parse_test.go index c278c626f..d86f9efe1 100644 --- a/lib/typing/ext/parse_test.go +++ b/lib/typing/ext/parse_test.go @@ -40,19 +40,19 @@ func TestParseFromInterface(t *testing.T) { // String - RFC3339MillisecondUTC value, err := ParseFromInterface("2024-09-19T16:05:18.630Z", TimestampTZKindType) assert.NoError(t, err) - assert.Equal(t, "2024-09-19T16:05:18.630Z", value.Format(RFC3339MillisecondUTC)) + assert.Equal(t, "2024-09-19T16:05:18.630Z", value.Format(RFC3339Millisecond)) } { // String - RFC3339MicrosecondUTC value, err := ParseFromInterface("2024-09-19T16:05:18.630000Z", TimestampTZKindType) assert.NoError(t, err) - assert.Equal(t, "2024-09-19T16:05:18.630000Z", value.Format(RFC3339MicrosecondUTC)) + assert.Equal(t, "2024-09-19T16:05:18.630000Z", value.Format(RFC3339Microsecond)) } { // String - RFC3339NanosecondUTC value, err := ParseFromInterface("2024-09-19T16:05:18.630000000Z", TimestampTZKindType) assert.NoError(t, err) - assert.Equal(t, "2024-09-19T16:05:18.630000000Z", value.Format(RFC3339NanosecondUTC)) + assert.Equal(t, "2024-09-19T16:05:18.630000000Z", value.Format(RFC3339Nanosecond)) } } diff --git a/lib/typing/ext/variables.go b/lib/typing/ext/variables.go index 0a1f0387c..fe040a967 100644 --- a/lib/typing/ext/variables.go +++ b/lib/typing/ext/variables.go @@ -11,11 +11,8 @@ const ( ) var supportedDateTimeLayouts = []string{ - // UTC - RFC3339MillisecondUTC, - RFC3339MicrosecondUTC, - RFC3339NanosecondUTC, // RFC 3339 + time.RFC3339Nano, RFC3339Millisecond, RFC3339Microsecond, RFC3339Nanosecond, @@ -49,9 +46,6 @@ const TimezoneOffsetFormat = "Z07:00" // RFC3339 variants const ( RFC3339NoTZ = "2006-01-02T15:04:05.999999999" - RFC3339MillisecondUTC = "2006-01-02T15:04:05.000Z" - RFC3339MicrosecondUTC = "2006-01-02T15:04:05.000000Z" - RFC3339NanosecondUTC = "2006-01-02T15:04:05.000000000Z" RFC3339Millisecond = "2006-01-02T15:04:05.000" + TimezoneOffsetFormat RFC3339MillisecondNoTZ = "2006-01-02T15:04:05.000" RFC3339Microsecond = "2006-01-02T15:04:05.000000" + TimezoneOffsetFormat