time.Duration
is an int64
value representing a duration in nanoseconds. As stated, max value of int64
is about 290 years, so bigger durations cannot be represented by it.
Simplest, naive solution
One simple solution is to convert your input to time.Duration
, which will represent one hundredth of your actual duration, because the input is in 100-nanosecond units. You may add this duration to a time starting with the reference date: 1601-01-01 UTC
a hundred times, and you're done:
func getTime(input int64) time.Time {
t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
d := time.Duration(input)
for i := 0; i < 100; i++ {
t = t.Add(d)
}
return t
}
Testing it:
fmt.Println(getTime(132118740000000000))
Output (try it on the Go Playground):
2019-09-02 05:00:00 +0000 UTC
Naive solution optimized
Yes, the above solution has a loop with 100 iterations, which may not be optimal.
One way to speed up the above is to reduce the number of iterations. We may do so if the input is not "very" large. For example if the input multiplied by 2 also fits into int64
, we could pre-multiply it by 2
, and then we would only need 50 iterations. Similarly, if the input*10
would also fit into int64
, we could pre-multiply it by 10
and then we would only need 10 iterations.
The input is 100-nanosecond units. 100
is dividable by 100, 50, 25, 20, 10, 5, 4, 2, so to not lose any nanoseconds, we could check these factors if the input multiplied by these still fits into int64
, and if so we can divide the iterations count by it. In the best case scenario (if duration is less than 2.9 years, we can reduce the iterations to 1).
Example doing so:
var divisors = []int64{100, 50, 25, 20, 10, 5, 4, 2}
func getTime(input int64) time.Time {
iterations := 100
for _, div := range divisors {
if input <= math.MaxInt64/div {
input *= div
iterations /= int(div)
break
}
}
t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
d := time.Duration(input)
for i := 0; i < iterations; i++ {
t = t.Add(d)
}
return t
}
This will output the same, try it on the Go Playground. In this example the number of iterations is only 2.
Fewest iterations
Similar to the above solution, but here in each iteration we will increment the time with the biggest duration possible. That is: time.Duration(math.MaxInt64)
, but since the input is in 100-nanosecond units, to be exact, we'll use time.Duration(math.MaxInt64).Truncate(100 * time.Nanosecond)
. We keep doing this until the remaining duration is less than the maximum, which will be the final addition to get the time instant we're looking for. As an extra, we also don't need the first loop which looked for the biggest divisor (to which the iterations count could be reduced).
func getTime(input int64) time.Time {
maxd := time.Duration(math.MaxInt64).Truncate(100 * time.Nanosecond)
maxdUnits := int64(maxd / 100) // number of 100-ns units
t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
for input > maxdUnits {
t = t.Add(maxd)
input -= maxdUnits
}
if input != 0 {
t = t.Add(time.Duration(input * 100))
}
return t
}
Output again is the same. Try this one on the Go Playground.
This solution guarantees fewest iterations. E.g. if duration is less than 290 years, there will be a single time.Add()
call. If duration is between 290 and 580 years, there will be 2 time.Add()
calls etc.
Note that in the final time.Add()
call we multiply input
by 100
to convert 100-nanosecond units to nanoseconds. This will always succeed, because the loop before that decrements it as long as its bigger than maxdUnits
. We also only call this final time.Add()
if there is still something left to add, to ensure the fewest iterations. In practice this will likely always be true, so this if
could be left out: even if input
is 0, adding zero will not change t
, I added it to be true to the "fewest iterations" title.