mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
Apply gci import order changes
This commit is contained in:
@ -21,11 +21,11 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
gtimeVal := gtime.NewFromTime(utcTime)
|
||||
gtimePtr := gtimeVal
|
||||
gtimeValue := *gtimeVal
|
||||
|
||||
|
||||
// Set different local timezone for more realistic testing
|
||||
shanghaiLocation, _ := time.LoadLocation("Asia/Shanghai")
|
||||
time.Local = shanghaiLocation
|
||||
|
||||
|
||||
// Benchmark 1: Direct type conversions (should be fastest)
|
||||
b.Run("DirectGTimeToTime", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
@ -33,21 +33,21 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Time(gtimePtr)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("DirectGTimeValueToTime", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = gconv.Time(gtimeValue)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("DirectGTimeToGTime", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = gconv.GTime(gtimePtr)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Benchmark 2: Builtin converter scenarios
|
||||
b.Run("BuiltinGTimeStruct", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
@ -56,7 +56,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Struct(gtimePtr, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("BuiltinGTimePtrStruct", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -64,7 +64,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Struct(gtimePtr, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("BuiltinGTimeValueStruct", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -72,7 +72,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Struct(gtimeValue, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Benchmark 3: String conversion scenarios
|
||||
b.Run("GTimeToString", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
@ -80,14 +80,14 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.String(gtimePtr)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("GTimeValueToString", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = gconv.String(gtimeValue)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("StringToGTime", func(b *testing.B) {
|
||||
timeStr := "2025-09-16T11:32:42.878465Z"
|
||||
b.ResetTimer()
|
||||
@ -95,7 +95,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.GTime(timeStr)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Benchmark 4: Map conversion scenarios (problematic in original issue)
|
||||
b.Run("MapToTime", func(b *testing.B) {
|
||||
mapData := map[string]interface{}{"time": gtimePtr}
|
||||
@ -104,7 +104,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Time(mapData)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("MapToGTime", func(b *testing.B) {
|
||||
mapData := map[string]interface{}{"time": gtimePtr}
|
||||
b.ResetTimer()
|
||||
@ -112,7 +112,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.GTime(mapData)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Benchmark 5: Struct field conversion scenarios
|
||||
b.Run("StructFieldConversion", func(b *testing.B) {
|
||||
type TestStruct struct {
|
||||
@ -125,7 +125,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Struct(mapData, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("StructGTimeFieldConversion", func(b *testing.B) {
|
||||
type TestStruct struct {
|
||||
Time gtime.Time `json:"time"`
|
||||
@ -137,7 +137,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Struct(mapData, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Benchmark 6: Slice conversion scenarios (the main issue scenario)
|
||||
b.Run("SliceConversionToTime", func(b *testing.B) {
|
||||
sliceData := []map[string]interface{}{{"time": gtimePtr}}
|
||||
@ -147,7 +147,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Structs(sliceData, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("SliceConversionToGTime", func(b *testing.B) {
|
||||
sliceData := []map[string]interface{}{{"time": gtimePtr}}
|
||||
b.ResetTimer()
|
||||
@ -156,7 +156,7 @@ func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
|
||||
_ = gconv.Structs(sliceData, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("SliceConversionToGTimePtr", func(b *testing.B) {
|
||||
sliceData := []map[string]interface{}{{"time": gtimePtr}}
|
||||
b.ResetTimer()
|
||||
@ -180,27 +180,27 @@ func BenchmarkGTimeConverter_TimezoneImpact(b *testing.B) {
|
||||
{"London", mustLoadLocation("Europe/London")},
|
||||
{"Tokyo", mustLoadLocation("Asia/Tokyo")},
|
||||
}
|
||||
|
||||
|
||||
baseTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
|
||||
|
||||
for _, tz := range timezones {
|
||||
testTime := baseTime.In(tz.loc)
|
||||
gtimeVal := gtime.NewFromTime(testTime)
|
||||
|
||||
|
||||
b.Run("DirectConversion_"+tz.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = gconv.Time(gtimeVal)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("StringConversion_"+tz.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = gconv.String(gtimeVal)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("StructsConversion_"+tz.name, func(b *testing.B) {
|
||||
sliceData := []map[string]interface{}{{"time": gtimeVal}}
|
||||
b.ResetTimer()
|
||||
@ -224,20 +224,20 @@ func BenchmarkGTimeConverter_PrecisionImpact(b *testing.B) {
|
||||
{"Microseconds", 123456000},
|
||||
{"Nanoseconds", 123456789},
|
||||
}
|
||||
|
||||
|
||||
baseTime := time.Date(2025, 9, 16, 11, 32, 42, 0, time.UTC)
|
||||
|
||||
|
||||
for _, p := range precisions {
|
||||
testTime := baseTime.Add(time.Duration(p.nanos))
|
||||
gtimeVal := gtime.NewFromTime(testTime)
|
||||
|
||||
|
||||
b.Run("Conversion_"+p.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = gconv.Time(gtimeVal)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("StringRoundTrip_"+p.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -245,10 +245,10 @@ func BenchmarkGTimeConverter_PrecisionImpact(b *testing.B) {
|
||||
_ = gconv.GTime(str)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("StructsConversion_"+p.name, func(b *testing.B) {
|
||||
sliceData := []map[string]interface{}{{"time": gtimeVal}}
|
||||
b.ResetTimer()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var result []time.Time
|
||||
_ = gconv.Structs(sliceData, &result)
|
||||
@ -261,7 +261,7 @@ func BenchmarkGTimeConverter_PrecisionImpact(b *testing.B) {
|
||||
func BenchmarkGTimeConverter_MemoryAllocation(b *testing.B) {
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
gtimeVal := gtime.NewFromTime(utcTime)
|
||||
|
||||
|
||||
// Benchmark memory allocation for different conversion types
|
||||
b.Run("DirectConversion_Allocs", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
@ -270,7 +270,7 @@ func BenchmarkGTimeConverter_MemoryAllocation(b *testing.B) {
|
||||
_ = gconv.Time(gtimeVal)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("BuiltinConverter_Allocs", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
@ -279,7 +279,7 @@ func BenchmarkGTimeConverter_MemoryAllocation(b *testing.B) {
|
||||
_ = gconv.Struct(gtimeVal, &result)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("StringConversion_Allocs", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
@ -287,7 +287,7 @@ func BenchmarkGTimeConverter_MemoryAllocation(b *testing.B) {
|
||||
_ = gconv.String(gtimeVal)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("SliceConversion_Allocs", func(b *testing.B) {
|
||||
sliceData := []map[string]interface{}{{"time": gtimeVal}}
|
||||
b.ReportAllocs()
|
||||
@ -304,7 +304,7 @@ func BenchmarkGTimeConverter_ComparisonWithStandard(b *testing.B) {
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
gtimeVal := gtime.NewFromTime(utcTime)
|
||||
timeStr := "2025-09-16T11:32:42.878465Z"
|
||||
|
||||
|
||||
// Compare gconv performance with standard library operations
|
||||
b.Run("GConv_TimeConversion", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
@ -312,28 +312,28 @@ func BenchmarkGTimeConverter_ComparisonWithStandard(b *testing.B) {
|
||||
_ = gconv.Time(gtimeVal)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("Standard_TimeParsing", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = time.Parse(time.RFC3339, timeStr)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("GConv_StringConversion", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = gconv.String(gtimeVal)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("Standard_TimeFormatting", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = utcTime.Format(time.RFC3339)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
b.Run("GConv_StructConversion", func(b *testing.B) {
|
||||
type TimeStruct struct {
|
||||
Time time.Time `json:"time"`
|
||||
@ -354,4 +354,4 @@ func mustLoadLocation(name string) *time.Location {
|
||||
panic(err)
|
||||
}
|
||||
return loc
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,21 +23,21 @@ func TestBuiltinGTimeConverter_Issue4429(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
// Simulate the issue environment: local timezone is Asia/Shanghai (+8)
|
||||
shanghaiLocation, _ := time.LoadLocation("Asia/Shanghai")
|
||||
time.Local = shanghaiLocation
|
||||
|
||||
|
||||
// Test data that matches the exact issue scenario
|
||||
// Database returns UTC time with microseconds
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
gtimeVal := gtime.NewFromTime(utcTime)
|
||||
|
||||
|
||||
originalName, originalOffset := gtimeVal.Zone()
|
||||
t.Logf("Original gtimeVal: %s (zone: %s, offset: %d)",
|
||||
t.Logf("Original gtimeVal: %s (zone: %s, offset: %d)",
|
||||
gtimeVal.Time, originalName, originalOffset/3600)
|
||||
t.Assert(originalOffset, 0) // Should be UTC (offset 0)
|
||||
|
||||
|
||||
// Test the exact scenario from the issue: result.Structs(&nowResult)
|
||||
// This simulates the ORM query result conversion
|
||||
result := []map[string]interface{}{{"now": gtimeVal}}
|
||||
@ -45,23 +45,23 @@ func TestBuiltinGTimeConverter_Issue4429(t *testing.T) {
|
||||
err := gconv.Structs(result, &nowResult)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(nowResult), 1)
|
||||
|
||||
|
||||
structsTime := nowResult[0]
|
||||
structsName, structsOffset := structsTime.Zone()
|
||||
|
||||
t.Logf("Structs result: %s (zone: %s, offset: %d)",
|
||||
|
||||
t.Logf("Structs result: %s (zone: %s, offset: %d)",
|
||||
structsTime, structsName, structsOffset/3600)
|
||||
|
||||
|
||||
// The critical assertions that fix issue #4429
|
||||
t.Assert(structsOffset, 0)
|
||||
t.Assert(gtimeVal.Time.Equal(structsTime), true)
|
||||
t.Assert(structsTime.Nanosecond(), utcTime.Nanosecond())
|
||||
|
||||
|
||||
// Verify the issue is fixed: result should be +0000, not +0800
|
||||
expectedUTCFormat := "2025-09-16 11:32:42.878465 +0000 UTC"
|
||||
actualFormat := structsTime.String()
|
||||
t.Assert(actualFormat, expectedUTCFormat)
|
||||
|
||||
|
||||
t.Logf("✅ Issue #4429 FIXED: Original +0000 preserved (not converted to +0800)")
|
||||
})
|
||||
}
|
||||
@ -74,71 +74,71 @@ func TestBuiltinGTimeConverter_DirectAssignment(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
// Set different local timezone to test independence
|
||||
parisLocation, _ := time.LoadLocation("Europe/Paris")
|
||||
time.Local = parisLocation
|
||||
|
||||
|
||||
// Test Case 1: gtime.Time to gtime.Time (value to value)
|
||||
t.Logf("=== Test Case 1: gtime.Time to gtime.Time ===")
|
||||
|
||||
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
sourceGTime := *gtime.NewFromTime(utcTime)
|
||||
|
||||
|
||||
var targetGTime gtime.Time
|
||||
err := gconv.Struct(sourceGTime, &targetGTime)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
// Verify direct assignment preserved everything
|
||||
t.Assert(targetGTime.Equal(&sourceGTime), true)
|
||||
t.Assert(targetGTime.Location().String(), sourceGTime.Location().String())
|
||||
t.Assert(targetGTime.Nanosecond(), sourceGTime.Nanosecond())
|
||||
|
||||
|
||||
_, sourceOffset := sourceGTime.Zone()
|
||||
_, targetOffset := targetGTime.Zone()
|
||||
t.Assert(targetOffset, sourceOffset)
|
||||
|
||||
|
||||
t.Logf("Source: %s, Target: %s - ✅ DIRECT ASSIGNMENT", sourceGTime.Time, targetGTime.Time)
|
||||
|
||||
|
||||
// Test Case 2: *gtime.Time to *gtime.Time (pointer to pointer)
|
||||
t.Logf("=== Test Case 2: *gtime.Time to *gtime.Time ===")
|
||||
|
||||
|
||||
sourcePtr := gtime.NewFromTime(utcTime)
|
||||
var targetPtr *gtime.Time
|
||||
|
||||
|
||||
err = gconv.Struct(sourcePtr, &targetPtr)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(targetPtr, nil)
|
||||
|
||||
|
||||
// Verify pointer assignment
|
||||
t.Assert(targetPtr.Equal(sourcePtr), true)
|
||||
t.Assert(targetPtr.Location().String(), sourcePtr.Location().String())
|
||||
|
||||
|
||||
t.Logf("Source Ptr: %s, Target Ptr: %s - ✅ DIRECT ASSIGNMENT", sourcePtr.Time, targetPtr.Time)
|
||||
|
||||
|
||||
// Test Case 3: gtime.Time to *gtime.Time (value to pointer)
|
||||
t.Logf("=== Test Case 3: gtime.Time to *gtime.Time ===")
|
||||
|
||||
|
||||
var targetFromValue *gtime.Time
|
||||
err = gconv.Struct(sourceGTime, &targetFromValue)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(targetFromValue, nil)
|
||||
|
||||
|
||||
t.Assert(targetFromValue.Equal(&sourceGTime), true)
|
||||
t.Assert(targetFromValue.Location().String(), sourceGTime.Location().String())
|
||||
|
||||
|
||||
t.Logf("Source Value: %s, Target Ptr: %s - ✅ DIRECT ASSIGNMENT", sourceGTime.Time, targetFromValue.Time)
|
||||
|
||||
|
||||
// Test Case 4: *gtime.Time to gtime.Time (pointer to value)
|
||||
t.Logf("=== Test Case 4: *gtime.Time to gtime.Time ===")
|
||||
|
||||
|
||||
var targetFromPtr gtime.Time
|
||||
err = gconv.Struct(sourcePtr, &targetFromPtr)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
t.Assert(targetFromPtr.Equal(sourcePtr), true)
|
||||
t.Assert(targetFromPtr.Location().String(), sourcePtr.Location().String())
|
||||
|
||||
|
||||
t.Logf("Source Ptr: %s, Target Value: %s - ✅ DIRECT ASSIGNMENT", sourcePtr.Time, targetFromPtr.Time)
|
||||
})
|
||||
}
|
||||
@ -147,40 +147,40 @@ func TestBuiltinGTimeConverter_DirectAssignment(t *testing.T) {
|
||||
func TestBuiltinGTimeConverter_FallbackPaths(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// Test scenarios where builtin converter falls back to general conversion
|
||||
|
||||
|
||||
// Test 1: String to gtime.Time (should use general conversion)
|
||||
t.Logf("=== Test 1: String to gtime.Time fallback ===")
|
||||
|
||||
|
||||
timeStr := "2025-09-16T11:32:42Z"
|
||||
var gtimeFromStr gtime.Time
|
||||
err := gconv.Struct(timeStr, >imeFromStr)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
// Should still preserve timezone from RFC3339 format
|
||||
_, offset := gtimeFromStr.Zone()
|
||||
t.Assert(offset, 0) // UTC offset from Z suffix
|
||||
t.Logf("String '%s' converted to gtime: %s - ✅ TIMEZONE PRESERVED", timeStr, gtimeFromStr.Time)
|
||||
|
||||
|
||||
// Test 2: Integer timestamp to gtime.Time
|
||||
t.Logf("=== Test 2: Integer timestamp to gtime.Time fallback ===")
|
||||
|
||||
|
||||
timestamp := int64(1726488762) // Unix timestamp
|
||||
var gtimeFromInt gtime.Time
|
||||
err = gconv.Struct(timestamp, >imeFromInt)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
expectedTime := time.Unix(timestamp, 0).UTC()
|
||||
t.Assert(gtimeFromInt.Unix(), expectedTime.Unix())
|
||||
t.Logf("Timestamp %d converted to gtime: %s - ✅ CONVERSION SUCCESS", timestamp, gtimeFromInt.Time)
|
||||
|
||||
|
||||
// Test 3: time.Time to gtime.Time (should use general conversion)
|
||||
t.Logf("=== Test 3: time.Time to gtime.Time fallback ===")
|
||||
|
||||
|
||||
goTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
var gtimeFromGoTime gtime.Time
|
||||
err = gconv.Struct(goTime, >imeFromGoTime)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
t.Assert(gtimeFromGoTime.Time.Equal(goTime), true)
|
||||
_, gtimeOffset := gtimeFromGoTime.Zone()
|
||||
_, goTimeOffset := goTime.Zone()
|
||||
@ -194,27 +194,27 @@ func TestBuiltinGTimeConverter_NilAndZeroHandling(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// Test 1: Nil *gtime.Time to gtime.Time
|
||||
t.Logf("=== Test 1: Nil *gtime.Time to gtime.Time ===")
|
||||
|
||||
|
||||
var nilGTime *gtime.Time = nil
|
||||
var resultGTime gtime.Time
|
||||
err := gconv.Struct(nilGTime, &resultGTime)
|
||||
t.AssertNil(err)
|
||||
t.Assert(resultGTime.IsZero(), true)
|
||||
t.Logf("Nil gtime converted to zero gtime: %s", resultGTime.Time)
|
||||
|
||||
|
||||
// Test 2: Nil *gtime.Time to *gtime.Time
|
||||
t.Logf("=== Test 2: Nil *gtime.Time to *gtime.Time ===")
|
||||
|
||||
|
||||
var resultPtr *gtime.Time
|
||||
err = gconv.Struct(nilGTime, &resultPtr)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(resultPtr, nil) // Should create new gtime.Time, not remain nil
|
||||
t.Assert(resultPtr.IsZero(), true)
|
||||
t.Logf("Nil gtime converted to zero gtime pointer: %s", resultPtr.Time)
|
||||
|
||||
|
||||
// Test 3: Zero gtime.Time to gtime.Time
|
||||
t.Logf("=== Test 3: Zero gtime.Time to gtime.Time ===")
|
||||
|
||||
|
||||
zeroGTime := gtime.Time{}
|
||||
var resultZero gtime.Time
|
||||
err = gconv.Struct(zeroGTime, &resultZero)
|
||||
@ -222,29 +222,29 @@ func TestBuiltinGTimeConverter_NilAndZeroHandling(t *testing.T) {
|
||||
t.Assert(resultZero.IsZero(), true)
|
||||
t.Assert(resultZero.Equal(&zeroGTime), true)
|
||||
t.Logf("Zero gtime preserved: %s", resultZero.Time)
|
||||
|
||||
|
||||
// Test 4: Zero gtime.Time in struct
|
||||
t.Logf("=== Test 4: Zero gtime.Time in struct ===")
|
||||
|
||||
|
||||
type TestStruct struct {
|
||||
ZeroTime gtime.Time `json:"zero_time"`
|
||||
NilTime *gtime.Time `json:"nil_time"`
|
||||
}
|
||||
|
||||
|
||||
inputData := map[string]interface{}{
|
||||
"zero_time": gtime.Time{},
|
||||
"nil_time": (*gtime.Time)(nil),
|
||||
}
|
||||
|
||||
|
||||
var resultStruct TestStruct
|
||||
err = gconv.Struct(inputData, &resultStruct)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
t.Assert(resultStruct.ZeroTime.IsZero(), true)
|
||||
t.AssertNE(resultStruct.NilTime, nil)
|
||||
t.Assert(resultStruct.NilTime.IsZero(), true)
|
||||
|
||||
t.Logf("Struct with zero/nil times: ZeroTime=%s, NilTime=%s",
|
||||
|
||||
t.Logf("Struct with zero/nil times: ZeroTime=%s, NilTime=%s",
|
||||
resultStruct.ZeroTime.Time, resultStruct.NilTime.Time)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,22 +23,22 @@ func TestBuiltinGTimeConverter(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
shanghaiLocation, _ := time.LoadLocation("Asia/Shanghai")
|
||||
time.Local = shanghaiLocation
|
||||
|
||||
|
||||
// Test data with various timezones
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
gtimeUTC := gtime.NewFromTime(utcTime)
|
||||
|
||||
|
||||
gmtLocation, _ := time.LoadLocation("GMT")
|
||||
gmtTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, gmtLocation)
|
||||
gtimeGMT := gtime.NewFromTime(gmtTime)
|
||||
|
||||
|
||||
estLocation, _ := time.LoadLocation("America/New_York")
|
||||
estTime := time.Date(2025, 9, 16, 7, 32, 42, 878465000, estLocation)
|
||||
gtimeEST := gtime.NewFromTime(estTime)
|
||||
|
||||
|
||||
// Test 1: Direct gtime.Time to gtime.Time conversion
|
||||
t.Logf("=== Test 1: Direct gtime.Time to gtime.Time conversion ===")
|
||||
var result1 gtime.Time
|
||||
@ -47,7 +47,7 @@ func TestBuiltinGTimeConverter(t *testing.T) {
|
||||
t.Assert(result1.Location().String(), gtimeUTC.Location().String())
|
||||
t.Assert(result1.Equal(gtimeUTC), true)
|
||||
t.Logf("Original: %s, Result: %s", gtimeUTC.Time, result1.Time)
|
||||
|
||||
|
||||
// Test 2: *gtime.Time to *gtime.Time conversion
|
||||
t.Logf("=== Test 2: *gtime.Time to *gtime.Time conversion ===")
|
||||
var result2 *gtime.Time
|
||||
@ -57,7 +57,7 @@ func TestBuiltinGTimeConverter(t *testing.T) {
|
||||
t.Assert(result2.Location().String(), gtimeUTC.Location().String())
|
||||
t.Assert(result2.Equal(gtimeUTC), true)
|
||||
t.Logf("Original: %s, Result: %s", gtimeUTC.Time, result2.Time)
|
||||
|
||||
|
||||
// Test 3: gtime.Time to *gtime.Time conversion
|
||||
t.Logf("=== Test 3: gtime.Time to *gtime.Time conversion ===")
|
||||
var result3 *gtime.Time
|
||||
@ -67,7 +67,7 @@ func TestBuiltinGTimeConverter(t *testing.T) {
|
||||
t.Assert(result3.Location().String(), gtimeUTC.Location().String())
|
||||
t.Assert(result3.Equal(gtimeUTC), true)
|
||||
t.Logf("Original: %s, Result: %s", gtimeUTC.Time, result3.Time)
|
||||
|
||||
|
||||
// Test 4: Multiple timezone preservation
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -78,19 +78,19 @@ func TestBuiltinGTimeConverter(t *testing.T) {
|
||||
{"GMT", gtimeGMT, 0},
|
||||
{"EST", gtimeEST, -4 * 3600}, // EST is UTC-4 in September
|
||||
}
|
||||
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Logf("=== Test 4.%s: %s timezone preservation ===", tc.name, tc.name)
|
||||
var result gtime.Time
|
||||
err := gconv.Struct(tc.input, &result)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
_, inputOffset := tc.input.Zone()
|
||||
_, resultOffset := result.Zone()
|
||||
|
||||
|
||||
t.Assert(resultOffset, inputOffset)
|
||||
t.Assert(result.Equal(tc.input), true)
|
||||
t.Logf("%s - Original: %s (offset: %d), Result: %s (offset: %d)",
|
||||
t.Logf("%s - Original: %s (offset: %d), Result: %s (offset: %d)",
|
||||
tc.name, tc.input.Time, inputOffset, result.Time, resultOffset)
|
||||
}
|
||||
})
|
||||
@ -103,7 +103,7 @@ func TestBuiltinGTimeConverter_EdgeCases(t *testing.T) {
|
||||
// The test case `gconv.Struct(nil, &result)` creates edge cases with unaddressable values
|
||||
// Core functionality is tested in other test cases
|
||||
t.Logf("=== Test 1: Nil *gtime.Time conversion - SKIPPED ===")
|
||||
|
||||
|
||||
// Test 2: Zero gtime.Time conversion
|
||||
t.Logf("=== Test 2: Zero gtime.Time conversion ===")
|
||||
zeroGtime := gtime.Time{}
|
||||
@ -112,28 +112,28 @@ func TestBuiltinGTimeConverter_EdgeCases(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(result2.IsZero(), true)
|
||||
t.Logf("Zero gtime preserved: %s", result2.Time)
|
||||
|
||||
|
||||
// Test 3: Conversion with microsecond precision
|
||||
t.Logf("=== Test 3: Microsecond precision preservation ===")
|
||||
preciseTime := time.Date(2025, 9, 16, 11, 32, 42, 123456789, time.UTC)
|
||||
gtimePrecise := gtime.NewFromTime(preciseTime)
|
||||
|
||||
|
||||
var result3 gtime.Time
|
||||
err = gconv.Struct(gtimePrecise, &result3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(result3.Nanosecond(), preciseTime.Nanosecond())
|
||||
t.Assert(result3.Equal(gtimePrecise), true)
|
||||
t.Logf("Precision preserved - Original: %s, Result: %s", gtimePrecise.Time, result3.Time)
|
||||
|
||||
|
||||
// Test 4: Conversion with different date components
|
||||
t.Logf("=== Test 4: Date component preservation ===")
|
||||
complexTime := time.Date(2025, 12, 31, 23, 59, 59, 999999999, time.UTC)
|
||||
gtimeComplex := gtime.NewFromTime(complexTime)
|
||||
|
||||
|
||||
var result4 gtime.Time
|
||||
err = gconv.Struct(gtimeComplex, &result4)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
t.Assert(result4.Year(), complexTime.Year())
|
||||
t.Assert(int(result4.Month()), int(complexTime.Month()))
|
||||
t.Assert(result4.Day(), complexTime.Day())
|
||||
@ -153,21 +153,21 @@ func TestBuiltinGTimeConverter_StructFields(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
tokyoLocation, _ := time.LoadLocation("Asia/Tokyo")
|
||||
time.Local = tokyoLocation
|
||||
|
||||
|
||||
// Test data
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
gtimeUTC := gtime.NewFromTime(utcTime)
|
||||
|
||||
|
||||
// Test struct with gtime.Time field
|
||||
type TestStructGTime struct {
|
||||
ID int `json:"id"`
|
||||
CreatedAt gtime.Time `json:"created_at"`
|
||||
ID int `json:"id"`
|
||||
CreatedAt gtime.Time `json:"created_at"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
|
||||
// Test 1: Map to struct with gtime fields
|
||||
t.Logf("=== Test 1: Map to struct with gtime fields ===")
|
||||
mapData := map[string]interface{}{
|
||||
@ -175,28 +175,28 @@ func TestBuiltinGTimeConverter_StructFields(t *testing.T) {
|
||||
"created_at": gtimeUTC,
|
||||
"updated_at": gtimeUTC,
|
||||
}
|
||||
|
||||
|
||||
var result1 TestStructGTime
|
||||
err := gconv.Struct(mapData, &result1)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
t.Assert(result1.ID, 1)
|
||||
t.Assert(result1.CreatedAt.Equal(gtimeUTC), true)
|
||||
t.AssertNE(result1.UpdatedAt, nil)
|
||||
t.Assert(result1.UpdatedAt.Equal(gtimeUTC), true)
|
||||
|
||||
|
||||
// Verify timezone preservation
|
||||
_, originalOffset := gtimeUTC.Zone()
|
||||
_, createdOffset := result1.CreatedAt.Zone()
|
||||
_, updatedOffset := result1.UpdatedAt.Zone()
|
||||
|
||||
|
||||
t.Assert(createdOffset, originalOffset)
|
||||
t.Assert(updatedOffset, originalOffset)
|
||||
|
||||
|
||||
t.Logf("Original: %s (offset: %d)", gtimeUTC.Time, originalOffset)
|
||||
t.Logf("CreatedAt: %s (offset: %d)", result1.CreatedAt.Time, createdOffset)
|
||||
t.Logf("UpdatedAt: %s (offset: %d)", result1.UpdatedAt.Time, updatedOffset)
|
||||
|
||||
|
||||
// Test 2: Struct to struct conversion
|
||||
t.Logf("=== Test 2: Struct to struct conversion ===")
|
||||
sourceStruct := TestStructGTime{
|
||||
@ -204,15 +204,15 @@ func TestBuiltinGTimeConverter_StructFields(t *testing.T) {
|
||||
CreatedAt: *gtimeUTC,
|
||||
UpdatedAt: gtimeUTC,
|
||||
}
|
||||
|
||||
|
||||
var result2 TestStructGTime
|
||||
err = gconv.Struct(sourceStruct, &result2)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
t.Assert(result2.ID, 2)
|
||||
t.Assert(result2.CreatedAt.Equal(&sourceStruct.CreatedAt), true)
|
||||
t.Assert(result2.UpdatedAt.Equal(sourceStruct.UpdatedAt), true)
|
||||
|
||||
|
||||
t.Logf("Struct to struct conversion successful")
|
||||
})
|
||||
}
|
||||
@ -225,72 +225,72 @@ func TestBuiltinGTimeConverter_SliceConversion(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
berlinLocation, _ := time.LoadLocation("Europe/Berlin")
|
||||
time.Local = berlinLocation
|
||||
|
||||
|
||||
// Test data with different timezones
|
||||
utcTime1 := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
utcTime2 := time.Date(2025, 9, 16, 15, 45, 30, 123456000, time.UTC)
|
||||
gtimeUTC1 := gtime.NewFromTime(utcTime1)
|
||||
gtimeUTC2 := gtime.NewFromTime(utcTime2)
|
||||
|
||||
|
||||
// Test 1: Slice of maps to slice of gtime.Time
|
||||
t.Logf("=== Test 1: Slice of maps to slice of gtime.Time ===")
|
||||
mapSlice := []map[string]interface{}{
|
||||
{"time": gtimeUTC1},
|
||||
{"time": gtimeUTC2},
|
||||
}
|
||||
|
||||
|
||||
var result1 []gtime.Time
|
||||
err := gconv.Structs(mapSlice, &result1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result1), 2)
|
||||
|
||||
|
||||
// Verify timezone preservation for each element
|
||||
for i, result := range result1 {
|
||||
expected := []*gtime.Time{gtimeUTC1, gtimeUTC2}[i]
|
||||
_, expectedOffset := expected.Zone()
|
||||
_, resultOffset := result.Zone()
|
||||
|
||||
|
||||
t.Assert(resultOffset, expectedOffset)
|
||||
t.Assert(result.Equal(expected), true)
|
||||
t.Logf("Element %d - Expected: %s (offset: %d), Result: %s (offset: %d)",
|
||||
i, expected.Time, expectedOffset, result.Time, resultOffset)
|
||||
}
|
||||
|
||||
|
||||
// Test 2: Slice of maps to slice of *gtime.Time
|
||||
t.Logf("=== Test 2: Slice of maps to slice of *gtime.Time ===")
|
||||
var result2 []*gtime.Time
|
||||
err = gconv.Structs(mapSlice, &result2)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result2), 2)
|
||||
|
||||
|
||||
for i, result := range result2 {
|
||||
t.AssertNE(result, nil)
|
||||
expected := []*gtime.Time{gtimeUTC1, gtimeUTC2}[i]
|
||||
_, expectedOffset := expected.Zone()
|
||||
_, resultOffset := result.Zone()
|
||||
|
||||
|
||||
t.Assert(resultOffset, expectedOffset)
|
||||
t.Assert(result.Equal(expected), true)
|
||||
t.Logf("Pointer Element %d - Expected: %s (offset: %d), Result: %s (offset: %d)",
|
||||
i, expected.Time, expectedOffset, result.Time, resultOffset)
|
||||
}
|
||||
|
||||
|
||||
// Test 3: Direct gtime slice conversion
|
||||
t.Logf("=== Test 3: Direct gtime slice conversion ===")
|
||||
gtimeSlice := []interface{}{*gtimeUTC1, gtimeUTC2}
|
||||
|
||||
|
||||
var result3 []gtime.Time
|
||||
err = gconv.Structs(gtimeSlice, &result3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result3), 2)
|
||||
|
||||
|
||||
for i, result := range result3 {
|
||||
expected := []*gtime.Time{gtimeUTC1, gtimeUTC2}[i]
|
||||
t.Assert(result.Equal(expected), true)
|
||||
t.Logf("Direct Element %d preserved timezone correctly", i)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,11 +23,11 @@ func TestGTimeTimezonePreservation_ComprehensiveScenarios(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
// Use a timezone that's different from UTC to catch timezone loss issues
|
||||
sydneyLocation, _ := time.LoadLocation("Australia/Sydney")
|
||||
time.Local = sydneyLocation
|
||||
|
||||
|
||||
// Test scenarios with different timezones
|
||||
testTimezones := []struct {
|
||||
name string
|
||||
@ -41,45 +41,45 @@ func TestGTimeTimezonePreservation_ComprehensiveScenarios(t *testing.T) {
|
||||
{"CET", mustLoadLocationComprehensive("Europe/Paris")},
|
||||
{"IST", mustLoadLocationComprehensive("Asia/Kolkata")},
|
||||
}
|
||||
|
||||
|
||||
baseTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
|
||||
|
||||
for _, tz := range testTimezones {
|
||||
t.Logf("=== Testing timezone: %s ===", tz.name)
|
||||
|
||||
|
||||
// Create time in specific timezone
|
||||
testTime := baseTime.In(tz.location)
|
||||
gtimeVal := gtime.NewFromTime(testTime)
|
||||
|
||||
|
||||
originalName, originalOffset := gtimeVal.Zone()
|
||||
t.Logf("Original %s time: %s (zone: %s, offset: %d hours)",
|
||||
t.Logf("Original %s time: %s (zone: %s, offset: %d hours)",
|
||||
tz.name, gtimeVal.Time, originalName, originalOffset/3600)
|
||||
|
||||
|
||||
// Test 1: Direct conversion
|
||||
convertedTime := gconv.Time(gtimeVal)
|
||||
_, convertedOffset := convertedTime.Zone()
|
||||
t.Assert(convertedOffset, originalOffset)
|
||||
t.Assert(gtimeVal.Time.Equal(convertedTime), true)
|
||||
|
||||
|
||||
// Test 2: GTime conversion
|
||||
reconvertedGTime := gconv.GTime(gtimeVal)
|
||||
t.AssertNE(reconvertedGTime, nil)
|
||||
_, reconvertedOffset := reconvertedGTime.Zone()
|
||||
t.Assert(reconvertedOffset, originalOffset)
|
||||
t.Assert(gtimeVal.Equal(reconvertedGTime), true)
|
||||
|
||||
|
||||
// Test 3: Struct conversion
|
||||
type TimeStruct struct {
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
|
||||
var timeStruct TimeStruct
|
||||
err := gconv.Struct(map[string]interface{}{"Time": gtimeVal}, &timeStruct)
|
||||
t.AssertNil(err)
|
||||
_, structOffset := timeStruct.Time.Zone()
|
||||
t.Assert(structOffset, originalOffset)
|
||||
t.Assert(gtimeVal.Time.Equal(timeStruct.Time), true)
|
||||
|
||||
|
||||
// Test 4: Structs (slice) conversion
|
||||
result := []map[string]interface{}{{"time": gtimeVal}}
|
||||
var timeSlice []time.Time
|
||||
@ -89,7 +89,7 @@ func TestGTimeTimezonePreservation_ComprehensiveScenarios(t *testing.T) {
|
||||
_, sliceOffset := timeSlice[0].Zone()
|
||||
t.Assert(sliceOffset, originalOffset)
|
||||
t.Assert(gtimeVal.Time.Equal(timeSlice[0]), true)
|
||||
|
||||
|
||||
t.Logf("%s timezone preservation: ✅ PASSED", tz.name)
|
||||
}
|
||||
})
|
||||
@ -103,10 +103,10 @@ func TestGTimeTimezonePreservation_DatabaseSimulation(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
shanghaiLocation, _ := time.LoadLocation("Asia/Shanghai")
|
||||
time.Local = shanghaiLocation
|
||||
|
||||
|
||||
// Simulate different database storage scenarios
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -121,7 +121,7 @@ func TestGTimeTimezonePreservation_DatabaseSimulation(t *testing.T) {
|
||||
expectedTz: "UTC",
|
||||
},
|
||||
{
|
||||
name: "GMT_Storage",
|
||||
name: "GMT_Storage",
|
||||
description: "Database stores timestamp in GMT",
|
||||
dbTime: time.Date(2025, 9, 16, 11, 32, 42, 878465000, mustLoadLocationComprehensive("GMT")),
|
||||
expectedTz: "GMT",
|
||||
@ -133,64 +133,64 @@ func TestGTimeTimezonePreservation_DatabaseSimulation(t *testing.T) {
|
||||
expectedTz: "Asia/Shanghai",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Logf("=== %s: %s ===", tc.name, tc.description)
|
||||
|
||||
|
||||
// Create gtime from database time (simulating ORM behavior)
|
||||
gtimeFromDB := gtime.NewFromTime(tc.dbTime)
|
||||
originalName, originalOffset := gtimeFromDB.Zone()
|
||||
|
||||
t.Logf("Database time: %s (zone: %s, offset: %d)",
|
||||
|
||||
t.Logf("Database time: %s (zone: %s, offset: %d)",
|
||||
gtimeFromDB.Time, originalName, originalOffset/3600)
|
||||
|
||||
|
||||
// Simulate ORM query result conversion - the critical path that was failing
|
||||
dbResult := []map[string]interface{}{
|
||||
{"created_at": gtimeFromDB},
|
||||
{"updated_at": gtimeFromDB},
|
||||
}
|
||||
|
||||
|
||||
// Convert to time.Time slice (common ORM usage pattern)
|
||||
var timestamps []time.Time
|
||||
err := gconv.Structs(dbResult, ×tamps)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(timestamps), 2)
|
||||
|
||||
|
||||
// Verify timezone preservation for both timestamps
|
||||
for i, ts := range timestamps {
|
||||
_, resultOffset := ts.Zone()
|
||||
t.Assert(resultOffset, originalOffset)
|
||||
t.Assert(gtimeFromDB.Time.Equal(ts), true)
|
||||
|
||||
t.Logf("Element %d: %s (offset: %d) - ✅ PRESERVED",
|
||||
|
||||
t.Logf("Element %d: %s (offset: %d) - ✅ PRESERVED",
|
||||
i, ts, resultOffset/3600)
|
||||
}
|
||||
|
||||
|
||||
// Also test struct field conversion
|
||||
type DatabaseRecord struct {
|
||||
ID int `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
|
||||
recordData := map[string]interface{}{
|
||||
"id": 1,
|
||||
"created_at": gtimeFromDB,
|
||||
"updated_at": gtimeFromDB,
|
||||
}
|
||||
|
||||
|
||||
var record DatabaseRecord
|
||||
err = gconv.Struct(recordData, &record)
|
||||
t.AssertNil(err)
|
||||
|
||||
|
||||
_, createdOffset := record.CreatedAt.Zone()
|
||||
_, updatedOffset := record.UpdatedAt.Zone()
|
||||
|
||||
|
||||
t.Assert(createdOffset, originalOffset)
|
||||
t.Assert(updatedOffset, originalOffset)
|
||||
t.Assert(gtimeFromDB.Time.Equal(record.CreatedAt), true)
|
||||
t.Assert(gtimeFromDB.Time.Equal(record.UpdatedAt), true)
|
||||
|
||||
|
||||
t.Logf("Struct fields: CreatedAt=%s (offset: %d), UpdatedAt=%s (offset: %d) - ✅ PRESERVED",
|
||||
record.CreatedAt, createdOffset/3600, record.UpdatedAt, updatedOffset/3600)
|
||||
}
|
||||
@ -205,14 +205,14 @@ func TestGTimeTimezonePreservation_PrecisionAndEdgeCases(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
// Use London timezone (has DST transitions)
|
||||
londonLocation, _ := time.LoadLocation("Europe/London")
|
||||
time.Local = londonLocation
|
||||
|
||||
|
||||
// Test precision preservation
|
||||
t.Logf("=== Precision Preservation Tests ===")
|
||||
|
||||
|
||||
precisionTests := []struct {
|
||||
name string
|
||||
nanos int
|
||||
@ -223,65 +223,65 @@ func TestGTimeTimezonePreservation_PrecisionAndEdgeCases(t *testing.T) {
|
||||
{"Zero_Nanos", 0},
|
||||
{"Max_Nanos", 999999999},
|
||||
}
|
||||
|
||||
for _, pt := range precisionTests {
|
||||
|
||||
for _, pt := range precisionTests {
|
||||
t.Logf("--- Testing %s precision ---", pt.name)
|
||||
|
||||
|
||||
testTime := time.Date(2025, 9, 16, 11, 32, 42, pt.nanos, time.UTC)
|
||||
gtimeVal := gtime.NewFromTime(testTime)
|
||||
|
||||
|
||||
// Test through Structs conversion (the problematic path)
|
||||
result := []map[string]interface{}{{"time": gtimeVal}}
|
||||
var timeSlice []time.Time
|
||||
err := gconv.Structs(result, &timeSlice)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(timeSlice), 1)
|
||||
|
||||
|
||||
convertedTime := timeSlice[0]
|
||||
t.Assert(convertedTime.Nanosecond(), pt.nanos)
|
||||
t.Assert(convertedTime.Equal(testTime), true)
|
||||
|
||||
t.Logf("%s: Original=%d ns, Converted=%d ns - ✅ PRESERVED",
|
||||
|
||||
t.Logf("%s: Original=%d ns, Converted=%d ns - ✅ PRESERVED",
|
||||
pt.name, pt.nanos, convertedTime.Nanosecond())
|
||||
}
|
||||
|
||||
|
||||
// Test edge cases
|
||||
t.Logf("=== Edge Cases Tests ===")
|
||||
|
||||
|
||||
// Test 1: Leap year
|
||||
leapTime := time.Date(2024, 2, 29, 11, 32, 42, 0, time.UTC)
|
||||
gtimeLeap := gtime.NewFromTime(leapTime)
|
||||
|
||||
|
||||
var leapResult []time.Time
|
||||
err := gconv.Structs([]map[string]interface{}{{"time": gtimeLeap}}, &leapResult)
|
||||
t.AssertNil(err)
|
||||
t.Assert(leapResult[0].Equal(leapTime), true)
|
||||
t.Logf("Leap year: %s - ✅ PRESERVED", leapResult[0])
|
||||
|
||||
|
||||
// Test 2: Year boundaries
|
||||
yearBoundary := time.Date(1999, 12, 31, 23, 59, 59, 999999999, time.UTC)
|
||||
gtimeYear := gtime.NewFromTime(yearBoundary)
|
||||
|
||||
|
||||
var yearResult []time.Time
|
||||
err = gconv.Structs([]map[string]interface{}{{"time": gtimeYear}}, &yearResult)
|
||||
t.AssertNil(err)
|
||||
t.Assert(yearResult[0].Equal(yearBoundary), true)
|
||||
t.Logf("Year boundary: %s - ✅ PRESERVED", yearResult[0])
|
||||
|
||||
|
||||
// Test 3: Unix epoch
|
||||
epochTime := time.Unix(0, 0).UTC()
|
||||
gtimeEpoch := gtime.NewFromTime(epochTime)
|
||||
|
||||
|
||||
var epochResult []time.Time
|
||||
err = gconv.Structs([]map[string]interface{}{{"time": gtimeEpoch}}, &epochResult)
|
||||
t.AssertNil(err)
|
||||
t.Assert(epochResult[0].Equal(epochTime), true)
|
||||
t.Logf("Unix epoch: %s - ✅ PRESERVED", epochResult[0])
|
||||
|
||||
|
||||
// Test 4: Future date
|
||||
futureTime := time.Date(2099, 12, 31, 23, 59, 59, 0, time.UTC)
|
||||
gtimeFuture := gtime.NewFromTime(futureTime)
|
||||
|
||||
|
||||
var futureResult []time.Time
|
||||
err = gconv.Structs([]map[string]interface{}{{"time": gtimeFuture}}, &futureResult)
|
||||
t.AssertNil(err)
|
||||
@ -296,17 +296,17 @@ func TestGTimeTimezonePreservation_PerformanceRegression(t *testing.T) {
|
||||
// Create test data
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
gtimeVal := gtime.NewFromTime(utcTime)
|
||||
|
||||
|
||||
// Performance test: Ensure timezone preservation doesn't significantly impact performance
|
||||
iterations := 1000
|
||||
|
||||
|
||||
// Test 1: Direct conversion performance
|
||||
start := time.Now()
|
||||
for i := 0; i < iterations; i++ {
|
||||
_ = gconv.Time(gtimeVal)
|
||||
}
|
||||
directDuration := time.Since(start)
|
||||
|
||||
|
||||
// Test 2: Struct conversion performance
|
||||
start = time.Now()
|
||||
for i := 0; i < iterations; i++ {
|
||||
@ -314,7 +314,7 @@ func TestGTimeTimezonePreservation_PerformanceRegression(t *testing.T) {
|
||||
_ = gconv.Struct(gtimeVal, &result)
|
||||
}
|
||||
structDuration := time.Since(start)
|
||||
|
||||
|
||||
// Test 3: Structs (slice) conversion performance
|
||||
mapData := []map[string]interface{}{{"time": gtimeVal}}
|
||||
start = time.Now()
|
||||
@ -323,22 +323,22 @@ func TestGTimeTimezonePreservation_PerformanceRegression(t *testing.T) {
|
||||
_ = gconv.Structs(mapData, &result)
|
||||
}
|
||||
sliceDuration := time.Since(start)
|
||||
|
||||
|
||||
// Performance should be reasonable (not exact assertions, just reasonable bounds)
|
||||
t.Logf("Performance Results for %d iterations:", iterations)
|
||||
t.Logf("Direct conversion: %v (avg: %v/op)", directDuration, directDuration/time.Duration(iterations))
|
||||
t.Logf("Struct conversion: %v (avg: %v/op)", structDuration, structDuration/time.Duration(iterations))
|
||||
t.Logf("Slice conversion: %v (avg: %v/op)", sliceDuration, sliceDuration/time.Duration(iterations))
|
||||
|
||||
|
||||
// Ensure performance is reasonable (under 1ms per operation)
|
||||
avgDirect := directDuration / time.Duration(iterations)
|
||||
avgStruct := structDuration / time.Duration(iterations)
|
||||
avgSlice := sliceDuration / time.Duration(iterations)
|
||||
|
||||
|
||||
t.Assert(avgDirect < time.Millisecond, true)
|
||||
t.Assert(avgStruct < time.Millisecond, true)
|
||||
t.Assert(avgSlice < time.Millisecond, true)
|
||||
|
||||
|
||||
t.Logf("All performance tests passed ✅")
|
||||
})
|
||||
}
|
||||
@ -350,4 +350,4 @@ func mustLoadLocationComprehensive(name string) *time.Location {
|
||||
panic(err)
|
||||
}
|
||||
return loc
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,38 +23,38 @@ func TestGTimeStringConversion_Basic(t *testing.T) {
|
||||
defer func() {
|
||||
time.Local = originalLocation
|
||||
}()
|
||||
|
||||
|
||||
parisLocation, _ := time.LoadLocation("Europe/Paris")
|
||||
time.Local = parisLocation
|
||||
|
||||
|
||||
// Test UTC time string conversion
|
||||
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
|
||||
gtimeVal := gtime.NewFromTime(utcTime)
|
||||
|
||||
|
||||
// Test gtime.Time to string
|
||||
resultStr := gconv.String(*gtimeVal)
|
||||
t.Logf("gtime to string: %s", resultStr)
|
||||
|
||||
|
||||
// Should use RFC3339 format (note: microseconds will be truncated if they're 0)
|
||||
expectedRFC3339 := "2025-09-16T11:32:42Z"
|
||||
t.Assert(resultStr, expectedRFC3339)
|
||||
|
||||
|
||||
// Test *gtime.Time to string
|
||||
ptrStr := gconv.String(gtimeVal)
|
||||
t.Assert(ptrStr, expectedRFC3339)
|
||||
|
||||
|
||||
// Test round-trip conversion
|
||||
reconverted := gconv.GTime(resultStr)
|
||||
t.AssertNE(reconverted, nil)
|
||||
|
||||
|
||||
// Check if times represent the same instant (more important than exact equality due to precision differences)
|
||||
t.Assert(gtimeVal.Time.Truncate(time.Second).Equal(reconverted.Time.Truncate(time.Second)), true)
|
||||
|
||||
|
||||
// Verify timezone preservation
|
||||
_, originalOffset := gtimeVal.Zone()
|
||||
_, reconvertedOffset := reconverted.Zone()
|
||||
t.Assert(reconvertedOffset, originalOffset)
|
||||
|
||||
|
||||
t.Logf("✅ String conversion preserves timezone correctly")
|
||||
})
|
||||
}
|
||||
@ -65,23 +65,23 @@ func TestGTimeStringConversion_Precision(t *testing.T) {
|
||||
// Test microsecond precision
|
||||
preciseTime := time.Date(2025, 9, 16, 11, 32, 42, 123456789, time.UTC)
|
||||
gtimeVal := gtime.NewFromTime(preciseTime)
|
||||
|
||||
|
||||
// Convert to string
|
||||
timeStr := gconv.String(gtimeVal)
|
||||
t.Logf("Precise time string: %s", timeStr)
|
||||
|
||||
|
||||
// Should include nanosecond precision
|
||||
expected := "2025-09-16T11:32:42.123456789Z"
|
||||
t.Assert(timeStr, expected)
|
||||
|
||||
|
||||
// Convert back
|
||||
reconverted := gconv.GTime(timeStr)
|
||||
t.AssertNE(reconverted, nil)
|
||||
|
||||
|
||||
// Verify precision preservation
|
||||
t.Assert(reconverted.Nanosecond(), preciseTime.Nanosecond())
|
||||
t.Assert(reconverted.Equal(gtimeVal), true)
|
||||
|
||||
|
||||
t.Logf("✅ Precision preserved in string conversion")
|
||||
})
|
||||
}
|
||||
@ -93,23 +93,23 @@ func TestGTimeStringConversion_EdgeCases(t *testing.T) {
|
||||
zeroGTime := gtime.Time{}
|
||||
zeroStr := gconv.String(zeroGTime)
|
||||
t.Assert(zeroStr, "")
|
||||
|
||||
|
||||
// Test nil gtime
|
||||
var nilGTime *gtime.Time = nil
|
||||
nilStr := gconv.String(nilGTime)
|
||||
t.Assert(nilStr, "")
|
||||
|
||||
|
||||
// Test very old date
|
||||
oldTime := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
oldGTime := gtime.NewFromTime(oldTime)
|
||||
oldStr := gconv.String(oldGTime)
|
||||
expectedOld := "1900-01-01T00:00:00Z"
|
||||
t.Assert(oldStr, expectedOld)
|
||||
|
||||
|
||||
// Test round-trip for old date
|
||||
fromOld := gconv.GTime(oldStr)
|
||||
t.Assert(fromOld.Equal(oldGTime), true)
|
||||
|
||||
|
||||
t.Logf("✅ Edge cases handled correctly")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user