Custom Time Duration Support

ISO/IEC JTC1 SC22 WG21 N2498=08-0008 - 2008-01-19

Howard E. Hinnant
Jeff Garland

Contents

Introduction

N2497 introduces Duration requirements which essentially create a concept for time durations in the standard library. These time durations will be very convenient and useful both for use in other parts of the standard library interface, and in client code. Several concrete types meeting the Duration requirements are also introduced:

class nanoseconds;
class microseconds;
class milliseconds;
class seconds;
class minutes;
class hours;

Because there is a documented set of requirements which all of the durations meet, this introduces the possibility of user-written time durations. Indeed N2497 was written with the intent of interoperating with user-written time durations. A user-written time duration might model (for example) the amount of time between display updates. For the purposes of this paper, an example user-written duration referred to as frame_duration which has units of 1/30 of a second will be used to illustrate the salient points described herein.

This paper proposes to make the creation of user-written durations (such as frame_duration) much easier, while at the same time lifting some of the restrictions N2497 places on duration comparison and arithmetic operations. Furthermore, if accepted, this proposal makes it easier for the client to write robust generic algorithms where the duration type itself is generic (a round_to_nearest duration conversion algorithm is given as an example).

The ideas in this paper were specifically not folded into N2497 because the authors of that paper felt that this work went well beyond the mandate of the Kona LWG Motion 14 and should be formally reviewed by the LWG as a separate issue.

This paper does not change the syntax that the clients will use when working with standard defined durations. It only changes the syntax for those clients wishing to support or write user-defined durations and algorithms trafficking in generic durations. However it does impact the ABI of the standard-defined durations. Because of this, it is important to target this work towards C++0X instead of a TR (where ABI breakage is generally intolerable).

How Does One Create A Time Duration?

It is instructive to go through the process of creating frame_duration according to the specification in N2497. In addition to the requirements listed in N2497, we may also want to add converting constructors from those standard-defined durations which will exactly convert to frame_duration (standard-defined durations have implicit conversions when the conversion is exact). Most of the member functions for frame_duration are easy, though repetitive, and included in the sketch below:

class frame_duration
{
    long long ticks_;
public:
    // traits information
    typedef long long tick_type;
    static const tick_type ticks_per_second = 30;
    static const tick_type seconds_per_tick = 0;
    static const bool is_subsecond = true;

    // construct
    frame_duration(long long ticks=0)
        : ticks_(ticks) {}

    // convert
    frame_duration(std::hours hr)
        : ticks_(hr.count() * (std::hours::seconds_per_tick * ticks_per_second)) {}
    frame_duration(std::minutes mn)
        : ticks_(mn.count() * (std::minutes::seconds_per_tick * ticks_per_second)) {}
    frame_duration(std::seconds sec)
        : ticks_(sec.count() * (std::seconds::seconds_per_tick * ticks_per_second)) {}

    // observer functions
    tick_type count() const {return ticks_;}

    // modifier functions
    template<typename RhsDuration>
        frame_duration& operator-=(const RhsDuration& d);
    template<typename RhsDuration>
        frame_duration& operator+=(const RhsDuration& d);
    frame_duration& operator*=(tick_type rhs) {ticks_ *= rhs;}
    frame_duration& operator/=(tick_type rhs) {ticks_ /= rhs;}

    // operations
    frame_duration operator-() const {return -ticks_;}
};

Implementing the += and -= operators is a little more difficult. We might choose to simply replace the templated members with a list of all of the standard durations which will exactly convert:

// modifier functions
frame_duration& operator-=(const std::hours& d)   {ticks_ -= frame_duration(d).count(); return *this;}
frame_duration& operator-=(const std::minutes& d) {ticks_ -= frame_duration(d).count(); return *this;}
frame_duration& operator-=(const std::seconds& d) {ticks_ -= frame_duration(d).count(); return *this;}
frame_duration& operator+=(const std::hours& d)   {ticks_ += frame_duration(d).count(); return *this;}
frame_duration& operator+=(const std::minutes& d) {ticks_ += frame_duration(d).count(); return *this;}
frame_duration& operator+=(const std::seconds& d) {ticks_ += frame_duration(d).count(); return *this;}

This is easy but repetitive. It also has the distinct disadvantage that it will not interoperate with other user-written duration types which may be exactly convertible to frame_duration. For example if someone else writes tenth_second, which would exactly convert to frame_duration, one will still not be able to add tenth_second to frame_duration.

To correct the above problem the author of frame_duration may choose to create a concept or type trait which represents the idea of RhsDuration being a duration which is exactly convertible to frame_duration. One could then restrict the RhsDuration template parameter, get away with a single implementation for the += and -= operators, and then interoperate seamlessly with tenth_second, even if tenth_second does not yet exist.

// modifier functions
template<typename RhsDuration>
typename std::enable_if
<
    is_duration_exactly_convertible_to<RhsDuration, frame_duration>::value,
    frame_duration&
>::type
    operator-=(const RhsDuration& d);

template<typename RhsDuration>
typename std::enable_if
<
    is_duration_exactly_convertible_to<RhsDuration, frame_duration>::value,
    frame_duration&
>::type
    operator+=(const RhsDuration& d);

This is a far more difficult task, not only because of the need to write is_duration_exactly_convertible_to, but also because of the need to handle the generalized resolution conversion within the body of these members. Having gone through this exercise the authors will simply relate their experience rather than show the code here. It is over 200 lines of support code which involves some template meta programming and is easy to get wrong (which we did on the first few tries). The use of concepts instead of enable_if will only provide a little relief (perhaps not reducing the line count at all).

How Should One Be Able To Create A Time Duration?

During the writing of N2497 it was noticed that there is a much better way to support the author of frame_duration. However it was a large enough change that it went well beyond "fixing the wording and minor problems of N2447" (as was our mandate in writing N2497).

This paper proposes to replace the Duration requirements with a class template:

template <class TickType, long long TicksPerSecond, long long SecondsPerTick>
class duration
{
    TickType ticks;  // exposition only
public:
    // traits information
    typedef TickType tick_type;
    static const long long ticks_per_second = TicksPerSecond;
    static const long long seconds_per_tick = SecondsPerTick;
    static const bool is_subsecond = seconds_per_tick == 0;

    static_assert(ticks_per_second >  1 && seconds_per_tick == 0 ||
                  ticks_per_second == 0 && seconds_per_tick >  1 ||
                  ticks_per_second == 1 && seconds_per_tick == 1,
                  "duration has inconsistent type");

    duration();
    duration(const tick_type& tick);

    // conversions
    template <class TT, long long TPS, long long SPT>
      // Requires duration<TT, TPS, SPT> is exactly convertible to duration
      duration(const duration<T, TPS, SPT>& d);

    // observer

    tick_type count() const;

    // arithmetic

    template <class TT, long long TPS, long long SPT>
      // Requires duration<TT, TPS, SPT> is exactly convertible to duration
      duration&
      operator-=(const duration<TT, TPS, SPT>& d);

    template <class TT, long long TPS, long long SPT>
      // Requires duration<TT, TPS, SPT> is exactly convertible to duration
      duration&
      operator+=(const duration<TT, TPS, SPT>& d);

    duration operator-() const;

    duration& operator*=(tick_type rhs);
    duration& operator/=(tick_type rhs);
};

The standard-defined durations then simply become typedefs.

typedef duration<int_least64_t, 1000L * 1000 * 1000,    0> nanoseconds;
typedef duration<int_least55_t,        1000L * 1000,    0> microseconds;
typedef duration<int_least45_t,                1000,    0> milliseconds;
typedef duration<int_least35_t,                   1,    1> seconds;
typedef duration<int_least29_t,                   0,   60> minutes;
typedef duration<int_least23_t,                   0, 3600> hours;

And the author of frame_duration now simply writes a single line of code instead of hundreds:

typedef std::duration<long long, 30, 0> frame_duration;

This frame_duration will interoperate with all exactly convertible standard durations, and with all exactly convertible user-written durations. The duration template takes care of all duration conversions and arithmetic while maintaining the invariant that only exact conversions and arithmetic is allowed, rejecting inexact operations at compile time. The embedded static_assert virtually eliminates the possibility of creating a buggy duration:

typedef std::duration<long, 5, 3> MyBuggyDuration;

int main()
{
    MyBuggyDuration d;
}

In instantiation of 'std::duration<long int, 5ll, 3ll>':
main.cpp:7:   instantiated from here
error: static assertion failed: "duration has inconsistent type"

The duration's tick_type can be an integral type, a floating point type, or a user-written class (such as an arbitrary range integer class). For example:

typedef std::duration<big_int, 1000LL * 1000 * 1000 * 1000, 0> picosecond;

The standard-defined durations all use a signed integral type for the tick_type. As long as the custom duration tick_types will explicitly convert to and from the signed integral types used by the standard-defined durations, then the user-defined durations will interoperate seamlessly with the standard-defined durations.

A reference implementation exists, is available under an open source license, and is actually shorter than the reference implementation of N2497.

Comparing duration

Consider the following code based on the duration template:

frame_duration    fd = 1;
std::milliseconds ms = 33;
bool b = ms < fd;

According to N2497 the above will not compile because frame_duration is not exactly convertible to std::milliseconds and vice-versa. This is a necessary constraint of N2497 because the only implementation technique for the comparison is to convert frame_duration to std::milliseconds and compare the two values of milliseconds. However the comparison could give the wrong answer since the conversion is not exact (130 of a second is exactly 3313 milliseconds).

But we know that 33 milliseconds is less than 130 of a second (by exactly 13000 of a second). So it is unfortunate to not support this comparison and return the correct answer (b is true). Indeed, very practical code can result in non-obvious incorrect run time results when comparisons do not give the correct result. With the introduction of the duration class template, we can now synthesize a new type that both frame_duration and std::milliseconds exactly convert to, convert both arguments to that new type, and then perform the comparison — exactly.

To describe this new synthesized type, the term CommonDuration is introduced and defined.

The CommonDuration type of duration<T1, TPS1, SPT1> and duration<T2, TPS2, SPT2> is:

[Note: Both duration<T1, TPS1, SPT1> and duration<T2, TPS2, SPT2> are exactly convertible to their CommonDuration type. --end note]

This paper proposes that all duration comparisons behave as if both arguments are converted to their CommonDuration type prior to comparison. Thus the restriction of comparisons in N2497 which says that one argument must be exactly convertible to the other can be lifted. Any two duration types can be exactly compared.

For example, the CommonDuration type of duration<long long, 1000, 0> (std::milliseconds) and duration<long long, 30, 0> (frame_duration) is duration<long long, 3000, 0>. In comparing 33 milliseconds with 1 frame_duration, the implementation will first convert to the CommonDuration type and compare 99 with 100 and subsequently return true. The computation of the type of the CommonDuration type is performed at compile time. And so the run time cost of the comparison is at most 2 multiplications in addition to the comparison.

duration Arithmetic

Though the += and -= operators of duration must remain constrained to exactly convertible types, the same is not true of the binary + and - operators. For example, 1 frame_duration minus 33 std::milliseconds is exactly 1 std::duration<long long, 3000, 0>.

This paper proposes that the return type of the binary + and - operators be changed from the FinestDuration type to the CommonDuration type. When one duration type is exactly convertible to the other, the CommonDuration type and the FinestDuration type resolve to the same type.

Having exact duration comparison and arithmetic in his tool box, the standard library client can write some very useful duration-generic code which is robust, even in the face of yet-to-be-defined custom duration types. For example here is user-written code which converts one duration to another using "round to nearest" when the conversion is inexact, and "round to even" on a tie:

template <class TT, long long TPS, long long SPT>
inline
std::duration<TT, TPS, SPT>
abs(std::duration<TT, TPS, SPT> d)
{
    if (d.count() < 0)
        return -d;
    return d;
}

template <class ToDuration, class TT, long long TPS, long long SPT>
ToDuration
round_to_nearest(const std::duration<TT, TPS, SPT>& f)
{
    ToDuration t1 = duration_cast<ToDuration>(f);
    int sign = f.count() >= 0 ? 1 : -1;
    ToDuration t2 = t1 + ToDuration(1) * sign;
    auto d1 = abs(f - t1);
    auto d2 = abs(t2 - f);
    if (d1 == d2)
    {
        if (t1.count() % 2 == 0)
            return t1;
         return t2;
    }
    if (d1 < d2)
        return t1;
    return t2;
}

There are several interesting points to note about the above example code:

duration_cast

Because duration_cast is an often needed function when working with durations, and because it is both lengthy and easy to get wrong, we feel that it should be standardized. Without it, even converting between the standard-defined durations can be tedious and error prone (and even more so when converting among user-defined durations). For example, the following code has a subtle bug:

std::hours hr = ...;
std::minutes mn = hr.count() * std::hours::seconds_per_tick / std::minutes::seconds_per_tick;

The bug is that the above code is susceptible to overflow (on common platforms), even when hr is within the 292 year range. The corrected code is:

std::minutes mn = hr.count() * (std::hours::seconds_per_tick / std::minutes::seconds_per_tick);

The parenthesis reduces the constant multiplier down to a range which will not overflow as long as hr is within the 292 year limit. It also increases efficiency as we now have only one integral multiplication at run time instead of one run time multiplication and one run time division (the division is exact and done at compile time).

However one can not blindly apply parenthesis to every duration conversion expression. For example consider converting in the other direction (minutes to hours).

std::hours hr = mn.count() * (std::minutes::seconds_per_tick / std::hours::seconds_per_tick);

Now the expression is incorrect with the parentheses. The above always multiplies mn.count() by 0! (that's zero-exclamation, not zero-factorial)

Dropping the parentheses will yield the correct amount most of the time:

std::hours hr = mn.count() * std::minutes::seconds_per_tick / std::hours::seconds_per_tick;

However the above expression is again vulnerable to overflow. The safe (and efficient) expression is:

std::hours hr = mn.count() / (std::hours::seconds_per_tick / std::minutes::seconds_per_tick);

This drops the expense down to one run time division and avoids the possibility of overflow.

It is far easier and less error prone to implement this logic once and for all, and then let the client simply say:

std::hours hr = std::duration_cast<std::hours>(mn);

The simplicity of the above expression is readily apparent when you read the specification of duration_cast which consists of nine distinct formulas for computing the conversion. Each of those nine formulas is designed to compute the correct conversion with a minimum expense, and a minimum, if not zero, chance of overflow error. The correct formula is chosen at compile time and depends only upon the types being converted between, and not the values. This encapsulation is especially important when writing duration-generic code such as round_to_nearest where the types of the durations are not known until the algorithm is instantiated.

Proposed Wording

The differences in the proposed wording are written with respect to N2497. The changes look large, but they are largely mechanical. They reduce to a few fundamental steps:

  1. Modify all functions templated on a Duration to instead be templated on duration<TT, TPS, SPT>.
  2. Replace the Duration requirements with a duration class template.
  3. Replace the classes nanoseonds ... hours with duration typedefs.
  4. Replace FinestDuration with CommonDuration.
  5. Add duration_cast.
  6. Remove the Requires: paragraph from the duration comparison and binary + and - operators.
  7. Remove any wording associated with constraining the Duration template parameter.

The resulting specification is actually smaller (more text is removed than is added).

30.1.1 Template Parameter Names [thread.tmplparmname]

Throughout this clause, the names of template parameters are used to express type requirements.

The requirements for Duration parameters are specified in chapter 31 [time].

30.1.4 Timing Specifications [thread.timing_specifications]

Several functions described in this clause take an argument to specify a timeout. These timeouts are specified as either a Dduration or a Time Point type as specified in [time].

30.2 Threads [thread.threads]

...

<thread> synopsis


namespace std {

...

namespace this_thread
{
    ...
    void sleep(const system_time& abs_t);
    template <class Duration TT, long long TPS, long long SPT>
        void sleep(const Dduration<TT, TPS, SPT>& rel_t);

}  // this_thread

}  // std

30.2.2 Namespace this_thread [thread.threads.this]


namespace this_thread {

...
void sleep(const system_time& abs_t);
template <class Duration TT, long long TPS, long long SPT>
    void sleep(const Dduration<TT, TPS, SPT>& rel_t);

}  // this_thread

...


template <class Duration TT, long long TPS, long long SPT>
    void sleep(const Dduration<TT, TPS, SPT>& rel_t);
Effects:
The current thread blocks for at least the amount of time specified. If the resolution of Dduration<TT, TPS, SPT> is finer than the native resolution, the time is rounded to the next larger value that can be represented in the native resolution.
Synchronization:
None.
Throws:
Nothing.

30.3.2 TimedMutex requirements [thread.timedmutex.requirements]

To meet the TimedMutex requirements, types shall meet the Mutex requirements. In addition, the following requirements shall be met, where rel_time denotes a value of a type meeting the Dduration ([time.duration]) requirements or abs_time denotes a value of type system_time:

...

Precondition:
If the resolution of the Dduration is finer than the native resolution, the time is rounded up to the nearest native resolution.

...

30.3.2.1 Class timed_mutex [thread.timed.class]

namespace std {

class timed_mutex
{
public:
    timed_mutex();
    ~timed_mutex();

    timed_mutex(const timed_mutex&) = delete;
    timed_mutex& operator=(const timed_mutex&) = delete;

    void lock();
    bool try_lock();
    template <class Duration TT, long long TPS, long long SPT>
        bool timed_lock(const Dduration<TT, TPS, SPT>& rel_time);
    bool timed_lock(const system_time& abs_time);
    void unlock();

    typedef implementation-defined native_handle_type;  // See [thread.native]
    native_handle_type native_handle();                // See [thread.native]
};

}  // std
30.3.2.2 Class recursive_timed_mutex [thread.timed.recursive]

namespace std {

class recursive_timed_mutex
{
public:
    recursive_timed_mutex();
    ~recursive_timed_mutex();

    recursive_timed_mutex(const recursive_timed_mutex&) = delete;
    recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;

    void lock();
    bool try_lock();
    template <class Duration TT, long long TPS, long long SPT>
        bool timed_lock(const Dduration<TT, TPS, SPT>& rel_time);
    bool timed_lock(const system_time& abs_time);
    void unlock();

    typedef implementation-defined native_handle_type;  // See [thread.native]
    native_handle_type native_handle();                // See [thread.native]
};

}  // std
30.3.3.2 Class unique_lock [thread.lock.unique]

namespace std {

template <class Mutex>
class unique_lock
{
public:
    typedef Mutex mutex_type;

    unique_lock();
    explicit unique_lock(mutex_type& m);
    unique_lock(mutex_type& m, defer_lock_t);
    unique_lock(mutex_type& m, try_to_lock_t);
    unique_lock(mutex_type& m, adopt_lock_t);
    unique_lock(mutex_type& m, const system_time& abs_time);
    template <class Duration TT, long long TPS, long long SPT>
        unique_lock(mutex_type& m, const Dduration<TT, TPS, SPT>& rel_time);
    ~unique_lock();

    unique_lock(unique_lock const&) = delete;
    unique_lock& operator=(unique_lock const&) = delete;

    unique_lock(unique_lock&& u);
    unique_lock& operator=(unique_lock&& u);

    void lock();
    bool try_lock();

    template <class Duration TT, long long TPS, long long SPT>
        bool timed_lock(const Dduration<TT, TPS, SPT>& rel_t);
    bool timed_lock(const system_time& abs_time);

    void unlock();

    bool owns_lock() const;
    explicit operator bool () const;
    mutex_type* mutex() const;

    void swap(unique_lock&& u);
    mutex_type* release();

private:
    mutex_type* pm;  // for exposition only
    bool owns;       // for exposition only
};

...

}  // std

...


template <class Duration TT, long long TPS, long long SPT>
  unique_lock(mutex_type& m, const Dduration<TT, TPS, SPT>& rel_time);
Remarks:
The implementation shall ensure that only Duration types ([time]) will bind to this constructor.
Precondition:
If mutex_type is not a recursive mutex, then the current thread does not own the mutex.
Effects:
Constructs an object of type unique_lock and calls m.timed_lock(rel_time).
Postconditions:
pm == &m
owns == the result of the call to m.timed_lock(rel_time)
Throws:
Nothing.

...


template <class Duration TT, long long TPS, long long SPT>
   bool timed_lock(const Dduration<TT, TPS, SPT>& rel_t);
Effects:
pm->timed_lock(rel_t).
Returns:
The result of the call to timed_lock(rel_t).
Postconditions:
owns == the result of the call to timed_lock(rel_t).
Throws:
lock_error, if on entry owns is true or if pm == 0.

30.4.1 Class condition_variable [thread.condvar]


namespace std {

class condition_variable
{
public:

    condition_variable();
    ~condition_variable();

    condition_variable(const condition_variable&) = delete;
    condition_variable& operator=(const condition_variable&) = delete;

    void notify_one();
    void notify_all();
    void wait(unique_lock<mutex>& lock);
    template <class Predicate>
        void wait(unique_lock<mutex>& lock, Predicate pred);
    template <class Duration TT, long long TPS, long long SPT>
        bool timed_wait(unique_lock<mutex>& lock, const Dduration<TT, TPS, SPT>& rel_time);
    bool timed_wait(unique_lock<mutex>& lock, const system_time& abs_time);
    template <class Predicate>
        bool timed_wait(unique_lock<mutex>& lock, const system_time& abs_time,
                        Predicate pred);
    template <class Duration TT, long long TPS, long long SPT, class Predicate>
        bool timed_wait(unique_lock<mutex>& lock, const Dduration<TT, TPS, SPT>& rel_time,
                        Predicate pred);

    typedef implementation-defined native_handle_type;  // See [thread.native]
    native_handle_type native_handle();                // See [thread.native]
};

}  // std

...


template <class Duration TT, long long TPS, long long SPT>
    bool timed_wait(unique_lock<mutex>& lock, const Dduration<TT, TPS, SPT>& rel_time);
Effects:

As if:


timed_wait(lock, get_current_time() + rel_time)
Returns:
false if the call is returning because the time duration specified by rel_time has elapsed, true otherwise.

...


template <class Duration TT, long long TPS, long long SPT, class Predicate>
    bool timed_wait(unique_lock<mutex>& lock, const Dduration<TT, TPS, SPT>& rel_time,
                    Predicate pred);

30.4.2 Class condition_variable_any [thread.condvarany]


namespace std {

class condition_variable_any
{
public:
    condition_variable_any();
    ~condition_variable_any();

    condition_variable_any(const condition_variable_any&) = delete;
    condition_variable_any& operator=(const condition_variable_any&) = delete;

    void notify_one();
    void notify_all();
    template <class Lock>
        void wait(Lock& lock);
    template <class Lock, class Predicate>
        void wait(Lock& lock, Predicate pred);
    template <class Lock>
        bool timed_wait(Lock& lock, const system_time& abs_time);
    template <class Lock, class Duration TT, long long TPS, long long SPT>
        bool timed_wait(Lock& lock, const Dduration<TT, TPS, SPT>& rel_time);
    template <class Lock, class Predicate>
        bool timed_wait(Lock& lock, const system_time& abs_time,
                        Predicate pred);
    template <class Lock, class Duration TT, long long TPS, long long SPT, class Predicate>
        bool timed_wait(Lock& lock, const Dduration<TT, TPS, SPT>& rel_time, Predicate pred);

    typedef implementation-defined native_handle_type;  // See [thread.native]
    native_handle_type native_handle();                // See [thread.native]
};

}  // std

...


template <class Lock, class Duration TT, long long TPS, long long SPT>
    bool timed_wait(Lock& lock, const Dduration<TT, TPS, SPT>& rel_time);

...


template <class Lock, class Duration TT, long long TPS, long long SPT, class Predicate>
    bool timed_wait(Lock& lock, const Dduration<TT, TPS, SPT>& rel_time, Predicate pred);

Chapter 31   Date Time Library [time]

...

Throughout this clause, the names of template parameters are used to express type requirements. Parameter names Duration, LhsDuration and RhsDuration express the Duration requirements ([time.duration.requirements]). For all non-member functions in this clause that are templated on Duration types, the implementation shall constrain these function templates such that they will only instantiate for Duration types.

The Dduration types shall represent durations of at least ± 292 years. The system_time type shall represent times at least within the range epoch + 292 years.

Header <date_time> Synopsis


namespace std {

// duration types
template <class TickType, long long TicksPerSecond, long long SecondsPerTick> class duration;

class typedef duration<int_least64_t, 1000L * 1000 * 1000,    0> nanoseconds;
class typedef duration<int_least55_t,        1000L * 1000,    0> microseconds;
class typedef duration<int_least45_t,                1000,    0> milliseconds;
class typedef duration<int_least35_t,                   1,    1> seconds;
class typedef duration<int_least29_t,                   0,   60> minutes;
class typedef duration<int_least23_t,                   0, 3600> hours;

template <class ToDuration, class TT, long long TPS, long long SPT>
  ToDuration duration_cast(const duration<TT, TPS, SPT>& fd);

// timepoint type
class system_time;

// non-member functions ([time.nonmembers])
system_time get_system_time();

template<typename Duration TT, long long TPS, long long SPT>
  system_time operator+(const Dduration<TT, TPS, SPT>& td, const system_time& rhs);

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator==(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator!=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator< (const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator<=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator> (const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator>=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  FinestCommonDuration operator+(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs)
template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  FinestCommonDuration operator-(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs)

template <class Duration TT, long long TPS, long long SPT>
  Dduration<TT, TPS, SPT> operator*(Dduration<TT, TPS, SPT> lhs, long rhs)
template <class Duration TT, long long TPS, long long SPT>
  Dduration<TT, TPS, SPT> operator*(long lhs, Dduration<TT, TPS, SPT> rhs)

template <class Duration TT, long long TPS, long long SPT>
  Dduration<TT, TPS, SPT> operator/(Dduration<TT, TPS, SPT> lhs, long rhs)
}  // std

The use of the int_leastN_t types above does not indicate the existence of these typedefs. This use indicates that the type chosen by the implementation must be a signed integral type of at least the size indicated.

Through this clause, type FinestDuration is whichever of LhsDuration or RhsDuration has the finest resolution. If their resolutions are the same, FinestDuration is LhsDuration.

The CommonDuration type of duration<T1, TPS1, SPT1> and duration<T2, TPS2, SPT2> is:

[Note: Both duration<T1, TPS1, SPT1> and duration<T2, TPS2, SPT2> are exactly convertible to their CommonDuration type. --end note]

31.1 Duration requirements Class template duration [time.duration.requirements]

This subclause describes requirements on duration types used to instantiate templates defined the class template duration used to represent time durations in the C++ Standard Library.

Objects of duration types provide time length values which can be positive or negative. Duration types provide comparison and arithmetic operations on those values.

Template definitions in the C++ Standard Library refer to the named Duration requirements for duration types whose details are specified below.

A duration type E is said to be exactly convertible to another duration type D if and only if:

A duration type shall be EqualityComparable, LessThanComparable, CopyConstructible, DefaultConstructible, CopyAssignable, Swappable, and Destructible. In addition, it must meet the requirements for well-formed expressions specified in the following table, where D and E are duration types, d denotes a value of type D, d0 denotes d.tick_count() at entry into the function, e denotes a const value of type E, c denotes a long value. E shall be exactly convertible to D (diagnostic required).

Remove this table:

expression return type return value
D::tick_type implementation defined  
D::ticks_per_second D::tick_type The number of ticks per second, or 0 for types for which the number of ticks per second is less than 1.
D::seconds_per_tick D::tick_type The number of seconds per tick, or 0 for types for which the number of seconds per tick is less than 1.
D::is_subsecond bool seconds_per_tick == 0
d.count() D::tick_type The most recent value established by a non-const function's postcondition.
-d D D(-d.tick_count())
    postcondition
d -= e D& d.tick_count() == d0 - x, where x is e.tick_count() converted to the resolution of D.
d += e D& d.tick_count() == d0 + x, where x is e.tick_count() converted to the resolution of D.
d /= c D& d.tick_count() == d0 / c
d *= c D& d.tick_count() == d0 * c

template <class TickType, long long TicksPerSecond, long long SecondsPerTick>
class duration
{
    TickType ticks;  // exposition only
public:
    // traits information
    typedef TickType tick_type;
    static const long long ticks_per_second = TicksPerSecond;
    static const long long seconds_per_tick = SecondsPerTick;
    static const bool is_subsecond = seconds_per_tick == 0;

    static_assert(ticks_per_second >  1 && seconds_per_tick == 0 ||
                  ticks_per_second == 0 && seconds_per_tick >  1 ||
                  ticks_per_second == 1 && seconds_per_tick == 1,
                  "duration has inconsistent type");

    duration();
    duration(const tick_type& tick);

    // conversions
    template <class TT, long long TPS, long long SPT>
      duration(const duration<TT, TPS, SPT>& d);

    // observer

    tick_type count() const;

    // arithmetic

    template <class TT, long long TPS, long long SPT>
      duration&
      operator-=(const duration<TT, TPS, SPT>& d);

    template <class TT, long long TPS, long long SPT>
      duration&
      operator+=(const duration<TT, TPS, SPT>& d);

    duration operator-() const;

    duration& operator*=(tick_type rhs);
    duration& operator/=(tick_type rhs);
};

The template parameter TickType must behave as an arithmetic type (supporting binary +, -, *, /, unary -, the compound assignment operators +=, -=, *=, /=, support all six comparison operators) and be explicitly convertible to and from long long.


duration();
Effects:
Default constructs an object of type duration.
Postcondition:
count() == tick_type();

duration(const tick_type& t);
Effects:
Constructs an object of type duration.
Postcondition:
count() == t;

template <class TT, long long TPS, long long SPT>
  duration(const duration<TT, TPS, SPT>& d);
Requires:
duration<TT, TPS, SPT> is exactly convertible to this duration type (diagnostic required).
Effects:
Constructs an object of type duration.
Postcondition:
count() == duration_cast<duration>(d).count()

tick_type count() const;
Returns:
ticks

template <class TT, long long TPS, long long SPT>
  duration&
  operator-=(const duration<TT, TPS, SPT>& d);
Requires:
duration<TT, TPS, SPT> is exactly convertible to this duration type (diagnostic required).
Effects:
ticks -= duration_cast<duration>(d).count().
Returns:
*this.

template <class TT, long long TPS, long long SPT>
  duration&
  operator+=(const duration<TT, TPS, SPT>& d);
Requires:
duration<TT, TPS, SPT> is exactly convertible to this duration type (diagnostic required).
Effects:
ticks += duration_cast<duration>(d).count().
Returns:
*this.

duration operator-() const;
Returns:
-ticks.

duration& operator*=(tick_type rhs);
Effects:
ticks *= rhs.
Returns:
*this.

duration& operator/=(tick_type rhs);
Effects:
ticks /= rhs.
Returns:
*this.

Remove section 31.2 Class nanoseconds [time.nanoseconds].

Remove section 31.3 Class microseconds [time.microseconds].

Remove section 31.4 Class milliseconds [time.milliseconds].

Remove section 31.5 Class seconds [time.seconds].

Remove section 31.6 Class minutes [time.minutes].

Remove section 31.7 Class hours [time.hours].

Add section 31.2 duration_cast [duration.cast]

31.2 duration_cast [duration.cast]


template <class To, class TT, long long TPS, long long SPT>
  To duration_cast(const duration<TT, TPS, SPT>& fd);
Requires:
To is a duration.
Effects:

Converts duration<TT, TPS, SPT> to To either exactly, or if the conversion is inexact, rounds according to the rules for division of the tick_type being used for the conversion (typically round towards zero when converting among standard-defined duration types). The computation is done using the tick_type of the duration of finer resolution among To and duration<TT, TPS, SPT>. If duration<TT, TPS, SPT> is exactly convertible to To, but not vice-versa, exactly one multiplication is used for the conversion. If To is exactly convertible to duration<TT, TPS, SPT>, but not vice-versa, exactly one division is used for the conversion. If duration<TT, TPS, SPT> is exactly convertible to To, and vice-versa, fd.count() is simply explicitly converted to To::tick_type and used to construct the result. If neither duration<TT, TPS, SPT> is exactly convertible to To, nor vice-versa, one multiplication and one division is used for the conversion. In this case the constants used in the conversion are first reduced to a minimum value in order to reduce the chances of overflow. Only explicit conversions are used between To::tick_type and duration<TT, TPS, SPT>::tick_type.

Returns:

In the following table, FinerTickType is the same type as duration<TT, TPS, SPT>::tick_type if the resolution of duration<TT, TPS, SPT> is finer than the resolution of To, else FinerTickType is the same type as To::tick_type. From is the same type as duration<TT, TPS, SPT>. GCD_SPT is an integral constant with the value of the greatest common divisor of duration<TT, TPS, SPT>::seconds_per_tick and To::seconds_per_tick. GCD_TPS is an integral constant with the value of the greatest common divisor of duration<TT, TPS, SPT>::ticks_per_second and To::ticks_per_second.

duration_cast return value
From:: is_subsecond To::
is_subsecond
From is
exactly
convertible
to To
To is
exactly
convertible
to From
Comments / Returns:
false false false false static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(From::seconds_per_tick/GCD_SPT)/
static_cast<FinerTickType>(To::seconds_per_tick/GCD_SPT))
false false false true static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())/
static_cast<FinerTickType>(To::seconds_per_tick/From::seconds_per_tick))
false false true false static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(From::seconds_per_tick/To::seconds_per_tick))
false false true true static_cast<typename To::tick_type>(fd.count())
false true false false Not possible
false true false true Not possible
false true true false static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(From::seconds_per_tick*To::ticks_per_second))
false true true true Not possible
true false false false Not possible
true false false true static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())/
static_cast<FinerTickType>(From::ticks_per_second*To::seconds_per_tick))
true false true false Not possible
true false true true Not possible
true true false false static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(To::ticks_per_second/GCD_TPS)/
static_cast<FinerTickType>(From::ticks_per_second/GCD_TPS))
true true false true static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())/
static_cast<FinerTickType>(From::ticks_per_second/To::ticks_per_second))
true true true false static_cast<typename To::tick_type>(
static_cast<FinerTickType>(fd.count())*
static_cast<FinerTickType>(To::ticks_per_second/From::ticks_per_second))
true true true true static_cast<typename To::tick_type>(fd.count())

31.8 Class system_time [time.system]

...


class system_time
{
public:

    system_time();
    explicit system_time(time_t, nanoseconds ns=0);

    time_t seconds_since_epoch() const;
    nanoseconds nanoseconds_since_epoch() const;

    // traits
    typedef implementation defined tick_type;
    static const tick_type ticks_per_second = nanoseconds::ticks_per_second;
    static const tick_type seconds_per_tick = 0;
    static const bool is_subsecond = true;

    // comparison functions
    bool operator==(const system_time& rhs) const;
    bool operator!=(const system_time& rhs) const;
    bool operator>(const system_time& rhs) const;
    bool operator>=(const system_time& rhs) const;
    bool operator<(const system_time& rhs) const;
    bool operator<=(const system_time& rhs) const;

    // arithmetic functions
    nanoseconds operator-(const system_time& rhs) const

    template<typename Duration TT, long long TPS, long long SPT>
    system_time operator+(const Dduration<TT, TPS, SPT>& td) const;

    template<typename Duration TT, long long TPS, long long SPT>
    system_time& operator+=(const Dduration<TT, TPS, SPT>& td);

    template<typename Duration TT, long long TPS, long long SPT>
    system_time operator-(const Dduration<TT, TPS, SPT>& td) const;

    template<typename Duration TT, long long TPS, long long SPT>
    system_time& operator-=(const Dduration<TT, TPS, SPT>& td)
};

...


template<typename Duration TT, long long TPS, long long SPT>
  system_time operator+(const Dduration<TT, TPS, SPT>& td) const;

...


template<typename Duration TT, long long TPS, long long SPT>
    system_time& operator+=(const Dduration<TT, TPS, SPT>& td);

...


template<typename Duration TT, long long TPS, long long SPT>
  system_time operator-(const Dduration<TT, TPS, SPT>& td) const;

...


template<typename Duration TT, long long TPS, long long SPT>
  system_time& operator-=(const Dduration<TT, TPS, SPT>& td)

31.9 Non-member functions [time.nonmembers]

...


template<typename Duration TT, long long TPS, long long SPT>
  system_time operator+(const Dduration<TT, TPS, SPT>& td, const system_time& rhs);

...


template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator==(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
FinestCommonDuration(lhs).count()) == FinestCommonDuration(rhs).count())

See [time] for description of FinestCommonDuration.

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator!=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
!(lhs==rhs).

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator< (const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
FinestCommonDuration(lhs).count()) < FinestCommonDuration(rhs).count())

See [time] for description of FinestCommonDuration.

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator<=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
!(rhs<lhs).

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator> (const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
rhs<lhs.

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  bool operator>=(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs);
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
!(lhs<rhs).

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  FinestCommonDuration operator+(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs)
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
FinestCommonDuration(lhs).count())+FinestCommonDuration(rhs).count())

See [time] for description of FinestCommonDuration.

template <class LhsDuration TT1, long long TPS1, long long SPT1, class RhsDuration TT2, long long TPS2, long long SPT2>
  FinestCommonDuration operator-(const LhsDduration<TT1, TPS1, SPT1>& lhs, const RhsDduration<TT2, TPS2, SPT2>& rhs)
Requires:
Either LhsDuration shall be exactly convertible to RhsDuration or RhsDuration shall be exactly convertible to LhsDuration (diagnostic required).
Returns:
FinestCommonDuration(lhs).count())-FinestCommonDuration(rhs).count())

See [time] for description of FinestCommonDuration.

template <class Duration TT, long long TPS, long long SPT>
  Dduration<TT, TPS, SPT> operator*(Dduration<TT, TPS, SPT> lhs, long typename duration<TT, TPS, SPT>::tick_type rhs)
Returns:
lhs *= rhs.

template <class Duration TT, long long TPS, long long SPT>
   Dduration<TT, TPS, SPT> operator*(long typename duration<TT, TPS, SPT>::tick_type lhs, Dduration<TT, TPS, SPT> rhs)
Returns:
rhs *= lhs.

template <class Duration TT, long long TPS, long long SPT>
   Dduration<TT, TPS, SPT> operator/(Dduration<TT, TPS, SPT> lhs, long typename duration<TT, TPS, SPT>::tick_type rhs)
Returns:
lhs /= rhs.

Acknowledgments

Much thanks to Daniel Krügler for the excellent review and suggestions.