golang time.Since()有几个月和几年

gem*_*pir 20 time duration go

我想尝试转换这样的时间戳:

2015-06-27T09:34:22+00:00
Run Code Online (Sandbox Code Playgroud)

到格式的时间所以它会说像9个月前1天2小时30分2秒.

类似的东西.

我使用time.Parsetime.Since达到这个目的:

6915h7m47.6901559s
Run Code Online (Sandbox Code Playgroud)

但是我如何从那里转换?这样的事情就是我想的:

for hours > 24 {
        days++
        hours -= 24
}
Run Code Online (Sandbox Code Playgroud)

但问题是,这个月不准确,因为几个月可能有28天,30天和31天.

有没有更好的方法来实现我想要的?

icz*_*cza 40

一个月的日子取决于日期,就像一年中的日子(闰年).

如果time.Since()用于获取自time.Time值以来经过的时间,或者time.Time使用该Time.Sub()方法计算2个值之间的差值,则结果time.Duration将丢失时间上下文(Duration仅为纳秒时间差).这意味着您无法准确无误地计算数值与年份,月份等的差异Duration.

正确的解决方案必须计算时间背景的差异.您可以计算每个字段(年,月,日,小时,分钟,秒)的差异,然后将结果标准化为不具有任何负值.Time如果它们之间的关系不是预期的,也建议交换值.

标准化意味着如果值为负,则添加该字段的最大值并将下一个字段减1.例如,如果seconds为负,则添加60到它并递减minutes1.要注意的一件事是规范化天数的差异(每月的天数),必须应用适当月份的天数.这可以通过这个小技巧轻松计算:

// Max days in year y1, month M1
t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC)
daysInMonth := 32 - t.Day()
Run Code Online (Sandbox Code Playgroud)

这背后的逻辑是,这一天32比任何一个月的最大日都要大.它将自动标准化(额外的天数滚动到下个月和日正确递减).当我们从32减去标准化之后的日子时,我们得到了该月的最后一天.

时区处理:

如果我们传入的两个时间值都在同一时区(time.Location)中,差异计算将只给出正确的结果.我们将检查结合到我们的函数中:如果不是这种情况,我们使用该Time.In()方法将其中一个时间值"转换"为与另一个相同的位置:

if a.Location() != b.Location() {
    b = b.In(a.Location())
}
Run Code Online (Sandbox Code Playgroud)

这是一个计算年,月,日,小时,分钟,秒差异的解决方案:

func diff(a, b time.Time) (year, month, day, hour, min, sec int) {
    if a.Location() != b.Location() {
        b = b.In(a.Location())
    }
    if a.After(b) {
        a, b = b, a
    }
    y1, M1, d1 := a.Date()
    y2, M2, d2 := b.Date()

    h1, m1, s1 := a.Clock()
    h2, m2, s2 := b.Clock()

    year = int(y2 - y1)
    month = int(M2 - M1)
    day = int(d2 - d1)
    hour = int(h2 - h1)
    min = int(m2 - m1)
    sec = int(s2 - s1)

    // Normalize negative values
    if sec < 0 {
        sec += 60
        min--
    }
    if min < 0 {
        min += 60
        hour--
    }
    if hour < 0 {
        hour += 24
        day--
    }
    if day < 0 {
        // days in month:
        t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC)
        day += 32 - t.Day()
        month--
    }
    if month < 0 {
        month += 12
        year--
    }

    return
}
Run Code Online (Sandbox Code Playgroud)

一些测试:

var a, b time.Time
a = time.Date(2015, 5, 1, 0, 0, 0, 0, time.UTC)
b = time.Date(2016, 6, 2, 1, 1, 1, 1, time.UTC)
fmt.Println(diff(a, b)) // Expected: 1 1 1 1 1 1

a = time.Date(2016, 1, 2, 0, 0, 0, 0, time.UTC)
b = time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC)
fmt.Println(diff(a, b)) // Expected: 0 0 30 0 0 0

a = time.Date(2016, 2, 2, 0, 0, 0, 0, time.UTC)
b = time.Date(2016, 3, 1, 0, 0, 0, 0, time.UTC)
fmt.Println(diff(a, b)) // Expected: 0 0 28 0 0 0

a = time.Date(2015, 2, 11, 0, 0, 0, 0, time.UTC)
b = time.Date(2016, 1, 12, 0, 0, 0, 0, time.UTC)
fmt.Println(diff(a, b)) // Expected: 0 11 1 0 0 0
Run Code Online (Sandbox Code Playgroud)

输出符合预期:

1 1 1 1 1 1
0 0 30 0 0 0
0 0 28 0 0 0
0 11 1 0 0 0
Run Code Online (Sandbox Code Playgroud)

Go Playground尝试一下.

要计算你的年龄:

// Your birthday: let's say it's January 2nd, 1980, 3:30 AM
birthday := time.Date(1980, 1, 2, 3, 30, 0, 0, time.UTC)
year, month, day, hour, min, sec := diff(birthday, time.Now())

fmt.Printf("You are %d years, %d months, %d days, %d hours, %d mins and %d seconds old.",
    year, month, day, hour, min, sec)
Run Code Online (Sandbox Code Playgroud)

示例输出:

You are 36 years, 3 months, 8 days, 11 hours, 57 mins and 41 seconds old.
Run Code Online (Sandbox Code Playgroud)

Go游乐场时间开始的神奇日期/时间是:2009-11-10 23:00:00 UTC
这是Go首次宣布的时间.我们来算一下Go的年龄:

goAnnounced := time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC)
year, month, day, hour, min, sec := diff(goAnnounced, time.Now())
fmt.Printf("Go was announced "+
    "%d years, %d months, %d days, %d hours, %d mins and %d seconds ago.",
    year, month, day, hour, min, sec)
Run Code Online (Sandbox Code Playgroud)

输出:

Go was announced 6 years, 4 months, 29 days, 16 hours, 53 mins and 31 seconds ago.
Run Code Online (Sandbox Code Playgroud)

  • 这是完美的,并且我想要它的精确度.谢谢!我希望有一天能够提供数月和数年的持续时间,因为在我看来,这样的事情应该是标准库的一部分. (4认同)

rob*_*les 5

izca提出的解决方案很棒,但它遗漏了一件事。如果添加如下示例,可以看到效果:

a = time.Date(2015, 1, 11, 0, 0, 0, 0, time.UTC)
b = time.Date(2015, 3, 10, 0, 0, 0, 0, time.UTC)
fmt.Println(diff(a, b))
// Expected: 0 1 27 0 0 0
// Actual output: 0 1 30 0 0 0
Run Code Online (Sandbox Code Playgroud)

playground

代码是根据第一个月的总天数 ( y1,M1)计算下一个不完整月份的剩余天数,但需要从较晚日期月份 ( y2,M2-1)的前一个月开始计算。

最终代码如下:

package main

import (
    "fmt"
    "time"
)


func DaysIn(year int, month time.Month) int {
    return time.Date(year, month+1, 0, 0, 0, 0, 0, time.UTC).Day()
}

func Elapsed(from, to time.Time) (inverted bool, years, months, days, hours, minutes, seconds, nanoseconds int) {
    if from.Location() != to.Location() {
        to = to.In(to.Location())
    }

    inverted = false
    if from.After(to) {
        inverted = true
        from, to = to, from
    }

    y1, M1, d1 := from.Date()
    y2, M2, d2 := to.Date()

    h1, m1, s1 := from.Clock()
    h2, m2, s2 := to.Clock()

    ns1, ns2 := from.Nanosecond(), to.Nanosecond()

    years = y2 - y1
    months = int(M2 - M1)
    days = d2 - d1

    hours = h2 - h1
    minutes = m2 - m1
    seconds = s2 - s1
    nanoseconds = ns2 - ns1

    if nanoseconds < 0 {
        nanoseconds += 1e9
        seconds--
    }
    if seconds < 0 {
        seconds += 60
        minutes--
    }
    if minutes < 0 {
        minutes += 60
        hours--
    }
    if hours < 0 {
        hours += 24
        days--
    }
    if days < 0 {
        days += DaysIn(y2, M2-1)
        months--
    }
    if months < 0 {
        months += 12
        years--
    }
    return
}

func main() {
    var a, b time.Time
    a = time.Date(2015, 5, 1, 0, 0, 0, 0, time.UTC)
    b = time.Date(2016, 6, 2, 1, 1, 1, 1, time.UTC)
    fmt.Println(Elapsed(a, b)) // Expected: false 1 1 1 1 1 1

    a = time.Date(2016, 1, 2, 0, 0, 0, 0, time.UTC)
    b = time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC)
    fmt.Println(Elapsed(a, b)) // Expected: false 0 0 30 0 0 0

    a = time.Date(2016, 2, 2, 0, 0, 0, 0, time.UTC)
    b = time.Date(2016, 3, 1, 0, 0, 0, 0, time.UTC)
    fmt.Println(Elapsed(a, b)) // Expected: false 0 0 28 0 0 0

    a = time.Date(2015, 2, 11, 0, 0, 0, 0, time.UTC)
    b = time.Date(2016, 1, 12, 0, 0, 0, 0, time.UTC)
    fmt.Println(Elapsed(a, b)) // Expected: false 0 11 1 0 0 0

    a = time.Date(2015, 1, 11, 0, 0, 0, 0, time.UTC)
    b = time.Date(2015, 3, 10, 0, 0, 0, 0, time.UTC)
    fmt.Println(Elapsed(a, b)) // Expected: false 0 1 27 0 0 0
}
Run Code Online (Sandbox Code Playgroud)

playground