简体   繁体   中英

time.Duration is unexpectedly 'divided' by 1'000'000

I'm using time.Duration to store data in a struct as follows:

type ApiAccessToken struct {
    ...
    ExpiredIn   *time.Duration `bson:"expired_in,omitempty" json:"expired_in,omitempty"`
    ...
}

and I set it using a constant like this:

...
const ApiAccessTokenDefaultExpiresIn = 7 * 24 * time.Hour
...
d := ApiAccessTokenDefaultExpiresIn
data := &ApiAccessToken{
    ...
    ExpiredIn: &d
    ...
}
...

then I use mgo to insert the data to database.

I did checking after creating the data instance and before inserting the data and the value of ExpiredIn was 604'800'000'000'000 but in MongoDB it became 604'800'000 (or NumberLong(604800000) ).

Any idea why? Thank you!

We would normally write custom MarshalJSON/UnmarshalJSON for specific types to control what happens to their values before/after marshaling/unmarshaling.

type ExpiredIn struct {
    time.Duration
}

func (e *ExpiredIn) MarshalJSON() ([]byte, error) {
    return []byte(string(e.Nanoseconds())), nil
}

func (e *ExpiredIn) UnmarshalJSON(data []byte) error {
    i, _ := strconv.ParseInt(string(data[:])), 10, 64)
    e.Duration = time.Duration(i)
    return nil

}

Here's the test code:

package main

import (
    "log"
    "time"

    "gopkg.in/mgo.v2"
)

type Token struct {
    ExpiredIn time.Duration
}

type ExpiredIn struct {
    time.Duration
}

func (e *ExpiredIn) MarshalJSON() ([]byte, error) {
    return []byte(string(e.Nanoseconds())), nil
}

func main() {
    session, err := mgo.Dial("mongodb://localhost:27017/test")
    if err != nil {
        panic(err)
    }
    defer session.Close()

    // Optional. Switch the session to a monotonic behavior.
    session.SetMode(mgo.Monotonic, true)

    c := session.DB("test").C("tokens")
    err = c.Insert(&Recipe{7 * 24 * time.Hour})
    if err != nil {
        log.Fatal(err)
    }
}

And you're done!

在此处输入图片说明

Current solution: multiply the returned ExpiredIn from MongoDB with time.Second so I got my Go-flavoured nanosecond time.Duration .

I ended up using the string representation of time.Duration because it simply works.

I created two functions for my ApiAccessToken struct that does the job of writing/reading the data.

func (tok *ApiAccessToken) SetExpiredIn(t time.Duration) {
    s := t.String() // read the string
    tok.ExpiredIn = &s
}

func (tok *ApiAccessToken) GetExpiredIn() (r bool, t time.Duration) {
    if tok.ExpiredIn != nil {
        var err error
        t, err = time.ParseDuration(*tok.ExpiredIn) // parse the string
        r = (err == nil)                            // can we use this?
    }
    return
}

And voila, it works!

串

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM