Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
X
xpkg
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
屠思豪
xpkg
Commits
6d8995d2
提交
6d8995d2
authored
10月 17, 2025
作者:
mooncake
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
修改xtime
上级
ddf10376
显示空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
2461 行增加
和
322 行删除
+2461
-322
go.mod
go.mod
+1
-1
date.go
utils/xtime/date.go
+222
-48
date_test.go
utils/xtime/date_test.go
+441
-146
datetime.go
utils/xtime/datetime.go
+265
-92
datetime_test.go
utils/xtime/datetime_test.go
+693
-0
time.go
utils/xtime/time.go
+224
-35
time_test.go
utils/xtime/time_test.go
+615
-0
没有找到文件。
go.mod
浏览文件 @
6d8995d2
...
...
@@ -47,7 +47,7 @@ require (
golang.org/x/sync v0.16.0
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.36.9
gopkg.in/yaml.v3 v3.0.1
// indirect
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.2
gorm.io/driver/postgres v1.5.4
gorm.io/driver/sqlite v1.5.4
...
...
utils/xtime/date.go
浏览文件 @
6d8995d2
...
...
@@ -2,105 +2,279 @@ package xtime
import
(
"database/sql/driver"
"encoding/json"
"fmt"
"strings"
"time"
"gopkg.in/yaml.v3"
)
// Date 自定义日期类型,基于 time.Time,但只关心年月日
type
Date
time
.
Time
// 使用常量确保格式一致性,便于维护
const
(
dateFormat
=
"2006-01-02"
// 标准ISO格式
)
// Scan 改进:增强健壮性,统一解析路径,优化错误信息
func
(
d
*
Date
)
Scan
(
value
interface
{})
error
{
if
value
==
nil
{
*
d
=
Date
(
time
.
Time
{})
// 明确处理数据库NULL值
return
nil
}
switch
v
:=
value
.
(
type
)
{
case
[]
byte
:
parsed
,
err
:=
time
.
Parse
(
"2006-01-02"
,
string
(
v
))
if
err
!=
nil
{
return
err
if
len
(
v
)
==
0
{
*
d
=
Date
(
time
.
Time
{})
return
nil
}
*
d
=
Date
(
parsed
)
return
d
.
parseString
(
string
(
v
)
)
case
string
:
parsed
,
err
:=
time
.
Parse
(
"2006-01-02"
,
v
)
if
err
!=
nil
{
return
err
if
v
==
""
{
*
d
=
Date
(
time
.
Time
{})
return
nil
}
*
d
=
Date
(
parsed
)
return
d
.
parseString
(
v
)
case
time
.
Time
:
if
v
,
ok
:=
value
.
(
time
.
Time
);
ok
{
*
d
=
Date
(
v
)
}
// 提取日期部分,忽略时间组件,同时保留原时区信息
y
,
m
,
day
:=
v
.
Date
()
*
d
=
Date
(
time
.
Date
(
y
,
m
,
day
,
0
,
0
,
0
,
0
,
v
.
Location
()))
return
nil
default
:
return
fmt
.
Errorf
(
"unsupported type: %T"
,
v
)
// 更清晰的错误信息,指导使用者
return
fmt
.
Errorf
(
"Date.Scan: 不支持的扫描类型 %T, 期望: string, []byte 或 time.Time"
,
value
)
}
return
nil
}
// Value 改进:更精确的零值处理和返回类型
func
(
d
Date
)
Value
()
(
driver
.
Value
,
error
)
{
return
d
.
Time
()
.
Format
(
"2006-01-02"
),
nil
t
:=
time
.
Time
(
d
)
if
t
.
IsZero
()
{
return
nil
,
nil
// 零值对应数据库NULL
}
// 返回格式化的日期字符串,确保数据库接收明确格式
return
t
.
Format
(
dateFormat
),
nil
}
// MarshalJSON
自定义JSON序列化
// MarshalJSON
改进:使用更安全的JSON编码方式
func
(
d
Date
)
MarshalJSON
()
([]
byte
,
error
)
{
return
[]
byte
(
`"`
+
d
.
Time
()
.
Format
(
"2006-01-02"
)
+
`"`
),
nil
t
:=
time
.
Time
(
d
)
if
t
.
IsZero
()
{
return
[]
byte
(
"null"
),
nil
}
// 使用标准JSON编码器,避免手动拼接可能导致的转义错误
return
json
.
Marshal
(
t
.
Format
(
dateFormat
))
}
// UnmarshalJSON
自定义JSON反序列化
// UnmarshalJSON
改进:增强格式兼容性和错误处理
func
(
d
*
Date
)
UnmarshalJSON
(
data
[]
byte
)
error
{
str
:=
strings
.
Trim
(
string
(
data
),
`"`
)
parsed
,
err
:=
time
.
Parse
(
"2006-01-02"
,
str
)
if
err
!=
nil
{
return
err
}
*
d
=
Date
(
parsed
)
str
:=
strings
.
TrimSpace
(
string
(
data
))
// 处理显式null和空字符串
if
str
==
"null"
||
str
==
`""`
||
str
==
""
{
*
d
=
Date
(
time
.
Time
{})
return
nil
}
// 安全地去除JSON字符串的引号
var
dateStr
string
if
len
(
str
)
>=
2
&&
str
[
0
]
==
'"'
&&
str
[
len
(
str
)
-
1
]
==
'"'
{
dateStr
=
str
[
1
:
len
(
str
)
-
1
]
}
else
{
// 如果不是引号包裹的字符串,尝试直接解析
dateStr
=
str
}
return
d
.
parseString
(
dateStr
)
}
// MarshalYAML
自定义YAML序列化
// MarshalYAML
改进:符合yaml.v3接口标准
func
(
d
Date
)
MarshalYAML
()
(
interface
{},
error
)
{
return
d
.
Time
()
.
Format
(
"2006-01-02"
),
nil
t
:=
time
.
Time
(
d
)
if
t
.
IsZero
()
{
return
nil
,
nil
}
return
t
.
Format
(
dateFormat
),
nil
}
// UnmarshalYAML 改进:使用正确的yaml.v3 Node接口
func
(
d
*
Date
)
UnmarshalYAML
(
value
*
yaml
.
Node
)
error
{
if
value
==
nil
||
value
.
Kind
!=
yaml
.
ScalarNode
{
return
fmt
.
Errorf
(
"Date.UnmarshalYAML: 日期必须为标量值(字符串)"
)
}
str
:=
strings
.
TrimSpace
(
value
.
Value
)
if
str
==
""
||
str
==
"null"
||
str
==
"~"
{
*
d
=
Date
(
time
.
Time
{})
return
nil
}
return
d
.
parseString
(
str
)
}
// String 返回日期字符串表示
func
(
d
Date
)
String
()
string
{
t
:=
time
.
Time
(
d
)
if
t
.
IsZero
()
{
return
""
// 零值返回空字符串,符合Go惯例
}
return
t
.
Format
(
dateFormat
)
}
// Time 返回time.Time类型(去除时间部分)
func
(
d
Date
)
Time
()
time
.
Time
{
return
time
.
Time
(
d
)
t
:=
time
.
Time
(
d
)
if
t
.
IsZero
()
{
return
time
.
Time
{}
}
// 确保只返回日期部分,时间设为00:00:00
y
,
m
,
day
:=
t
.
Date
()
return
time
.
Date
(
y
,
m
,
day
,
0
,
0
,
0
,
0
,
t
.
Location
())
}
// UnmarshalYAML 自定义YAML反序列化
func
(
d
*
Date
)
UnmarshalYAML
(
value
interface
{})
error
{
str
,
ok
:=
value
.
(
string
)
if
!
ok
{
return
fmt
.
Errorf
(
"date must be a string"
)
// parseString 内部解析方法改进:支持多种格式,优化错误处理
func
(
d
*
Date
)
parseString
(
s
string
)
error
{
s
=
strings
.
TrimSpace
(
s
)
if
s
==
""
{
*
d
=
Date
(
time
.
Time
{})
return
nil
}
parsed
,
err
:=
time
.
Parse
(
"2006-01-02"
,
str
)
if
err
!=
nil
{
return
err
// 定义支持的日期格式(按优先级排序)
formats
:=
[]
string
{
"2006-01-02"
,
// 标准ISO格式(优先)
"2006/01/02"
,
// 斜杠格式
"20060102"
,
// 紧凑格式
"02-01-2006"
,
// 日-月-年格式
"02/01/2006"
,
// 日/月/年格式
"January 2, 2006"
,
// 英文全写格式
"Jan 2, 2006"
,
// 英文缩写格式
}
*
d
=
Date
(
parsed
)
var
firstErr
error
for
_
,
format
:=
range
formats
{
parsed
,
err
:=
time
.
Parse
(
format
,
s
)
if
err
==
nil
{
// 成功解析,提取日期部分
y
,
m
,
day
:=
parsed
.
Date
()
*
d
=
Date
(
time
.
Date
(
y
,
m
,
day
,
0
,
0
,
0
,
0
,
parsed
.
Location
()))
return
nil
}
if
firstErr
==
nil
{
firstErr
=
err
}
}
return
fmt
.
Errorf
(
"Date.parseString: 无法解析日期 %q, 支持的格式示例: 2006-01-02"
,
s
)
}
// String 返回日期字符串表示
func
(
d
Date
)
String
()
string
{
return
d
.
Time
()
.
Format
(
"2006-01-02"
)
// Equal 改进:精确的日期比较(只比较年月日)
func
(
d
Date
)
Equal
(
other
Date
)
bool
{
t1
:=
time
.
Time
(
d
)
.
UTC
()
.
Truncate
(
24
*
time
.
Hour
)
t2
:=
time
.
Time
(
other
)
.
UTC
()
.
Truncate
(
24
*
time
.
Hour
)
return
t1
.
Equal
(
t2
)
}
// Before 检查当前日期是否在另一个日期之前
func
(
d
Date
)
Before
(
other
Date
)
bool
{
if
d
.
Equal
(
other
)
{
return
false
}
return
d
.
Time
()
.
Before
(
other
.
Time
())
}
// After 检查当前日期是否在另一个日期之后
func
(
d
Date
)
After
(
other
Date
)
bool
{
if
d
.
Equal
(
other
)
{
return
false
}
return
d
.
Time
()
.
After
(
other
.
Time
())
}
// IsZero 检查是否为零值
func
(
d
Date
)
IsZero
()
bool
{
return
time
.
Time
(
d
)
.
IsZero
()
}
// AddDays 添加指定天数
func
(
d
Date
)
AddDays
(
days
int
)
Date
{
t
:=
d
.
Time
()
.
AddDate
(
0
,
0
,
days
)
return
Date
(
t
)
}
// DaysBetween 计算两个日期之间的天数差(考虑自然日)
func
(
d
Date
)
DaysBetween
(
other
Date
)
int
{
// 将两个日期都规范到UTC的零点,消除时间和时区影响
t1
:=
d
.
Time
()
.
UTC
()
.
Truncate
(
24
*
time
.
Hour
)
t2
:=
other
.
Time
()
.
UTC
()
.
Truncate
(
24
*
time
.
Hour
)
// 计算天数差(使用正确的顺序)
hours
:=
t1
.
Sub
(
t2
)
.
Hours
()
days
:=
int
(
hours
/
24
)
return
days
}
// Today 返回当前日期
func
Today
Date
()
Date
{
// Today 返回当前日期
(忽略时间部分)
func
Today
()
Date
{
now
:=
time
.
Now
()
return
Date
(
time
.
Date
(
now
.
Year
(),
now
.
Month
(),
now
.
Day
(),
0
,
0
,
0
,
0
,
now
.
Location
()))
y
,
m
,
day
:=
now
.
Date
()
return
Date
(
time
.
Date
(
y
,
m
,
day
,
0
,
0
,
0
,
0
,
now
.
Location
()))
}
// ParseDate 从字符串解析日期
func
ParseDate
(
s
string
)
(
Date
,
error
)
{
t
,
err
:=
time
.
Parse
(
"2006-01-02"
,
s
)
if
err
!=
nil
{
return
Date
{},
err
}
return
Date
(
t
),
nil
var
d
Date
err
:=
d
.
parseString
(
s
)
return
d
,
err
}
// MustParseDate 从字符串解析日期,
如果解析失败会
panic
// MustParseDate 从字符串解析日期,
解析失败时
panic
func
MustParseDate
(
s
string
)
Date
{
d
,
err
:=
ParseDate
(
s
)
if
err
!=
nil
{
panic
(
err
)
panic
(
fmt
.
Sprintf
(
"MustParseDate 解析失败: %q, 错误: %v"
,
s
,
err
)
)
}
return
d
}
// 新增便捷方法
// Format 使用自定义格式格式化日期
func
(
d
Date
)
Format
(
layout
string
)
string
{
return
d
.
Time
()
.
Format
(
layout
)
}
// Weekday 返回星期几
func
(
d
Date
)
Weekday
()
time
.
Weekday
{
return
d
.
Time
()
.
Weekday
()
}
// Year 返回年份
func
(
d
Date
)
Year
()
int
{
return
d
.
Time
()
.
Year
()
}
// Month 返回月份
func
(
d
Date
)
Month
()
time
.
Month
{
return
d
.
Time
()
.
Month
()
}
// Day 返回日期
func
(
d
Date
)
Day
()
int
{
return
d
.
Time
()
.
Day
()
}
// IsWeekend 检查是否为周末
func
(
d
Date
)
IsWeekend
()
bool
{
weekday
:=
d
.
Weekday
()
return
weekday
==
time
.
Saturday
||
weekday
==
time
.
Sunday
}
// DaysSince 计算从该日期到现在的天数
func
(
d
Date
)
DaysSince
()
int
{
return
Today
()
.
DaysBetween
(
d
)
}
utils/xtime/date_test.go
浏览文件 @
6d8995d2
package
xtime
import
(
"
database/sql/driver
"
"
encoding/json
"
"testing"
"time"
"g
ithub.com/stretchr/testify/assert
"
"g
opkg.in/yaml.v3
"
)
// TestDate_Scan 测试数据库扫描功能
func
TestDate_Scan
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
interface
{}
want
Date
want
string
wantErr
bool
}{
{
name
:
"
valid byte slice
"
,
input
:
[]
byte
(
"2023-05-15"
),
want
:
MustParseDate
(
"2023-05-15"
)
,
name
:
"
有效时间类型
"
,
input
:
time
.
Date
(
2023
,
10
,
15
,
15
,
30
,
0
,
0
,
time
.
UTC
),
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"
valid string
"
,
input
:
"2023-05-15"
,
want
:
MustParseDate
(
"2023-05-15"
)
,
name
:
"
有效字节切片
"
,
input
:
[]
byte
(
"2023-10-15"
)
,
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"invalid type"
,
input
:
123
,
want
:
Date
{},
wantErr
:
true
,
name
:
"有效字符串"
,
input
:
"2023-10-15"
,
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"空值处理"
,
input
:
nil
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"invalid date format"
,
input
:
"15-05-2023"
,
want
:
Date
{},
name
:
"空字符串"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"无效类型"
,
input
:
123
,
// 数字类型不支持
want
:
""
,
wantErr
:
true
,
},
}
...
...
@@ -45,257 +58,539 @@ func TestDate_Scan(t *testing.T) {
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
var
d
Date
err
:=
d
.
Scan
(
tt
.
input
)
if
tt
.
wantErr
{
assert
.
Error
(
t
,
err
)
}
else
{
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
want
,
d
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"Scan() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
d
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"Scan() = %v, want %v"
,
d
.
String
(),
tt
.
want
)
}
})
}
}
// TestDate_Value 测试数据库值生成
func
TestDate_Value
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
date
Date
want
driver
.
Value
input
Date
want
interface
{}
wantErr
bool
}{
{
name
:
"
valid date
"
,
date
:
MustParseDate
(
"2023-05-15"
),
want
:
"2023-
05
-15"
,
name
:
"
有效日期
"
,
input
:
Date
(
time
.
Date
(
2023
,
10
,
15
,
0
,
0
,
0
,
0
,
time
.
UTC
)
),
want
:
"2023-
10
-15"
,
wantErr
:
false
,
},
{
name
:
"
zero date
"
,
date
:
Date
{}
,
want
:
"0001-01-01"
,
name
:
"
零值日期
"
,
input
:
Date
(
time
.
Time
{})
,
want
:
nil
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
tt
.
date
.
Value
()
if
tt
.
wantErr
{
assert
.
Error
(
t
,
err
)
}
else
{
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
want
,
got
)
got
,
err
:=
tt
.
input
.
Value
()
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"Value() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
got
!=
tt
.
want
{
t
.
Errorf
(
"Value() = %v, want %v"
,
got
,
tt
.
want
)
}
})
}
}
func
TestDate_MarshalJSON
(
t
*
testing
.
T
)
{
// TestDate_JSON 测试JSON序列化和反序列化[3](@ref)
func
TestDate_JSON
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
date
Date
input
string
want
string
wantErr
bool
}{
{
name
:
"
valid date
"
,
date
:
MustParseDate
(
"2023-05-15"
)
,
want
:
`"2023-05-15"`
,
name
:
"
有效日期JSON
"
,
input
:
`"2023-10-15"`
,
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"zero date"
,
date
:
Date
{},
want
:
`"0001-01-01"`
,
name
:
"空值JSON"
,
input
:
"null"
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"空字符串JSON"
,
input
:
`""`
,
want
:
""
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
tt
.
date
.
MarshalJSON
()
if
tt
.
wantErr
{
assert
.
Error
(
t
,
err
)
}
else
{
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
want
,
string
(
got
))
// 测试反序列化
var
d
Date
err
:=
json
.
Unmarshal
([]
byte
(
tt
.
input
),
&
d
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"UnmarshalJSON() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
d
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"UnmarshalJSON() = %v, want %v"
,
d
.
String
(),
tt
.
want
)
return
}
// 测试序列化(仅对有效用例)
if
!
tt
.
wantErr
&&
tt
.
want
!=
""
{
bytes
,
err
:=
json
.
Marshal
(
d
)
if
err
!=
nil
{
t
.
Errorf
(
"MarshalJSON() error = %v"
,
err
)
return
}
expectedJSON
:=
`"`
+
tt
.
want
+
`"`
if
string
(
bytes
)
!=
expectedJSON
{
t
.
Errorf
(
"MarshalJSON() = %s, want %s"
,
string
(
bytes
),
expectedJSON
)
}
}
})
}
}
func
TestDate_UnmarshalJSON
(
t
*
testing
.
T
)
{
// TestDate_YAML 测试YAML序列化和反序列化
func
TestDate_YAML
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
string
want
Date
want
string
wantErr
bool
}{
{
name
:
"
valid date
"
,
input
:
`"2023-05-15"`
,
want
:
MustParseDate
(
"2023-05-15"
)
,
name
:
"
有效日期YAML
"
,
input
:
"2023-10-15"
,
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"invalid format"
,
input
:
`"15-05-2023"`
,
want
:
Date
{},
wantErr
:
true
,
},
{
name
:
"not a string"
,
input
:
`123`
,
want
:
Date
{},
wantErr
:
true
,
name
:
"空字符串YAML"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
// 创建YAML节点进行测试[4](@ref)
node
:=
&
yaml
.
Node
{
Kind
:
yaml
.
ScalarNode
,
Value
:
tt
.
input
,
}
var
d
Date
err
:=
d
.
UnmarshalJSON
([]
byte
(
tt
.
input
))
if
tt
.
wantErr
{
assert
.
Error
(
t
,
err
)
}
else
{
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
want
,
d
)
err
:=
d
.
UnmarshalYAML
(
node
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"UnmarshalYAML() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
d
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"UnmarshalYAML() = %v, want %v"
,
d
.
String
(),
tt
.
want
)
}
})
}
}
func
TestDate_MarshalYAML
(
t
*
testing
.
T
)
{
// TestDate_String 测试字符串表示
func
TestDate_String
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
date
Date
input
Date
want
string
wantErr
bool
}{
{
name
:
"valid date"
,
date
:
MustParseDate
(
"2023-05-15"
),
want
:
"2023-05-15"
,
wantErr
:
false
,
name
:
"有效日期"
,
input
:
Date
(
time
.
Date
(
2023
,
10
,
15
,
0
,
0
,
0
,
0
,
time
.
UTC
)),
want
:
"2023-10-15"
,
},
{
name
:
"zero date"
,
date
:
Date
{},
want
:
"0001-01-01"
,
wantErr
:
false
,
name
:
"零值日期"
,
input
:
Date
(
time
.
Time
{}),
want
:
""
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
tt
.
date
.
MarshalYAML
()
if
tt
.
wantErr
{
assert
.
Error
(
t
,
err
)
}
else
{
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
want
,
got
)
if
got
:=
tt
.
input
.
String
();
got
!=
tt
.
want
{
t
.
Errorf
(
"String() = %v, want %v"
,
got
,
tt
.
want
)
}
})
}
}
func
TestDate_UnmarshalYAML
(
t
*
testing
.
T
)
{
// TestParseDate 测试日期解析函数[6](@ref)
func
TestParseDate
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
interface
{}
want
Date
input
string
want
string
wantErr
bool
}{
{
name
:
"
valid date string
"
,
input
:
"2023-
05
-15"
,
want
:
MustParseDate
(
"2023-05-15"
)
,
name
:
"
有效日期解析
"
,
input
:
"2023-
10
-15"
,
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"invalid format"
,
input
:
"15-05-2023"
,
want
:
Date
{},
wantErr
:
true
,
name
:
"斜杠格式日期"
,
input
:
"2023/10/15"
,
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"紧凑格式日期"
,
input
:
"20231015"
,
want
:
"2023-10-15"
,
wantErr
:
false
,
},
{
name
:
"
not a string
"
,
input
:
123
,
want
:
Date
{}
,
name
:
"
无效日期格式
"
,
input
:
"invalid-date"
,
want
:
""
,
wantErr
:
true
,
},
{
name
:
"空字符串"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
var
d
Date
err
:=
d
.
UnmarshalYAML
(
tt
.
input
)
if
tt
.
wantErr
{
assert
.
Error
(
t
,
err
)
}
else
{
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
want
,
d
)
got
,
err
:=
ParseDate
(
tt
.
input
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"ParseDate() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
got
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"ParseDate() = %v, want %v"
,
got
.
String
(),
tt
.
want
)
}
})
}
}
func
TestDate_Time
(
t
*
testing
.
T
)
{
date
:=
MustParseDate
(
"2023-05-15"
)
expected
:=
time
.
Date
(
2023
,
time
.
May
,
15
,
0
,
0
,
0
,
0
,
time
.
UTC
)
assert
.
Equal
(
t
,
expected
,
date
.
Time
()
.
UTC
())
// TestDate_Comparison 测试日期比较方法[7,8](@ref)
func
TestDate_Comparison
(
t
*
testing
.
T
)
{
date1
:=
MustParseDate
(
"2023-10-15"
)
date2
:=
MustParseDate
(
"2023-10-16"
)
date3
:=
MustParseDate
(
"2023-10-15"
)
// 与date1相同
t
.
Run
(
"After方法"
,
func
(
t
*
testing
.
T
)
{
if
!
date2
.
After
(
date1
)
{
t
.
Error
(
"date2应该在date1之后"
)
}
if
date1
.
After
(
date2
)
{
t
.
Error
(
"date1不应该在date2之后"
)
}
})
t
.
Run
(
"Before方法"
,
func
(
t
*
testing
.
T
)
{
if
!
date1
.
Before
(
date2
)
{
t
.
Error
(
"date1应该在date2之前"
)
}
if
date2
.
Before
(
date1
)
{
t
.
Error
(
"date2不应该在date1之前"
)
}
})
t
.
Run
(
"Equal方法"
,
func
(
t
*
testing
.
T
)
{
if
!
date1
.
Equal
(
date3
)
{
t
.
Error
(
"date1应该等于date3"
)
}
if
date1
.
Equal
(
date2
)
{
t
.
Error
(
"date1不应该等于date2"
)
}
})
}
func
TestDate_String
(
t
*
testing
.
T
)
{
date
:=
MustParseDate
(
"2023-05-15"
)
assert
.
Equal
(
t
,
"2023-05-15"
,
date
.
String
())
// TestDate_Arithmetic 测试日期算术运算
func
TestDate_Arithmetic
(
t
*
testing
.
T
)
{
baseDate
:=
MustParseDate
(
"2023-10-15"
)
t
.
Run
(
"AddDays方法"
,
func
(
t
*
testing
.
T
)
{
result
:=
baseDate
.
AddDays
(
5
)
expected
:=
MustParseDate
(
"2023-10-20"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddDays(5) = %v, want %v"
,
result
,
expected
)
}
result
=
baseDate
.
AddDays
(
-
3
)
expected
=
MustParseDate
(
"2023-10-12"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddDays(-3) = %v, want %v"
,
result
,
expected
)
}
})
t
.
Run
(
"DaysBetween方法"
,
func
(
t
*
testing
.
T
)
{
date1
:=
MustParseDate
(
"2023-10-15"
)
date2
:=
MustParseDate
(
"2023-10-20"
)
days
:=
date2
.
DaysBetween
(
date1
)
if
days
!=
5
{
t
.
Errorf
(
"DaysBetween() = %v, want 5"
,
days
)
}
days
=
date1
.
DaysBetween
(
date2
)
if
days
!=
-
5
{
t
.
Errorf
(
"DaysBetween() = %v, want -5"
,
days
)
}
})
}
// TestDate_Components 测试日期组件获取
func
TestDate_Components
(
t
*
testing
.
T
)
{
date
:=
MustParseDate
(
"2023-10-15"
)
t
.
Run
(
"Year方法"
,
func
(
t
*
testing
.
T
)
{
if
year
:=
date
.
Year
();
year
!=
2023
{
t
.
Errorf
(
"Year() = %v, want 2023"
,
year
)
}
})
t
.
Run
(
"Month方法"
,
func
(
t
*
testing
.
T
)
{
if
month
:=
date
.
Month
();
month
!=
time
.
October
{
t
.
Errorf
(
"Month() = %v, want October"
,
month
)
}
})
t
.
Run
(
"Day方法"
,
func
(
t
*
testing
.
T
)
{
if
day
:=
date
.
Day
();
day
!=
15
{
t
.
Errorf
(
"Day() = %v, want 15"
,
day
)
}
})
t
.
Run
(
"Weekday方法"
,
func
(
t
*
testing
.
T
)
{
// 2023-10-15是星期日
if
weekday
:=
date
.
Weekday
();
weekday
!=
time
.
Sunday
{
t
.
Errorf
(
"Weekday() = %v, want Sunday"
,
weekday
)
}
})
}
// TestDate_ZeroValue 测试零值处理
func
TestDate_ZeroValue
(
t
*
testing
.
T
)
{
var
zeroDate
Date
t
.
Run
(
"IsZero方法"
,
func
(
t
*
testing
.
T
)
{
if
!
zeroDate
.
IsZero
()
{
t
.
Error
(
"IsZero()应该返回true对于零值"
)
}
validDate
:=
MustParseDate
(
"2023-10-15"
)
if
validDate
.
IsZero
()
{
t
.
Error
(
"IsZero()应该返回false对于有效日期"
)
}
})
t
.
Run
(
"零值的字符串表示"
,
func
(
t
*
testing
.
T
)
{
if
zeroDate
.
String
()
!=
""
{
t
.
Errorf
(
"零值的String()应该返回空字符串, 得到: %s"
,
zeroDate
.
String
())
}
})
t
.
Run
(
"零值的JSON序列化"
,
func
(
t
*
testing
.
T
)
{
bytes
,
err
:=
json
.
Marshal
(
zeroDate
)
if
err
!=
nil
{
t
.
Errorf
(
"零值JSON序列化错误: %v"
,
err
)
}
if
string
(
bytes
)
!=
"null"
{
t
.
Errorf
(
"零值JSON应该序列化为null, 得到: %s"
,
string
(
bytes
))
}
})
}
// TestToday 测试Today函数[7,8](@ref)
func
TestToday
(
t
*
testing
.
T
)
{
today
:=
Today
()
now
:=
time
.
Now
()
expected
:=
time
.
Date
(
now
.
Year
(),
now
.
Month
(),
now
.
Day
(),
0
,
0
,
0
,
0
,
now
.
Location
())
assert
.
Equal
(
t
,
expected
,
TodayDate
()
.
Time
())
expected
:=
Date
(
time
.
Date
(
now
.
Year
(),
now
.
Month
(),
now
.
Day
(),
0
,
0
,
0
,
0
,
now
.
Location
()))
if
!
today
.
Equal
(
expected
)
{
t
.
Errorf
(
"Today() = %v, want %v"
,
today
,
expected
)
}
}
func
TestParseDate
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
// TestMustParseDate 测试MustParseDate函数
func
TestMustParseDate
(
t
*
testing
.
T
)
{
t
.
Run
(
"有效日期"
,
func
(
t
*
testing
.
T
)
{
defer
func
()
{
if
r
:=
recover
();
r
!=
nil
{
t
.
Error
(
"有效日期不应该引起panic"
)
}
}()
date
:=
MustParseDate
(
"2023-10-15"
)
if
date
.
String
()
!=
"2023-10-15"
{
t
.
Errorf
(
"MustParseDate() = %v, want 2023-10-15"
,
date
.
String
())
}
})
t
.
Run
(
"无效日期应该panic"
,
func
(
t
*
testing
.
T
)
{
defer
func
()
{
if
r
:=
recover
();
r
==
nil
{
t
.
Error
(
"无效日期应该引起panic"
)
}
}()
_
=
MustParseDate
(
"invalid-date"
)
})
}
// BenchmarkDate_Parse 性能测试:日期解析[3](@ref)
func
BenchmarkDate_Parse
(
b
*
testing
.
B
)
{
testCases
:=
[]
string
{
"2023-10-15"
,
"2023/10/15"
,
"20231015"
,
""
,
}
for
_
,
tc
:=
range
testCases
{
b
.
Run
(
tc
,
func
(
b
*
testing
.
B
)
{
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
,
_
=
ParseDate
(
tc
)
}
})
}
}
// BenchmarkDate_JSONMarshal 性能测试:JSON序列化
func
BenchmarkDate_JSONMarshal
(
b
*
testing
.
B
)
{
date
:=
MustParseDate
(
"2023-10-15"
)
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
,
_
=
json
.
Marshal
(
date
)
}
}
// BenchmarkDate_JSONUnmarshal 性能测试:JSON反序列化
func
BenchmarkDate_JSONUnmarshal
(
b
*
testing
.
B
)
{
testCases
:=
[]
struct
{
name
string
input
string
want
Date
wantErr
bool
data
[]
byte
}{
{
name
:
"valid date"
,
input
:
"2023-05-15"
,
want
:
MustParseDate
(
"2023-05-15"
),
wantErr
:
false
,
},
{
name
:
"invalid format"
,
input
:
"15-05-2023"
,
want
:
Date
{},
wantErr
:
true
,
},
{
"有效日期"
,
[]
byte
(
`"2023-10-15"`
)},
{
"空值"
,
[]
byte
(
`null`
)},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
ParseDate
(
tt
.
input
)
if
tt
.
wantErr
{
assert
.
Error
(
t
,
err
)
}
else
{
assert
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
tt
.
want
,
got
)
for
_
,
tc
:=
range
testCases
{
b
.
Run
(
tc
.
name
,
func
(
b
*
testing
.
B
)
{
var
date
Date
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
=
json
.
Unmarshal
(
tc
.
data
,
&
date
)
}
})
}
}
func
TestMustParseDate
(
t
*
testing
.
T
)
{
assert
.
NotPanics
(
t
,
func
()
{
_
=
MustParseDate
(
"2023-05-15"
)
// BenchmarkDate_Value 性能测试:数据库值生成
func
BenchmarkDate_Value
(
b
*
testing
.
B
)
{
date
:=
MustParseDate
(
"2023-10-15"
)
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
,
_
=
date
.
Value
()
}
}
// TestDate_EdgeCases 测试边界情况[4](@ref)
func
TestDate_EdgeCases
(
t
*
testing
.
T
)
{
t
.
Run
(
"闰年测试"
,
func
(
t
*
testing
.
T
)
{
leapDate
:=
MustParseDate
(
"2020-02-29"
)
if
leapDate
.
String
()
!=
"2020-02-29"
{
t
.
Error
(
"闰年日期处理错误"
)
}
})
assert
.
Panics
(
t
,
func
()
{
_
=
MustParseDate
(
"invalid-date"
)
t
.
Run
(
"时区处理"
,
func
(
t
*
testing
.
T
)
{
utcTime
:=
time
.
Date
(
2023
,
10
,
15
,
22
,
0
,
0
,
0
,
time
.
UTC
)
cstTime
:=
time
.
Date
(
2023
,
10
,
16
,
6
,
0
,
0
,
0
,
time
.
FixedZone
(
"CST"
,
8
*
3600
))
utcDate
:=
Date
(
utcTime
)
cstDate
:=
Date
(
cstTime
)
if
!
utcDate
.
Equal
(
cstDate
)
{
t
.
Error
(
"不同时区的同一天应该相等"
)
}
})
t
.
Run
(
"月份边界"
,
func
(
t
*
testing
.
T
)
{
lastDay
:=
MustParseDate
(
"2023-01-31"
)
nextMonth
:=
lastDay
.
AddDays
(
1
)
if
nextMonth
.
String
()
!=
"2023-02-01"
{
t
.
Errorf
(
"月份边界处理错误: %v"
,
nextMonth
)
}
})
}
// TestDate_Integration 集成测试[1](@ref)
func
TestDate_Integration
(
t
*
testing
.
T
)
{
t
.
Run
(
"完整序列化循环"
,
func
(
t
*
testing
.
T
)
{
original
:=
MustParseDate
(
"2023-10-15"
)
// JSON序列化 -> 反序列化
jsonData
,
err
:=
json
.
Marshal
(
original
)
if
err
!=
nil
{
t
.
Fatalf
(
"JSON序列化失败: %v"
,
err
)
}
var
fromJSON
Date
err
=
json
.
Unmarshal
(
jsonData
,
&
fromJSON
)
if
err
!=
nil
{
t
.
Fatalf
(
"JSON反序列化失败: %v"
,
err
)
}
if
!
original
.
Equal
(
fromJSON
)
{
t
.
Error
(
"JSON序列化循环失败"
)
}
// 数据库值循环
dbValue
,
err
:=
original
.
Value
()
if
err
!=
nil
{
t
.
Fatalf
(
"Value()失败: %v"
,
err
)
}
var
fromDB
Date
err
=
fromDB
.
Scan
(
dbValue
)
if
err
!=
nil
{
t
.
Fatalf
(
"Scan()失败: %v"
,
err
)
}
if
!
original
.
Equal
(
fromDB
)
{
t
.
Error
(
"数据库循环失败"
)
}
})
}
utils/xtime/datetime.go
浏览文件 @
6d8995d2
...
...
@@ -2,10 +2,14 @@ package xtime
import
(
"database/sql/driver"
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
"time"
"gopkg.in/yaml.v3"
)
const
(
...
...
@@ -27,89 +31,231 @@ var (
LocBeiJing
,
_
=
time
.
LoadLocation
(
"Asia/Shanghai"
)
)
//
定义time.Time的别名
//
DateTime 定义基于 time.Time 的自定义日期时间类型
type
DateTime
time
.
Time
// 自定义json序列化
func
(
x
DateTime
)
MarshalJSON
()
([]
byte
,
error
)
{
if
x
.
IsZero
()
{
return
[]
byte
(
`""`
),
nil
// 定义支持的时间格式常量(按优先级排序)
const
(
DateTimeFormat
=
"2006-01-02 15:04:05"
// 标准日期时间格式
DateFormat
=
"2006-01-02"
// 仅日期格式
RFC3339Format
=
time
.
RFC3339
// RFC3339 标准格式
CompactDateTimeFormat
=
"20060102150405"
// 紧凑格式
)
// MarshalJSON 自定义JSON序列化
func
(
dt
DateTime
)
MarshalJSON
()
([]
byte
,
error
)
{
t
:=
time
.
Time
(
dt
)
if
t
.
IsZero
()
{
return
[]
byte
(
"null"
),
nil
}
var
jsonTimeStr
=
fmt
.
Sprintf
(
`"%s"`
,
time
.
Time
(
x
)
.
Format
(
"2006-01-02 15:04:05"
))
return
[]
byte
(
jsonTimeStr
),
nil
// 使用标准JSON编码确保引号正确处理
return
json
.
Marshal
(
t
.
Format
(
DateTimeFormat
))
}
// 自定义json反序列化
func
(
x
*
DateTime
)
UnmarshalJSON
(
data
[]
byte
)
error
{
if
len
(
data
)
==
0
||
string
(
data
)
==
`""`
{
*
x
=
DateTime
{}
// UnmarshalJSON 自定义JSON反序列化,支持多种格式
func
(
dt
*
DateTime
)
UnmarshalJSON
(
data
[]
byte
)
error
{
str
:=
strings
.
TrimSpace
(
string
(
data
))
// 处理空值和null
if
str
==
"null"
||
str
==
`""`
||
str
==
""
{
*
dt
=
DateTime
(
time
.
Time
{})
return
nil
}
t
,
err
:=
time
.
Parse
(
`"2006-01-02 15:04:05"`
,
string
(
data
))
if
err
!=
nil
{
return
err
// 移除JSON字符串的引号
if
len
(
str
)
>=
2
&&
str
[
0
]
==
'"'
&&
str
[
len
(
str
)
-
1
]
==
'"'
{
str
=
str
[
1
:
len
(
str
)
-
1
]
}
*
x
=
DateTime
(
t
)
return
dt
.
parseString
(
str
)
}
// MarshalYAML 自定义YAML序列化
func
(
dt
DateTime
)
MarshalYAML
()
(
interface
{},
error
)
{
t
:=
time
.
Time
(
dt
)
if
t
.
IsZero
()
{
return
nil
,
nil
}
return
t
.
Format
(
DateTimeFormat
),
nil
}
// UnmarshalYAML 自定义YAML反序列化
func
(
dt
*
DateTime
)
UnmarshalYAML
(
value
*
yaml
.
Node
)
error
{
if
value
==
nil
||
value
.
Kind
!=
yaml
.
ScalarNode
{
return
fmt
.
Errorf
(
"DateTime.UnmarshalYAML: 日期时间必须为字符串值"
)
}
str
:=
strings
.
TrimSpace
(
value
.
Value
)
if
str
==
""
||
str
==
"null"
{
*
dt
=
DateTime
(
time
.
Time
{})
return
nil
}
return
dt
.
parseString
(
str
)
}
func
(
x
DateTime
)
Time
()
time
.
Time
{
return
time
.
Time
(
x
)
// String 返回日期时间的字符串表示
func
(
dt
DateTime
)
String
()
string
{
t
:=
time
.
Time
(
dt
)
if
t
.
IsZero
()
{
return
""
}
return
t
.
Format
(
DateTimeFormat
)
}
func
(
x
DateTime
)
IsZero
()
bool
{
return
x
.
Time
()
.
IsZero
()
// parseString 内部解析方法,支持多种时间格式
func
(
dt
*
DateTime
)
parseString
(
s
string
)
error
{
s
=
strings
.
TrimSpace
(
s
)
if
s
==
""
{
*
dt
=
DateTime
(
time
.
Time
{})
return
nil
}
// 支持的时间格式列表(按优先级排序)
formats
:=
[]
string
{
DateTimeFormat
,
// "2006-01-02 15:04:05"
DateFormat
,
// "2006-01-02"
RFC3339Format
,
// RFC3339格式
"2006/01/02 15:04:05"
,
// 斜杠分隔格式
CompactDateTimeFormat
,
// 紧凑格式
time
.
RFC1123
,
// HTTP日期格式
}
var
firstErr
error
for
_
,
format
:=
range
formats
{
parsed
,
err
:=
time
.
Parse
(
format
,
s
)
if
err
==
nil
{
*
dt
=
DateTime
(
parsed
)
return
nil
}
if
firstErr
==
nil
{
firstErr
=
err
}
}
return
fmt
.
Errorf
(
"DateTime.parseString: 无法解析日期时间 %q, 支持的格式示例: %s"
,
s
,
DateTimeFormat
)
}
func
(
x
DateTime
)
IsNotZero
()
bool
{
return
!
x
.
IsZero
()
// Time 转换为标准的 time.Time
func
(
dt
DateTime
)
Time
()
time
.
Time
{
return
time
.
Time
(
dt
)
}
func
NewDateTime
(
t
time
.
Time
)
DateTime
{
return
DateTime
(
t
)
// IsZero 检查是否为零值
func
(
dt
DateTime
)
IsZero
()
bool
{
return
time
.
Time
(
dt
)
.
IsZero
()
}
func
Now
()
DateTime
{
return
DateTime
(
time
.
Now
())
// IsNotZero 检查是否非零值
func
(
dt
DateTime
)
IsNotZero
()
bool
{
return
!
dt
.
IsZero
()
}
func
NowPtr
()
*
DateTime
{
j
:=
DateTime
(
time
.
Now
())
return
&
j
// Format 使用自定义格式格式化日期时间
func
(
dt
DateTime
)
Format
(
layout
string
)
string
{
return
time
.
Time
(
dt
)
.
Format
(
layout
)
}
// Value
insert timestamp into mysql need this function.
func
(
t
DateTime
)
Value
()
(
driver
.
Value
,
error
)
{
var
zeroTime
time
.
Time
if
t
ime
.
Time
(
t
)
.
UnixNano
()
==
zeroTime
.
UnixNan
o
()
{
// Value
实现 driver.Valuer 接口,用于数据库存储
func
(
d
t
DateTime
)
Value
()
(
driver
.
Value
,
error
)
{
t
:=
time
.
Time
(
dt
)
if
t
.
IsZer
o
()
{
return
nil
,
nil
}
return
t
ime
.
Time
(
t
)
,
nil
return
t
,
nil
}
// Scan value of time.Time
func
(
t
*
DateTime
)
Scan
(
value
interface
{})
error
{
// Scan 实现 sql.Scanner 接口,用于从数据库读取
func
(
dt
*
DateTime
)
Scan
(
value
interface
{})
error
{
if
value
==
nil
{
*
dt
=
DateTime
(
time
.
Time
{})
return
nil
}
switch
v
:=
value
.
(
type
)
{
case
time
.
Time
:
val
,
ok
:=
value
.
(
time
.
Time
)
if
ok
{
*
t
=
DateTime
(
val
)
*
dt
=
DateTime
(
v
)
return
nil
case
[]
byte
:
if
len
(
v
)
==
0
{
*
dt
=
DateTime
(
time
.
Time
{})
return
nil
}
return
fmt
.
Errorf
(
"unsupported time format: %T"
,
v
)
case
[]
uint8
:
// 假设是RFC3339格式的二进制时间
str
:=
string
(
v
)
val
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
str
)
if
err
!=
nil
{
return
err
}
*
t
=
DateTime
(
val
)
return
dt
.
parseString
(
string
(
v
))
case
string
:
if
v
==
""
{
*
dt
=
DateTime
(
time
.
Time
{})
return
nil
}
return
dt
.
parseString
(
v
)
default
:
return
fmt
.
Errorf
(
"
unsupported time format: %T"
,
v
)
return
fmt
.
Errorf
(
"
DateTime.Scan: 不支持的扫描类型 %T"
,
value
)
}
}
// After 检查当前时间是否在另一个时间之后
func
(
dt
DateTime
)
After
(
other
DateTime
)
bool
{
return
time
.
Time
(
dt
)
.
After
(
time
.
Time
(
other
))
}
// Before 检查当前时间是否在另一个时间之前
func
(
dt
DateTime
)
Before
(
other
DateTime
)
bool
{
return
time
.
Time
(
dt
)
.
Before
(
time
.
Time
(
other
))
}
// Equal 检查两个时间是否相等
func
(
dt
DateTime
)
Equal
(
other
DateTime
)
bool
{
return
time
.
Time
(
dt
)
.
Equal
(
time
.
Time
(
other
))
}
// Add 添加时间间隔
func
(
dt
DateTime
)
Add
(
duration
time
.
Duration
)
DateTime
{
return
DateTime
(
time
.
Time
(
dt
)
.
Add
(
duration
))
}
// Unix 返回Unix时间戳
func
(
dt
DateTime
)
Unix
()
int64
{
return
time
.
Time
(
dt
)
.
Unix
()
}
// UnixNano 返回纳秒级Unix时间戳
func
(
dt
DateTime
)
UnixNano
()
int64
{
return
time
.
Time
(
dt
)
.
UnixNano
()
}
// 构造函数
// NewDateTime 从 time.Time 创建 DateTime
func
NewDateTime
(
t
time
.
Time
)
DateTime
{
return
DateTime
(
t
)
}
// Now 返回当前日期时间
func
Now
()
DateTime
{
return
DateTime
(
time
.
Now
())
}
// NowPtr 返回当前日期时间的指针
func
NowPtr
()
*
DateTime
{
dt
:=
DateTime
(
time
.
Now
())
return
&
dt
}
// ParseDateTime 从字符串解析日期时间
func
ParseDateTime
(
s
string
)
(
DateTime
,
error
)
{
var
dt
DateTime
err
:=
dt
.
parseString
(
s
)
return
dt
,
err
}
// MustParseDateTime 从字符串解析日期时间,解析失败时panic
func
MustParseDateTime
(
s
string
)
DateTime
{
dt
,
err
:=
ParseDateTime
(
s
)
if
err
!=
nil
{
panic
(
fmt
.
Sprintf
(
"MustParseDateTime 解析失败: %q, 错误: %v"
,
s
,
err
))
}
return
dt
}
func
BeginOfDay
(
t
time
.
Time
)
DateTime
{
y
,
m
,
d
:=
t
.
Date
()
begin
:=
time
.
Date
(
y
,
m
,
d
,
0
,
0
,
0
,
0
,
t
.
Location
())
...
...
@@ -187,34 +333,10 @@ func ParseExtendedDuration(s string) (time.Duration, error) {
return
total
,
nil
}
func
(
x
DateTime
)
Format
(
layout
string
)
string
{
return
x
.
Time
()
.
Format
(
layout
)
}
func
(
x
DateTime
)
FormatYYYYMMDDHHmmSS
()
string
{
return
x
.
Time
()
.
Format
(
Layout_YYYYMMDDHHmmSS
)
}
// const TimeFormat = "2006-01-02 15:04:05"
// // Value insert timestamp into mysql need this function.
// func (t DateTime) Value() (driver.Value, error) {
// var zeroTime time.Time
// if time.Time(t).UnixNano() == zeroTime.UnixNano() {
// return nil, nil
// }
// return time.Time(t), nil
// }
// // Scan value of time.Time
// func (t *DateTime) Scan(v interface{}) error {
// value, ok := v.(time.Time)
// if ok {
// *t = DateTime(value)
// return nil
// }
// return fmt.Errorf("can not convert %v to timestamp", v)
// }
func
(
x
DateTime
)
Date
()
DateTime
{
y
,
m
,
d
:=
x
.
Time
()
.
Date
()
trimmedTime
:=
time
.
Date
(
y
,
m
,
d
,
0
,
0
,
0
,
0
,
x
.
Time
()
.
Location
())
...
...
@@ -245,8 +367,39 @@ func (t0 DateTime) MonthEnd() DateTime {
return
DateTime
(
t1
)
}
func
(
t
DateTime
)
AddDate
(
y
,
m
,
d
int
)
DateTime
{
return
DateTime
(
t
.
Time
()
.
AddDate
(
y
,
m
,
d
))
// func (t DateTime) AddDate(y, m, d int) DateTime {
// return DateTime(t.Time().AddDate(y, m, d))
// }
// AddDate 添加年、月、日,正确处理月份边界溢出
func
(
dt
DateTime
)
AddDate
(
years
,
months
,
days
int
)
DateTime
{
t
:=
time
.
Time
(
dt
)
y
,
m
,
d
:=
t
.
Date
()
h
,
min
,
s
:=
t
.
Clock
()
nsec
:=
t
.
Nanosecond
()
loc
:=
t
.
Location
()
// 计算新月份和新年
newMonth
:=
int
(
m
)
+
months
newYear
:=
y
+
years
+
(
newMonth
-
1
)
/
12
newMonth
=
(
newMonth
-
1
)
%
12
+
1
if
newMonth
<=
0
{
newMonth
+=
12
newYear
--
}
// 计算新月的最大天数
maxDay
:=
daysInMonth
(
newYear
,
time
.
Month
(
newMonth
))
newDay
:=
d
if
d
>
maxDay
{
newDay
=
maxDay
}
// 创建新时间,年月日部分
newTime
:=
time
.
Date
(
newYear
,
time
.
Month
(
newMonth
),
newDay
,
h
,
min
,
s
,
nsec
,
loc
)
// 然后加减天数
newTime
=
newTime
.
AddDate
(
0
,
0
,
days
)
return
DateTime
(
newTime
)
}
// WeekDay 1-7 周一到周日
...
...
@@ -255,21 +408,18 @@ func (x DateTime) WeekDay() int {
}
// 1-31 1号到31号
func
(
x
DateTime
)
Day
()
int
{
return
int
(
x
.
Time
()
.
Day
())
}
//
func (x DateTime) Day() int {
//
return int(x.Time().Day())
//
}
// 1-12 1月到12月
func
(
x
DateTime
)
Month
()
int
{
return
int
(
x
.
Time
()
.
Month
())
}
// 1970年到现在第几年
func
(
x
DateTime
)
Year
()
int
{
return
int
(
x
.
Time
()
.
Year
())
}
func
(
x
DateTime
)
After
(
t1
DateTime
)
bool
{
return
x
.
Time
()
.
After
(
t1
.
Time
())
func
daysInMonth
(
year
int
,
month
time
.
Month
)
int
{
// 下个月的第0天是本月最后一天
return
time
.
Date
(
year
,
month
+
1
,
0
,
0
,
0
,
0
,
0
,
time
.
UTC
)
.
Day
()
}
// 转为北京时间
...
...
@@ -300,14 +450,6 @@ func ParseYYYYMMDDHHmmSS(value string) DateTime {
return
DateTime
(
t
)
}
func
ParseTime
(
layout
,
value
string
)
(
DateTime
,
error
)
{
t
,
err
:=
time
.
Parse
(
layout
,
value
)
if
err
!=
nil
{
return
DateTime
{},
err
}
return
DateTime
(
t
),
nil
}
func
IsSameDay
(
t1
,
t2
DateTime
)
bool
{
t1Local
:=
t1
.
Local
()
t2Local
:=
t2
.
Local
()
...
...
@@ -358,7 +500,7 @@ func Yesterday() DateTime {
return
DateTime
(
time
.
Now
()
.
AddDate
(
0
,
0
,
-
1
))
}
func
Today
()
DateTime
{
func
Today
DateTime
()
DateTime
{
return
DateTime
(
time
.
Now
())
}
...
...
@@ -385,3 +527,34 @@ func DaysBetween(t0, t1 DateTime) int {
days
:=
int
(
diff
.
Hours
()
/
24
)
return
days
}
// 时间组件获取方法
func
(
dt
DateTime
)
Hour
()
int
{
return
time
.
Time
(
dt
)
.
Hour
()
}
func
(
dt
DateTime
)
Minute
()
int
{
return
time
.
Time
(
dt
)
.
Minute
()
}
func
(
dt
DateTime
)
Second
()
int
{
return
time
.
Time
(
dt
)
.
Second
()
}
func
(
dt
DateTime
)
Year
()
int
{
return
time
.
Time
(
dt
)
.
Year
()
}
// func (dt DateTime) Month() time.Month {
// return time.Time(dt).Month()
// }
func
(
dt
DateTime
)
Day
()
int
{
return
time
.
Time
(
dt
)
.
Day
()
}
// 月份常量(Go语言time包内置,直接使用)
// time.January (1月), time.February (2月), time.March (3月)
// time.April (4月), time.May (5月), time.June (6月)
// time.July (7月), time.August (8月), time.September (9月)
// time.October (10月), time.November (11月), time.December (12月)
utils/xtime/datetime_test.go
0 → 100644
浏览文件 @
6d8995d2
package
xtime
import
(
"encoding/json"
"testing"
"time"
"gopkg.in/yaml.v3"
)
// TestDateTime_Scan 测试数据库扫描功能
func
TestDateTime_Scan
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
interface
{}
want
string
wantErr
bool
}{
{
name
:
"有效time.Time类型"
,
input
:
time
.
Date
(
2023
,
10
,
17
,
14
,
30
,
0
,
0
,
time
.
UTC
),
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"有效字节切片"
,
input
:
[]
byte
(
"2023-10-17 14:30:00"
),
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"有效字符串"
,
input
:
"2023-10-17 14:30:00"
,
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"空值处理"
,
input
:
nil
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"空字符串"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"空字节切片"
,
input
:
[]
byte
{},
want
:
""
,
wantErr
:
false
,
},
{
name
:
"无效类型"
,
input
:
123
,
want
:
""
,
wantErr
:
true
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
var
dt
DateTime
err
:=
dt
.
Scan
(
tt
.
input
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"Scan() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
dt
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"Scan() = %v, want %v"
,
dt
.
String
(),
tt
.
want
)
}
})
}
}
// TestDateTime_Value 测试数据库值生成
func
TestDateTime_Value
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
DateTime
want
interface
{}
wantErr
bool
}{
{
name
:
"有效日期时间"
,
input
:
NewDateTime
(
time
.
Date
(
2023
,
10
,
17
,
14
,
30
,
0
,
0
,
time
.
UTC
)),
want
:
time
.
Date
(
2023
,
10
,
17
,
14
,
30
,
0
,
0
,
time
.
UTC
),
wantErr
:
false
,
},
{
name
:
"零值日期时间"
,
input
:
DateTime
{},
want
:
nil
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
tt
.
input
.
Value
()
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"Value() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
{
if
tt
.
want
==
nil
{
if
got
!=
nil
{
t
.
Errorf
(
"Value() = %v, want nil"
,
got
)
}
}
else
{
wantTime
:=
tt
.
want
.
(
time
.
Time
)
gotTime
:=
got
.
(
time
.
Time
)
if
!
gotTime
.
Equal
(
wantTime
)
{
t
.
Errorf
(
"Value() = %v, want %v"
,
got
,
tt
.
want
)
}
}
}
})
}
}
// TestDateTime_JSON 测试JSON序列化和反序列化
func
TestDateTime_JSON
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
string
want
string
wantErr
bool
}{
{
name
:
"有效日期时间JSON"
,
input
:
`"2023-10-17 14:30:00"`
,
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"空值JSON"
,
input
:
"null"
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"空字符串JSON"
,
input
:
`""`
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"无效日期时间格式"
,
input
:
`"2023-13-45 25:70:00"`
,
want
:
""
,
wantErr
:
true
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
// 测试反序列化
var
dt
DateTime
err
:=
json
.
Unmarshal
([]
byte
(
tt
.
input
),
&
dt
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"UnmarshalJSON() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
dt
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"UnmarshalJSON() = %v, want %v"
,
dt
.
String
(),
tt
.
want
)
return
}
// 测试序列化(仅对有效用例)
if
!
tt
.
wantErr
&&
tt
.
want
!=
""
{
bytes
,
err
:=
json
.
Marshal
(
dt
)
if
err
!=
nil
{
t
.
Errorf
(
"MarshalJSON() error = %v"
,
err
)
return
}
expectedJSON
:=
`"`
+
tt
.
want
+
`"`
if
string
(
bytes
)
!=
expectedJSON
{
t
.
Errorf
(
"MarshalJSON() = %s, want %s"
,
string
(
bytes
),
expectedJSON
)
}
}
})
}
}
// TestDateTime_YAML 测试YAML序列化和反序列化
func
TestDateTime_YAML
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
string
want
string
wantErr
bool
}{
{
name
:
"有效日期时间YAML"
,
input
:
"2023-10-17 14:30:00"
,
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"空字符串YAML"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
// 创建YAML节点进行测试
node
:=
&
yaml
.
Node
{
Kind
:
yaml
.
ScalarNode
,
Value
:
tt
.
input
,
}
var
dt
DateTime
err
:=
dt
.
UnmarshalYAML
(
node
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"UnmarshalYAML() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
dt
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"UnmarshalYAML() = %v, want %v"
,
dt
.
String
(),
tt
.
want
)
}
// 测试序列化
if
!
tt
.
wantErr
&&
tt
.
want
!=
""
{
result
,
err
:=
dt
.
MarshalYAML
()
if
err
!=
nil
{
t
.
Errorf
(
"MarshalYAML() error = %v"
,
err
)
return
}
if
result
!=
tt
.
want
{
t
.
Errorf
(
"MarshalYAML() = %v, want %v"
,
result
,
tt
.
want
)
}
}
})
}
}
// TestDateTime_String 测试字符串表示
func
TestDateTime_String
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
DateTime
want
string
}{
{
name
:
"有效日期时间"
,
input
:
NewDateTime
(
time
.
Date
(
2023
,
10
,
17
,
14
,
30
,
0
,
0
,
time
.
UTC
)),
want
:
"2023-10-17 14:30:00"
,
},
{
name
:
"零值日期时间"
,
input
:
DateTime
{},
want
:
""
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
if
got
:=
tt
.
input
.
String
();
got
!=
tt
.
want
{
t
.
Errorf
(
"String() = %v, want %v"
,
got
,
tt
.
want
)
}
})
}
}
// TestParseDateTime 测试日期时间解析函数
func
TestParseDateTime
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
string
want
string
wantErr
bool
}{
{
name
:
"标准格式解析"
,
input
:
"2023-10-17 14:30:00"
,
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"斜杠格式日期"
,
input
:
"2023/10/17 14:30:00"
,
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"RFC3339格式"
,
input
:
"2023-10-17T14:30:00Z"
,
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"紧凑格式"
,
input
:
"20231017143000"
,
want
:
"2023-10-17 14:30:00"
,
wantErr
:
false
,
},
{
name
:
"无效日期时间格式"
,
input
:
"invalid-datetime"
,
want
:
""
,
wantErr
:
true
,
},
{
name
:
"空字符串"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
ParseDateTime
(
tt
.
input
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"ParseDateTime() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
got
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"ParseDateTime() = %v, want %v"
,
got
.
String
(),
tt
.
want
)
}
})
}
}
// TestDateTime_Comparison 测试日期时间比较方法
func
TestDateTime_Comparison
(
t
*
testing
.
T
)
{
dt1
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
dt2
:=
MustParseDateTime
(
"2023-10-17 15:45:00"
)
dt3
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
// 与dt1相同
t
.
Run
(
"After方法"
,
func
(
t
*
testing
.
T
)
{
if
!
dt2
.
After
(
dt1
)
{
t
.
Error
(
"dt2应该在dt1之后"
)
}
if
dt1
.
After
(
dt2
)
{
t
.
Error
(
"dt1不应该在dt2之后"
)
}
})
t
.
Run
(
"Before方法"
,
func
(
t
*
testing
.
T
)
{
if
!
dt1
.
Before
(
dt2
)
{
t
.
Error
(
"dt1应该在dt2之前"
)
}
if
dt2
.
Before
(
dt1
)
{
t
.
Error
(
"dt2不应该在dt1之前"
)
}
})
t
.
Run
(
"Equal方法"
,
func
(
t
*
testing
.
T
)
{
if
!
dt1
.
Equal
(
dt3
)
{
t
.
Error
(
"dt1应该等于dt3"
)
}
if
dt1
.
Equal
(
dt2
)
{
t
.
Error
(
"dt1不应该等于dt2"
)
}
})
}
// TestDateTime_Arithmetic 测试日期时间算术运算
func
TestDateTime_Arithmetic
(
t
*
testing
.
T
)
{
baseDateTime
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
t
.
Run
(
"Add方法"
,
func
(
t
*
testing
.
T
)
{
result
:=
baseDateTime
.
Add
(
2
*
time
.
Hour
)
expected
:=
MustParseDateTime
(
"2023-10-17 16:30:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"Add(2小时) = %v, want %v"
,
result
,
expected
)
}
result
=
baseDateTime
.
Add
(
-
30
*
time
.
Minute
)
expected
=
MustParseDateTime
(
"2023-10-17 14:00:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"Add(-30分钟) = %v, want %v"
,
result
,
expected
)
}
})
t
.
Run
(
"AddDate方法"
,
func
(
t
*
testing
.
T
)
{
result
:=
baseDateTime
.
AddDate
(
0
,
1
,
0
)
// 加1个月
expected
:=
MustParseDateTime
(
"2023-11-17 14:30:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddDate(0,1,0) = %v, want %v"
,
result
,
expected
)
}
result
=
baseDateTime
.
AddDate
(
1
,
0
,
0
)
// 加1年
expected
=
MustParseDateTime
(
"2024-10-17 14:30:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddDate(1,0,0) = %v, want %v"
,
result
,
expected
)
}
})
}
// TestDateTime_Components 测试日期时间组件获取
func
TestDateTime_Components
(
t
*
testing
.
T
)
{
dt
:=
MustParseDateTime
(
"2023-10-17 14:30:45"
)
t
.
Run
(
"Year方法"
,
func
(
t
*
testing
.
T
)
{
if
year
:=
dt
.
Year
();
year
!=
2023
{
t
.
Errorf
(
"Year() = %v, want 2023"
,
year
)
}
})
t
.
Run
(
"Month方法"
,
func
(
t
*
testing
.
T
)
{
if
month
:=
dt
.
Month
();
month
!=
10
{
t
.
Errorf
(
"Month() = %v, want October"
,
month
)
}
})
t
.
Run
(
"Day方法"
,
func
(
t
*
testing
.
T
)
{
if
day
:=
dt
.
Day
();
day
!=
17
{
t
.
Errorf
(
"Day() = %v, want 17"
,
day
)
}
})
t
.
Run
(
"Hour方法"
,
func
(
t
*
testing
.
T
)
{
if
hour
:=
dt
.
Hour
();
hour
!=
14
{
t
.
Errorf
(
"Hour() = %v, want 14"
,
hour
)
}
})
t
.
Run
(
"Minute方法"
,
func
(
t
*
testing
.
T
)
{
if
minute
:=
dt
.
Minute
();
minute
!=
30
{
t
.
Errorf
(
"Minute() = %v, want 30"
,
minute
)
}
})
t
.
Run
(
"Second方法"
,
func
(
t
*
testing
.
T
)
{
if
second
:=
dt
.
Second
();
second
!=
45
{
t
.
Errorf
(
"Second() = %v, want 45"
,
second
)
}
})
}
// TestDateTime_ZeroValue 测试零值处理
func
TestDateTime_ZeroValue
(
t
*
testing
.
T
)
{
var
zeroDateTime
DateTime
t
.
Run
(
"IsZero方法"
,
func
(
t
*
testing
.
T
)
{
if
!
zeroDateTime
.
IsZero
()
{
t
.
Error
(
"IsZero()应该返回true对于零值"
)
}
validDateTime
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
if
validDateTime
.
IsZero
()
{
t
.
Error
(
"IsZero()应该返回false对于有效日期时间"
)
}
})
t
.
Run
(
"IsNotZero方法"
,
func
(
t
*
testing
.
T
)
{
if
zeroDateTime
.
IsNotZero
()
{
t
.
Error
(
"IsNotZero()应该返回false对于零值"
)
}
validDateTime
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
if
!
validDateTime
.
IsNotZero
()
{
t
.
Error
(
"IsNotZero()应该返回true对于有效日期时间"
)
}
})
t
.
Run
(
"零值的字符串表示"
,
func
(
t
*
testing
.
T
)
{
if
zeroDateTime
.
String
()
!=
""
{
t
.
Errorf
(
"零值的String()应该返回空字符串, 得到: %s"
,
zeroDateTime
.
String
())
}
})
t
.
Run
(
"零值的JSON序列化"
,
func
(
t
*
testing
.
T
)
{
bytes
,
err
:=
json
.
Marshal
(
zeroDateTime
)
if
err
!=
nil
{
t
.
Errorf
(
"零值JSON序列化错误: %v"
,
err
)
}
if
string
(
bytes
)
!=
"null"
{
t
.
Errorf
(
"零值JSON应该序列化为null, 得到: %s"
,
string
(
bytes
))
}
})
}
// TestNow 测试Now函数
func
TestNow
(
t
*
testing
.
T
)
{
now
:=
Now
()
current
:=
time
.
Now
()
// 检查返回的日期时间组件是否匹配当前时间(允许微小误差)
if
now
.
Year
()
!=
current
.
Year
()
||
now
.
Month
()
!=
int
(
current
.
Month
())
||
now
.
Day
()
!=
current
.
Day
()
{
t
.
Error
(
"Now()返回的日期应该与当前日期匹配"
)
}
}
// TestMustParseDateTime 测试MustParseDateTime函数
func
TestMustParseDateTime
(
t
*
testing
.
T
)
{
t
.
Run
(
"有效日期时间"
,
func
(
t
*
testing
.
T
)
{
defer
func
()
{
if
r
:=
recover
();
r
!=
nil
{
t
.
Error
(
"有效日期时间不应该引起panic"
)
}
}()
dt
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
if
dt
.
String
()
!=
"2023-10-17 14:30:00"
{
t
.
Errorf
(
"MustParseDateTime() = %v, want 2023-10-17 14:30:00"
,
dt
.
String
())
}
})
t
.
Run
(
"无效日期时间应该panic"
,
func
(
t
*
testing
.
T
)
{
defer
func
()
{
if
r
:=
recover
();
r
==
nil
{
t
.
Error
(
"无效日期时间应该引起panic"
)
}
}()
_
=
MustParseDateTime
(
"invalid-datetime"
)
})
}
// TestDateTime_Format 测试自定义格式格式化
func
TestDateTime_Format
(
t
*
testing
.
T
)
{
dt
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
t
.
Run
(
"自定义格式"
,
func
(
t
*
testing
.
T
)
{
formatted
:=
dt
.
Format
(
"2006/01/02 15:04"
)
expected
:=
"2023/10/17 14:30"
if
formatted
!=
expected
{
t
.
Errorf
(
"Format() = %v, want %v"
,
formatted
,
expected
)
}
})
t
.
Run
(
"日期格式"
,
func
(
t
*
testing
.
T
)
{
formatted
:=
dt
.
Format
(
"2006-01-02"
)
expected
:=
"2023-10-17"
if
formatted
!=
expected
{
t
.
Errorf
(
"Format() = %v, want %v"
,
formatted
,
expected
)
}
})
}
// TestDateTime_Time 测试Time方法
func
TestDateTime_Time
(
t
*
testing
.
T
)
{
dt
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
timeVal
:=
dt
.
Time
()
if
timeVal
.
Year
()
!=
2023
||
timeVal
.
Month
()
!=
10
||
timeVal
.
Day
()
!=
17
||
timeVal
.
Hour
()
!=
14
||
timeVal
.
Minute
()
!=
30
||
timeVal
.
Second
()
!=
0
{
t
.
Error
(
"Time()返回的time.Time值与原始值不匹配"
)
}
}
// TestDateTime_Unix 测试时间戳方法
func
TestDateTime_Unix
(
t
*
testing
.
T
)
{
dt
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
unixTime
:=
dt
.
Unix
()
// 验证时间戳是否正确(使用已知值进行验证)
expected
:=
time
.
Date
(
2023
,
10
,
17
,
14
,
30
,
0
,
0
,
time
.
UTC
)
.
Unix
()
if
unixTime
!=
expected
{
t
.
Errorf
(
"Unix() = %v, want %v"
,
unixTime
,
expected
)
}
}
// TestDateTime_EdgeCases 测试边界情况
func
TestDateTime_EdgeCases
(
t
*
testing
.
T
)
{
t
.
Run
(
"闰年测试"
,
func
(
t
*
testing
.
T
)
{
leapDateTime
:=
MustParseDateTime
(
"2020-02-29 14:30:00"
)
if
leapDateTime
.
String
()
!=
"2020-02-29 14:30:00"
{
t
.
Error
(
"闰年日期时间处理错误"
)
}
})
t
.
Run
(
"时区处理"
,
func
(
t
*
testing
.
T
)
{
utcTime
:=
time
.
Date
(
2023
,
10
,
17
,
22
,
0
,
0
,
0
,
time
.
UTC
)
cstTime
:=
time
.
Date
(
2023
,
10
,
18
,
6
,
0
,
0
,
0
,
time
.
FixedZone
(
"CST"
,
8
*
3600
))
utcDateTime
:=
DateTime
(
utcTime
)
cstDateTime
:=
DateTime
(
cstTime
)
// 不同时区的相同时间点应该相等
if
!
utcDateTime
.
Equal
(
cstDateTime
)
{
t
.
Error
(
"不同时区的相同时间点应该相等"
)
}
})
t
.
Run
(
"月份边界"
,
func
(
t
*
testing
.
T
)
{
lastDay
:=
MustParseDateTime
(
"2023-01-31 23:59:59"
)
nextMonth
:=
lastDay
.
AddDate
(
0
,
1
,
0
)
// 加1个月
if
nextMonth
.
String
()
!=
"2023-02-28 23:59:59"
{
t
.
Errorf
(
"月份边界处理错误: %v"
,
nextMonth
)
}
})
}
// TestDateTime_Integration 集成测试
func
TestDateTime_Integration
(
t
*
testing
.
T
)
{
t
.
Run
(
"完整序列化循环"
,
func
(
t
*
testing
.
T
)
{
original
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
// JSON序列化 -> 反序列化
jsonData
,
err
:=
json
.
Marshal
(
original
)
if
err
!=
nil
{
t
.
Fatalf
(
"JSON序列化失败: %v"
,
err
)
}
var
fromJSON
DateTime
err
=
json
.
Unmarshal
(
jsonData
,
&
fromJSON
)
if
err
!=
nil
{
t
.
Fatalf
(
"JSON反序列化失败: %v"
,
err
)
}
if
!
original
.
Equal
(
fromJSON
)
{
t
.
Error
(
"JSON序列化循环失败"
)
}
// 数据库值循环
dbValue
,
err
:=
original
.
Value
()
if
err
!=
nil
{
t
.
Fatalf
(
"Value()失败: %v"
,
err
)
}
var
fromDB
DateTime
err
=
fromDB
.
Scan
(
dbValue
)
if
err
!=
nil
{
t
.
Fatalf
(
"Scan()失败: %v"
,
err
)
}
if
!
original
.
Equal
(
fromDB
)
{
t
.
Error
(
"数据库循环失败"
)
}
})
}
// BenchmarkDateTime_Parse 性能测试:日期时间解析
func
BenchmarkDateTime_Parse
(
b
*
testing
.
B
)
{
testCases
:=
[]
string
{
"2023-10-17 14:30:00"
,
"2023/10/17 14:30:00"
,
"2023-10-17T14:30:00Z"
,
""
,
}
for
_
,
tc
:=
range
testCases
{
b
.
Run
(
tc
,
func
(
b
*
testing
.
B
)
{
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
,
_
=
ParseDateTime
(
tc
)
}
})
}
}
// BenchmarkDateTime_JSONMarshal 性能测试:JSON序列化
func
BenchmarkDateTime_JSONMarshal
(
b
*
testing
.
B
)
{
dt
:=
MustParseDateTime
(
"2023-10-17 14:30:00"
)
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
,
_
=
json
.
Marshal
(
dt
)
}
}
// BenchmarkDateTime_JSONUnmarshal 性能测试:JSON反序列化
func
BenchmarkDateTime_JSONUnmarshal
(
b
*
testing
.
B
)
{
testCases
:=
[]
struct
{
name
string
data
[]
byte
}{
{
"有效日期时间"
,
[]
byte
(
`"2023-10-17 14:30:00"`
)},
{
"空值"
,
[]
byte
(
`null`
)},
}
for
_
,
tc
:=
range
testCases
{
b
.
Run
(
tc
.
name
,
func
(
b
*
testing
.
B
)
{
var
dt
DateTime
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
=
json
.
Unmarshal
(
tc
.
data
,
&
dt
)
}
})
}
}
utils/xtime/time.go
浏览文件 @
6d8995d2
...
...
@@ -2,75 +2,264 @@ package xtime
import
(
"database/sql/driver"
"encoding/json"
"fmt"
"strings"
"time"
"gopkg.in/yaml.v3"
)
type
Time
time
.
Time
const
timeFormat
=
"15:04:05"
// Scan 改进:增强空值处理和错误信息
func
(
t
*
Time
)
Scan
(
value
interface
{})
error
{
if
value
==
nil
{
*
t
=
Time
(
time
.
Time
{})
// 明确处理数据库NULL值
return
nil
}
switch
v
:=
value
.
(
type
)
{
case
[]
byte
:
parsed
,
err
:=
time
.
Parse
(
"15:04:05"
,
string
(
v
))
if
err
!=
nil
{
return
err
if
len
(
v
)
==
0
{
*
t
=
Time
(
time
.
Time
{})
return
nil
}
*
t
=
Time
(
parsed
)
return
t
.
parseString
(
string
(
v
)
)
case
string
:
parsed
,
err
:=
time
.
Parse
(
"15:04:05"
,
v
)
if
err
!=
nil
{
return
err
if
v
==
""
{
*
t
=
Time
(
time
.
Time
{})
return
nil
}
*
t
=
Time
(
parsed
)
return
t
.
parseString
(
v
)
case
time
.
Time
:
if
v
,
ok
:=
value
.
(
time
.
Time
);
ok
{
*
t
=
Time
(
v
)
}
// 提取时间部分,忽略日期
hour
,
min
,
sec
:=
v
.
Clock
()
*
t
=
Time
(
time
.
Date
(
0
,
1
,
1
,
hour
,
min
,
sec
,
0
,
v
.
Location
()))
return
nil
default
:
return
fmt
.
Errorf
(
"
unsupported type: %T"
,
v
)
return
fmt
.
Errorf
(
"
Time.Scan: 不支持的扫描类型 %T, 期望: string, []byte 或 time.Time"
,
value
)
}
return
nil
}
// Value 改进:更安全的零值处理
func
(
t
Time
)
Value
()
(
driver
.
Value
,
error
)
{
return
t
.
Time
()
.
Format
(
"15:04:05"
),
nil
if
t
.
IsZero
()
{
return
nil
,
nil
// 零值对应数据库NULL
}
return
t
.
Time
()
.
Format
(
timeFormat
),
nil
}
// MarshalJSON
自定义JSON序列化
// MarshalJSON
改进:使用标准JSON编码和更好的零值处理
func
(
t
Time
)
MarshalJSON
()
([]
byte
,
error
)
{
return
[]
byte
(
`"`
+
t
.
Time
()
.
Format
(
"15:04:05"
)
+
`"`
),
nil
if
t
.
IsZero
()
{
return
[]
byte
(
"null"
),
nil
}
// 使用标准JSON编码器避免手动拼接问题
return
json
.
Marshal
(
t
.
Time
()
.
Format
(
timeFormat
))
}
// UnmarshalJSON
自定义JSON反序列化
// UnmarshalJSON
改进:增强格式兼容性
func
(
t
*
Time
)
UnmarshalJSON
(
data
[]
byte
)
error
{
str
:=
strings
.
Trim
(
string
(
data
),
`"`
)
parsed
,
err
:=
time
.
Parse
(
"15:04:05"
,
str
)
if
err
!=
nil
{
return
err
}
*
t
=
Time
(
parsed
)
str
:=
strings
.
TrimSpace
(
string
(
data
))
// 处理null和空值
if
str
==
"null"
||
str
==
`""`
||
str
==
""
{
*
t
=
Time
(
time
.
Time
{})
return
nil
}
// 安全去除JSON引号
var
timeStr
string
if
len
(
str
)
>=
2
&&
str
[
0
]
==
'"'
&&
str
[
len
(
str
)
-
1
]
==
'"'
{
timeStr
=
str
[
1
:
len
(
str
)
-
1
]
}
else
{
timeStr
=
str
}
return
t
.
parseString
(
timeStr
)
}
// MarshalYAML
自定义YAML序列化
// MarshalYAML
改进:符合yaml.v3接口
func
(
t
Time
)
MarshalYAML
()
(
interface
{},
error
)
{
return
t
.
Time
()
.
Format
(
"15:04:05"
),
nil
if
t
.
IsZero
()
{
return
nil
,
nil
}
return
t
.
Time
()
.
Format
(
timeFormat
),
nil
}
// UnmarshalYAML 改进:使用正确的yaml.v3接口
func
(
t
*
Time
)
UnmarshalYAML
(
value
*
yaml
.
Node
)
error
{
if
value
==
nil
||
value
.
Kind
!=
yaml
.
ScalarNode
{
return
fmt
.
Errorf
(
"Time.UnmarshalYAML: 时间必须为标量值(字符串)"
)
}
str
:=
strings
.
TrimSpace
(
value
.
Value
)
if
str
==
""
||
str
==
"null"
{
*
t
=
Time
(
time
.
Time
{})
return
nil
}
return
t
.
parseString
(
str
)
}
// Time 返回time.Time类型(仅包含时间部分)
func
(
t
Time
)
Time
()
time
.
Time
{
return
time
.
Time
(
t
)
tm
:=
time
.
Time
(
t
)
if
tm
.
IsZero
()
{
return
time
.
Time
{}
}
// 确保只返回时间部分,日期设为基准值
hour
,
min
,
sec
:=
tm
.
Clock
()
return
time
.
Date
(
0
,
1
,
1
,
hour
,
min
,
sec
,
0
,
tm
.
Location
())
}
// UnmarshalYAML 自定义YAML反序列化
func
(
t
*
Time
)
UnmarshalYAML
(
value
interface
{})
error
{
str
,
ok
:=
value
.
(
string
)
if
!
ok
{
return
fmt
.
Errorf
(
"time-only must be a string"
)
// String 返回时间字符串表示
func
(
t
Time
)
String
()
string
{
if
t
.
IsZero
()
{
return
""
}
parsed
,
err
:=
time
.
Parse
(
"15:04:05"
,
str
)
if
err
!=
nil
{
return
err
return
t
.
Time
()
.
Format
(
timeFormat
)
}
// IsZero 检查是否为零值
func
(
t
Time
)
IsZero
()
bool
{
return
time
.
Time
(
t
)
.
IsZero
()
}
// 内部解析方法 - 统一时间解析逻辑
// func (t *Time) parseString(s string) error {
// s = strings.TrimSpace(s)
// if s == "" {
// *t = Time(time.Time{})
// return nil
// }
// // 支持多种时间格式
// formats := []string{
// "15:04:05", // 标准格式
// "15:04", // 省略秒
// "15:04:05.000", // 带毫秒
// }
// var firstErr error
// for _, format := range formats {
// parsed, err := time.Parse(format, s)
// if err == nil {
// // 成功解析,提取时间部分
// hour, min, sec := parsed.Clock()
// *t = Time(time.Date(0, 1, 1, hour, min, sec, 0, parsed.Location()))
// return nil
// }
// if firstErr == nil {
// firstErr = err
// }
// }
// return fmt.Errorf("Time.parseString: 无法解析时间 %q, 支持的格式示例: 15:04:05", s)
// }
// parseString 内部解析方法 - 统一时间解析逻辑
func
(
t
*
Time
)
parseString
(
s
string
)
error
{
s
=
strings
.
TrimSpace
(
s
)
if
s
==
""
{
*
t
=
Time
(
time
.
Time
{})
return
nil
}
*
t
=
Time
(
parsed
)
// 支持多种时间格式(按优先级排序)
formats
:=
[]
string
{
"15:04:05"
,
// 标准格式
"15:04"
,
// 省略秒
"15:04:05.000"
,
// 带毫秒
"15:04:05.000000"
,
// 带微秒
"15:04:05.000000000"
,
// 带纳秒
"150405"
,
// 紧凑格式(6位数字:小时分钟秒)
"15:04:05-0700"
,
// 带时区偏移
"15:04:05Z07:00"
,
// 带时区(RFC3339格式)
}
var
firstErr
error
for
_
,
format
:=
range
formats
{
parsed
,
err
:=
time
.
Parse
(
format
,
s
)
if
err
==
nil
{
// 成功解析,提取时间部分
hour
,
min
,
sec
:=
parsed
.
Clock
()
*
t
=
Time
(
time
.
Date
(
0
,
1
,
1
,
hour
,
min
,
sec
,
0
,
parsed
.
Location
()))
return
nil
}
if
firstErr
==
nil
{
firstErr
=
err
}
}
return
fmt
.
Errorf
(
"Time.parseString: 无法解析时间 %q, 支持的格式示例: 15:04:05, 15:04, 143000"
,
s
)
}
// 新增实用方法
// Hour 返回小时
func
(
t
Time
)
Hour
()
int
{
return
t
.
Time
()
.
Hour
()
}
// Minute 返回分钟
func
(
t
Time
)
Minute
()
int
{
return
t
.
Time
()
.
Minute
()
}
// Second 返回秒
func
(
t
Time
)
Second
()
int
{
return
t
.
Time
()
.
Second
()
}
// Before 检查当前时间是否在另一个时间之前
func
(
t
Time
)
Before
(
other
Time
)
bool
{
return
t
.
Time
()
.
Before
(
other
.
Time
())
}
// After 检查当前时间是否在另一个时间之后
func
(
t
Time
)
After
(
other
Time
)
bool
{
return
t
.
Time
()
.
After
(
other
.
Time
())
}
// Equal 检查两个时间是否相等
func
(
t
Time
)
Equal
(
other
Time
)
bool
{
return
t
.
Time
()
.
Equal
(
other
.
Time
())
}
// AddHours 添加指定小时
func
(
t
Time
)
AddHours
(
hours
int
)
Time
{
newTime
:=
t
.
Time
()
.
Add
(
time
.
Duration
(
hours
)
*
time
.
Hour
)
return
Time
(
newTime
)
}
// AddMinutes 添加指定分钟
func
(
t
Time
)
AddMinutes
(
minutes
int
)
Time
{
newTime
:=
t
.
Time
()
.
Add
(
time
.
Duration
(
minutes
)
*
time
.
Minute
)
return
Time
(
newTime
)
}
// ParseTime 从字符串解析时间
func
ParseTime
(
s
string
)
(
Time
,
error
)
{
var
t
Time
err
:=
t
.
parseString
(
s
)
return
t
,
err
}
// MustParseTime 从字符串解析时间,解析失败时panic
func
MustParseTime
(
s
string
)
Time
{
t
,
err
:=
ParseTime
(
s
)
if
err
!=
nil
{
panic
(
fmt
.
Sprintf
(
"MustParseTime 解析失败: %q, 错误: %v"
,
s
,
err
))
}
return
t
}
// NowTime 返回当前时间(忽略日期部分)
func
NowTime
()
Time
{
now
:=
time
.
Now
()
return
Time
(
time
.
Date
(
0
,
1
,
1
,
now
.
Hour
(),
now
.
Minute
(),
now
.
Second
(),
0
,
now
.
Location
()))
}
utils/xtime/time_test.go
0 → 100644
浏览文件 @
6d8995d2
package
xtime
import
(
"encoding/json"
"testing"
"time"
"gopkg.in/yaml.v3"
)
// TestTime_Scan 测试数据库扫描功能
func
TestTime_Scan
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
interface
{}
want
string
wantErr
bool
}{
{
name
:
"有效字节切片"
,
input
:
[]
byte
(
"14:30:00"
),
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"有效字符串"
,
input
:
"14:30:00"
,
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"有效time.Time类型"
,
input
:
time
.
Date
(
0
,
1
,
1
,
14
,
30
,
0
,
0
,
time
.
UTC
),
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"空值处理"
,
input
:
nil
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"空字符串"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"空字节切片"
,
input
:
[]
byte
{},
want
:
""
,
wantErr
:
false
,
},
{
name
:
"无效类型"
,
input
:
123
,
want
:
""
,
wantErr
:
true
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
var
tm
Time
err
:=
tm
.
Scan
(
tt
.
input
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"Scan() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
tm
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"Scan() = %v, want %v"
,
tm
.
String
(),
tt
.
want
)
}
})
}
}
// TestTime_Value 测试数据库值生成
func
TestTime_Value
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
Time
want
interface
{}
wantErr
bool
}{
{
name
:
"有效时间"
,
input
:
MustParseTime
(
"14:30:00"
),
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"零值时间"
,
input
:
Time
{},
want
:
nil
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
tt
.
input
.
Value
()
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"Value() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
got
!=
tt
.
want
{
t
.
Errorf
(
"Value() = %v, want %v"
,
got
,
tt
.
want
)
}
})
}
}
// TestTime_JSON 测试JSON序列化和反序列化
func
TestTime_JSON
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
string
want
string
wantErr
bool
}{
{
name
:
"有效时间JSON"
,
input
:
`"14:30:00"`
,
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"空值JSON"
,
input
:
"null"
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"空字符串JSON"
,
input
:
`""`
,
want
:
""
,
wantErr
:
false
,
},
{
name
:
"无效时间格式"
,
input
:
`"25:70:00"`
,
// 无效的时间
want
:
""
,
wantErr
:
true
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
// 测试反序列化
var
tm
Time
err
:=
json
.
Unmarshal
([]
byte
(
tt
.
input
),
&
tm
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"UnmarshalJSON() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
tm
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"UnmarshalJSON() = %v, want %v"
,
tm
.
String
(),
tt
.
want
)
return
}
// 测试序列化(仅对有效用例)
if
!
tt
.
wantErr
&&
tt
.
want
!=
""
{
bytes
,
err
:=
json
.
Marshal
(
tm
)
if
err
!=
nil
{
t
.
Errorf
(
"MarshalJSON() error = %v"
,
err
)
return
}
expectedJSON
:=
`"`
+
tt
.
want
+
`"`
if
string
(
bytes
)
!=
expectedJSON
{
t
.
Errorf
(
"MarshalJSON() = %s, want %s"
,
string
(
bytes
),
expectedJSON
)
}
}
})
}
}
// TestTime_YAML 测试YAML序列化和反序列化
func
TestTime_YAML
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
string
want
string
wantErr
bool
}{
{
name
:
"有效时间YAML"
,
input
:
"14:30:00"
,
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"空字符串YAML"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
// 创建YAML节点进行测试
node
:=
&
yaml
.
Node
{
Kind
:
yaml
.
ScalarNode
,
Value
:
tt
.
input
,
}
var
tm
Time
err
:=
tm
.
UnmarshalYAML
(
node
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"UnmarshalYAML() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
tm
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"UnmarshalYAML() = %v, want %v"
,
tm
.
String
(),
tt
.
want
)
}
// 测试序列化
if
!
tt
.
wantErr
&&
tt
.
want
!=
""
{
result
,
err
:=
tm
.
MarshalYAML
()
if
err
!=
nil
{
t
.
Errorf
(
"MarshalYAML() error = %v"
,
err
)
return
}
if
result
!=
tt
.
want
{
t
.
Errorf
(
"MarshalYAML() = %v, want %v"
,
result
,
tt
.
want
)
}
}
})
}
}
// TestTime_String 测试字符串表示
func
TestTime_String
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
Time
want
string
}{
{
name
:
"有效时间"
,
input
:
MustParseTime
(
"14:30:00"
),
want
:
"14:30:00"
,
},
{
name
:
"零值时间"
,
input
:
Time
{},
want
:
""
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
if
got
:=
tt
.
input
.
String
();
got
!=
tt
.
want
{
t
.
Errorf
(
"String() = %v, want %v"
,
got
,
tt
.
want
)
}
})
}
}
// TestParseTime 测试时间解析函数
func
TestParseTime
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
input
string
want
string
wantErr
bool
}{
{
name
:
"标准格式解析"
,
input
:
"14:30:00"
,
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"省略秒格式"
,
input
:
"14:30"
,
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"紧凑格式"
,
input
:
"143000"
,
want
:
"14:30:00"
,
wantErr
:
false
,
},
{
name
:
"无效时间格式"
,
input
:
"25:70:00"
,
// 无效的小时和分钟
want
:
""
,
wantErr
:
true
,
},
{
name
:
"空字符串"
,
input
:
""
,
want
:
""
,
wantErr
:
false
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
,
err
:=
ParseTime
(
tt
.
input
)
if
(
err
!=
nil
)
!=
tt
.
wantErr
{
t
.
Errorf
(
"ParseTime() error = %v, wantErr %v"
,
err
,
tt
.
wantErr
)
return
}
if
!
tt
.
wantErr
&&
got
.
String
()
!=
tt
.
want
{
t
.
Errorf
(
"ParseTime() = %v, want %v"
,
got
.
String
(),
tt
.
want
)
}
})
}
}
// TestTime_Comparison 测试时间比较方法
func
TestTime_Comparison
(
t
*
testing
.
T
)
{
time1
:=
MustParseTime
(
"14:30:00"
)
time2
:=
MustParseTime
(
"15:45:00"
)
time3
:=
MustParseTime
(
"14:30:00"
)
// 与time1相同
t
.
Run
(
"After方法"
,
func
(
t
*
testing
.
T
)
{
if
!
time2
.
After
(
time1
)
{
t
.
Error
(
"time2应该在time1之后"
)
}
if
time1
.
After
(
time2
)
{
t
.
Error
(
"time1不应该在time2之后"
)
}
})
t
.
Run
(
"Before方法"
,
func
(
t
*
testing
.
T
)
{
if
!
time1
.
Before
(
time2
)
{
t
.
Error
(
"time1应该在time2之前"
)
}
if
time2
.
Before
(
time1
)
{
t
.
Error
(
"time2不应该在time1之前"
)
}
})
t
.
Run
(
"Equal方法"
,
func
(
t
*
testing
.
T
)
{
if
!
time1
.
Equal
(
time3
)
{
t
.
Error
(
"time1应该等于time3"
)
}
if
time1
.
Equal
(
time2
)
{
t
.
Error
(
"time1不应该等于time2"
)
}
})
}
// TestTime_Arithmetic 测试时间算术运算
func
TestTime_Arithmetic
(
t
*
testing
.
T
)
{
baseTime
:=
MustParseTime
(
"14:30:00"
)
t
.
Run
(
"AddHours方法"
,
func
(
t
*
testing
.
T
)
{
result
:=
baseTime
.
AddHours
(
2
)
expected
:=
MustParseTime
(
"16:30:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddHours(2) = %v, want %v"
,
result
,
expected
)
}
result
=
baseTime
.
AddHours
(
-
1
)
expected
=
MustParseTime
(
"13:30:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddHours(-1) = %v, want %v"
,
result
,
expected
)
}
// 测试跨日边界
lateTime
:=
MustParseTime
(
"23:30:00"
)
result
=
lateTime
.
AddHours
(
2
)
expected
=
MustParseTime
(
"01:30:00"
)
// 第二天凌晨
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddHours跨日边界 = %v, want %v"
,
result
,
expected
)
}
})
t
.
Run
(
"AddMinutes方法"
,
func
(
t
*
testing
.
T
)
{
result
:=
baseTime
.
AddMinutes
(
30
)
expected
:=
MustParseTime
(
"15:00:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddMinutes(30) = %v, want %v"
,
result
,
expected
)
}
result
=
baseTime
.
AddMinutes
(
-
15
)
expected
=
MustParseTime
(
"14:15:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddMinutes(-15) = %v, want %v"
,
result
,
expected
)
}
// 测试跨小时边界
result
=
baseTime
.
AddMinutes
(
90
)
// 1小时30分钟
expected
=
MustParseTime
(
"16:00:00"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"AddMinutes跨小时边界 = %v, want %v"
,
result
,
expected
)
}
})
}
// TestTime_Components 测试时间组件获取
func
TestTime_Components
(
t
*
testing
.
T
)
{
tm
:=
MustParseTime
(
"14:30:45"
)
t
.
Run
(
"Hour方法"
,
func
(
t
*
testing
.
T
)
{
if
hour
:=
tm
.
Hour
();
hour
!=
14
{
t
.
Errorf
(
"Hour() = %v, want 14"
,
hour
)
}
})
t
.
Run
(
"Minute方法"
,
func
(
t
*
testing
.
T
)
{
if
minute
:=
tm
.
Minute
();
minute
!=
30
{
t
.
Errorf
(
"Minute() = %v, want 30"
,
minute
)
}
})
t
.
Run
(
"Second方法"
,
func
(
t
*
testing
.
T
)
{
if
second
:=
tm
.
Second
();
second
!=
45
{
t
.
Errorf
(
"Second() = %v, want 45"
,
second
)
}
})
}
// TestTime_ZeroValue 测试零值处理
func
TestTime_ZeroValue
(
t
*
testing
.
T
)
{
var
zeroTime
Time
t
.
Run
(
"IsZero方法"
,
func
(
t
*
testing
.
T
)
{
if
!
zeroTime
.
IsZero
()
{
t
.
Error
(
"IsZero()应该返回true对于零值"
)
}
validTime
:=
MustParseTime
(
"14:30:00"
)
if
validTime
.
IsZero
()
{
t
.
Error
(
"IsZero()应该返回false对于有效时间"
)
}
})
t
.
Run
(
"零值的字符串表示"
,
func
(
t
*
testing
.
T
)
{
if
zeroTime
.
String
()
!=
""
{
t
.
Errorf
(
"零值的String()应该返回空字符串, 得到: %s"
,
zeroTime
.
String
())
}
})
t
.
Run
(
"零值的JSON序列化"
,
func
(
t
*
testing
.
T
)
{
bytes
,
err
:=
json
.
Marshal
(
zeroTime
)
if
err
!=
nil
{
t
.
Errorf
(
"零值JSON序列化错误: %v"
,
err
)
}
if
string
(
bytes
)
!=
"null"
{
t
.
Errorf
(
"零值JSON应该序列化为null, 得到: %s"
,
string
(
bytes
))
}
})
}
// TestNowTime 测试NowTime函数
func
TestNowTime
(
t
*
testing
.
T
)
{
now
:=
NowTime
()
current
:=
time
.
Now
()
// 检查返回的时间组件是否匹配当前时间
if
now
.
Hour
()
!=
current
.
Hour
()
||
now
.
Minute
()
!=
current
.
Minute
()
||
now
.
Second
()
!=
current
.
Second
()
{
t
.
Error
(
"NowTime()返回的时间应该与当前时间匹配"
)
}
}
// TestMustParseTime 测试MustParseTime函数
func
TestMustParseTime
(
t
*
testing
.
T
)
{
t
.
Run
(
"有效时间"
,
func
(
t
*
testing
.
T
)
{
defer
func
()
{
if
r
:=
recover
();
r
!=
nil
{
t
.
Error
(
"有效时间不应该引起panic"
)
}
}()
tm
:=
MustParseTime
(
"14:30:00"
)
if
tm
.
String
()
!=
"14:30:00"
{
t
.
Errorf
(
"MustParseTime() = %v, want 14:30:00"
,
tm
.
String
())
}
})
t
.
Run
(
"无效时间应该panic"
,
func
(
t
*
testing
.
T
)
{
defer
func
()
{
if
r
:=
recover
();
r
==
nil
{
t
.
Error
(
"无效时间应该引起panic"
)
}
}()
_
=
MustParseTime
(
"25:70:00"
)
})
}
// TestTime_EdgeCases 测试边界情况
func
TestTime_EdgeCases
(
t
*
testing
.
T
)
{
t
.
Run
(
"午夜时间"
,
func
(
t
*
testing
.
T
)
{
midnight
:=
MustParseTime
(
"00:00:00"
)
if
midnight
.
String
()
!=
"00:00:00"
{
t
.
Error
(
"午夜时间处理错误"
)
}
})
t
.
Run
(
"最大有效时间"
,
func
(
t
*
testing
.
T
)
{
maxTime
:=
MustParseTime
(
"23:59:59"
)
if
maxTime
.
String
()
!=
"23:59:59"
{
t
.
Error
(
"最大时间处理错误"
)
}
})
t
.
Run
(
"时间溢出处理"
,
func
(
t
*
testing
.
T
)
{
// 测试接近午夜的时间加法
almostMidnight
:=
MustParseTime
(
"23:59:30"
)
result
:=
almostMidnight
.
AddMinutes
(
2
)
// 应该变成00:01:30
expected
:=
MustParseTime
(
"00:01:30"
)
if
!
result
.
Equal
(
expected
)
{
t
.
Errorf
(
"时间溢出处理错误: %v, want %v"
,
result
,
expected
)
}
})
}
// TestTime_Integration 集成测试
func
TestTime_Integration
(
t
*
testing
.
T
)
{
t
.
Run
(
"完整序列化循环"
,
func
(
t
*
testing
.
T
)
{
original
:=
MustParseTime
(
"14:30:00"
)
// JSON序列化 -> 反序列化
jsonData
,
err
:=
json
.
Marshal
(
original
)
if
err
!=
nil
{
t
.
Fatalf
(
"JSON序列化失败: %v"
,
err
)
}
var
fromJSON
Time
err
=
json
.
Unmarshal
(
jsonData
,
&
fromJSON
)
if
err
!=
nil
{
t
.
Fatalf
(
"JSON反序列化失败: %v"
,
err
)
}
if
!
original
.
Equal
(
fromJSON
)
{
t
.
Error
(
"JSON序列化循环失败"
)
}
// 数据库值循环
dbValue
,
err
:=
original
.
Value
()
if
err
!=
nil
{
t
.
Fatalf
(
"Value()失败: %v"
,
err
)
}
var
fromDB
Time
err
=
fromDB
.
Scan
(
dbValue
)
if
err
!=
nil
{
t
.
Fatalf
(
"Scan()失败: %v"
,
err
)
}
if
!
original
.
Equal
(
fromDB
)
{
t
.
Error
(
"数据库循环失败"
)
}
})
}
// BenchmarkTime_Parse 性能测试:时间解析
func
BenchmarkTime_Parse
(
b
*
testing
.
B
)
{
testCases
:=
[]
string
{
"14:30:00"
,
"14:30"
,
"143000"
,
""
,
}
for
_
,
tc
:=
range
testCases
{
b
.
Run
(
tc
,
func
(
b
*
testing
.
B
)
{
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
,
_
=
ParseTime
(
tc
)
}
})
}
}
// BenchmarkTime_JSONMarshal 性能测试:JSON序列化
func
BenchmarkTime_JSONMarshal
(
b
*
testing
.
B
)
{
tm
:=
MustParseTime
(
"14:30:00"
)
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
,
_
=
json
.
Marshal
(
tm
)
}
}
// BenchmarkTime_JSONUnmarshal 性能测试:JSON反序列化
func
BenchmarkTime_JSONUnmarshal
(
b
*
testing
.
B
)
{
testCases
:=
[]
struct
{
name
string
data
[]
byte
}{
{
"有效时间"
,
[]
byte
(
`"14:30:00"`
)},
{
"空值"
,
[]
byte
(
`null`
)},
}
for
_
,
tc
:=
range
testCases
{
b
.
Run
(
tc
.
name
,
func
(
b
*
testing
.
B
)
{
var
tm
Time
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
_
=
json
.
Unmarshal
(
tc
.
data
,
&
tm
)
}
})
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论