Duration type doesn't preserve seconds accuracy
24 views (last 30 days)
Show older comments
I've been working on some time conversion routines and ran into an issue I didn't expect. Basically, the duration type doesn't preserve seconds accuracy. This is caused by the fact that the duration type stores the duration as a single variable called millis (i.e., milliseconds) instead of separate d,h,m,ms or h,m,ms. Why this would be coded in that way does not make any sense to me. E.g., because of this you can get issues such as this:
format longg
dt1 = datetime(2000,1,1,0,0,1.2345678912345);
disp(dt1.Second)
dt2 = datetime(2000,1,1);
[~,~,s] = hms(dt1-dt2)
All well and good, the seconds is represented to full precision in the datetime variable, and the precision of the seconds difference is preserved in the subtraction because the datetimes are close to one another. But if they are not:
dt3 = datetime(1900,1,1);
[~,~,s] = hms(dt1-dt3)
You suddenly get a different result because the difference got mashed into a single millis variable and that large days value caused precision loss in the seconds. The hms() function can't pull out seconds accuracy that isn't there. And simply constructing a duration type from scratch doesn't solve anything either because of the internal duration storage as a single millis variable. E.g.,
[~,~,s] = hms(duration(0,0,1.2345678912345))
[~,~,s] = hms(duration(1e6,0,1.2345678912345))
I know, I know ... "You shouldn't depend on this level of accuracy in floating point calculations" etc. But that's not the point. The point is as long as you have a datetime type that stores individual d,h,m,s properties, then IMHO the duration type should follow suit and preserve accuracy in calculations as much as possible to match it. If the duration type stored the data internally as d,h,m,s (or ms or ns) and took care when doing the internal arithmetic, the above differences could be avoided. In fact, since the millis is a private internal variable, I think TMW could still do this in future versions of MATLAB without breaking any user code since there shouldn't be any user code that accesses the millis property. Any chance of this, TMW?
And speaking of the datetime type, IMHO the internal variable should have been millis or nanos instead of seconds. That way standard Terrestrial Time epochs such as J2000 (January 1, 2000, 11:58:55.816 UTC) etc. could be represented exactly internally. But since datetime y,m,d,h,m,s properties are public this unfortunately can't be changed now.
0 Comments
Answers (2)
Stephen23
on 14 May 2024
"Duration type doesn't preserve seconds accuracy"
That is exactly what CALENDARDURATION objects are for:
format longg
dt1 = datetime(2000,1,1,0,0,1.2345678912345);
dt2 = datetime(2000,1,1);
dt3 = datetime(1900,1,1);
ddt = between(dt3,dt1)
3 Comments
Peter Perkins
on 15 May 2024
calendarDuration is for calendar arithmetic. The differences between d1 and d2, and between d2 and d3 are exactly one calendar year. By fuzzy, I think you mean that some calendar years are 365.2425*86400s long, others are 366.2425*86400s, and still others are 1s longer than both of those. In the same way, some calendar months are 30*86400s long, or maybe 31* or 29* or 28*. Or 1s longer than some of those. That's what calendarDuration is for, that's why there are two "duration" types, and why there is a days function and a caldays function. But it sounds like that's not what you need.
I think there are good ways to do what you want, but I'm not yet clear on the details.
Peter Perkins
on 14 May 2024
Edited: Peter Perkins
on 14 May 2024
James, Stephen's response is what I would have said, but there are a lot of things in this post, and I thought I would add to Stephen's response.
The title of this post is, "Duration type doesn't preserve seconds accuracy." That title might be interpreted in several ways, one of which I know you don't mean.
1) It might be interpreted as, duration doesn't provide enough accuracy to represent elapsed time to the accuracy of one second, or maybe to the accuracy of subseconds. Clearly it does, and you didn't mean this, but the title is misleading.
format long g
d1 = seconds(0.123456789)
d2 = seconds(123456789) + d1
d3 = d2 - d1
(I've ignored the fact that 0.123456789 isn't actually 0.123456789, it's the closest d.p. to that; before this even gets to the seconds function you are done for in that respect. Better to use milliseconds(123.456789) if you really care about such precision. Why no nanoseconds function? Because at the moment, as you say, duration uses units of milliseconds internally.)
2) What you mean is that duration does not have as much precision as datetime, so that on the one hand datetime can represent very precise timestamps, down to at least nanoseconds, over the age of the universe, but if you take differences of datetimes, duration is not able to represent those elapsed times to the same precision over large intervals. That last part, "over large intervals" is important. duration has the same precision as double, in units of (as you say) milliseconds). Computing the difference in datetimes that are close together as a duration will get you answers accurate to sub-nanosecond, but computing differences in datetimes that area century apart as a duration will only be able to preserve down something like tenths of microseconds:
eps(100*365.2425*86400000)/1000
So what you say is correct, but I would ask in response, do you have any real uses where you need that kind of precision over that long a range? I'm genuinely asking. If you are doing particle physics or something, with elapsed times of like picoseconds, you can absolutely use durations, because it seems unlikely that you'd care about elapsed times longer than, what, a few minutes?
Some other scattered things:
- "the duration type stores the duration as a single variable called millis (i.e., milliseconds) instead of separate d,h,m,ms or h,m,ms. Why this would be coded in that way does not make any sense to me." You would be very unhappy with the performance of an implementation using separate components. It would be doing modulo arithmetic all over.
- "And speaking of the datetime type, IMHO the internal variable should have been millis or nanos instead of seconds." Not sure why you're tinking seconds. datetime does NOT store time in units of seconds, it uses units of milliseconds. J2000 is represented exactly. You may have created it suboptimally (by calling the constructor using 55.816 seconds, instead of 55s, 816ms), but that's a different story. Is there something somewhere in the doc that is misleading you?
4 Comments
See Also
Categories
Find more on Matrix Indexing in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!