// bsls_timeinterval.h                                                -*-C++-*-
#ifndef INCLUDED_BSLS_TIMEINTERVAL
#define INCLUDED_BSLS_TIMEINTERVAL

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a representation of a time interval.
//
//@CLASSES:
//   bsls::TimeInterval: time interval with nanosecond resolution
//
//@SEE_ALSO: bdlt_time, bdlt_datetimeinterval
//
//@DESCRIPTION: This component provides a value-semantic type,
// `bsls::TimeInterval`, that is capable of representing a signed time
// interval with nanosecond resolution.
//
///Representation
///--------------
// A time interval has a value that is independent of its representation.
// Conceptually, a time interval may be thought of as a signed,
// arbitrary-precision floating-point number denominated in seconds (or in
// days, or in fortnights, if one prefers).  A `bsls::TimeInterval` represents
// this value as two fields: seconds and nanoseconds.  In the "canonical
// representation" of a time interval, the `seconds` field may have any 64-bit
// signed integer value, with the `nanoseconds` field limited to the range
// `[ -999,999,999..999,999,999 ]`, and with the additional constraint that the
// two fields are either both non-negative or both non-positive.  When setting
// the value of a time interval via its two-field representation, any integer
// value may be used in either field, with the constraint that the resulting
// number of seconds be representable as a 64-bit signed integer.  Similarly,
// the two field values may be accessed in the canonical representation using
// the `seconds` and `nanoseconds` methods.
//
// Binary arithmetic and relational operators taking two `bsls::TimeInterval`
// objects, or a `bsls::TimeInterval` object and a `double`, are provided.  A
// `double` operand, representing a real number of seconds, is first converted
// to a `bsls::TimeInterval` object before performing the operation.  Under
// such circumstances, the fractional part of the `double`, if any, is rounded
// to the nearest whole number of nanoseconds.
//
///User-Defined Literals
///---------------------
// The user-defined literal `operator""_h`, `operator""_min`,
// `operator""_s`, `operator""_ms`, `operator""_us` and `operator""_ns` are
// declared for the `TimeInterval`.  These suffixes can be applied to integer
// literals and allow to create an object, representing the specified number of
// hours, minutes, seconds, milliseconds, microseconds or nanoseconds
// respectively:
// ```
// using namespace bsls::TimeIntervalLiterals;
//
// bsls::TimeInterval i0 = 10_h;
// assert(36000   == i0.seconds()    );
// assert(0       == i0.nanoseconds());
//
// bsls::TimeInterval i1 = 10001_ms;
// assert(10      == i1.seconds()    );
// assert(1000000 == i1.nanoseconds());
//
// bsls::TimeInterval i2 = 100_ns;
// assert(0       == i2.seconds()    );
// assert(100     == i2.nanoseconds());
// ```
// The operators providing literals are available in the
// `BloombergLP::bsls::literals::TimeIntervalLiterals` namespace (where
// `literals` and `TimeIntervalLiterals` are both inline namespaces). Because
// of inline namespaces, there are several viable options for a using
// declaration, but *we* *recommend*
// `using namespace bsls::TimeIntervalLiterals`, which minimizes the scope of
// the using declaration.
//
// Note that user defined literals can be used only if the compiler supports
// the C++11 standard.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Creating and Modifying a `bsls::TimeInterval`
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The following example demonstrates how to create and manipulate a
// `bsls::TimeInterval` object.
//
// First, we default construct a `TimeInterval` object, `interval`:
// ```
// bsls::TimeInterval interval;
//
// assert(0 == interval.seconds());
// assert(0 == interval.nanoseconds());
// ```
// Next, we set the value of `interval` to 1 second and 10 nanoseconds (a time
// interval of 1000000010 nanoseconds):
// ```
// interval.setInterval(1, 10);
//
// assert( 1 == interval.seconds());
// assert(10 == interval.nanoseconds());
// ```
// Then, we add 3 seconds to `interval`:
// ```
// interval.addInterval(3, 0);
//
// assert( 4 == interval.seconds());
// assert(10 == interval.nanoseconds());
// ```
// Next, we create a copy of `interval`, `intervalPrime`:
// ```
// bsls::TimeInterval intervalPrime(interval);
//
// assert(intervalPrime == interval);
// ```
// Finally, we assign 3.14 seconds to `intervalPrime`, and then add 2.73
// seconds more:
// ```
// intervalPrime =  3.14;
// intervalPrime += 2.73;
//
// assert(        5 == intervalPrime.seconds());
// assert(870000000 == intervalPrime.nanoseconds());
// ```

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>
#include <bsls_preconditions.h>
#include <bsls_types.h>

#if BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#include <chrono>
#include <type_traits>
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

#include <iosfwd>
#include <limits.h>  // 'LLONG_MIN', 'LLONG_MAX'

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bsls_nativestd.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

// BDE_VERIFY pragma: push
// BDE_VERIFY pragma: -FABC01  // 'add*' operations are ordered by time unit

#if BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#define BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

namespace BloombergLP {
namespace bsls {

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS

                  // =======================================
                  // template struct TimeInterval_IsDuration
                  // =======================================

/// Template metafunction to determine if the specified `TYPE` is a
/// `std::chrono::duration`.
template <class TYPE>
struct TimeInterval_IsDuration : std::false_type {
};

/// Template metafunction specialization for `std::chrono::duration` types.
template <class REP, class PER>
struct TimeInterval_IsDuration<std::chrono::duration<REP, PER> >
                                                             : std::true_type {
};

                   // =============================
                   // struct TimeInterval_RepTraits
                   // =============================

/// Trait metafunction that determines whether the specified `REP` type is
/// considered a floating point type.
template <class REP>
struct TimeInterval_RepTraits {

    static const bool k_IS_FLOAT =
                              std::is_floating_point<REP>::value
                           || std::chrono::treat_as_floating_point<REP>::value;
    // This compile time constant is 'true' if the 'REP' (template type
    // argument) is indicated to be a floating point type by the underlying
    // library.  Otherwise, if the underlying library does not consider 'REP'
    // floating point anywhere (see note), 'k_IS_FLOAT' is 'false'.  Note that
    // to cover all scenarios we need to examine *two* traits.  The first one
    // that tells if the representation type is a floating point (arithmetic)
    // type or not (from '<type_traits>'), and another that tells that
    // representation type is considered a floating point type from the
    // 'std::chrono' perspective.  Formally the second one just duplicates the
    // first one, but *in theory* either can be specialized to be true,
    // independently.  Also note that conversions from 'std::chrono::duration'
    // with a floating point representation to 'bsls::TimeInterval' are *not*
    // supported for now.
};

                   // ==================================
                   // struct TimeInterval_DurationTraits
                   // ==================================

/// Trait metafunction that determines whether the
/// `std::chrono::duration<REP, PERIOD>` object can be converted to
/// `bsls::TimeInterval` either implicitly or explicitly.
template <class REP, class PERIOD>
struct TimeInterval_DurationTraits {

    /// This compile time constant is `true` if the `REP` (template type
    /// argument) is indicated to be a floating point type by the underlying
    /// library.  Otherwise, if the underlying library does not consider
    /// `REP` floating point anywhere (see `TimeInterval_RepTraits`),
    /// `k_IS_FLOAT` is `false`.
    static const bool k_IS_FLOAT    = TimeInterval_RepTraits<REP>::k_IS_FLOAT;

    /// This compile time constant is `true` if any possible value of an
    /// 'std::chrono::duration<REP, PERIOD> object will be represented by
    /// integer nanoseconds (fractions of nanoseconds are not required).
    /// Otherwise this value is `false`.
    static const bool k_IS_IMPLICIT = (std::nano::den % PERIOD::den == 0);

    /// This compile time constant is `true` if
    /// `std::chrono::duration<REP, PERIOD>` objects will be implicitly
    /// converted to `TimeInterval`, and `false` otherwise.  This value is
    /// intended to be used with `enable_if` to enable implicitly converting
    /// function overloads.  Note that this boolean value is mutually
    /// exclusive with `k_EXPLICIT_CONVERION_ENABLED` as in they will never
    /// be both `true` for the same `REP" and `PERIOD', but they may be both
    /// `false` for floats.
    static const bool k_IMPLICIT_CONVERSION_ENABLED =
                                                 !k_IS_FLOAT &&  k_IS_IMPLICIT;

    /// This compile time constant is `true` if
    /// `std::chrono::duration<REP, PERIOD>` objects can be explicitly
    /// converted to `TimeInterval`, and `false` otherwise.  This value is
    /// intended to be used with `enable_if` to enable explicitly converting
    /// function overloads.  Note that this boolean value is mutually
    /// exclusive with `k_IMPLICIT_CONVERION_ENABLED` as in they will never
    /// be both `true` for the same `REP" and `PERIOD', but they may be both
    /// `false` for floats.
    static const bool k_EXPLICIT_CONVERSION_ENABLED =
                                                 !k_IS_FLOAT && !k_IS_IMPLICIT;
};

#endif
                        // ==================
                        // class TimeInterval
                        // ==================

/// Each instance of this value-semantic type represents a time interval
/// with nanosecond resolution.  In the "canonical representation" of a time
/// interval, the `seconds` field may have any 64-bit signed integer value,
/// with the `nanoseconds` field limited to the range
/// `[ -999,999,999..999,999,999 ]`, and with the additional constraint that
/// the two fields are either both non-negative or both non-positive.
class TimeInterval {

    // PRIVATE TYPES
    enum {
        k_MILLISECS_PER_SEC     = 1000,        // one thousand

        k_MICROSECS_PER_SEC     = 1000000,     // one million

        k_NANOSECS_PER_MICROSEC = 1000,        // one thousand

        k_NANOSECS_PER_MILLISEC = 1000000,     // one million

        k_NANOSECS_PER_SEC      = 1000000000,  // one billion

        k_SECONDS_PER_MINUTE    = 60,

        k_SECONDS_PER_HOUR      = 60 * k_SECONDS_PER_MINUTE,

        k_SECONDS_PER_DAY       = 24 * k_SECONDS_PER_HOUR
    };

    // DATA
    bsls::Types::Int64 d_seconds;      // field for seconds
    int                d_nanoseconds;  // field for nanoseconds

    // PRIVATE CLASS METHODS

    /// Return `true` if the sum of the specified `lhs` and `rhs` can be
    /// represented using a 64-bit signed integer, and `false` otherwise.
    BSLS_KEYWORD_CONSTEXPR
    static bool isSumValidInt64(bsls::Types::Int64 lhs,
                                bsls::Types::Int64 rhs);

  public:
    // CLASS METHODS

    /// Return `true` if a `TimeInterval` object can be constructed from the
    /// specified `seconds` and `nanoseconds`, and `false` otherwise.  A
    /// time interval can be constructed from `seconds` and `nanoseconds` if
    /// their sum results in a time interval whose total number of seconds
    /// can be represented with a 64-bit signed integer.
    BSLS_KEYWORD_CONSTEXPR
    static bool isValid(bsls::Types::Int64 seconds,
                        int                nanoseconds);

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
    /// Return `true` if a `TimeInterval` object can be constructed from the
    /// specified `duration`, and `false` otherwise.  A time interval can be
    /// constructed from `duration` if duration's value converted to seconds
    /// can be represented with a 64-bit signed integer.
    template <class REP, class PERIOD>
    static bool isValid(const std::chrono::duration<REP, PERIOD>& duration);
#endif

                                  // Aspects

    /// Return the maximum valid BDEX format version, as indicated by the
    /// specified `versionSelector`, to be passed to the `bdexStreamOut`
    /// method.  Note that it is highly recommended that `versionSelector`
    /// be formatted as "YYYYMMDD", a date representation.  Also note that
    /// `versionSelector` should be a *compile*-time-chosen value that
    /// selects a format version supported by both externalizer and
    /// unexternalizer.  See the `bslx` package-level documentation for more
    /// information on BDEX streaming of value-semantic types and
    /// containers.
    static int maxSupportedBdexVersion(int versionSelector);

    // CREATORS

    /// Create a time interval having the value of 0 seconds and 0
    /// nanoseconds.
    BSLS_KEYWORD_CONSTEXPR
    TimeInterval();

    /// Create a time interval having the value given by the sum of the
    /// specified integral number of `seconds` and `nanoseconds`.  The
    /// behavior is undefined unless the total number of seconds in the
    /// resulting time interval can be represented with a 64-bit signed
    /// integer (see `isValid`).  Note that there is no restriction on the
    /// sign or magnitude of either argument except that they must not
    /// violate the method's preconditions.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    TimeInterval(bsls::Types::Int64 seconds, int nanoseconds);

    /// Create a time interval having the value represented by the specified
    /// real number of `seconds`.  The fractional part of `seconds`, if any,
    /// is rounded to the nearest whole number of nanoseconds.  The
    /// behavior is undefined unless the total number of seconds in the
    /// resulting time interval can be represented with a 64-bit signed
    /// integer.
    explicit TimeInterval(double seconds);

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
    /// Create a time interval having the value represented by the specified
    /// `duration`.  Only integer representations of `duration` are
    /// supported (i.e. `REP_TYPE` is integer). If the `duration` can be
    /// represented *exactly* by an integer nanoseconds this constructor
    /// will be implicit.  Otherwise, the constructor will be explicit.  The
    /// behavior is undefined unless the `duration` can be converted to a
    /// valid `TimeInterval` object, whose `seconds` field may have any
    /// 64-bit signed integer value and `nanoseconds` field limited to the
    /// range `[ -999,999,999..999,999,999 ]`.  Note that the current
    /// implementation of the lossy conversions (e.g., fractions of a
    /// nanosecond) truncates the values towards zero, however this behavior
    /// may change without notice in the future, so *do not* rely on this.
    template <class REP_TYPE, class PERIOD_TYPE>
    BSLS_KEYWORD_CONSTEXPR_CPP14 TimeInterval(
        const std::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration,
    typename std::enable_if<TimeInterval_DurationTraits<
                                   REP_TYPE,
                                   PERIOD_TYPE>::k_IMPLICIT_CONVERSION_ENABLED,
                            int>::type * = 0);
    template <class REP_TYPE, class PERIOD_TYPE>
    explicit BSLS_KEYWORD_CONSTEXPR_CPP14 TimeInterval(
        const std::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration,
    typename std::enable_if<TimeInterval_DurationTraits<
                                   REP_TYPE,
                                   PERIOD_TYPE>::k_EXPLICIT_CONVERSION_ENABLED,
                            int>::type * = 0);
#endif

    //! TimeInterval(const TimeInterval& original) = default;
        // Create a time interval having the value of the specified 'original'
        // time interval.  Note that this trivial copy constructor is
        // generated by the compiler.

    //! ~TimeInterval() = default;
        // Destroy this time interval object.  Note that this trivial
        // destructor is generated by the compiler.

    // MANIPULATORS

                                  // Operator Overloads

    //! TimeInterval& operator=(const TimeInterval& rhs) = default;
        // Assign to this time interval the value of the specified 'rhs' time
        // interval, and return a reference providing modifiable access to this
        // object.  Note that this trivial assignment operation is generated by
        // the compiler.

    /// Assign to this time interval the value of the specified `rhs` real
    /// number of seconds, and return a reference providing modifiable
    /// access to this object.  The fractional part of `rhs`, if any, is
    /// rounded to the nearest whole number of nanoseconds.  The behavior
    /// is undefined unless `rhs` can be converted to a valid
    /// `TimeInterval` object.
    TimeInterval& operator=(double rhs);

    /// Add to this time interval the value of the specified `rhs` time
    /// interval, and return a reference providing modifiable access to
    /// this object.  The behavior is undefined unless the total number of
    /// seconds in the resulting time interval can be represented with a
    /// 64-bit signed integer.
    TimeInterval& operator+=(const TimeInterval& rhs);

    /// Add to this time interval the value of the specified `rhs` real
    /// number of seconds, and return a reference providing modifiable
    /// access to this object.  The fractional part of `rhs`, if any, is
    /// rounded to the nearest whole number of nanoseconds before being
    /// added to this object.  The behavior is undefined unless `rhs` can
    /// be converted to a valid `TimeInterval` object, and the total number
    /// of seconds in the resulting time interval can be represented with a
    /// 64-bit signed integer.
    TimeInterval& operator+=(double rhs);

    /// Subtract from this time interval the value of the specified `rhs`
    /// time interval, and return a reference providing modifiable access to
    /// this object.  The behavior is undefined unless
    /// `LLONG_MIN != rhs.seconds()`, and the total number of seconds in the
    /// resulting time interval can be represented with a 64-bit signed
    /// integer.
    TimeInterval& operator-=(const TimeInterval& rhs);

    /// Subtract from this time interval the value of the specified `rhs`
    /// real number of seconds, and return a reference providing modifiable
    /// access to this object.  The fractional part of `rhs`, if any, is
    /// rounded to the nearest whole number of nanoseconds before being
    /// subtracted from this object.  The behavior is undefined unless
    /// `rhs` can be converted to a valid `TimeInterval` object whose
    /// `seconds` field is greater than `LLONG_MIN`, and the total number of
    /// seconds in the resulting time interval can be represented with a
    /// 64-bit signed integer.
    TimeInterval& operator-=(double rhs);

                                  // Add Operations

    /// Add to this time interval the number of seconds represented by the
    /// specified integral number of `days`, and return a reference
    /// providing modifiable access to this object.  The behavior is
    /// undefined unless the number of seconds in `days`, and the total
    /// number of seconds in the resulting time interval, can both be
    /// represented with 64-bit signed integers.  Note that `days` may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    TimeInterval& addDays(bsls::Types::Int64 days);

    /// Add to this time interval the number of seconds represented by the
    /// specified integral number of `hours`, and return a reference
    /// providing modifiable access to this object.  The behavior is
    /// undefined unless the number of seconds in `hours`, and the total
    /// number of seconds in the resulting time interval, can both be
    /// represented with 64-bit signed integers.  Note that `hours` may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    TimeInterval& addHours(bsls::Types::Int64 hours);

    /// Add to this time interval the number of seconds represented by the
    /// specified integral number of `minutes`, and return a reference
    /// providing modifiable access to this object.  The behavior is
    /// undefined unless the number of seconds in `minutes`, and the total
    /// number of seconds in the resulting time interval, can both be
    /// represented with 64-bit signed integers.  Note that `minutes` may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    TimeInterval& addMinutes(bsls::Types::Int64 minutes);

    /// Add to this time interval the specified integral number of
    /// `seconds`, and return a reference providing modifiable access to
    /// this object.  The behavior is undefined unless the total number of
    /// seconds in the resulting time interval can be represented with a
    /// 64-bit signed integer.  Note that `seconds` may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    TimeInterval& addSeconds(bsls::Types::Int64 seconds);

    /// Add to this time interval the specified integral number of
    /// `milliseconds`, and return a reference providing modifiable access
    /// to this object.  The behavior is undefined unless the total number
    /// of seconds in the resulting time interval can be represented with a
    /// 64-bit signed integer.  Note that `milliseconds` may be negative.
    TimeInterval& addMilliseconds(bsls::Types::Int64 milliseconds);

    /// Add to this time interval the specified integral number of
    /// `microseconds`, and return a reference providing modifiable access
    /// to this object.  The behavior is undefined unless the total number
    /// of seconds in the resulting time interval can be represented with a
    /// 64-bit signed integer.  Note that `microseconds` may be negative.
    TimeInterval& addMicroseconds(bsls::Types::Int64 microseconds);

    /// Add to this time interval the specified integral number of
    /// `nanoseconds`, and return a reference providing modifiable access to
    /// this object.  The behavior is undefined unless the total number of
    /// seconds in the resulting time interval can be represented with a
    /// 64-bit signed integer.  Note that `nanoseconds` may be negative.
    TimeInterval& addNanoseconds(bsls::Types::Int64 nanoseconds);

                                  // Set Operations

    /// Set the overall value of this object to indicate the specified
    /// integral number of `days`.  The behavior is undefined unless the
    /// number of seconds in `days` can be represented with a 64-bit signed
    /// integer.  Note that `days` may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setTotalDays(bsls::Types::Int64 days);

    /// Set the overall value of this object to indicate the specified
    /// integral number of `hours`.  The behavior is undefined unless the
    /// number of seconds in `hours` can be represented with a 64-bit signed
    /// integer.  Note that `hours` may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setTotalHours(bsls::Types::Int64 hours);

    /// Set the overall value of this object to indicate the specified
    /// integral number of `minutes`.  The behavior is undefined unless the
    /// number of seconds in `minutes` can be represented with a 64-bit
    /// signed integer.  Note that `minutes` may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setTotalMinutes(bsls::Types::Int64 minutes);

    /// Set the overall value of this object to indicate the specified
    /// integral number of `seconds`.  Note that `seconds` may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setTotalSeconds(bsls::Types::Int64 seconds);

    /// Set the overall value of this object to indicate the specified
    /// integral number of `milliseconds`.  Note that `milliseconds` may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setTotalMilliseconds(bsls::Types::Int64 milliseconds);

    /// Set the overall value of this object to indicate the specified
    /// integral number of `microseconds`.  Note that `microseconds` may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setTotalMicroseconds(bsls::Types::Int64 microseconds);

    /// Set the overall value of this object to indicate the specified
    /// integral number of `nanoseconds`.  Note that `nanoseconds` may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setTotalNanoseconds(bsls::Types::Int64 nanoseconds);

                                  // Time-Interval-Based Manipulators

    /// Add to this time interval the specified integral number of
    /// `seconds`, and the optionally specified integral number of
    /// `nanoseconds`.  If unspecified, `nanoseconds` is 0.  Return a
    /// reference providing modifiable access to this object.  The behavior
    /// is undefined unless `seconds() + seconds`, and the total number of
    /// seconds in the resulting time interval, can both be represented with
    /// 64-bit signed integers.
    TimeInterval& addInterval(bsls::Types::Int64 seconds, int nanoseconds = 0);

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
    /// Add to this time interval the specified `duration`.  Return a
    /// reference providing modifiable access to this object.  The behavior
    /// is undefined unless the `duration` can be converted to a valid
    /// `TimeInterval` object, whose `seconds` field may have any
    /// 64-bit signed integer value and `nanoseconds` field limited to the
    /// range `[ -999,999,999..999,999,999 ]`.  Also the behavior is
    /// undefined unless the total number of seconds in the resulting time
    /// interval can be represented with 64-bit signed integer.  Note that
    /// this operation is allowed only if representation type of the
    /// `duration` is not a floating point type and the `duration` itself
    /// can be *exactly* represented by an integer nanoseconds.
    template <class REP_TYPE, class PERIOD_TYPE>
    BSLS_KEYWORD_CONSTEXPR_CPP14 TimeInterval&
    addDuration(const std::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration,
                typename std::enable_if<
                    TimeInterval_DurationTraits<REP_TYPE, PERIOD_TYPE>::
                                                 k_IMPLICIT_CONVERSION_ENABLED,
                    int>::type * = 0);
#endif

    /// Set this time interval to have the value given by the sum of the
    /// specified integral number of `seconds`, and the optionally specified
    /// integral number of `nanoseconds`.  If unspecified, `nanoseconds` is
    /// 0.  The behavior is undefined unless the total number of seconds in
    /// the resulting time interval can be represented with a 64-bit signed
    /// integer (see `isValid`).  Note that there is no restriction on the
    /// sign or magnitude of either argument except that they must not
    /// violate the method's preconditions.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setInterval(bsls::Types::Int64 seconds, int nanoseconds = 0);

    /// Set this time interval to have the value given by the sum of the
    /// specified integral number of `seconds`, and the optionally specified
    /// integral number of `nanoseconds`, where `seconds` and `nanoseconds`
    /// form a canonical representation of a time interval (see
    /// {Representation}).  If unspecified, `nanoseconds` is 0.  The
    /// behavior is undefined unless
    /// `-999,999,999 <= nanoseconds <= +999,999,999` and `seconds` and
    /// `nanoseconds` are either both non-negative or both non-positive.
    /// Note that this function provides a subset of the defined behavior of
    /// `setInterval` chosen to minimize runtime performance cost.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    void setIntervalRaw(bsls::Types::Int64 seconds, int nanoseconds = 0);

                                  // Aspects

    /// Assign to this object the value read from the specified input
    /// `stream` using the specified `version` format, and return a
    /// reference to `stream`.  If `stream` is initially invalid, this
    /// operation has no effect.  If `version` is not supported, this object
    /// is unaltered and `stream` is invalidated, but otherwise unmodified.
    /// If `version` is supported but `stream` becomes invalid during this
    /// operation, this object has an undefined, but valid, state.  Note
    /// that no version is read from `stream`.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);

    // ACCESSORS
#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
    /// Return `true` if the value of this time interval is within the valid
    /// range of the parameterized `DURATION_TYPE`, and `false` otherwise.
    /// Note that this function does not participate in overload resolution
    /// unless `DURATION_TYPE` is an instantiation of
    /// `std::chrono::duration`.
    template <class DURATION_TYPE>
    bool isInDurationRange(
         typename std::enable_if<TimeInterval_IsDuration<DURATION_TYPE>::value,
                                 int>::type * = 0) const;
#endif

    /// Return the nanoseconds field in the canonical representation of the
    /// value of this time interval.
    BSLS_KEYWORD_CONSTEXPR
    int nanoseconds() const;

    /// Return the seconds field in the canonical representation of the
    /// value of this time interval.
    BSLS_KEYWORD_CONSTEXPR
    bsls::Types::Int64 seconds() const;

    /// Return the value of this time interval as an integral number of
    /// days, rounded towards zero.  Note that the return value may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR
    bsls::Types::Int64 totalDays() const;

    /// Return the value of this time interval as an integral number of
    /// hours, rounded towards zero.  Note that the return value may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR
    bsls::Types::Int64 totalHours() const;

    /// Return the value of this time interval as an integral number of
    /// minutes, rounded towards zero.  Note that the return value may be
    /// negative.
    BSLS_KEYWORD_CONSTEXPR
    bsls::Types::Int64 totalMinutes() const;

    /// Return the value of this time interval as an integral number of
    /// seconds, rounded towards zero.  Note that the return value may be
    /// negative.  Also note that this method returns the same value as
    /// `seconds`.
    BSLS_KEYWORD_CONSTEXPR
    bsls::Types::Int64 totalSeconds() const;

    /// Return the value of this time interval as an integral number of
    /// milliseconds, rounded towards zero.  The behavior is undefined
    /// unless the number of milliseconds can be represented with a 64-bit
    /// signed integer.  Note that the return value may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    bsls::Types::Int64 totalMilliseconds() const;

    /// Return the value of this time interval as an integral number of
    /// microseconds, rounded towards zero.  The behavior is undefined
    /// unless the number of microseconds can be represented with a 64-bit
    /// signed integer.  Note that the return value may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    bsls::Types::Int64 totalMicroseconds() const;

    /// Return the value of this time interval as an integral number of
    /// nanoseconds.  The behavior is undefined unless the number of
    /// nanoseconds can be represented using a 64-bit signed integer.  Note
    /// that the return value may be negative.
    BSLS_KEYWORD_CONSTEXPR_CPP14
    bsls::Types::Int64 totalNanoseconds() const;

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
    /// Return the value of this time interval as a `std::chrono::duration`
    /// object.   This function participates in overloading if
    /// `DURATION_TYPE` is actually an `std::chrono::duration` instance, and
    /// if it has *not* a floating point representation.  The behavior is
    /// undefined unless the total number of nanoseconds can be represented
    /// using a `DURATION_TYPE`.  Note that the return value may be
    /// negative.
    template <class DURATION_TYPE>
    BSLS_KEYWORD_CONSTEXPR_CPP14
    typename std::enable_if<
              TimeInterval_IsDuration<DURATION_TYPE>::value &&
              !TimeInterval_RepTraits<typename DURATION_TYPE::rep>::k_IS_FLOAT,
              DURATION_TYPE>::type
    asDuration() const;
#endif

    /// Return the value of this time interval as a real number of seconds.
    /// Note that the return value may be negative and may have a fractional
    /// part (representing the nanosecond field of this object).  Also note
    /// that the conversion from the internal representation to a `double`
    /// may *lose* precision.
    double totalSecondsAsDouble() const;

                                  // Aspects

    /// Write the value of this object, using the specified `version`
    /// format, to the specified output `stream`, and return a reference to
    /// `stream`.  If `stream` is initially invalid, this operation has no
    /// effect.  If `version` is not supported, `stream` is invalidated, but
    /// otherwise unmodified.  Note that `version` is not written to
    /// `stream`.  See the `bslx` package-level documentation for more
    /// information on BDEX streaming of value-semantic types and
    /// containers.
    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;

    /// Write the value of this object to the specified output `stream` in a
    /// human-readable format, and return a reference providing modifiable
    /// access to `stream`.  Optionally specify an initial indentation
    /// `level`, whose absolute value is incremented recursively for nested
    /// objects.  If `level` is specified, optionally specify
    /// `spacesPerLevel`, whose absolute value indicates the number of
    /// spaces per indentation level for this and all of its nested objects.
    /// If `level` is negative, suppress indentation of the first line.  If
    /// `spacesPerLevel` is negative, format the entire output on one line,
    /// suppressing all but the initial indentation (as governed by
    /// `level`).  If `stream` is not valid on entry, this operation has no
    /// effect.  Note that the format is not fully specified, and can change
    /// without notice.
    std::ostream& print(std::ostream& stream,
                               int                  level          = 0,
                               int                  spacesPerLevel = 4) const;

#ifndef BDE_OPENSOURCE_PUBLICATION  // pending deprecation
    // DEPRECATED

    /// Return the most current BDEX streaming version number supported by
    /// this class.
    ///
    /// @DEPRECATED: Use `maxSupportedBdexVersion(int)` instead.
    static int maxSupportedBdexVersion();

#endif // BDE_OPENSOURCE_PUBLICATION -- pending deprecation
#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE2.22

    /// Return the most current BDEX streaming version number supported by
    /// this class.
    ///
    /// @DEPRECATED: Use `maxSupportedBdexVersion(int)` instead.
    static int maxSupportedVersion();

    /// Format this time to the specified output `stream`, and return a
    /// reference to the modifiable `stream`.
    ///
    /// @DEPRECATED: Use `print` instead.
    template <class STREAM>
    STREAM& streamOut(STREAM& stream) const;

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE2.22

};

// FREE OPERATORS

/// Return a `TimeInterval` value that is the sum of the specified `lhs` and
/// `rhs` time intervals.  The behavior is undefined unless (1) operands of
/// type `double` can be converted to valid `TimeInterval` objects, and (2)
/// the resulting time interval can be represented with a 64-bit signed
/// integer.
TimeInterval operator+(const TimeInterval& lhs, const TimeInterval& rhs);
TimeInterval operator+(const TimeInterval& lhs, double rhs);
TimeInterval operator+(double lhs, const TimeInterval& rhs);

/// Return a `TimeInterval` value that is the difference between the
/// specified `lhs` and `rhs` time intervals.  The behavior is undefined
/// unless (1) operands of type `double` can be converted to valid
/// `TimeInterval` objects, (2) the value on the right-hand side
/// (potentially after conversion to a `TimeInterval`) has a number of
/// seconds that is not `LLONG_MIN`, and (3) the resulting time interval can
/// be represented with a 64-bit signed integer.
TimeInterval operator-(const TimeInterval& lhs, const TimeInterval& rhs);
TimeInterval operator-(const TimeInterval& lhs, double rhs);
TimeInterval operator-(double lhs, const TimeInterval& rhs);

/// Return a `TimeInterval` value that is the negative of the specified
/// `rhs` time interval.  The behavior is undefined unless
/// `LLONG_MIN != rhs.seconds()`.
TimeInterval operator-(const TimeInterval& rhs);

/// Return `true` if the specified `lhs` and `rhs` time intervals have the
/// same value, and `false` otherwise.  Two time intervals have the same
/// value if their respective second and nanosecond fields have the same
/// value.  The behavior is undefined unless operands of type `double` can
/// be converted to valid `TimeInterval` objects.
bool operator==(const TimeInterval& lhs, const TimeInterval& rhs);
bool operator==(const TimeInterval& lhs, double rhs);
bool operator==(double lhs, const TimeInterval& rhs);

/// Return `true` if the specified `lhs` and `rhs` time intervals do not
/// have the same value, and `false` otherwise.  Two time intervals do not
/// have the same value if their respective second or nanosecond fields
/// differ in value.  The behavior is undefined unless operands of type
/// `double` can be converted to valid `TimeInterval` objects.
bool operator!=(const TimeInterval& lhs, const TimeInterval& rhs);
bool operator!=(const TimeInterval& lhs, double rhs);
bool operator!=(double lhs, const TimeInterval& rhs);

/// Return `true` if the nominal relation between the specified `lhs` and
/// `rhs` time interval values holds, and `false` otherwise.  The behavior
/// is undefined unless operands of type `double` can be converted to valid
/// `TimeInterval` objects.
bool operator< (const TimeInterval& lhs, const TimeInterval& rhs);
bool operator< (const TimeInterval& lhs, double rhs);
bool operator< (double lhs, const TimeInterval& rhs);
bool operator<=(const TimeInterval& lhs, const TimeInterval& rhs);
bool operator<=(const TimeInterval& lhs, double rhs);
bool operator<=(double lhs, const TimeInterval& rhs);
bool operator> (const TimeInterval& lhs, const TimeInterval& rhs);
bool operator> (const TimeInterval& lhs, double rhs);
bool operator> (double lhs, const TimeInterval& rhs);
bool operator>=(const TimeInterval& lhs, const TimeInterval& rhs);
bool operator>=(const TimeInterval& lhs, double rhs);
bool operator>=(double lhs, const TimeInterval& rhs);

/// Write the value of the specified `timeInterval` to the specified output
/// `stream` in a single-line format, and return a reference providing
/// modifiable access to `stream`.  If `stream` is not valid on entry, this
/// operation has no effect.  Note that this human-readable format is not
/// fully specified and can change without notice.  Also note that this
/// method has the same behavior as `object.print(stream, 0, -1)`.
std::ostream& operator<<(std::ostream&       stream,
                         const TimeInterval& timeInterval);

#if defined(BSLS_COMPILERFEATURES_SUPPORT_INLINE_NAMESPACE)  && \
    defined(BSLS_COMPILERFEATURES_SUPPORT_USER_DEFINED_LITERALS)
inline namespace literals {
inline namespace TimeIntervalLiterals {

/// This user defined literal operator converts the specified `hours` value
/// to the respective `TimeInterval` value.  The behavior is undefined
/// unless the specified number of hours can be converted to valid
/// `TimeInterval` object.  (See the
/// "User-Defined Literals" section in the component-level documentation.)
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval operator ""_h(  unsigned long long int hours);

/// This user defined literal operator converts the specified `minutes`
/// value to the respective `TimeInterval` value.  The behavior is undefined
/// unless the specified number of minutes can be converted to valid
/// `TimeInterval` object.  (See the
/// "User-Defined Literals" section in the component-level documentation.)
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval operator ""_min(unsigned long long int minutes);

/// This user defined literal operator converts the specified `seconds`
/// value to the respective `TimeInterval` value.  The behavior is undefined
/// unless the specified number of seconds can be converted to valid
/// `TimeInterval` object.  (See the
/// "User-Defined Literals" section in the component-level documentation.)
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval operator ""_s(  unsigned long long int seconds);

/// This user defined literal operator converts the specified `milliseconds`
/// value to the respective `TimeInterval` value.  (See the
/// "User-Defined Literals" section in the component-level documentation.)
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval operator ""_ms( unsigned long long int milliseconds);

/// This user defined literal operator converts the specified `microseconds`
/// value to the respective `TimeInterval` value.  (See the
/// "User-Defined Literals" section in the component-level documentation.)
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval operator ""_us( unsigned long long int microseconds);

/// This user defined literal operator converts the specified `nanoseconds`
/// value to the respective `TimeInterval` value.  (See the
/// "User-Defined Literals" section in the component-level documentation.)
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval operator ""_ns( unsigned long long int nanoseconds);

}  // close TimeIntervalLiterals namespace
}  // close literals namespace
#endif  // BSLS_COMPILERFEATURES_SUPPORT_INLINE_NAMESPACE &&
        // BSLS_COMPILERFEATURES_SUPPORT_USER_DEFINED_LITERALS

// ============================================================================
//                         INLINE DEFINITIONS
// ============================================================================

                        // ------------------
                        // class TimeInterval
                        // ------------------

// PRIVATE CLASS METHODS
inline BSLS_KEYWORD_CONSTEXPR
bool TimeInterval::isSumValidInt64(bsls::Types::Int64 lhs,
                                   bsls::Types::Int64 rhs)
{
    // {DRQS 164912552} Sun CC miscomplies this ternary operator when the
    // function is invoked from the 'TimeInterval' constructor.
    // return lhs > 0 ? LLONG_MAX - lhs >= rhs : LLONG_MIN - lhs <= rhs;
    return (lhs >  0 && LLONG_MAX - lhs >= rhs) ||
           (lhs <= 0 && LLONG_MIN - lhs <= rhs);
}

// CLASS METHODS
inline
int TimeInterval::maxSupportedBdexVersion(int /* versionSelector */)
{
    return 1;
}

inline BSLS_KEYWORD_CONSTEXPR
bool TimeInterval::isValid(bsls::Types::Int64 seconds,
                           int                nanoseconds)
{
    return isSumValidInt64(seconds, nanoseconds / k_NANOSECS_PER_SEC);
}

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS

template <class REP, class PERIOD>
bool TimeInterval::isValid(const std::chrono::duration<REP, PERIOD>& duration)
{
    std::chrono::duration<long double> minValue =
                                 std::chrono::duration<long double>(LLONG_MIN);
    --minValue;
    std::chrono::duration<long double> maxValue =
                                 std::chrono::duration<long double>(LLONG_MAX);
    ++maxValue;
    const std::chrono::duration<long double> safeDuration =
               std::chrono::duration_cast<std::chrono::duration<long double> >(
                                                                     duration);

    return (safeDuration >= minValue && safeDuration <= maxValue);
}
#endif  // BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS

// CREATORS
inline BSLS_KEYWORD_CONSTEXPR
TimeInterval::TimeInterval()
: d_seconds(0)
, d_nanoseconds(0)
{
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval::TimeInterval(bsls::Types::Int64 seconds,
                           int                nanoseconds)
: d_seconds(0)
, d_nanoseconds(0)
{
    // A seemingly redundant initializer list is needed since members must be
    // initialized by mem-initializer in 'constexpr' constructor.

    setInterval(seconds, nanoseconds);
}

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
template <class REP_TYPE, class PERIOD_TYPE>
inline
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval::TimeInterval(
    const std::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration,
    typename std::enable_if<TimeInterval_DurationTraits<
                                   REP_TYPE,
                                   PERIOD_TYPE>::k_IMPLICIT_CONVERSION_ENABLED,
                            int>::type *)
{
    BSLS_ASSERT((isValid<REP_TYPE, PERIOD_TYPE>(duration)));
    using SecondsRatio = std::ratio<1>;
    using TimeIntervalSeconds =
                     std::chrono::duration<bsls::Types::Int64, SecondsRatio>;
    using TimeIntervalNanoseconds = std::chrono::duration<int, std::nano>;

    const bsls::Types::Int64 k_SECONDS     =
             std::chrono::duration_cast<TimeIntervalSeconds>(duration).count();
    const int                k_NANOSECONDS =
            std::chrono::duration_cast<TimeIntervalNanoseconds>(
                            duration - TimeIntervalSeconds(k_SECONDS)).count();
    setInterval(k_SECONDS, k_NANOSECONDS);
}

template <class REP_TYPE, class PERIOD_TYPE>
inline
BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval::TimeInterval(
    const std::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration,
    typename std::enable_if<TimeInterval_DurationTraits<
                                   REP_TYPE,
                                   PERIOD_TYPE>::k_EXPLICIT_CONVERSION_ENABLED,
                            int>::type *)
{
    BSLS_ASSERT((isValid<REP_TYPE, PERIOD_TYPE>(duration)));
    const bsls::Types::Int64 k_SECONDS     =
            std::chrono::duration_cast<std::chrono::seconds>(duration).count();
    const int                k_NANOSECONDS = static_cast<int>(
        std::chrono::duration_cast<std::chrono::nanoseconds>(
                          duration - std::chrono::seconds(k_SECONDS)).count());
    setInterval(k_SECONDS, k_NANOSECONDS);
}
#endif

// MANIPULATORS
inline
TimeInterval& TimeInterval::operator=(double rhs)
{
    *this = TimeInterval(rhs);
    return *this;
}

inline
TimeInterval& TimeInterval::operator+=(const TimeInterval& rhs)
{
    return addInterval(rhs.d_seconds, rhs.d_nanoseconds);
}

inline
TimeInterval& TimeInterval::operator+=(double rhs)
{
    *this += TimeInterval(rhs);
    return *this;
}

inline
TimeInterval& TimeInterval::operator-=(const TimeInterval& rhs)
{
    BSLS_ASSERT_SAFE(LLONG_MIN < rhs.seconds());

    return addInterval(-rhs.d_seconds, -rhs.d_nanoseconds);
}

inline
TimeInterval& TimeInterval::operator-=(double rhs)
{
    *this -= TimeInterval(rhs);
    return *this;
}

                                  // Add Operations

inline BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval& TimeInterval::addDays(bsls::Types::Int64 days)
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_SECONDS_PER_DAY >= days &&
                     LLONG_MIN / k_SECONDS_PER_DAY <= days);

    return addSeconds(days * k_SECONDS_PER_DAY);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval& TimeInterval::addHours(bsls::Types::Int64 hours)
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_SECONDS_PER_HOUR >= hours &&
                     LLONG_MIN / k_SECONDS_PER_HOUR <= hours);

    return addSeconds(hours * k_SECONDS_PER_HOUR);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval& TimeInterval::addMinutes(bsls::Types::Int64 minutes)
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_SECONDS_PER_MINUTE >= minutes &&
                     LLONG_MIN / k_SECONDS_PER_MINUTE <= minutes);

    return addSeconds(minutes * k_SECONDS_PER_MINUTE);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
TimeInterval& TimeInterval::addSeconds(bsls::Types::Int64 seconds)
{
    BSLS_ASSERT_SAFE(isSumValidInt64(seconds, d_seconds));

    d_seconds += seconds;
    if (d_seconds > 0 && d_nanoseconds < 0) {
        --d_seconds;
        d_nanoseconds += k_NANOSECS_PER_SEC;
    }
    else if (d_seconds < 0 && d_nanoseconds > 0) {
        ++d_seconds;
        d_nanoseconds -= k_NANOSECS_PER_SEC;
    }

    return *this;
}

inline
TimeInterval& TimeInterval::addMilliseconds(bsls::Types::Int64 milliseconds)
{
    return addInterval(          milliseconds / k_MILLISECS_PER_SEC,
               static_cast<int>((milliseconds % k_MILLISECS_PER_SEC) *
                                                     k_NANOSECS_PER_MILLISEC));
}

inline
TimeInterval& TimeInterval::addMicroseconds(bsls::Types::Int64 microseconds)
{
    return addInterval(          microseconds / k_MICROSECS_PER_SEC,
               static_cast<int>((microseconds % k_MICROSECS_PER_SEC) *
                                                     k_NANOSECS_PER_MICROSEC));
}

inline
TimeInterval& TimeInterval::addNanoseconds(bsls::Types::Int64 nanoseconds)
{
    return addInterval(                 nanoseconds / k_NANOSECS_PER_SEC,
                       static_cast<int>(nanoseconds % k_NANOSECS_PER_SEC));
}

                                  // Set Operations

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setTotalDays(bsls::Types::Int64 days)
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_SECONDS_PER_DAY >= days &&
                     LLONG_MIN / k_SECONDS_PER_DAY <= days);

    return setTotalSeconds(days * k_SECONDS_PER_DAY);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setTotalHours(bsls::Types::Int64 hours)
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_SECONDS_PER_HOUR >= hours &&
                     LLONG_MIN / k_SECONDS_PER_HOUR <= hours);

    return setTotalSeconds(hours * k_SECONDS_PER_HOUR);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setTotalMinutes(bsls::Types::Int64 minutes)
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_SECONDS_PER_MINUTE >= minutes &&
                     LLONG_MIN / k_SECONDS_PER_MINUTE <= minutes);

    return setTotalSeconds(minutes * k_SECONDS_PER_MINUTE);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setTotalSeconds(bsls::Types::Int64 seconds)
{
    d_seconds     = seconds;
    d_nanoseconds = 0;
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setTotalMilliseconds(bsls::Types::Int64 milliseconds)
{
    setInterval(                  milliseconds / k_MILLISECS_PER_SEC,
                static_cast<int>((milliseconds % k_MILLISECS_PER_SEC) *
                                                     k_NANOSECS_PER_MILLISEC));
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setTotalMicroseconds(bsls::Types::Int64 microseconds)
{
    setInterval(                  microseconds / k_MICROSECS_PER_SEC,
                static_cast<int>((microseconds % k_MICROSECS_PER_SEC) *
                                                     k_NANOSECS_PER_MICROSEC));

}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setTotalNanoseconds(bsls::Types::Int64 nanoseconds)
{
    setInterval(                 nanoseconds / k_NANOSECS_PER_SEC,
                static_cast<int>(nanoseconds % k_NANOSECS_PER_SEC));
}

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
template <class REP_TYPE, class PERIOD_TYPE>
inline
BSLS_KEYWORD_CONSTEXPR_CPP14 TimeInterval&
TimeInterval::addDuration(
       const std::chrono::duration<REP_TYPE, PERIOD_TYPE>& duration,
       typename std::enable_if<TimeInterval_DurationTraits<
                                   REP_TYPE,
                                   PERIOD_TYPE>::k_IMPLICIT_CONVERSION_ENABLED,
                               int>::type *)
{
    BSLS_ASSERT((isValid<REP_TYPE, PERIOD_TYPE>(duration)));

    const bsls::Types::Int64 k_SECONDS     =
            std::chrono::duration_cast<std::chrono::seconds>(duration).count();
    const int                k_NANOSECONDS = static_cast<int>(
                          std::chrono::duration_cast<std::chrono::nanoseconds>(
                          duration - std::chrono::seconds(k_SECONDS)).count());
    return addInterval(k_SECONDS, k_NANOSECONDS);
}
#endif

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setInterval(bsls::Types::Int64 seconds,
                               int                nanoseconds)
{
    BSLS_PRECONDITIONS_BEGIN();
    BSLS_ASSERT(isValid(seconds, nanoseconds));
    BSLS_PRECONDITIONS_END();

    d_seconds = seconds;
    if (nanoseconds >= k_NANOSECS_PER_SEC
     || nanoseconds <= -k_NANOSECS_PER_SEC) {
        d_seconds += nanoseconds / k_NANOSECS_PER_SEC;
        d_nanoseconds = static_cast<int>(nanoseconds % k_NANOSECS_PER_SEC);
    }
    else {
        d_nanoseconds = static_cast<int>(nanoseconds);
    }

    if (d_seconds > 0 && d_nanoseconds < 0) {
        --d_seconds;
        d_nanoseconds += k_NANOSECS_PER_SEC;
    }
    else if (d_seconds < 0 && d_nanoseconds > 0) {
        ++d_seconds;
        d_nanoseconds -= k_NANOSECS_PER_SEC;
    }

}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
void TimeInterval::setIntervalRaw(bsls::Types::Int64 seconds,
                                  int                nanoseconds)
{
    BSLS_ASSERT_SAFE(-k_NANOSECS_PER_SEC < nanoseconds &&
                      k_NANOSECS_PER_SEC > nanoseconds);
    BSLS_ASSERT_SAFE((seconds >= 0 && nanoseconds >= 0) ||
                     (seconds <= 0 && nanoseconds <= 0));

    d_seconds     = seconds;
    d_nanoseconds = nanoseconds;
}

                                  // Aspects

template <class STREAM>
STREAM& TimeInterval::bdexStreamIn(STREAM& stream, int version)
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 1: {
            bsls::Types::Int64 seconds     = 0;
            int                nanoseconds = 0;
            stream.getInt64(seconds);
            stream.getInt32(nanoseconds);

            if (stream && (   (seconds >= 0 && nanoseconds >= 0)
                           || (seconds <= 0 && nanoseconds <= 0))
                       && nanoseconds > -k_NANOSECS_PER_SEC
                       && nanoseconds <  k_NANOSECS_PER_SEC) {
                d_seconds     = seconds;
                d_nanoseconds = nanoseconds;
            }
            else {
                stream.invalidate();
            }
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

// ACCESSORS
#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
template <class DURATION_TYPE>
bool TimeInterval::isInDurationRange(
         typename std::enable_if<TimeInterval_IsDuration<DURATION_TYPE>::value,
                                 int>::type *) const
{
    using SecondsRatio = std::ratio<1>;
    using TimeIntervalSeconds =
                       std::chrono::duration<bsls::Types::Int64, SecondsRatio>;
    using TimeIntervalNanoseconds = std::chrono::duration<int, std::nano>;
    using Period = typename DURATION_TYPE::period;
    using LongDoubleTo = std::chrono::duration<long double, Period>;

    const LongDoubleTo MIN_VALUE =
                std::chrono::duration_cast<LongDoubleTo>(DURATION_TYPE::min());

    const LongDoubleTo MAX_VALUE =
                std::chrono::duration_cast<LongDoubleTo>(DURATION_TYPE::max());

    const LongDoubleTo value = std::chrono::duration_cast<LongDoubleTo>(
                                                TimeIntervalSeconds(seconds()))
                             + std::chrono::duration_cast<LongDoubleTo>(
                                       TimeIntervalNanoseconds(nanoseconds()));

    return (MIN_VALUE <= value && value <= MAX_VALUE);
}
#endif  // BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS

inline BSLS_KEYWORD_CONSTEXPR
int TimeInterval::nanoseconds() const
{
    return d_nanoseconds;
}

inline BSLS_KEYWORD_CONSTEXPR
bsls::Types::Int64 TimeInterval::seconds() const
{
    return d_seconds;
}

inline BSLS_KEYWORD_CONSTEXPR
bsls::Types::Int64 TimeInterval::totalDays() const
{
    return d_seconds / k_SECONDS_PER_DAY;
}

inline BSLS_KEYWORD_CONSTEXPR
bsls::Types::Int64 TimeInterval::totalHours() const
{
    return d_seconds / k_SECONDS_PER_HOUR;
}

inline BSLS_KEYWORD_CONSTEXPR
bsls::Types::Int64 TimeInterval::totalMinutes() const
{
    return d_seconds / k_SECONDS_PER_MINUTE;
}

inline BSLS_KEYWORD_CONSTEXPR
bsls::Types::Int64 TimeInterval::totalSeconds() const
{
    return d_seconds;
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::Types::Int64 TimeInterval::totalMilliseconds() const
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_MILLISECS_PER_SEC >= d_seconds &&
                     LLONG_MIN / k_MILLISECS_PER_SEC <= d_seconds);
    BSLS_ASSERT_SAFE(isSumValidInt64(d_seconds * k_MILLISECS_PER_SEC,
                                     d_nanoseconds / k_NANOSECS_PER_MILLISEC));


    return d_seconds     * k_MILLISECS_PER_SEC
         + d_nanoseconds / k_NANOSECS_PER_MILLISEC;
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::Types::Int64 TimeInterval::totalMicroseconds() const
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_MICROSECS_PER_SEC >= d_seconds &&
                     LLONG_MIN / k_MICROSECS_PER_SEC <= d_seconds);
    BSLS_ASSERT_SAFE(isSumValidInt64(d_seconds     * k_MICROSECS_PER_SEC,
                                     d_nanoseconds / k_NANOSECS_PER_MICROSEC));

    return d_seconds     * k_MICROSECS_PER_SEC
         + d_nanoseconds / k_NANOSECS_PER_MICROSEC;
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::Types::Int64 TimeInterval::totalNanoseconds() const
{
    BSLS_ASSERT_SAFE(LLONG_MAX / k_NANOSECS_PER_SEC >= d_seconds &&
                     LLONG_MIN / k_NANOSECS_PER_SEC <= d_seconds);
    BSLS_ASSERT_SAFE(isSumValidInt64(d_seconds * k_NANOSECS_PER_SEC,
                                     d_nanoseconds));

    return d_seconds * k_NANOSECS_PER_SEC + d_nanoseconds;
}

#ifdef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS
template <class DURATION_TYPE>
inline
BSLS_KEYWORD_CONSTEXPR_CPP14
typename std::enable_if<
              TimeInterval_IsDuration<DURATION_TYPE>::value &&
              !TimeInterval_RepTraits<typename DURATION_TYPE::rep>::k_IS_FLOAT,
              DURATION_TYPE>::type
TimeInterval::asDuration() const
{
    using SecondsRatio = std::ratio<1>;
    using TimeIntervalSeconds =
                       std::chrono::duration<bsls::Types::Int64, SecondsRatio>;
    using TimeIntervalNanoseconds = std::chrono::duration<int, std::nano>;

    BSLS_ASSERT(isInDurationRange<DURATION_TYPE>());

    return (std::chrono::duration_cast<DURATION_TYPE>(TimeIntervalSeconds(
                                                                    d_seconds))
          + std::chrono::duration_cast<DURATION_TYPE>(TimeIntervalNanoseconds(
                                                              d_nanoseconds)));
}
#endif

inline
double TimeInterval::totalSecondsAsDouble() const
{
    return static_cast<double>(d_seconds) + d_nanoseconds /
                                       static_cast<double>(k_NANOSECS_PER_SEC);
}

                                  // Aspects

template <class STREAM>
STREAM& TimeInterval::bdexStreamOut(STREAM& stream, int version) const
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 1: {
            stream.putInt64(d_seconds);
            stream.putInt32(d_nanoseconds);
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}


#ifndef BDE_OPENSOURCE_PUBLICATION  // pending deprecation

// DEPRECATED METHODS
inline
int TimeInterval::maxSupportedBdexVersion()
{
    return maxSupportedBdexVersion(0);
}

#endif // BDE_OPENSOURCE_PUBLICATION -- pending deprecation
#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE2.22
inline
int TimeInterval::maxSupportedVersion()
{
    return maxSupportedBdexVersion(0);
}

template <class STREAM>
inline
STREAM& TimeInterval::streamOut(STREAM& stream) const
{
    return print(stream, 0, -1);
}

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE2.22

}  // close package namespace

// FREE OPERATORS
inline
bsls::TimeInterval bsls::operator+(const TimeInterval& lhs,
                                   const TimeInterval& rhs)
{
    TimeInterval result(lhs);
    return result.addInterval(rhs.seconds(), rhs.nanoseconds());
}

inline
bsls::TimeInterval bsls::operator+(const TimeInterval& lhs, double rhs)
{
    return lhs + TimeInterval(rhs);
}

inline
bsls::TimeInterval bsls::operator+(double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) + rhs;
}

inline
bsls::TimeInterval bsls::operator-(const TimeInterval& lhs,
                                   const TimeInterval& rhs)

{
    BSLS_ASSERT_SAFE(LLONG_MIN != rhs.seconds());

    TimeInterval result(lhs);
    result.addInterval(-rhs.seconds(), -rhs.nanoseconds());
    return result;
}

inline
bsls::TimeInterval bsls::operator-(const TimeInterval& lhs, double rhs)
{
    return lhs - TimeInterval(rhs);
}

inline
bsls::TimeInterval bsls::operator-(double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) - rhs;
}

inline
bsls::TimeInterval bsls::operator-(const TimeInterval& rhs)
{
    BSLS_ASSERT_SAFE(LLONG_MIN != rhs.seconds());

    return TimeInterval(-rhs.seconds(), -rhs.nanoseconds());
}

inline
bool bsls::operator==(const TimeInterval& lhs, const TimeInterval& rhs)
{
    return lhs.seconds()     == rhs.seconds()
        && lhs.nanoseconds() == rhs.nanoseconds();
}

inline
bool bsls::operator==(const TimeInterval& lhs, double rhs)
{
    return lhs == TimeInterval(rhs);
}

inline
bool bsls::operator==(double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) == rhs;
}

inline
bool bsls::operator!=(const TimeInterval& lhs, const TimeInterval& rhs)
{
    return lhs.seconds()     != rhs.seconds()
        || lhs.nanoseconds() != rhs.nanoseconds();
}

inline
bool bsls::operator!=(const TimeInterval& lhs, double rhs)
{
    return lhs != TimeInterval(rhs);
}

inline
bool bsls::operator!=(double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) != rhs;
}

inline
bool bsls::operator< (const TimeInterval& lhs, const TimeInterval& rhs)
{
    return lhs.seconds() < rhs.seconds()
        || (lhs.seconds() == rhs.seconds()
            && lhs.nanoseconds() < rhs.nanoseconds());
}

inline
bool bsls::operator< (const TimeInterval& lhs, double rhs)
{
    return lhs < TimeInterval(rhs);
}

inline
bool bsls::operator< (double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) < rhs;
}

inline
bool bsls::operator<=(const TimeInterval& lhs, const TimeInterval& rhs)
{
    return lhs.seconds() < rhs.seconds()
        || (lhs.seconds() == rhs.seconds()
            && lhs.nanoseconds() <= rhs.nanoseconds());
}

inline
bool bsls::operator<=(const TimeInterval& lhs, double rhs)
{
    return lhs <= TimeInterval(rhs);
}

inline
bool bsls::operator<=(double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) <= rhs;
}

inline
bool bsls::operator> (const TimeInterval& lhs, const TimeInterval& rhs)
{
    return lhs.seconds() > rhs.seconds()
        || (lhs.seconds() == rhs.seconds()
            && lhs.nanoseconds() > rhs.nanoseconds());
}

inline
bool bsls::operator> (const TimeInterval& lhs, double rhs)
{
    return lhs > TimeInterval(rhs);
}

inline
bool bsls::operator> (double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) > rhs;
}

inline
bool bsls::operator>=(const TimeInterval& lhs, const TimeInterval& rhs)
{
    return lhs.seconds() > rhs.seconds()
        || (lhs.seconds() == rhs.seconds()
            && lhs.nanoseconds() >= rhs.nanoseconds());
}

inline
bool bsls::operator>=(const TimeInterval& lhs, double rhs)
{
    return lhs >= TimeInterval(rhs);
}

inline
bool bsls::operator>=(double lhs, const TimeInterval& rhs)
{
    return TimeInterval(lhs) >= rhs;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_INLINE_NAMESPACE)  && \
    defined(BSLS_COMPILERFEATURES_SUPPORT_USER_DEFINED_LITERALS)

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::TimeInterval bsls::TimeIntervalLiterals::operator""_h(
                                                  unsigned long long int hours)
{
    BSLS_ASSERT((LLONG_MAX/3600) >= hours);
    return TimeInterval(static_cast<bsls::Types::Int64>(hours*3600), 0);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::TimeInterval bsls::TimeIntervalLiterals::operator""_min(
                                                unsigned long long int minutes)
{
    BSLS_ASSERT((LLONG_MAX/60) >= minutes);
    return TimeInterval(static_cast<bsls::Types::Int64>(minutes*60), 0);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::TimeInterval bsls::TimeIntervalLiterals::operator""_s(
                                                unsigned long long int seconds)
{
    BSLS_ASSERT(LLONG_MAX > seconds);
    return TimeInterval(static_cast<bsls::Types::Int64>(seconds), 0);
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::TimeInterval bsls::TimeIntervalLiterals::operator""_ms(
                                           unsigned long long int milliseconds)
{
    const bsls::Types::Int64 k_MILLISECS_PER_SEC     = 1000;
    const bsls::Types::Int64 k_NANOSECS_PER_MILLISEC = 1000000;

    return TimeInterval(milliseconds / k_MILLISECS_PER_SEC,
                        static_cast<int>((milliseconds % k_MILLISECS_PER_SEC) *
                                         k_NANOSECS_PER_MILLISEC));
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::TimeInterval bsls::TimeIntervalLiterals::operator""_us(
                                           unsigned long long int microseconds)
{
    const bsls::Types::Int64 k_MICROSECS_PER_SEC     = 1000000;
    const bsls::Types::Int64 k_NANOSECS_PER_MICROSEC = 1000;

    return TimeInterval(microseconds / k_MICROSECS_PER_SEC,
                        static_cast<int>((microseconds % k_MICROSECS_PER_SEC) *
                                         k_NANOSECS_PER_MICROSEC));
}

inline BSLS_KEYWORD_CONSTEXPR_CPP14
bsls::TimeInterval bsls::TimeIntervalLiterals::operator""_ns(
                                            unsigned long long int nanoseconds)
{
    const bsls::Types::Int64 k_NANOSECS_PER_SEC = 1000000000;

    return TimeInterval(nanoseconds / k_NANOSECS_PER_SEC,
                        static_cast<int>(nanoseconds % k_NANOSECS_PER_SEC));
}

#endif  // BSLS_COMPILERFEATURES_SUPPORT_INLINE_NAMESPACE &&
        // BSLS_COMPILERFEATURES_SUPPORT_USER_DEFINED_LITERALS

// BDE_VERIFY pragma: pop

// IMPLEMENTATION NOTE: A 'is_trivially_copyable' trait declaration has been
// moved to 'bslmf_istriviallycopyable.h' to work around issues on the Sun CC
// 5.13 compiler.  We had previously forward declared
// 'bsl::is_trivially_copyable' and specialized it for 'TimeInterval' here (see
// the 2.24 release tags).
//..
//  namespace bsl {
//  template <>
//  struct is_trivially_copyable<BloombergLP::bsls::TimeInterval> :
//                                                            bsl::true_type {
//      // This template specialization for 'is_trivially_copyable' indicates
//      // that 'Date' is a trivially copyable type.
//  };
//  }
//..

}  // close enterprise namespace

#undef BSLS_TIMEINTERVAL_PROVIDES_CHRONO_CONVERSIONS

#endif

// ----------------------------------------------------------------------------
// Copyright 2020 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
