DEV Community

Discussion on: Get the number of days between two dates in Go

Collapse
 
samwho profile image
Sam Rose

Not quite as robust as I'd like. Not all days have 24 hours in them. 😔

Collapse
 
alexyslozada profile image
Alexys Lozada
func daysBetween(a, b time.Time) float64 {
    if a.After(b) {
        a, b = b, a
    }
    return math.Ceil(b.Sub(a).Hours() / 24.0)
}
Thread Thread
 
jonstodle profile image
Jon Stødle

This still doesn't work: Not all days have 24 hours.

In most countries in Europe and North America it varies between 23 and 25.

centralEuropeTime, _ := time.LoadLocation("Europe/Oslo")
twentyThreeHours := time.Date(2022, 3, 27, 0, 0, 0, 0, centralEuropeTime).Sub(time.Date(2022, 3, 28, 0, 0, 0, 0, centralEuropeTime))
twentyFiveHours := time.Date(2022, 10, 30, 0, 0, 0, 0, centralEuropeTime).Sub(time.Date(2022, 10, 31, 0, 0, 0, 0, centralEuropeTime))
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
jonmithe profile image
jonmithe • Edited

Yes and no I think for different reasons.

The time.Sub here operates on the internal time value golang uses not the "timezone" value. This internal is UTC seconds since jan 01 0001 for most cases.

As such in UTC all days do have 24 hours in them so this maths is sound.

However, the error you are seeing is because the time initialisation applies the DST offset. Take time.Date(2022, 3, 28, 0, 0, 0, 0, centralEuropeTime), when that is initialised it is converted into go's core UTC time which applies the DST offset, so when in DST the UTC time will be 23:00 the day before.

So the maths is still valid in that the 24 hour per day calculation is fine because its all UTC, its just the end or start bound has accidentally been dragged backwards an hour due to its initialisation which can yield an off by 1 error.

(I'm talking about 1 hour DST, of course there could be different values of this)

As my main comment suggests, represent the dates straight up in UTC and that fixes the conversion / initialisation and the maths works.

twentyFourHours := time.Date(2022, 3, 27, 0, 0, 0, 0, time.UTC).Sub(time.Date(2022, 3, 28, 0, 0, 0, 0, time.UTC))

This is good for straight up date maths. If you want to start getting clever with times / clock components, then you need to handle the timezone in different ways, again I talked about that on my root comment.

And as I mentioned in the post as well, duration.Hours() does a float conversion and that is unnecessary and not good. Embrace durations and use integer division i.e. time.Sub...(...) / (24 * time.Hour).