время golang.С() С месяцев и лет
Я пытаюсь преобразовать метку времени, как это:
2015-06-27T09:34:22+00:00
до времени с формата, так что он сказал бы, как 9 месяцев назад 1 день 2 часа 30 минут 2 секунд.
что-то в этом роде.
Я time.Parse
и time.Since
до этого:
6915h7m47.6901559s
но как мне конвертировать оттуда? Что-то вроде этого я и подумал:--6-->
for hours > 24 {
days++
hours -= 24
}
но проблема в том, что это не точный в течение нескольких месяцев, потому что месяцы могут 28, 30 и 31 дней.
есть ли лучший способ достичь того, что я хочу?
5 ответов
дни в месяц зависит от даты, так же, как дни в году (високосные годы).
если вы используете time.Since()
чтобы получить прошедшее время с time.Time
значение, или когда вы вычисляете разницу между 2 time.Time
значений с помощью Time.Sub()
метод, результат time.Duration
, который теряет контекст времени (как Duration
это просто разница во времени в наносекундах). Это означает, что вы не можете точно и однозначно вычислить разницу в годах, месяцах и т. д. из Duration
значение.
правильное решение должно рассчитать разницу в контексте времени. Вы можете вычислить значение для каждого поля (год, месяц, день, час, минута, секунда), а затем нормализовать результат, чтобы не иметь отрицательных значений. Также рекомендуется поменять местами Time
значения, если отношение между ними не является ожидаемым.
нормализация означает, если значение отрицательно, добавьте максимальное значение этого поля и уменьшите следующее поле на 1. Например, если seconds
отрицательное, добавить 60
и декремента minutes
к 1. Одна вещь, на которую нужно обратить внимание, - это нормализация разницы дней (дней в месяце), количество дней в соответствующем месяце должно быть применено. Это можно легко вычислить с помощью этого маленького трюка:
// 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 playground:2009-11-10 23:00:00 UTC
Это время, когда было впервые объявлено. Давайте подсчитаем, сколько лет 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.
вы можете попробовать работать с my дата пакет, который включает в себя период пакет для работы с периодами времени в стиле ISO (Википедия).
тип периода поставляется с форматером, который понимает множество, печатая читаемые строки, такие как" 9 лет, 2 месяца "и" 3 часа, 4 минуты, 1 секунду", а также эквиваленты ISO ("P9Y2M "и"PT3H4M1S").
периоды, конечно, сложно из-за переменной длины дней (по ДСТ) и месяцев (по Григорианскому календарю). The period
пакет пытается помочь вам, предоставляя API, который позволяет как точные, так и неточные вычисления. На короткие периоды (до ±3276 часов) он может преобразовать продолжительность точно.
duration := time.Since(...)
p, _ := period.NewOf(duration)
str := p.String()
Если вам нужны точные продолжительности над более длинными пядями, то вам нужно использовать между функцией (которая воплощает превосходный ответ icza).
p := period.Between(t1, t2)
str := p.String()
решение, предложенное 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+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
}
что-то вроде этого будет работать, вероятно, не самым эффективным, но это так же точно, как вы получите:
func main() {
a := time.Date(2015, 10, 15, 0, 0, 0, 0, time.UTC)
b := time.Date(2016, 11, 15, 0, 0, 0, 0, time.UTC)
fmt.Println(monthYearDiff(a, b))
}
func monthYearDiff(a, b time.Time) (years, months int) {
m := a.Month()
for a.Before(b) {
a = a.Add(time.Hour * 24)
m2 := a.Month()
if m2 != m {
months++
}
m = m2
}
years = months / 12
months = months % 12
return
}