|
Boost : |
From: Stephan T. Lavavej (stl_at_[hidden])
Date: 2003-08-04 22:17:03
time_duration behaves highly nonintuitively. A time_duration should be
convertible to seconds by calculating td.hours() * 3600 + td.minutes() * 60
+ td.seconds(), right? Wrong!
This is the correct way to do it:
int seconds_from_time_duration(const boost::posix_time::time_duration& td) {
const time_duration positive = td.is_negative() ? td.invert_sign() : td;
return (td.is_negative() ? -1 : 1)
* (positive.hours() * 3600
+ positive.minutes() * 60
+ positive.seconds()
);
}
I developed this function after being bitten by time_duration's nonintuitive
behavior. This entertaining story goes as follows:
On 2:56:15 July 21, 1969 (UTC), Neil Armstrong first set foot on the Moon.
On 0:00:00 January 1, 1970 (UTC), the Unix epoch began.
When represented as Boost times, Armstrong - Unix is a negative
time_duration.
I printed out the hours, minutes, and seconds fields of this time_duration
and got:
Hours: -3933
Minutes: 3
Seconds: 45
Adding -3933 hours to Unix gives 3:00:00 July 21, 1969. -3 minutes and -45
seconds must then be added to produce Armstrong time. But the time_duration
specifies that 3 minutes and 45 seconds should be added. Ooops.
In short, the .minutes() and .seconds() of a negative time_duration should
be negative, but are not. Since the negative sign is attached to the hours
only, the complicated above seconds_from_time_duration() function must be
used in order to avoid incorrectly converting time_durations to seconds.
I believe that there are two solutions to this bug:
1. Make all of time_duration's quantities unsigned and store whether the
time_duration is negative in a boolean. This cleanly separates storing the
direction of the time_duration from the magnitude. However, this is
undesirable because converting a time_duration to seconds would still
require (td.is_negative() ? -1 : 1) * (td.hours() * 3600 + td.minutes() * 60
+ td.seconds()). If the user forget the parentheses around the second
expression, then again the result would be correct except when the negative
time_duration has nonzero seconds or minutes.
2. If a negative time_duration is produced, all of its fields should be
negative or zero. This has the advantage of allowing easy conversion to
seconds: td.hours() * 3600 + td.minutes() * 60 + td.seconds(), which is what
a user will naturally want to write. Users may construct time_durations with
differently signed fields, but these will have well-defined meanings (just
as 1 hour 1 minute 61 seconds is equal to 1 hour 2 minutes 1 second, 1 hour
0 minutes -1 seconds is equal to 0 hours 59 minutes 59 seconds).
I believe that (2) is the correct solution and has no downsides.
Stephan T. Lavavej
http://stl.caltech.edu
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk