Boost logo

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