Reputation: 423
I'm trying to get amount of days between two dates in Go but as I'm getting the difference in time (hours) and then divided by the amount of time per day, I have an issue.
Problem: if dates are different (7 of May and 8 of May) but the time between then is lower than 24h, my code counts as there are no days in between.
What I want: count real days in between.
// Days
firstDay := firstDate.Unix()
lastDay := lastDate.Unix()
fmt.Println("firstDay: ", firstDay)
fmt.Println("lastDay: ", lastDay)
if firstDay > lastDay {
fmt.Println("IS TO SMALL")
return
}
// businessDays =
businessDays := (lastDay - firstDay) / 86400
fmt.Println("businessDays: ", businessDays)
Thank you very much.
Upvotes: 8
Views: 5169
Reputation: 1617
Duration.Hours()
to get hoursmath.Ceil()
to find calendar daysTime.Truncate()
to truncate hour and minute, only save dayThe source code:
package main
import (
"fmt"
"math"
"time"
)
func main() {
d1, err := time.Parse("200601021504", "201801020001")
if err != nil {
panic(err)
}
d2, err := time.Parse("200601021504", "201801020002")
if err != nil {
panic(err)
}
newD1 := d1.Truncate(time.Hour * 24)
newD2 := d2.Truncate(time.Hour * 24)
fmt.Printf("days: %v\n", math.Ceil(newD2.Sub(newD1).Hours()/24))
}
Upvotes: 9
Reputation: 166795
There are many things to worry about. For example, due to daylight saving time (DST), not all days are 24 hours. What if the times are in different time zones? And so on.
Here's a solution that addresses these issues.
package main
import (
"fmt"
"time"
)
// CalendarDays returns the calendar difference between times (t2 - t1) as days.
func CalendarDays(t2, t1 time.Time) int {
y, m, d := t2.Date()
u2 := time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
y, m, d = t1.In(t2.Location()).Date()
u1 := time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
days := u2.Sub(u1) / (24 * time.Hour)
return int(days)
}
func main() {
first := time.Now().Round(0)
end := first.Add(48 * time.Hour)
for last := first; last.Before(end); last = last.Add(6 * time.Hour) {
fmt.Println("Days:", CalendarDays(last, first), "Last:", last, "First:", first)
}
}
Playground: https://play.golang.org/p/wKwQzgfKa8f
Output:
Days: 0 Last: 2018-02-05 17:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Days: 0 Last: 2018-02-05 23:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Days: 1 Last: 2018-02-06 05:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Days: 1 Last: 2018-02-06 11:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Days: 1 Last: 2018-02-06 17:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Days: 1 Last: 2018-02-06 23:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Days: 2 Last: 2018-02-07 05:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Days: 2 Last: 2018-02-07 11:38:15.623292254 -0500 EST First: 2018-02-05 17:38:15.623292254 -0500 EST
Upvotes: 4
Reputation: 8232
As performance is now an issue (which I doubt), I wrote some benchmarks with approaches I come around:
func TrimToDate(t time.Time) time.Time {
y, m, d := t.Date()
return time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
}
func CountTrim(t1, t2 time.Time) int {
return int((t2.Unix() - TrimToDate(t1.Unix())) / 86400)
}
func CountUnixAdd(t1, t2 time.Time) int {
Days := (t1.Unix() - t2.Unix()) / 86400
if t1.Add(time.Duration(Days)*24*time.Hour).Day() != t2.Day() {
Days++
}
return int(Days)
}
func CountDivMul(t1, t2 time.Time) int {
d1 := t1.Unix() / 86400 * 86400
return int((t2.Unix() - d1) / 86400)
}
Where CountTrim
use the time.Time.Date
method to trim the hours and minutes and so on. Note that only once TrimToDate
needs to be called, as the remaining would be discarded by the integer division.
CountUnixAdd
is a plain and simple way: Test if it is the same date, if not, adds one.
CountDivMul
is almost the same idea of trimming to day, but use a more hack-y way: using integer division to get rid of the remainnings and then multiply back.
The benchmark result on my laptop:
goos: windows
goarch: amd64
pkg: test/stackoverflow/dateprb
BenchmarkCountTrim-8 20000000 62.7 ns/op
BenchmarkCountDivMul-8 1000000000 2.12 ns/op
BenchmarkCountUnixAdd-8 20000000 77.8 ns/op
PASS
ok test/stackoverflow/dateprb 5.367s
Unsurprisingly, the hack-y way is way faster than the other while the two usual way is almost the same.
Full code (of benchmark and of functions) here
Upvotes: 1