time.Duration被意外“除以”1000000问题求助
问题根源与解决方案
这个问题其实是mgo的bson序列化器对time.Duration的默认处理逻辑导致的,咱们一步步拆解清楚:
为什么数值会被"除以1000000"?
- Go里的
time.Duration本质是int64类型,单位是纳秒,比如你定义的ApiAccessTokenDefaultExpiresIn = 7 * 24 * time.Hour,实际值是604800000000000(604800秒 × 1e9纳秒/秒)。 - mgo的bson序列化器为了贴合MongoDB生态中常用的时间间隔存储习惯(大部分系统用毫秒存时长),会自动把
time.Duration的纳秒值转换为毫秒值存储——也就是除以1e6,所以你看到的数值会变成原来的1/1000000。
两种解决方案,按需选择
方案1:强制存储原始纳秒值(自定义序列化)
如果你需要精确保存纳秒级的时长,就需要给结构体字段自定义bson的序列化/反序列化逻辑,绕过mgo的默认转换。
方法A:自定义Duration类型
先定义一个替代time.Duration的类型,实现bson的序列化接口:
import "gopkg.in/mgo.v2/bson" // 自定义纳秒级Duration类型 type NanosecondDuration time.Duration // 实现MarshalBSON,直接序列化原始纳秒值 func (d NanosecondDuration) MarshalBSON() ([]byte, error) { return bson.Marshal(int64(d)) } // 实现UnmarshalBSON,把数据库的数值转成纳秒Duration func (d *NanosecondDuration) UnmarshalBSON(data []byte) error { var ns int64 if err := bson.Unmarshal(data, &ns); err != nil { return err } *d = NanosecondDuration(ns) return nil }
然后修改你的结构体:
type ApiAccessToken struct { // 其他字段... ExpiredIn *NanosecondDuration `bson:"expired_in,omitempty" json:"expired_in,omitempty"` }
赋值的时候做个类型转换:
const ApiAccessTokenDefaultExpiresIn = 7 * 24 * time.Hour d := NanosecondDuration(ApiAccessTokenDefaultExpiresIn) data := &ApiAccessToken{ // 其他字段... ExpiredIn: &d, }
方法B:给结构体自定义序列化
如果不想修改字段类型,可以直接给ApiAccessToken实现bson序列化接口,单独处理ExpiredIn字段:
import "gopkg.in/mgo.v2/bson" func (a *ApiAccessToken) MarshalBSON() ([]byte, error) { // 用匿名结构体做别名,避免递归序列化 type alias ApiAccessToken tmp := struct { *alias ExpiredIn *int64 `bson:"expired_in,omitempty"` }{ alias: (*alias)(a), } // 把Duration转成原始纳秒值存进去 if a.ExpiredIn != nil { ns := int64(*a.ExpiredIn) tmp.ExpiredIn = &ns } return bson.Marshal(tmp) } func (a *ApiAccessToken) UnmarshalBSON(data []byte) error { type alias ApiAccessToken tmp := struct { *alias ExpiredIn *int64 `bson:"expired_in,omitempty"` }{ alias: (*alias)(a), } if err := bson.Unmarshal(data, &tmp); err != nil { return err } // 把数据库的纳秒值转成Duration if tmp.ExpiredIn != nil { d := time.Duration(*tmp.ExpiredIn) a.ExpiredIn = &d } return nil }
方案2:接受毫秒存储,读取时转换
如果你的业务不需要纳秒级精度,完全可以接受毫秒存储,那读取数据的时候把数据库里的毫秒值转成纳秒time.Duration即可:
// 假设从数据库读取到的毫秒值为dbExpiredIn(int64类型) var dbExpiredIn int64 // 转成time.Duration expiredIn := time.Duration(dbExpiredIn) * time.Millisecond
这样就能得到和原始赋值一致的时长了。
内容的提问来源于stack exchange,提问作者T.N.
相关产品推荐
相关产品推荐

