// bdlt_time.h                                                        -*-C++-*-
#ifndef INCLUDED_BDLT_TIME
#define INCLUDED_BDLT_TIME

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

//@PURPOSE: Provide a value-semantic type representing time-of-day.
//
//@CLASSES:
//  bdlt::Time: time-of-day type (with microsecond resolution)
//
//@DESCRIPTION: This component implements a value-semantic time class,
// 'bdlt::Time', that can represent the time of day to a resolution of one
// microsecond (using a 24-hour clock).  Valid time values range from
// 00:00:00.000000 (i.e., midnight) through 23:59:59.999999 (i.e., one
// microsecond before midnight).  A time value can be specified via five
// separate integer attribute values denoting hours '[0 .. 23]', minutes
// '[0 .. 59]', seconds '[0 .. 59]', milliseconds '[0 .. 999]', and
// microseconds '[0 .. 999]'.  In addition, the 'bdlt::Time' type has one more
// valid value, 24:00:00.000000, that can be set explicitly and accessed.  The
// value 24:00:00.000000 behaves, in most cases, as if it were the value
// 00:00:00.000000; however, for all relational comparison operators,
// 24:00:00.000000 is not a valid argument and, therefore, would result in
// undefined behavior.  Each of the 'add' manipulators, along with modifying
// the value of the object, return the (signed) number of times that the
// 23:59:59.999999 - 00:00:00.000000 boundary was crossed in performing the
// addition.
//
///ISO Standard Text Representation
///--------------------------------
// A common standard text representation of a date and time value is described
// by ISO 8601.  BDE provides the 'bdlt_iso8601util' component for conversion
// to and from the standard ISO8601 format.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic 'bdlt::Time' Usage
///- - - - - - - - - - - - - - - - - -
// This example demonstrates how to create and use a 'bdlt::Time' object.
//
// First, create an object 't1' having the default value, and then verify that
// it represents the value 24:00:00.000000:
//..
//  bdlt::Time t1;               assert(24 == t1.hour());
//                               assert( 0 == t1.minute());
//                               assert( 0 == t1.second());
//                               assert( 0 == t1.millisecond());
//                               assert( 0 == t1.microsecond());
//..
// Then, set 't1' to the value 2:34pm (14:34:00.000000):
//..
//  t1.setTime(14, 34);          assert(14 == t1.hour());
//                               assert(34 == t1.minute());
//                               assert( 0 == t1.second());
//                               assert( 0 == t1.millisecond());
//                               assert( 0 == t1.microsecond());
//..
// Next, use 'setTimeIfValid' to attempt to assign the invalid value 24:15 to
// 't1', then verify the method returns an error status and the value of 't1'
// is unmodified:
//..
//  int ret = t1.setTimeIfValid(24, 15);
//                               assert( 0 != ret);          // 24:15 is not
//                                                           // valid
//
//                               assert(14 == t1.hour());    // no effect
//                               assert(34 == t1.minute());  // on the
//                               assert( 0 == t1.second());  // object
//                               assert( 0 == t1.millisecond());
//                               assert( 0 == t1.microsecond());
//..
// Then, create 't2' as a copy of 't1':
//..
//  bdlt::Time t2(t1);            assert(t1 == t2);
//..
// Next, add 5 minutes and 7 seconds to the value of 't2' (in two steps), and
// confirm the value of 't2':
//..
//  t2.addMinutes(5);
//  t2.addSeconds(7);
//                               assert(14 == t2.hour());
//                               assert(39 == t2.minute());
//                               assert( 7 == t2.second());
//                               assert( 0 == t2.millisecond());
//                               assert( 0 == t2.microsecond());
//..
// Then, subtract 't1' from 't2' to yield a 'bdlt::DatetimeInterval' 'dt'
// representing the time-interval between those two times, and verify the value
// of 'dt' is 5 minutes and 7 seconds (or 307 seconds):
//..
//  bdlt::DatetimeInterval dt = t2 - t1;
//                               assert(307 == dt.totalSeconds());
//..
// Finally, stream the value of 't2' to 'stdout':
//..
//  bsl::cout << t2 << bsl::endl;
//..
// The streaming operator produces the following output on 'stdout':
//..
//  14:39:07.000000
//..

#include <bdlscm_version.h>

#include <bdlt_datetimeinterval.h>
#include <bdlt_timeunitratio.h>

#include <bdlb_bitutil.h>

#include <bslh_hash.h>

#include <bslmf_integralconstant.h>
#include <bslmf_istriviallycopyable.h>

#include <bsls_assert.h>
#include <bsls_atomic.h>
#include <bsls_log.h>
#include <bsls_performancehint.h>
#include <bsls_platform.h>
#include <bsls_review.h>
#include <bsls_stackaddressutil.h>
#include <bsls_types.h>

#include <bsl_iosfwd.h>
#include <bsl_cstring.h> // memset
#include <bsl_sstream.h>

namespace BloombergLP {
namespace bdlt {

                                 // ==========
                                 // class Time
                                 // ==========

class Time {
    // This class implements a value-semantic type that represents the time of
    // day to a resolution of one microsecond.  Each object of this (almost)
    // simply constrained attribute class *always* represents a valid time
    // value to a resolution of one microsecond.  The valid range for times is
    // 00:00:00.000000 through 23:59:59.999999, except that 24:00:00.000000
    // represents the default-constructed value.  The value 24:00:00.000000
    // behaves, in most cases, as if it were the value 00:00:00.000000;
    // however, for all relational comparison operators, 24:00:00.000000 is not
    // a valid argument and, therefore, would result in undefined behavior.
    // Each add operation on a 'Time' object returns the (signed) number of
    // times that the 23:59:59.999999 - 00:00:00.000000 boundary was crossed
    // while performing the operation.  Attempting to construct a 'Time' with
    // any attribute outside its valid range (or with an hour attribute value
    // of 24 and any other attribute non-zero) has undefined behavior.

    // PRIVATE TYPES
    enum {
        k_DEFAULT_FRACTIONAL_SECOND_PRECISION = 6
    };

    // CLASS DATA
    static const bsls::Types::Int64 k_REP_MASK  = 0x0000004000000000ULL;

    static bsls::AtomicInt64 s_invalidRepresentationCount;

    // DATA
    bsls::Types::Int64 d_value;  // encoded offset from 00:00:00.000000

    // FRIENDS
    friend DatetimeInterval operator-(const Time&, const Time&);
    friend bool operator==(const Time&, const Time&);
    friend bool operator!=(const Time&, const Time&);
    friend bool operator< (const Time&, const Time&);
    friend bool operator<=(const Time&, const Time&);
    friend bool operator>=(const Time&, const Time&);
    friend bool operator> (const Time&, const Time&);
    template <class HASHALG>
    friend void hashAppend(HASHALG& hashAlg, const Time&);

    // PRIVATE MANIPULATORS
    void setMicrosecondsFromMidnight(bsls::Types::Int64 totalMicroseconds);
        // Assign to 'd_value' the representation of time such that the
        // difference between this representation of time and 00:00:00.000000
        // is the specified 'totalMicroseconds'.  If
        // 'TimeUnitRatio::k_US_PER_D == totalMicroseconds', assign to
        // 'd_value' the representation of 24:00:00.000000.  The behavior is
        // undefined unless
        // '0 <= totalMicroseconds <= TimeUnitRatio::k_US_PER_D'.

    // PRIVATE ACCESSORS
    bsls::Types::Int64 microsecondsFromMidnight() const;
        // Return the difference, in microseconds, between the value of this
        // object and 00:00:00.000000.  If the value of this object is
        // 24:00:00.000000, it is treated as 00:00:00.000000.

    bsls::Types::Int64 invalidMicrosecondsFromMidnight() const;
        // Invoke a review failure notifying that a 'bdlt::Time' instance is
        // being used in an invalid state.  The behavior is undefined unless
        // this object has the old represenation ('k_REP_MASK > d_value') and
        // 'BSLS_ASSERT_SAFE' is not active.

    bsls::Types::Int64 updatedRepresentation() const;
        // If 'd_value' was stored using the current representation scheme,
        // return 'd_value'.  Otherwise, return the representation of the time
        // corresponding to 'd_value' total milliseconds since 00:00:00.000000
        // (i.e., convert from the old representation scheme to the current
        // scheme), or return the current representation of 24:00:00.000000 if
        // 'd_value' is the old representation of the default-constructed
        // value.

  public:
    // CLASS METHODS
    static bool isValid(int hour,
                        int minute = 0,
                        int second = 0,
                        int millisecond = 0,
                        int microsecond = 0);
        // Return 'true' if the specified 'hour', and the optionally specified
        // 'minute', 'second', 'millisecond', and 'microsecond', represent a
        // valid 'Time' value, and 'false' otherwise.  Unspecified arguments
        // default to 0.  The 'hour', 'minute', 'second', 'millisecond', and
        // 'microsecond' attributes comprise a valid 'Time' value if
        // '0 <= hour < 24', '0 <= minute < 60', '0 <= second < 60',
        // '0 <= millisecond < 1000', and '0 <= microsecond < 1000'.
        // Additionally, 24:00:00.000000 also represents a valid 'Time' value.

                                  // Aspects

    static int maxSupportedBdexVersion(int versionSelector);
        // 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.

    // CREATORS
    Time();
        // Create a 'Time' object having the value 24:00:00.000000.

    explicit
    Time(int hour,
         int minute = 0,
         int second = 0,
         int millisecond = 0,
         int microsecond = 0);
        // Create a 'Time' object having the (valid) value represented by the
        // specified 'hour', and the optionally specified 'minute', 'second',
        // 'millisecond', and 'microsecond'.  Unspecified arguments default to
        // 0.  The behavior is undefined unless all of the specified values are
        // within their valid ranges (see 'isValid').

    Time(const Time& original);
        // Create a 'Time' object having the value of the specified 'original'
        // time.

    //! ~Time() = default;
        // Destroy this 'Time' object.

    // MANIPULATORS
    Time& operator=(const Time& rhs);
        // Assign to this time object the value of the specified 'rhs' object,
        // and return a reference providing modifiable access to this object.

    Time& operator+=(const DatetimeInterval& rhs);
        // Add to this time object the value of the specified 'rhs' datetime
        // interval, and return a reference providing modifiable access to this
        // object.

    Time& operator-=(const DatetimeInterval& rhs);
        // Subtract from this time object the value of the specified 'rhs'
        // datetime interval, and return a reference providing modifiable
        // access to this object.

    int addHours(int hours);
        // Increase the value of this time object by the specified number of
        // 'hours', and return the (signed) number of times that the
        // 23:59:59.999999 - 00:00:00.000000 boundary was crossed in performing
        // the operation.  Note that 'hours' may be negative.

    int addMinutes(int minutes);
        // Increase the value of this time object by the specified number of
        // 'minutes', and return the (signed) number of times that the
        // 23:59:59.999999 - 00:00:00.000000 boundary was crossed in performing
        // the operation.  Note that 'minutes' may be negative.

    int addSeconds(int seconds);
        // Increase the value of this time object by the specified number of
        // 'seconds', and return the (signed) number of times that the
        // 23:59:59.999999 - 00:00:00.000000 boundary was crossed in performing
        // the operation.  Note that 'seconds' may be negative.

    int addMilliseconds(int milliseconds);
        // Increase the value of this time object by the specified number of
        // 'milliseconds', and return the (signed) number of times that the
        // 23:59:59.999999 - 00:00:00.000000 boundary was crossed in performing
        // the operation.  Note that 'milliseconds' may be negative.

    int addMicroseconds(bsls::Types::Int64 microseconds);
        // Increase the value of this time object by the specified number of
        // 'microseconds', and return the (signed) number of times that the
        // 23:59:59.999999 - 00:00:00.000000 boundary was crossed in performing
        // the operation.  Note that 'microseconds' may be negative.

    int addInterval(const DatetimeInterval& interval);
        // Increase the value of this time object by the specified 'interval'
        // of time, and return the (signed) number of times that the
        // 23:59:59.999999 - 00:00:00.000000 boundary was crossed in performing
        // the operation.  The behavior is undefined unless the number of
        // crossings that would be returned can be represented by an 'int'.

    int addTime(int                hours,
                int                minutes = 0,
                int                seconds = 0,
                int                milliseconds = 0,
                bsls::Types::Int64 microseconds = 0);
        // Add to the value of this time object the specified (signed) number
        // of 'hours', and the optionally specified (signed) numbers of
        // 'minutes', 'seconds', 'milliseconds', and 'microseconds'; return the
        // (signed) number of times that the 23:59:59.999999 -
        // 00:00:00.000000 boundary was crossed in performing the operation.
        // Unspecified arguments default to 0.

    void setHour(int hour);
        // Set the "hour" attribute of this time object to the specified
        // 'hour'; if 'hour' is 24, set the remaining attributes of this object
        // to 0.  The behavior is undefined unless '0 <= hour <= 24'.

    int setHourIfValid(int hour);
        // Set the "hour" attribute of this time object to the specified 'hour'
        // value *if* '0 <= hour <= 24'.  If '24 == hour', set the remaining
        // attributes to 0.  Return 0 on success, and a non-zero value (with no
        // effect) otherwise.

    void setMinute(int minute);
        // Set the "minute" attribute of this time object to the specified
        // 'minute'; if the 'hour' attribute is 24, set the 'hour' attribute to
        // 0.  The behavior is undefined unless '0 <= minute < 60'.

    int setMinuteIfValid(int minute);
        // Set the "minute" attribute of this time object to the specified
        // 'minute' *if* '0 <= minute < 60'; if the 'hour' attribute is 24, set
        // the 'hour' attribute to 0.  Return 0 on success, and a non-zero
        // value (with no effect) otherwise.

    void setSecond(int second);
        // Set the "second" attribute of this time object to the specified
        // 'second'; if the 'hour' attribute is 24, set the 'hour' attribute to
        // 0.  The behavior is undefined unless '0 <= second < 60'.

    int setSecondIfValid(int second);
        // Set the "second" attribute of this time object to the specified
        // 'second' *if* '0 <= second < 60'; if the 'hour' attribute is 24, set
        // the 'hour' attribute to 0.  Return 0 on success, and a non-zero
        // value (with no effect) otherwise.

    void setMillisecond(int millisecond);
        // Set the "millisecond" attribute of this time object to the specified
        // 'millisecond'; if the 'hour' attribute is 24, set the 'hour'
        // attribute to 0.  The behavior is undefined unless
        // '0 <= millisecond < 1000'.

    int setMillisecondIfValid(int millisecond);
        // Set the "millisecond" attribute of this time object to the specified
        // 'millisecond' *if* '0 <= millisecond < 1000'; if the 'hour'
        // attribute is 24, set the 'hour' attribute to 0.  Return 0 on
        // success, and a non-zero value (with no effect) otherwise.

    void setMicrosecond(int microsecond);
        // Set the "microsecond" attribute of this time object to the specified
        // 'microsecond'; if the 'hour' attribute is 24, set the 'hour'
        // attribute to 0.  The behavior is undefined unless
        // '0 <= microsecond < 1000'.

    int setMicrosecondIfValid(int microsecond);
        // Set the "microsecond" attribute of this time object to the specified
        // 'microsecond' *if* '0 <= microsecond < 1000'; if the 'hour'
        // attribute is 24, set the 'hour' attribute to 0.  Return 0 on
        // success, and a non-zero value (with no effect) otherwise.

    void setTime(int hour,
                 int minute = 0,
                 int second = 0,
                 int millisecond = 0,
                 int microsecond = 0);
        // Set the value of this time object to the specified 'hour', and the
        // optionally specified 'minute', 'second', 'millisecond', and
        // 'microsecond'.  Unspecified arguments default to 0.  The behavior is
        // undefined unless all of the specified values are within their valid
        // ranges (see 'isValid').

    int setTimeIfValid(int hour,
                       int minute = 0,
                       int second = 0,
                       int millisecond = 0,
                       int microsecond = 0);
        // Set the value of this time object to the specified 'hour', and the
        // optionally specified 'minute', 'second', 'millisecond', and
        // 'microsecond', if they would comprise a valid 'Time' value (see
        // 'isValid').  Return 0 on success, and a non-zero value (with no
        // effect) otherwise.  Unspecified arguments default to 0.

                                  // Aspects

    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);
        // 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.

    // ACCESSORS
    void getTime(int *hour,
                 int *minute = 0,
                 int *second = 0,
                 int *millisecond = 0,
                 int *microsecond = 0) const;
        // Load, into the specified 'hour', and the optionally specified
        // 'minute', 'second', 'millisecond', and 'microsecond', the respective
        // 'hour', 'minute', 'second', 'millisecond', and 'microsecond'
        // attribute values from this time object.  Unspecified arguments
        // default to 0.  Supplying 0 for an address argument suppresses the
        // loading of the value for the corresponding attribute, but has no
        // effect on the loading of other attribute values.

    int hour() const;
        // Return the value of the 'hour' attribute of this time object.

    int minute() const;
        // Return the value of the 'minute' attribute of this time object.

    int second() const;
        // Return the value of the 'second' attribute of this time object.

    int millisecond() const;
        // Return the value of the 'millisecond' attribute of this time object.

    int microsecond() const;
        // Return the value of the 'microsecond' attribute of this time object.

    int printToBuffer(char *result,
                      int   numBytes,
                      int   fractionalSecondPrecision = 6) const;
        // Efficiently write to the specified 'result' buffer no more than the
        // specified 'numBytes' of a representation of the value of this
        // object.  Optionally specify 'fractionalSecondPrecision' digits to
        // indicate how many fractional second digits to output.  If
        // 'fractionalSecondPrecision' is not specified then 6 fractional
        // second digits will be output (3 digits for milliseconds and 3 digits
        // for microseconds).  Return the number of characters (not including
        // the null character) that would have been written if the limit due to
        // 'numBytes' were not imposed.  'result' is null-terminated unless
        // 'numBytes' is 0.  The behavior is undefined unless '0 <= numBytes',
        // '0 <= fractionalSecondPrecision <= 6', and 'result' refers to at
        // least 'numBytes' contiguous bytes.  Note that the return value is
        // greater than or equal to 'numBytes' if the output representation was
        // truncated to avoid 'result' overrun.

                                  // Aspects

    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;
        // 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.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in a
        // human-readable format, and return a reference 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 this
        // human-readable format is not fully specified, and can change without
        // notice.

#ifndef BDE_OPENSOURCE_PUBLICATION  // pending deprecation

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

#endif // BDE_OPENSOURCE_PUBLICATION -- pending deprecation
#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // BDE2.22
    static int maxSupportedVersion();
        // !DEPRECATED!: Use 'maxSupportedBdexVersion(int)' instead.
        //
        // Return the most current BDEX streaming version number supported by
        // this class.

    bsl::ostream& streamOut(bsl::ostream& stream) const;
        // !DEPRECATED!: Use 'print' instead.
        //
        // Format this time to the specified output 'stream', and return a
        // reference to the modifiable 'stream'.

    int validateAndSetTime(int hour,
                           int minute = 0,
                           int second = 0,
                           int millisecond = 0);
        // !DEPRECATED!: Use 'setTimeIfValid' instead.
        //
        // Set the value of this time object to the specified 'hour', and the
        // optionally specified 'minute', 'second', and 'millisecond', if they
        // would comprise a valid 'Time' value (see 'isValid').  Return 0 on
        // success, and a non-zero value (with no effect) otherwise.
        // Unspecified arguments default to 0.

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE2.22

};

// FREE OPERATORS
template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const Time& object);
    // Pass the specified 'object' to the specified 'hashAlg'.  This function
    // integrates with the 'bslh' modular hashing system and effectively
    // provides a 'bsl::hash' specialization for 'Time'.

Time operator+(const Time& lhs, const DatetimeInterval& rhs);
    // Return a 'Time' value that is the sum of the specified 'lhs' time and
    // the specified 'rhs' datetime interval.

Time operator+(const DatetimeInterval& lhs, const Time& rhs);
    // Return a 'Time' value that is the sum of the specified 'lhs' datetime
    // interval and the specified 'rhs' time.

Time operator-(const Time& lhs, const DatetimeInterval& rhs);
    // Return a 'Time' value that is the difference between the specified 'lhs'
    // time and the specified 'rhs' datetime interval.

DatetimeInterval operator-(const Time& lhs, const Time& rhs);
    // Return a 'DatetimeInterval' object initialized with the difference
    // between the specified 'lhs' and 'rhs' time values.

bool operator==(const Time& lhs, const Time& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' time objects have the
    // same value, and 'false' otherwise.  Two time objects have the same value
    // if each of their corresponding 'hour', 'minute', 'second',
    // 'millisecond', and 'microsecond' attributes respectively have the same
    // value.

bool operator!=(const Time& lhs, const Time& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' time objects do not have
    // the same value, and 'false' otherwise.  Two time objects do not have the
    // same value if any of their corresponding 'hour', 'minute', 'second',
    // 'millisecond', and 'microsecond' attributes respectively do not have the
    // same value.

bool operator<(const Time& lhs, const Time& rhs);
    // Return 'true' if the specified 'lhs' time value is less than the
    // specified 'rhs' time value, and 'false' otherwise.  The behavior is
    // undefined unless 'lhs != Time()' and 'rhs != Time()' (i.e., they do not
    // have the, default, value 24:00:00.000000).

bool operator<=(const Time& lhs, const Time& rhs);
    // Return 'true' if the specified 'lhs' time value is less than or equal to
    // the specified 'rhs' time value, and 'false' otherwise.  The behavior is
    // undefined unless 'lhs != Time()' and 'rhs != Time()' (i.e., they do not
    // have the, default, value 24:00:00.000000).

bool operator>(const Time& lhs, const Time& rhs);
    // Return 'true' if the specified 'lhs' time value is greater than the
    // specified 'rhs' time value, and 'false' otherwise.  The behavior is
    // undefined unless 'lhs != Time()' and 'rhs != Time()' (i.e., they do not
    // have the, default, value 24:00:00.000000).

bool operator>=(const Time& lhs, const Time& rhs);
    // Return 'true' if the specified 'lhs' time value is greater than or equal
    // to the specified 'rhs' time value, and 'false' otherwise.  The behavior
    // is undefined unless 'lhs != Time()' and 'rhs != Time()' (i.e., they do
    // not have the, default, value 24:00:00.000000).

bsl::ostream& operator<<(bsl::ostream& stream, const Time& time);
    // Write the value of the specified 'time' object to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

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

                                 // ----------
                                 // class Time
                                 // ----------

// PRIVATE MANIPULATORS
inline
void Time::setMicrosecondsFromMidnight(bsls::Types::Int64 totalMicroseconds)
{
    BSLS_REVIEW(                        0 <= totalMicroseconds);
    BSLS_REVIEW(TimeUnitRatio::k_US_PER_D >= totalMicroseconds);

    d_value = totalMicroseconds | k_REP_MASK;
}

// PRIVATE ACCESSORS
inline
bsls::Types::Int64 Time::microsecondsFromMidnight() const
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(k_REP_MASK > d_value)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return invalidMicrosecondsFromMidnight();                     // RETURN
    }

    return d_value & (~k_REP_MASK);
}

inline
bsls::Types::Int64 Time::updatedRepresentation() const
{
    return microsecondsFromMidnight() | k_REP_MASK;
}

// CLASS METHODS
inline
bool Time::isValid(int hour,
                   int minute,
                   int second,
                   int millisecond,
                   int microsecond)
{
    return (0 <= hour        && hour    < bdlt::TimeUnitRatio::k_H_PER_D_32 &&
            0 <= minute      && minute  < bdlt::TimeUnitRatio::k_M_PER_H_32 &&
            0 <= second      && second  < bdlt::TimeUnitRatio::k_S_PER_M_32 &&
            0 <= millisecond && millisecond
                                        < bdlt::TimeUnitRatio::k_MS_PER_S_32 &&
            0 <= microsecond && microsecond
                                        < bdlt::TimeUnitRatio::k_US_PER_MS_32)
        || (bdlt::TimeUnitRatio::k_H_PER_D_32 == hour &&
            0                                 == minute &&
            0                                 == second &&
            0                                 == millisecond &&
            0                                 == microsecond);
}

                                  // Aspects

inline
int Time::maxSupportedBdexVersion(int versionSelector)
{
    if (versionSelector >= 20170401) {
        return 2;                                                     // RETURN
    }
    return 1;
}

// CREATORS
inline
Time::Time()
: d_value(TimeUnitRatio::k_US_PER_D | k_REP_MASK)
{
}

inline
Time::Time(int hour, int minute, int second, int millisecond, int microsecond)
{
    BSLS_ASSERT_SAFE(isValid(hour, minute, second, millisecond, microsecond));

    setMicrosecondsFromMidnight(  TimeUnitRatio::k_US_PER_H  * hour
                                + TimeUnitRatio::k_US_PER_M  * minute
                                + TimeUnitRatio::k_US_PER_S  * second
                                + TimeUnitRatio::k_US_PER_MS * millisecond
                                + microsecond);
}

inline
Time::Time(const Time& original)
: d_value(original.d_value)
{
    d_value = updatedRepresentation();
}

// MANIPULATORS
inline
Time& Time::operator=(const Time& rhs)
{
    d_value = rhs.d_value;
    d_value = updatedRepresentation();

    return *this;
}

inline
Time& Time::operator+=(const DatetimeInterval& rhs)
{
    addInterval(rhs);
    return *this;
}

inline
Time& Time::operator-=(const DatetimeInterval& rhs)
{
    addInterval(-rhs);
    return *this;
}

inline
int Time::setHourIfValid(int hour)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (0 <= hour && hour <= 24) {
        setHour(hour);
        return k_SUCCESS;                                             // RETURN
    }
    return k_FAILURE;
}

inline
int Time::setMicrosecondIfValid(int microsecond)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (0 <= microsecond && microsecond <= 999) {
        setMicrosecond(microsecond);
        return k_SUCCESS;                                             // RETURN
    }
    return k_FAILURE;
}

inline
int Time::setMillisecondIfValid(int millisecond)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (0 <= millisecond && millisecond <= 999) {
        setMillisecond(millisecond);
        return k_SUCCESS;                                             // RETURN
    }
    return k_FAILURE;
}

inline
int Time::setMinuteIfValid(int minute)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (0 <= minute && minute <= 59) {
        setMinute(minute);
        return k_SUCCESS;                                             // RETURN
    }
    return k_FAILURE;
}

inline
int Time::setSecondIfValid(int second)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (0 <= second && second <= 59) {
        setSecond(second);
        return k_SUCCESS;                                             // RETURN
    }
    return k_FAILURE;
}

inline
int Time::setTimeIfValid(int hour,
                         int minute,
                         int second,
                         int millisecond,
                         int microsecond)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (isValid(hour, minute, second, millisecond, microsecond)) {
        setTime(hour, minute, second, millisecond, microsecond);
        return k_SUCCESS;                                             // RETURN
    }

    return k_FAILURE;
}

                                  // Aspects

template <class STREAM>
STREAM& Time::bdexStreamIn(STREAM& stream, int version)
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 2: {
            bsls::Types::Int64 tmp = 0;
            stream.getInt64(tmp);

            if (   stream
                && 0 <= tmp
                && TimeUnitRatio::k_US_PER_D >= tmp) {
                setMicrosecondsFromMidnight(tmp);
            }
            else {
                stream.invalidate();
            }
          } break;
          case 1: {
            int tmp = 0;
            stream.getInt32(tmp);

            if (   stream
                && static_cast<unsigned int>(tmp) <=
                   static_cast<unsigned int>(TimeUnitRatio::k_MS_PER_D_32)) {
                setMicrosecondsFromMidnight(TimeUnitRatio::k_US_PER_MS * tmp);
            }
            else {
                stream.invalidate();
            }
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

// ACCESSORS
inline
int Time::hour() const
{
    return static_cast<int>(microsecondsFromMidnight()
                                                  / TimeUnitRatio::k_US_PER_H);
}

inline
int Time::microsecond() const
{
    return static_cast<int>(  microsecondsFromMidnight()
                            % TimeUnitRatio::k_US_PER_MS);
}

inline
int Time::millisecond() const
{
    return static_cast<int>(  microsecondsFromMidnight()
                            / TimeUnitRatio::k_US_PER_MS
                            % TimeUnitRatio::k_MS_PER_S);
}

inline
int Time::minute() const
{
    return static_cast<int>(  microsecondsFromMidnight()
                            / TimeUnitRatio::k_US_PER_M
                            % TimeUnitRatio::k_M_PER_H);
}

inline
int Time::second() const
{
    return static_cast<int>(  microsecondsFromMidnight()
                            / TimeUnitRatio::k_US_PER_S
                            % TimeUnitRatio::k_S_PER_M);
}

                                  // Aspects

template <class STREAM>
STREAM& Time::bdexStreamOut(STREAM& stream, int version) const
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 2: {
            stream.putInt64(microsecondsFromMidnight());
          } break;
          case 1: {
            stream.putInt32(static_cast<int>(microsecondsFromMidnight()
                                                / TimeUnitRatio::k_US_PER_MS));
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

#ifndef BDE_OPENSOURCE_PUBLICATION  // pending deprecation

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

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

inline
bsl::ostream& Time::streamOut(bsl::ostream& stream) const
{
    return stream << *this;
}

inline
int Time::validateAndSetTime(int hour, int minute, int second, int millisecond)
{
    return setTimeIfValid(hour, minute, second, millisecond);
}

#endif  // BDE_OMIT_INTERNAL_DEPRECATED -- BDE2.22

}  // close package namespace

// FREE OPERATORS
inline
bdlt::Time bdlt::operator+(const Time& lhs, const DatetimeInterval& rhs)
{
    Time result(lhs);
    return result += rhs;
}

inline
bdlt::Time bdlt::operator+(const DatetimeInterval& lhs, const Time& rhs)
{
    Time result(rhs);
    return result += lhs;
}

inline
bdlt::Time bdlt::operator-(const Time& lhs, const DatetimeInterval& rhs)
{
    Time result(lhs);
    return result -= rhs;
}

inline
bdlt::DatetimeInterval bdlt::operator-(const Time& lhs, const Time& rhs)
{
    DatetimeInterval timeInterval;

    timeInterval.setTotalMicroseconds(
             lhs.microsecondsFromMidnight() % bdlt::TimeUnitRatio::k_US_PER_D
           - rhs.microsecondsFromMidnight() % bdlt::TimeUnitRatio::k_US_PER_D);

    return timeInterval;
}

inline
bool bdlt::operator==(const Time& lhs, const Time& rhs)
{
    bsls::Types::Int64 lhsValue = lhs.microsecondsFromMidnight();
    bsls::Types::Int64 rhsValue = rhs.microsecondsFromMidnight();

    return lhsValue == rhsValue;
}

inline
bool bdlt::operator!=(const Time& lhs, const Time& rhs)
{
    bsls::Types::Int64 lhsValue = lhs.microsecondsFromMidnight();
    bsls::Types::Int64 rhsValue = rhs.microsecondsFromMidnight();

    return lhsValue != rhsValue;
}

inline
bool bdlt::operator<(const Time& lhs, const Time& rhs)
{
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != lhs.microsecondsFromMidnight());
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != rhs.microsecondsFromMidnight());

    bsls::Types::Int64 lhsValue = lhs.microsecondsFromMidnight();
    bsls::Types::Int64 rhsValue = rhs.microsecondsFromMidnight();

    return lhsValue < rhsValue;
}

inline
bool bdlt::operator<=(const Time& lhs, const Time& rhs)
{
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != lhs.microsecondsFromMidnight());
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != rhs.microsecondsFromMidnight());

    bsls::Types::Int64 lhsValue = lhs.microsecondsFromMidnight();
    bsls::Types::Int64 rhsValue = rhs.microsecondsFromMidnight();

    return lhsValue <= rhsValue;
}

inline
bool bdlt::operator>(const Time& lhs, const Time& rhs)
{
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != lhs.microsecondsFromMidnight());
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != rhs.microsecondsFromMidnight());

    bsls::Types::Int64 lhsValue = lhs.microsecondsFromMidnight();
    bsls::Types::Int64 rhsValue = rhs.microsecondsFromMidnight();

    return lhsValue > rhsValue;
}

inline
bool bdlt::operator>=(const Time& lhs, const Time& rhs)
{
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != lhs.microsecondsFromMidnight());
    BSLS_ASSERT_SAFE(bdlt::TimeUnitRatio::k_US_PER_D
                                            != rhs.microsecondsFromMidnight());

    bsls::Types::Int64 lhsValue = lhs.microsecondsFromMidnight();
    bsls::Types::Int64 rhsValue = rhs.microsecondsFromMidnight();

    return lhsValue >= rhsValue;
}

inline
bsl::ostream& bdlt::operator<<(bsl::ostream& stream, const Time& time)
{
    return time.print(stream, 0, -1);
}

// FREE FUNCTIONS
template <class HASHALG>
inline
void bdlt::hashAppend(HASHALG& hashAlg, const Time& object)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlg, object.microsecondsFromMidnight());
}

}  // close enterprise namespace

namespace bsl {

// TRAITS
template <>
struct is_trivially_copyable<BloombergLP::bdlt::Time> : bsl::true_type {
    // This template specialization for 'is_trivially_copyable' indicates that
    // 'bdlt::Time' is a trivially copyable type.
};

}  // close namespace bsl

#endif

// ----------------------------------------------------------------------------
// Copyright 2017 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 ----------------------------------