You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:40:32