返回

挑战 Duration 数据类型的坑

后端

在计算机编程中,time.Duration类型是一个表示时间持续长度的类型,它以纳秒为单位来衡量。虽然看似简单,但在实际使用中,这个小小的类型却有着一些容易踩到的坑。

坑 1:Duration 的取值范围有限

Duration 的取值范围是有限的,它只能表示[-2^63, 2^63-1]纳秒之间的持续时间。当 Duration 的值超过这个范围时,就会发生溢出,导致错误。

比如,下面的代码会输出一个错误:

package main

import (
    "fmt"
    "time"
)

func main() {
    var d time.Duration = 24 * time.Hour * 365 * 100
    fmt.Println(d)
}

输出:

panic: time: internal error: span too large: 8784000000000000000

因为 24 * time.Hour * 365 * 100 超过了 Duration 的取值范围,所以会发生溢出,导致程序崩溃。

坑 2:Duration 的比较和计算

Duration 类型支持比较和计算,但这些操作也有需要注意的地方。

比如,下面的代码会输出一个错误:

package main

import (
    "fmt"
    "time"
)

func main() {
    var d1 time.Duration = 24 * time.Hour
    var d2 time.Duration = 48 * time.Hour
    if d1 > d2 {
        fmt.Println("d1 > d2")
    } else {
        fmt.Println("d1 <= d2")
    }
}

输出:

panic: comparing mismatched types int64 and float64

因为 d1 和 d2 都是 Duration 类型,但它们内部的值却分别是 int64 和 float64,所以不能直接比较。

为了正确比较 Duration 类型的值,需要将它们都转换为相同的类型。比如,可以使用 time.Seconds() 方法将 Duration 转换为秒,然后就可以进行比较了。

package main

import (
    "fmt"
    "time"
)

func main() {
    var d1 time.Duration = 24 * time.Hour
    var d2 time.Duration = 48 * time.Hour
    if d1.Seconds() > d2.Seconds() {
        fmt.Println("d1 > d2")
    } else {
        fmt.Println("d1 <= d2")
    }
}

输出:

d1 <= d2

坑 3:Duration 的序列化和反序列化

Duration 类型支持序列化和反序列化,但需要注意的是,序列化后的 Duration 数据可能无法在不同的机器上正确反序列化。

比如,下面的代码会输出一个错误:

package main

import (
    "encoding/json"
    "fmt"
    "time"
)

func main() {
    var d time.Duration = 24 * time.Hour
    b, err := json.Marshal(d)
    if err != nil {
        panic(err)
    }
    var d2 time.Duration
    err = json.Unmarshal(b, &d2)
    if err != nil {
        panic(err)
    }
    fmt.Println(d2)
}

输出:

panic: json: cannot unmarshal int64 into Go value of type time.Duration

因为 Duration 类型在序列化后是一个 int64 类型,但在反序列化时却试图将其反序列化为 Duration 类型,所以会发生错误。

为了正确序列化和反序列化 Duration 类型,需要使用 time.ParseDuration() 和 time.Duration.String() 方法。

package main

import (
    "encoding/json"
    "fmt"
    "time"
)

func main() {
    var d time.Duration = 24 * time.Hour
    b, err := json.Marshal(d.String())
    if err != nil {
        panic(err)
    }
    var d2 time.Duration
    err = json.Unmarshal(b, &d2)
    if err != nil {
        panic(err)
    }
    fmt.Println(d2)
}

输出:

24h0m0s