为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
桃花长相依 2022-01-04 10:36:38
我正在尝试转换这样的时间戳:2015-06-27T09:34:22+00:00到格式后的时间,所以它会说像 9 个月前 1 天 2 小时 30 分 2 秒。类似的东西。我使用time.Parse并time.Since达到这个目的:6915h7m47.6901559s但是我如何从那里转换?我的想法是这样的:for hours > 24 {        days++        hours -= 24}但问题在于,这在几个月内不会准确,因为几个月可能有 28、30 和 31 天。有没有更好的方法来实现我想要的?
查看完整描述

3 回答

?
慕斯王

TA贡献1864条经验 获得超2个赞

前言:我在 中发布了此实用程序github.com/icza/gox,请参阅timex.Diff()


一个月中的天数取决于日期,就像一年中的天数(闰年)。

如果您time.Since()用来获取自一个time.Time值以来经过的时间,或者当您time.Time使用该Time.Sub()方法计算两个值之间的差异时,结果是 atime.Duration丢失了时间上下文(就像Duration时间差以纳秒为单位)。这意味着您无法准确无误地计算出一个Duration值的年、月等差异。

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

归一化意味着如果一个值是负数,加上那个字段的最大值,然后将下一个字段减1。例如,如果seconds是负数,加起来60minutes1。要注意的一件事是在对天差进行归一化时(月中的天数),必须应用适当月份中的天数。用这个小技巧可以很容易地计算出来:

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

这背后的逻辑是32任何一个月中的一天都大于最大天数。它将自动标准化(额外的天数滚动到下个月,天数适当减少)。当我们从 32 中减去归一化后的天数时,我们会得到当月的最后一天。

时区处理:

如果我们传入的两个时间值都在同一时区 ( time.Location) 中,差异计算才会给出正确的结果。我们将检查合并到我们的函数中:如果不是这种情况,我们将使用以下Time.In()方法“转换”一个时间值与另一个时间值位于同一位置:

if a.Location() != b.Location() {
    b = b.In(a.Location())}

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

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

}

一些测试:


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

输出如预期:


1 1 1 1 1 1

0 0 30 0 0 0

0 0 28 0 0 0

0 11 1 0 0 0

在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)

示例输出:


You are 36 years, 3 months, 8 days, 11 hours, 57 mins and 41 seconds old.

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)

输出:


Go was announced 6 years, 4 months, 29 days, 16 hours, 53 mins and 31 seconds ago.


查看完整回答
反对 回复 2022-01-04
?
莫回无

TA贡献1865条经验 获得超7个赞

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


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


最终代码如下:


package main


import (

    "fmt"

    "time"

)


func DaysIn(year int, month time.Month) int {

    return time.Date(year, month, 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 days < 0 {

        days += DaysIn(y2, M2)

        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: 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: 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: 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: 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: 0 1 27 0 0 0


    a = time.Date(2015, 12, 31, 0, 0, 0, 0, time.UTC)

    b = time.Date(2015, 3, 1, 0, 0, 0, 0, time.UTC)

    fmt.Println(Elapsed(a, b)) // Expected: 0 9 30 0 0 0


    a = time.Date(2015, 12, 31, 0, 0, 0, 0, time.UTC)

    b = time.Date(2016, 3, 1, 0, 0, 0, 0, time.UTC)

    fmt.Println(Elapsed(a, b)) // Expected: 0 2 1 0 0 0


    a = time.Date(2015, 12, 31, 0, 0, 0, 0, time.UTC)

    b = time.Date(2016, 2, 28, 0, 0, 0, 0, time.UTC)

    fmt.Println(Elapsed(a, b)) // Expected: 0 2 1 0 0 0

}


查看完整回答
反对 回复 2022-01-04
?
斯蒂芬大帝

TA贡献1827条经验 获得超8个赞

如果你使用 PostgreSQL,你可以很容易地用age函数得到结果。


假设您有两个日期a和b。


就像 icza 说的,要小心,a并且b必须在同一时区。


首先,您可以age使用两个参数进行调用,在您的情况下为 datea和 date b。此函数返回包含年、月、周、日、小时、分钟、秒和毫秒的间隔类型。


SELECT age('2016-03-31', '2016-06-30'); -- result is: -2 mons -30 days

第二种可能性是使用age带有一个参数的函数。结果也是一个间隔,但在这种情况下,age从 current_date(午夜)中减去。假设今天是 2016/06/16:


SELECT age(timestamp '2016-06-30'); -- result is: -14 days

请注意,timestamp需要关键字来投射日期“2016-06-30”。


有关更多详细信息,您可以使用date_part或直接extract返回一个特定字段(年、月、日...)的函数。


SELECT date_part('month', age('2016-03-31', '2016-06-30')); --result is: -2

SELECT date_part('day',   age('2016-03-31', '2016-06-30')); --result is: -30

完整请求:


SELECT  

    date_part('year', diff) as year

  , date_part('month', diff) as month

  , date_part('day', diff) as day

FROM (

  SELECT age(timestamp '2016-06-30') AS diff

) as qdiff;


-- result is: 

-- year month day

-- 0    0     -14

(使用 CTE - 通用表表达式):


WITH qdiff AS (

  SELECT age(timestamp '2016-06-30') AS diff

)

SELECT  

    date_part('year', diff) as year

  , date_part('month', diff) as month

  , date_part('day', diff) as day

FROM qdiff


-- result is: 

-- year month day

-- 0    0     -14

PostgreSQL 文档(当前版本):https : //www.postgresql.org/docs/current/static/functions-datetime.html


查看完整回答
反对 回复 2022-01-04
  • 3 回答
  • 0 关注
  • 213 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信