慕斯王
前言:我在 中发布了此实用程序github.com/icza/gox,请参阅timex.Diff()。一个月中的天数取决于日期,就像一年中的天数(闰年)。如果您time.Since()用来获取自一个time.Time值以来经过的时间,或者当您time.Time使用该Time.Sub()方法计算两个值之间的差异时,结果是 atime.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()这背后的逻辑是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.Timea = 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 1a = 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 0a = 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 0a = 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 10 0 30 0 0 00 0 28 0 0 00 11 1 0 0 0在Go Playground上试试。要计算您的年龄:// Your birthday: let's say it's January 2nd, 1980, 3:30 AMbirthday := 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.
莫回无
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 mainimport ( "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}