Предисловие: я выпустил эту утилиту в github.com/icza/gox
, см. timex.Diff()
.
Количество дней в месяце зависит от даты, как и количество дней в году (високосные годы).
Если вы используете time.Since()
для получения времени, прошедшего с time.Time
или при вычислении разницы между двумя 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.
Волшебная дата/время начала игровой площадки Го: 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.
person
icza
schedule
10.04.2016