Beego ORM 用 QueryTable().Filter() 查时间范围需传 time.Time 值并用 __gte/__lte 操作符,禁止传字符串或时间戳;字段须为 time.Time 类型,统一使用 UTC 时区,避免 __range。

Beego ORM 怎么用 QueryTable().Filter() 查时间范围
Beego 的 ORM 查询时间范围,核心是用 Filter() 配合时间字段的比较操作符,不是写原生 SQL 或拼字符串。直接传入 Go 的 time.Time 值即可,ORM 会自动处理数据库时区和类型转换(前提是模型字段是 time.Time 类型)。
常见错误是传字符串(如 "2024-01-01")或 Unix 时间戳整数——这会导致查询无声失败或全表扫描,因为字段类型不匹配,数据库无法走索引。
- 确保模型中时间字段声明为
time.Time,例如:CreatedAt time.Time `orm:"auto_now_add;type(datetime)"` - 查询时用
time.Time变量,别用字符串:start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) - 支持的标准操作符有:
gt(大于)、gte(大于等于)、lt(小于)、lte(小于等于),组合使用实现闭区间或半开区间 - MySQL 默认时区影响结果:如果数据库用
LOCALTIME而应用用 UTC,查出来的记录可能偏移 8 小时;建议统一用 UTC 存储和查询
Beego ORM 时间范围查询完整示例(含边界处理)
查 2024-01-01 00:00:00 到 2024-01-31 23:59:59 的订单:
o := orm.NewOrm()
var orders []Order
start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, 1, 31, 23, 59, 59, 0, time.UTC)
qs := o.QueryTable(&Order{})
qs = qs.Filter("created_at__gte", start).Filter("created_at__lte", end)
_, err := qs.All(&orders)
if err != nil {
// 处理 err
}
注意:created_at__gte 中的双下划线是 Beego ORM 的约定语法,不是字段名本身;字段名必须和结构体 tag 里的 column 或默认映射一致。如果数据库字段是 create_time,而结构体没显式指定 tag,那就要用 create_time__gte。
- 别漏掉
time.Location:用time.Now().In(time.UTC)替代裸time.Now(),避免本地时区干扰 - 如果要查“今天”,别用
time.Now().Format("2006-01-02")拼字符串——而是用StartOfDay()和EndOfDay()自己算time.Time - PostgreSQL 用户注意:
type(datetime)在 pg 中实际映射为timestamp with time zone,Beego 会自动加AT TIME ZONE,但需确认 DB 时区配置是否与应用一致
为什么 Filter("created_at__range", []interface{}{start, end}) 不推荐
Beego ORM 确实支持 __range 语法,但底层实现是把两个值展开成 BETWEEN,且对 time.Time 支持不稳定——某些版本会忽略纳秒部分、或在 SQLite 下出错。更严重的是,它无法表达“左闭右开”区间(比如 [start, end)),而很多业务场景(如按小时滚动统计)需要这种语义。
-
__range是黑盒行为,调试困难;出问题时日志里只显示BETWEEN ? AND ?,看不出具体传了什么时间值 - MySQL 5.7+ 对
BETWEEN的时间处理有隐式截断(如datetime字段存到秒,但传入带毫秒的time.Time,可能被四舍五入) - 明确拆成
__gte+__lt更可控,也方便后续加其他条件(比如再加.Filter("status", "paid"))
查不到数据?先检查这三个地方
时间范围查询返回空,大概率不是语法错,而是环境或数据本身的问题:
- 数据库时间字段值是
NULL:Beego ORM 默认不匹配 NULL,哪怕你查的是__gte;加.Exclude("created_at__isnull", true)显式排除,或确认数据非空 - Go 应用和 MySQL 服务时区不一致:运行
SELECT @@global.time_zone, @@session.time_zone;和o.Raw("SELECT NOW()").Exec()对比输出,看是否差整点 - 字段类型不匹配:比如数据库是
INT UNSIGNED存 Unix 时间戳,但模型定义为time.Time——这时 ORM 会尝试转换失败,静默跳过该条件;应统一用int64字段 + 手动转time.Unix()
时间字段的精度、时区、空值、类型一致性,比写对 Filter 语法重要得多。











