【问题标题】:Why is time.Since returning negative durations on Windows?为什么是 time.Since 在 Windows 上返回负持续时间?
【发布时间】:2018-06-30 23:16:05
【问题描述】:

我一直在尝试使用一些 go,并在 windows 上发现了一些奇怪的行为。如果我通过解析特定格式的时间字符串来构造一个时间对象,然后使用像time.Since() 这样的函数,我会得到负的持续时间。

代码示例:

package main

import (
    "fmt"
    "time"
    "strconv"
)

func convertToTimeObject(dateStr string) time.Time {
    layout := "2006-01-02T15:04:05.000Z"
    t, _:= time.Parse(layout, dateStr)

    return t
}

func main() {
    timeOlder := convertToTimeObject(time.Now().Add(-30*time.Second).Format("2006-01-02T15:04:05.000Z"))
    duration := time.Since(timeOlder)
    fmt.Println("Duration in seconds: " + strconv.Itoa(int(duration.Seconds())))
}

如果你在 Linux 或 Go Playground link 上运行它,你会得到预期的 Duration in seconds: 30 的结果。

但是,在 Windows 上,使用 Go 1.10.3 运行同一段代码会得到Duration in seconds: -19769

我已经为此努力了好几个小时。对我可能遗漏的内容有任何帮助吗? 从现在起我得到的唯一线索是,当 go 的 time 包计算两个时间对象(time.Now() 和我解析的时间对象)的秒数时,其中一个具有属性 hasMonotonic 而一个没有t,这导致 go 计算两者的秒数大不相同。

我不是及时的专家,所以希望能得到一些帮助。我打算为 Go 提交一个错误,但想在这里向专家询问是否有明显的我可能遗漏的东西。

【问题讨论】:

  • 非常接近 5.5 小时。这可能会让你在印度,注意当地时间与 UTC。
  • 您的布局不完整。 Z 后面应该跟 07070007:00 之一。见golang.org/pkg/time/#pkg-constants
  • @HansPassant 啊,是的,这是有道理的。这就解释了为什么它在我的 Linux 机器上工作(时区设置为 UTC)。然而,这不应该发生在 IMO。请参阅我对彼得的回复。另外,既然在您的帮助下我知道这不仅仅是 Windows 的问题,您是否建议更改问题的标题? SFO 的新手。
  • @Peter - 时间在ISO8601,其中单数 Z 表示它是 UTC 时间,与 GMT 的零偏移量。从这个意义上说,布局确实有时区的上下文,但可能与 Go 不兼容?
  • Z 没有偏移格式打印一个时间值好像它是 UTC:play.golang.org/p/OvmsGJ5NLge。它实际上并没有在打印之前将其转换为 UTC。

标签: go time


【解决方案1】:

我想我弄清楚了您的代码 sn-p 的奇怪行为的原因是什么,并且可以提供解决方案。相关docs内容如下:

since 返回自 t 以来经过的时间。它是 time.Now().Sub(t) 的简写。

但是:

now 返回当前本地时间。

这意味着您正在格式化 timeOlder 并从未格式化的本地时间中减去它。这当然会导致意外行为。一个简单的解决方案是在减去 timeOlder 之前根据您的格式解析本地时间。

适用于我的机器的解决方案(不过,举一个游乐场的例子可能没有多大意义):

func convertToTimeObject(dateStr string) time.Time {
    layout := "2006-01-02T15:04:05.000Z"
    t, err := time.Parse(layout, dateStr)
    // check the error!
    if err != nil {
        log.Fatalf("error while parsing time: %s\n", err)
    }
    return t
}

func main() {
    timeOlder := convertToTimeObject(time.Now().Add(-30 * time.Second).Format("2006-01-02T15:04:05.000Z"))
    duration := time.Since(timeOlder)

    // replace time.Since() with a correctly parsed time.Now(), because
    // time.Since() returns the time elapsed since the current LOCAL time.
    t := time.Now().Format("2006-01-02T15:04:05.000Z")
    timeNow := convertToTimeObject(t)

    // print the different results
    fmt.Println("duration in seconds:", strconv.Itoa(int(duration.Seconds())))
    fmt.Printf("duration: %v\n", timeNow.Sub(timeOlder))
}

输出:

duration in seconds: 14430
duration: 30s

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-12
    • 2014-11-21
    • 1970-01-01
    • 2020-08-13
    • 2012-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多