Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
09eadca
Initial plan
Copilot Sep 16, 2025
18dc6c2
Add direct *gtime.Time handling in Time converter
Copilot Sep 16, 2025
276de61
Partial fix for timezone preservation and comprehensive test
Copilot Sep 16, 2025
118c483
Complete fix for gtime timezone preservation in Structs conversion
Copilot Sep 16, 2025
7ed9913
Optimize and improve gtime timezone preservation implementation
Copilot Sep 16, 2025
d3b7499
Apply gci import order changes
github-actions[bot] Sep 16, 2025
3a99162
Deep analysis and enhanced fix for gtime timezone preservation
Copilot Sep 16, 2025
c38ea8c
Apply gci import order changes
github-actions[bot] Sep 16, 2025
57ff056
Enhanced builtin converter for gtime timezone preservation
Copilot Sep 16, 2025
40688fd
Apply gci import order changes
github-actions[bot] Sep 16, 2025
426f33a
Add comprehensive test suite for gtime timezone preservation
Copilot Sep 16, 2025
1e01e30
Fix builtin converter to preserve gtime timezone from map inputs
Copilot Sep 16, 2025
a7e9cdb
Apply gci import order changes
github-actions[bot] Sep 16, 2025
12f7a6e
Add comprehensive theoretical analysis and detailed test examples for…
Copilot Sep 16, 2025
c3e5172
Apply gci import order changes
github-actions[bot] Sep 16, 2025
c68b828
feat: 添加对 copilot 分支的支持
hailaz Sep 18, 2025
c3a2a2f
fix: 修正 JSON 标签中的空白字符
hailaz Sep 18, 2025
aa48e7b
fix: 移除 GTime 函数中多余的时区处理逻辑
hailaz Sep 18, 2025
e30fc76
fix: 更新 GTime 和 Time 函数以支持 map[string]any 类型
hailaz Sep 18, 2025
12e2408
fix: 修复基准测试中未使用的 reflect.TypeOf 返回值
hailaz Sep 18, 2025
7ff9c0a
fix: 优化 Time 函数中对 *gtime.Time 的处理逻辑以确保时区信息的保留
hailaz Sep 18, 2025
f16bd34
fix: 优化 Time 函数中对 map 输入的处理逻辑,移除多余的格式参数
hailaz Sep 18, 2025
8253646
fix: 优化 gtime.Time 转换逻辑,减少内存分配并提高性能
hailaz Sep 18, 2025
cef1c11
Merge branch 'master' into copilot/fix-4429
houseme Sep 25, 2025
e3b9d51
Merge branch 'master' into copilot/fix-4429
joy999 Sep 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
357 changes: 357 additions & 0 deletions os/gtime/gtime_z_bench_comprehensive_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gtime_test

import (
"testing"
"time"

"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)

// BenchmarkGTimeConverter_ComprehensiveScenarios benchmarks various gtime conversion scenarios
func BenchmarkGTimeConverter_ComprehensiveScenarios(b *testing.B) {
// Set up test data
utcTime := time.Date(2025, 9, 16, 11, 32, 42, 878465000, time.UTC)
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()
for i := 0; i < b.N; i++ {
_ = 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()
for i := 0; i < b.N; i++ {
var result gtime.Time
_ = gconv.Struct(gtimePtr, &result)
}
})

b.Run("BuiltinGTimePtrStruct", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result *gtime.Time
_ = gconv.Struct(gtimePtr, &result)
}
})

b.Run("BuiltinGTimeValueStruct", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result gtime.Time
_ = gconv.Struct(gtimeValue, &result)
}
})

// Benchmark 3: String conversion scenarios
b.Run("GTimeToString", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = 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()
for i := 0; i < b.N; i++ {
_ = 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}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = gconv.Time(mapData)
}
})

b.Run("MapToGTime", func(b *testing.B) {
mapData := map[string]interface{}{"time": gtimePtr}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = gconv.GTime(mapData)
}
})

// Benchmark 5: Struct field conversion scenarios
b.Run("StructFieldConversion", func(b *testing.B) {
type TestStruct struct {
Time time.Time `json:"time"`
}
mapData := map[string]interface{}{"Time": gtimePtr}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result TestStruct
_ = gconv.Struct(mapData, &result)
}
})

b.Run("StructGTimeFieldConversion", func(b *testing.B) {
type TestStruct struct {
Time gtime.Time `json:"time"`
}
mapData := map[string]interface{}{"Time": gtimePtr}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result TestStruct
_ = 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}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result []time.Time
_ = gconv.Structs(sliceData, &result)
}
})

b.Run("SliceConversionToGTime", func(b *testing.B) {
sliceData := []map[string]interface{}{{"time": gtimePtr}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result []gtime.Time
_ = gconv.Structs(sliceData, &result)
}
})

b.Run("SliceConversionToGTimePtr", func(b *testing.B) {
sliceData := []map[string]interface{}{{"time": gtimePtr}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result []*gtime.Time
_ = gconv.Structs(sliceData, &result)
}
})
}

// BenchmarkGTimeConverter_TimezoneImpact benchmarks timezone impact on performance
func BenchmarkGTimeConverter_TimezoneImpact(b *testing.B) {
// Test performance with different timezones
timezones := []struct {
name string
loc *time.Location
}{
{"UTC", time.UTC},
{"Shanghai", mustLoadLocation("Asia/Shanghai")},
{"NewYork", mustLoadLocation("America/New_York")},
{"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()
for i := 0; i < b.N; i++ {
var result []time.Time
_ = gconv.Structs(sliceData, &result)
}
})
}
}

// BenchmarkGTimeConverter_PrecisionImpact benchmarks precision impact on performance
func BenchmarkGTimeConverter_PrecisionImpact(b *testing.B) {
// Test performance with different precision levels
precisions := []struct {
name string
nanos int
}{
{"Seconds", 0},
{"Milliseconds", 123000000},
{"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++ {
str := gconv.String(gtimeVal)
_ = gconv.GTime(str)
}
})

b.Run("StructsConversion_"+p.name, func(b *testing.B) {
sliceData := []map[string]interface{}{{"time": gtimeVal}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result []time.Time
_ = gconv.Structs(sliceData, &result)
}
})
}
}

// BenchmarkGTimeConverter_MemoryAllocation benchmarks memory allocation patterns
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()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = gconv.Time(gtimeVal)
}
})

b.Run("BuiltinConverter_Allocs", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result gtime.Time
_ = gconv.Struct(gtimeVal, &result)
}
})

b.Run("StringConversion_Allocs", func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = gconv.String(gtimeVal)
}
})

b.Run("SliceConversion_Allocs", func(b *testing.B) {
sliceData := []map[string]interface{}{{"time": gtimeVal}}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result []time.Time
_ = gconv.Structs(sliceData, &result)
}
})
}

// BenchmarkGTimeConverter_ComparisonWithStandard compares performance with standard library
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()
for i := 0; i < b.N; i++ {
_ = 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"`
}
mapData := map[string]interface{}{"Time": gtimeVal}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result TimeStruct
_ = gconv.Struct(mapData, &result)
}
})
}

// Helper function
func mustLoadLocation(name string) *time.Location {
loc, err := time.LoadLocation(name)
if err != nil {
panic(err)
}
return loc
}
Loading