There is currently a PR on the repo with very lively discussion about what the following operation should do:
using Dates
a = now()
b = a + Nanosecond(1)
(the situation is analogous with Microsecond)
Currently, the result is always exactly equal to a, because the DateTime arithmetic implemented in Dates simply truncates the non-DateTime argument to milliseconds (as that is the supported precision of DateTime), and whatever the result is, will be added to DateTime:
julia> using Dates
julia> a = now()
2023-08-18T14:35:44.462
julia> a === a + Nanosecond(1)
true
julia> a === a + Microsecond(999)
true
julia> a === a + Microsecond(1000) # now we're at the millisecond scale
false
julia> (a + Microsecond(1000)) === (a + Microsecond(1999))
true
This has consequences when trying to accumulate a number of durations into a DateTime, for example to figure out how long some continued measurements took, perhaps from samples of an oscilloscope:
julia> a = now()
julia> reduce(+, fill(Microsecond(1234), 10000), init=a)
2023-08-18T14:22:02.527
julia> a + reduce(+, fill(Microsecond(1234), 10000), init=Nanosecond(0))
2023-08-18T14:22:04.867
This, where two different forms of accumulation lead to different results, happens because in the first form due to the repeated truncation there is an accumulation of error, and thus the result will not be as exact as it could be. In essence, DateTime + Nanosecond or DateTime + Microsecond are not associative. This is problematic, because the dataloss is silent, and near impossible to catch after the fact - especially if you donāt have access to the original data anymore.
There are more or less three ways to resolve this:
- Keep the current behavior, and simply document that this kind of arithmetic does some form of rounding.
- Increase the precision of
DateTimeup toNanosecondprecision (and not support smaller scales at all, effectively pushing the failure case to higher precisions, but not solving it entirely). This would necessitate an increase in size ofDateTime(from currently 64 bits to 128 bits, to accomodate the additional data needed to keep track of everything). - Deprecate the existing behavior, and direct users to use
round/trunc/ceil/flooron their too-small-to-fit-in-DateTimecalculations, to explicitly specify the behavior they want. Only arithmetic with exact results are allowed - the methods in question would be removed when (if ever) a breaking release in Julia happens.
Iām personally in favor of option 3), because itās my believe that a programming language (and standard library, targeting a very general use case) should have as few foot guns as possible, and should give hints & nudges towards correct usage for their application, with as few guesses about what the user cares about as possible.
Iād like to hear what the community thinks though, and in particular Iād like to hear from people who often use this kind of arithmetic and what theyād expect to happen in these circumstances.