// bdlt_timetable.h                                                   -*-C++-*-
#ifndef INCLUDED_BDLT_TIMETABLE
#define INCLUDED_BDLT_TIMETABLE

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

//@PURPOSE: Provide a repository for accessing timetable information.
//
//@CLASSES:
//  bdlt::Timetable: repository for accessing timetable information
//  bdlt::TimetableTransition: datetime and transition code value
//
//@DESCRIPTION: This component provides a value-semantic class,
// 'bdlt::Timetable', that represents a timetable of state transitions over a
// *valid* *range* of dates, an associated iterator,
// 'bdlt::Timetable::const_iterator', that provides non-modifiable access to
// the timetable's state transitions, and a class, 'bdlt::TimetableTransition',
// that represents a change of state at a datetime.
//
// 'bdlt::Timetable' is designed to be especially efficient at determining the
// state in effect at a given 'bdlt::Datetime' value (within the valid range
// for a particular 'bdlt::Timetable' object), and iterating through the state
// transitions.
//
// 'bdlt::TimetableTransition' consists of a 'bdlt::Datetime' and a (single)
// non-negative integral code (of type 'int') that defines the "state" that
// becomes effective at that datetime.  The meaning of the integral code
// ascribed to each transition is defined by the client.  There can be at most
// one 'bdlt::TimetableTransition' defined for any datetime value within the
// range of a 'bdlt::Timetable'.  Consequently, there is at most one
// (client-defined) state in effect at any datetime in a timetable.
//
// Default-constructed timetables are empty, and have an empty valid range.
// Timetables can also be constructed with an initial (non-empty) valid range.
// The 'setValidRange' method modifies the valid range of a timetable, and a
// suite of "add" methods can be used to populate a timetable with state
// transitions.
//
// Timetables are value-semantic objects, and, as such, necessarily support all
// of the standard value-semantic operations, such as default construction,
// copy construction and copy assignment, and equality comparison.
//
///Exception-Safety Guarantees
///---------------------------
// All methods of 'bdlt::Timetable' are exception-safe, but in general provide
// only the basic guarantee (i.e., no guarantee of rollback): If an exception
// occurs (i.e., while attempting to allocate memory), the timetable object is
// left in a coherent state, but (unless otherwise specified) its *value* is
// undefined.
//
// All methods of 'bdlt::TimetableTransition' are exception-safe.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: 'Exchange Schedule'
/// - - - - - - - - - - - - - - -
// Suppose we want to track the open and close times for an exchange.  Most
// Mondays (and Tuesdays, Wednesdays, etc.) will have the same schedule,
// although some may differ.  We can use 'bdlt::Timetable' to efficiently store
// this data.
//
// First, we create an instance of 'bdlt::Timetable' with the desired valid
// range:
//..
//  bdlt::Timetable timetable(bdlt::Date(2018, 1, 1),
//                            bdlt::Date(2018, 12, 31));
//..
// Then, we define the codes for start-of-trading and end-of-trading and
// populate the typical transitions into the timetable:
//..
//  const int k_TRADING    = 0;
//  const int k_NO_TRADING = 1;
//
//  timetable.setInitialTransitionCode(k_NO_TRADING);
//
//  for (int i = 0; i < 5; ++ i) {
//      timetable.addTransitions(static_cast<bdlt::DayOfWeek::Enum>(
//                                                 bdlt::DayOfWeek::e_MON + i),
//                               bdlt::Time(8, 30),
//                               k_TRADING,
//                               timetable.firstDate(),
//                               timetable.lastDate());
//
//      timetable.addTransitions(static_cast<bdlt::DayOfWeek::Enum>(
//                                                 bdlt::DayOfWeek::e_MON + i),
//                               bdlt::Time(16, 30),
//                               k_NO_TRADING,
//                               timetable.firstDate(),
//                               timetable.lastDate());
//  }
//..
// Next, we add a holiday on January 19, 2018:
//..
//  timetable.removeTransitions(bdlt::Date(2018, 1, 19));
//..
// Then, we add a half-day on November 23, 2018:
//..
//  timetable.addTransition(bdlt::Datetime(2018, 11, 23, 12, 30),
//                          k_NO_TRADING);
//
//  timetable.removeTransition(bdlt::Datetime(2018, 11, 23, 16, 30));
//..
// Finally, we verify the transition code in effect at a few datetimes.
//..
//  assert(k_NO_TRADING == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018,  1, 15,  8,  0)));
//
//  assert(k_TRADING    == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018,  1, 15,  8, 30)));
//
//  assert(k_TRADING    == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018,  1, 15, 16,  0)));
//
//  assert(k_NO_TRADING == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018,  1, 15, 16, 30)));
//
//  assert(k_NO_TRADING == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018, 11, 23,  8,  0)));
//
//  assert(k_TRADING    == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018, 11, 23,  8, 30)));
//
//  assert(k_TRADING    == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018, 11, 23, 12,  0)));
//
//  assert(k_NO_TRADING == timetable.transitionCodeInEffect(
//                                      bdlt::Datetime(2018, 11, 23, 12, 30)));
//..

#include <bdlscm_version.h>

#include <bdlt_date.h>
#include <bdlt_datetime.h>
#include <bdlt_dayofweek.h>
#include <bdlt_time.h>

#include <bdlc_compactedarray.h>

#include <bslalg_swaputil.h>

#include <bslh_hash.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bsls_assert.h>
#include <bsls_review.h>

#include <bsl_algorithm.h>
#include <bsl_cstddef.h>
#include <bsl_iosfwd.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace bdlt {

// FORWARD DECLARATIONS
class Timetable;
class Timetable_Day;
class Timetable_ConstIterator;

                        // =========================
                        // class TimetableTransition
                        // =========================

class TimetableTransition {
    // This simply-constrained attribute class represents a state transition,
    // implemented as a datetime for when the transition occurs, and a code to
    // indicate the new state.

    // DATA
    Datetime d_datetime;  // datetime of the transition
    int      d_code;      // code in effect at, and after, 'd_datetime'

    // FRIENDS
    friend class TimetableTransition_Ref;
    friend class Timetable_ConstIterator;

  private:
    // PRIVATE CREATORS
    TimetableTransition();
        // Create a 'TimetableTransition' having datetime value
        // 'Datetime(Date())' and code 'k_UNSET_TRANSITION_CODE'.

    TimetableTransition(const Datetime& datetime, int code);
        // Create a 'TimetableTransition' having the specified 'datetime' and
        // 'code'.  The behavior is undefined unless '24 > datetime.hour()' and
        // '0 <= code || k_UNSET_TRANSITION_CODE == code'.

  public:
    // CONSTANTS
    enum { k_UNSET_TRANSITION_CODE = -1 };  // value representing an unset
                                            // transition code

    // CREATORS
    TimetableTransition(const TimetableTransition& original);
        // Create a 'TimetableTransition' having the same value as the
        // specified 'original' object.

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

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

    // ACCESSORS
    const Datetime& datetime() const;
        // Return the datetime of this transition.

    int code() const;
        // Return the code of this transition.

                             // Aspects

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level' and return a
        // reference to the modifiable 'stream'.  If 'level' is specified,
        // optionally specify 'spacesPerLevel', 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.
};

// FREE OPERATORS
bool operator==(const TimetableTransition& lhs,
                const TimetableTransition& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' timetable transitions
    // have the same value, and 'false' otherwise.  Two timetable transitions
    // have the same value if they have the same datetime and code.

bool operator!=(const TimetableTransition& lhs,
                const TimetableTransition& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' timetable transitions do
    // not have the same value, and 'false' otherwise.  Two timetable
    // transitions do not have the same value if they do not have the same
    // datetime or the same code.

bool operator<(const TimetableTransition& lhs,
               const TimetableTransition& rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', and 'false' otherwise.  Timetable transition 'lhs' has a value
    // less than timetable transition 'rhs' if
    // 'lhs.datetime() < rhs.datetime()', or 'lhs.datetime() == rhs.datetime()'
    // and 'lhs.code() < rhs.code()'.

bool operator<(const TimetableTransition& lhs, const Datetime& rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', and 'false' otherwise.  Timetable transition 'lhs' has a value
    // less than datetime 'rhs' if 'lhs.datetime() < rhs'.  The behavior is
    // undefined unless '24 > rhs.hour()'.

bool operator<(const Datetime& lhs, const TimetableTransition& rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', and 'false' otherwise.  Datetime 'lhs' has a value less than
    // timetable transition 'rhs' if 'lhs < rhs.datetime()'.  The behavior is
    // undefined unless '24 > lhs.hour()'.

// HASH SPECIALIZATIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const TimetableTransition& 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 'TimetableTransition'.

                      // =============================
                      // class TimetableTransition_Ref
                      // =============================

class TimetableTransition_Ref : public TimetableTransition {
    // This private class is used by the arrow operator of the timetable
    // iterator class.  The objects instantiated from this class serve as
    // references to 'TimetableTransition' objects.

    friend class Timetable_ConstIterator;

  private:
    // NOT IMPLEMENTED
    TimetableTransition_Ref& operator=(const TimetableTransition_Ref&);

    // PRIVATE CREATORS
    TimetableTransition_Ref();
        // Create a timetable transition reference object using the default
        // 'TimetableTransition' constructor.

  public:
    // CREATORS
    explicit TimetableTransition_Ref(const TimetableTransition& transition);
        // Create a timetable transition reference object using the specified
        // 'transition'.

    TimetableTransition_Ref(const TimetableTransition_Ref& original);
        // Create a timetable transition reference object having the value of
        // the specified 'original' object.

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

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

                  // =====================================
                  // class Timetable_CompactableTransition
                  // =====================================

class Timetable_CompactableTransition {
    // This simply-constrained attribute class represents a state transition,
    // implemented as a time for when the transition occurs, and a code to
    // indicate the new state.

    // DATA
    Time d_time;  // time of the transition
    int  d_code;  // code in effect at, and after, 'd_time'

    // FRIENDS
    friend class Timetable;
    friend class Timetable_Day;

  public:
    // CONSTANTS
    enum { k_UNSET_TRANSITION_CODE =
                                TimetableTransition::k_UNSET_TRANSITION_CODE };
                                            // value representing an unset
                                            // transition code

    // CREATORS
    Timetable_CompactableTransition();
        // Create a 'Timetable_CompactableTransition' having time value
        // 'Time(0)' and code 'k_UNSET_TRANSITION_CODE'.

    Timetable_CompactableTransition(const Time& time, int code);
        // Create a 'Timetable_CompactableTransition' having the specified
        // 'time' and 'code'.  The behavior is undefined unless
        // '24 > time.hour()' and
        // '0 <= code || k_UNSET_TRANSITION_CODE == code'.

    Timetable_CompactableTransition(
                              const Timetable_CompactableTransition& original);
        // Create a 'Timetable_CompactableTransition' having the same value as
        // the specified 'original' object.

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

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

    // ACCESSORS
    const Time& time() const;
        // Return the time of this compactable transition.

    int code() const;
        // Return the code of this compactable transition.

                             // Aspects

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level' and return a
        // reference to the modifiable 'stream'.  If 'level' is specified,
        // optionally specify 'spacesPerLevel', 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.
};

// FREE OPERATORS
bool operator==(const Timetable_CompactableTransition& lhs,
                const Timetable_CompactableTransition& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' compactable transitions
    // have the same value, and 'false' otherwise.  Two compactable transitions
    // have the same value if they have the same time and code.

bool operator!=(const Timetable_CompactableTransition& lhs,
                const Timetable_CompactableTransition& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' compactable transitions
    // do not have the same value, and 'false' otherwise.  Two compactable
    // transitions do not have the same value if they do not have the same time
    // or the same code.

bool operator<(const Timetable_CompactableTransition& lhs,
               const Timetable_CompactableTransition& rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', and 'false' otherwise.  Compactable transition 'lhs' has a value
    // less than compactable transition 'rhs' if 'lhs.time() < rhs.time()', or
    // 'lhs.time() == rhs.time()' and 'lhs.code() < rhs.code()'.

bool operator<(const Timetable_CompactableTransition& lhs,
               const Time&                            rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', and 'false' otherwise.  Compactable transition 'lhs' has a value
    // less than time 'rhs' if 'lhs.time() < rhs'.  The behavior is undefined
    // unless '24 > rhs.hour()'.

bool operator<(const Time&                            lhs,
               const Timetable_CompactableTransition& rhs);
    // Return 'true' if the specified 'lhs' has a value less than the specified
    // 'rhs', and 'false' otherwise.  Time 'lhs' has a value less than
    // compactable transition 'rhs' if 'lhs < rhs.time()'.  The behavior is
    // undefined unless '24 > lhs.hour()'.

// HASH SPECIALIZATIONS
template <class HASHALG>
void hashAppend(HASHALG&                               hashAlg,
                const Timetable_CompactableTransition& 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
    // 'Timetable_CompactableTransition'.

                           // ===================
                           // class Timetable_Day
                           // ===================

class Timetable_Day {
    // This class implements a value-semantic repository of time-indexed state
    // transitions over one date (this class implements one day of a
    // timetable).  A 'Timetable_Day' can be "populated" with state transitions
    // via the 'addTransition' method, and queried for the transition code in
    // effect at a specified time via the 'transitionCodeInEffect' method.
    // Note that, as an optimization for the 'transitionCodeInEffect' method,
    // the transition code in effect before the first possible transition is
    // stored in 'd_initialTransitionCode'.

    // DATA
    int                                          d_initialTransitionCode;
                                             // transition code in effect at
                                             // the start of this daily
                                             // timetable

    bsl::vector<Timetable_CompactableTransition> d_transitions;
                                             // ordered vector of transitions

    // FRIENDS
    friend class Timetable_ConstIterator;

    friend bool operator==(const Timetable_Day&, const Timetable_Day&);
    friend bool operator!=(const Timetable_Day&, const Timetable_Day&);
    friend bool operator< (const Timetable_Day&, const Timetable_Day&);

    template <class HASHALG>
    friend void hashAppend(HASHALG&, const Timetable_Day&);

  public:
    // CONSTANTS
    enum { k_UNSET_TRANSITION_CODE =
                                TimetableTransition::k_UNSET_TRANSITION_CODE };
                                            // value representing an unset
                                            // transition code

    // CREATORS
    explicit
    Timetable_Day(bslma::Allocator *basicAllocator = 0);
        // Create an empty 'Timetable_Day' (i.e., a daily timetable having no
        // transitions) whose initial transition code is
        // 'k_UNSET_TRANSITION_CODE'.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

    Timetable_Day(const Timetable_Day&  original,
                  bslma::Allocator     *basicAllocator = 0);
        // Create a 'Timetable_Day' having the same value as the specified
        // 'original' object.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

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

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

    bool addTransition(const Time& time, int code);
        // Add a transition to this daily timetable at the specified 'time'
        // having the specified 'code'.  If 'time' is already a transition
        // point, replace the existing code with 'code'.  Return 'true' if the
        // value returned by 'finalTransitionCode()' prior to this operation is
        // not equal to the value returned by 'finalTransitionCode()' after
        // this operation, and 'false' otherwise.  The behavior is undefined
        // unless '24 > time.hour()' and
        // '0 <= code || k_UNSET_TRANSITION_CODE == code'.

    bool removeAllTransitions();
        // Remove all transitions from this daily timetable.  Return 'true' if
        // the value returned by 'finalTransitionCode()' prior to this
        // operation is not equal to the value returned by
        // 'finalTransitionCode()' after this operation, and 'false' otherwise.

    bool removeTransition(const Time& time);
        // If a transition occurs at the specified 'time', remove the
        // transition from this daily timetable.  Otherwise, return without
        // modifying this daily timetable.  Return 'true' if the value returned
        // by 'finalTransitionCode()' prior to this operation is not equal to
        // the value returned by 'finalTransitionCode()' after this operation,
        // and 'false' otherwise.  The behavior is undefined unless
        // '24 > time.hour()'.

    bool setInitialTransitionCode(int code);
        // Set the transition code in effect prior to the start of this daily
        // timetable to the specified 'code'.  Return 'true' if the value
        // returned by 'finalTransitionCode()' prior to this operation is not
        // equal to the value returned by 'finalTransitionCode()' after this
        // operation, and 'false' otherwise.  The behavior is undefined unless
        // '0 <= code || k_UNSET_TRANSITION_CODE == code'.

    // ACCESSORS
    int finalTransitionCode() const;
        // Return the transition code that is in effect at the end of this
        // daily timetable.  Note that if this daily timetable has no
        // transitions, 'initialTransitionCode()' is returned.

    int initialTransitionCode() const;
        // Return the transition code in effect prior to the start of this
        // daily timetable.

    bsl::size_t size() const;
        // Return the number of transitions in this daily timetable.

    int transitionCodeInEffect(const Time& time) const;
        // Return the transition code associated with the latest transition
        // that occurs on or before the specified 'time' in this daily
        // timetable.  If this daily timetable has no such transition, return
        // 'initialTransitionCode()'.  The behavior is undefined unless
        // '24 > time.hour()'.
};

// FREE OPERATORS
bool operator==(const Timetable_Day& lhs, const Timetable_Day& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' daily timetables have the
    // same value, and 'false' otherwise.  Two daily timetables have the same
    // value if they have the same initial transition code, the same number of
    // transitions, and each corresponding pair of transitions has the same
    // value.

bool operator!=(const Timetable_Day& lhs, const Timetable_Day& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' daily timetables do not
    // have the same value, and 'false' otherwise.  Two daily timetables do not
    // have the same value if they do not have the same initial transition
    // code, they do not have the same number of transitions, or there is a
    // corresponding pair of transitions that do not have the same value.

bool operator<(const Timetable_Day& lhs, const Timetable_Day& rhs);
    // Return 'true' if the specified 'lhs' daily timetable is less than the
    // specified 'rhs' daily timetable, and 'false' otherwise.  The 'lhs' daily
    // timetable is less than the 'rhs' daily timetable if
    // 'lhs.initialTransitionCode() < rhs.initialTransitionCode()', or
    // 'lhs.initialTransitionCode() == rhs.initialTransitionCode()' and
    // 'lhs.d_transitions < rhs.d_transitions'.

// HASH SPECIALIZATIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const Timetable_Day& 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 'Timetable_Day'.

                             // ===============
                             // class Timetable
                             // ===============

class Timetable {
    // This class implements a value-semantic repository of datetime-indexed
    // state transitions over a *valid* *range* of dates.  This valid range,
    // '[firstDate() .. lastDate()]', spans the first and last dates of a
    // timetable's accessible contents.  A timetable can be "populated" with
    // state transitions via a suite of "add" methods.  Note that the behavior
    // of requesting *any* timetable information for a supplied date whose
    // value is outside the current *valid* *range* for that timetable is
    // undefined.

    // DATA
    Date                                d_firstDate;  // start of valid range

    Date                                d_lastDate;   // end of valid range

    int                                 d_initialTransitionCode;
                                                      // transition code in
                                                      // effect *before* the
                                                      // valid range

    bdlc::CompactedArray<Timetable_Day> d_timetable;  // daily timetables

    // FRIENDS
    friend class Timetable_ConstIterator;

    friend bool operator==(const Timetable&, const Timetable&);
    friend bool operator!=(const Timetable&, const Timetable&);

    template <class HASHALG>
    friend void hashAppend(HASHALG&, const Timetable&);

  public:
    // CONSTANTS
    enum { k_UNSET_TRANSITION_CODE =
                                TimetableTransition::k_UNSET_TRANSITION_CODE };
                                            // value representing an unset
                                            // transition code

    // TYPES
    typedef Timetable_ConstIterator const_iterator;

    // CREATORS
    explicit Timetable(bslma::Allocator *basicAllocator = 0);
        // Create an empty 'Timetable' (i.e., a timetable having no
        // transitions) whose initial transition code is
        // 'k_UNSET_TRANSITION_CODE'.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

    Timetable(
             const Date&       firstDate,
             const Date&       lastDate,
             int               initialTransitionCode = k_UNSET_TRANSITION_CODE,
             bslma::Allocator *basicAllocator = 0);
        // Create a timetable having a valid range from the specified
        // 'firstDate' through the specified 'lastDate' and having the
        // optionally specified 'initialTransitionCode'.  If
        // 'initialTransitionCode' is not specified, the initial transition
        // code is set to 'k_UNSET_TRANSITION_CODE'.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  The behavior is
        // undefined unless 'firstDate <= lastDate', and
        // '0 <= initialTransitionCode' or
        // 'k_UNSET_TRANSITION_CODE == initialTransitionCode'.

    Timetable(const Timetable& original, bslma::Allocator *basicAllocator = 0);
        // Create a timetable having the value of the specified 'original'
        // timetable.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.

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

    // MANIPULATORS
    Timetable& operator=(const Timetable& rhs);
        // Assign to this timetable the value of the specified 'rhs' timetable,
        // and return a reference providing modifiable access to this
        // timetable.  This operation invalidates all iterators.

    void addTransition(const Date& date, const Time& time, int code);
        // Add a transition to this timetable on the specified 'date' at the
        // specified 'time' having the specified 'code'.  If 'time' is already
        // a transition point on 'date', replace the existing code with 'code'.
        // The addition of a transition, but not the replacement of the code of
        // an existing transition, invalidates all iterators.  The behavior is
        // undefined unless '24 > time.hour()', 'date' is within the valid
        // range of this timetable, and
        // '0 <= code || k_UNSET_TRANSITION_CODE == code'.

    void addTransition(const Datetime& datetime, int code);
        // Add a transition to this timetable at the specified 'datetime'
        // having the specified 'code'.  If 'datetime' is already a transition
        // point, replace the existing code with 'code'.  The addition of a
        // transition, but not the replacement of the code of an existing
        // transition, invalidates all iterators.  The behavior is undefined
        // unless '24 > datetime.hour()', 'datetime.date()' is within the valid
        // range of this timetable, and
        // '0 <= code || k_UNSET_TRANSITION_CODE == code'.

    void addTransitions(const DayOfWeek::Enum& dayOfWeek,
                        const Time&            time,
                        int                    code,
                        const Date&            firstDate,
                        const Date&            lastDate);
        // Add transitions to this timetable that occur at the specified
        // 'time', having the specified 'code', on all dates that are of the
        // specified 'dayOfWeek' within the closed interval of dates from the
        // specified 'firstDate' to the specified 'lastDate'.  For every date
        // on which this transition will occur, if 'time' is already a
        // transition point, replace the existing code with 'code'.  The
        // addition of a transition, but not the replacement of the code of an
        // existing transition, invalidates all iterators.  The behavior is
        // undefined unless '24 > time.hour()', 'firstDate <= lastDate',
        // 'firstDate' and 'lastDate' are within the valid range of this
        // timetable, and '0 <= code || k_UNSET_TRANSITION_CODE == code'.

    void removeAllTransitions();
        // Remove all transitions from this timetable.  The removal of a
        // transition invalidates all iterators.

    void removeTransition(const Date& date, const Time& time);
        // If a transition occurs on the specified 'date' at the specified
        // 'time', remove the transition from this timetable.  Otherwise,
        // return without modifying this timetable.  The removal of a
        // transition invalidates all iterators.  The behavior is undefined
        // unless '24 > time.hour()' and 'date' is within the valid range of
        // this timetable.

    void removeTransition(const Datetime& datetime);
        // If a transition occurs at the specified 'datetime', remove the
        // transition from this timetable.  Otherwise, return without modifying
        // this timetable.  The removal of a transition invalidates all
        // iterators.  The behavior is undefined unless '24 > datetime.hour()'
        // and 'datetime.date()' is within the valid range of this timetable.

    void removeTransitions(const Date& date);
        // Remove all transitions from this timetable that occur on the
        // specified 'date'.  The removal of a transition invalidates all
        // iterators.  The behavior is undefined unless 'date' is within the
        // valid range of this timetable.

    void removeTransitions(const DayOfWeek::Enum& dayOfWeek,
                           const Time&            time,
                           const Date&            firstDate,
                           const Date&            lastDate);
        // Remove all transitions from this timetable that occur at the
        // specified 'time' on all dates that are of the specified 'dayOfWeek'
        // within the closed interval of dates from the specified 'firstDate'
        // to the specified 'lastDate'.  The removal of a transition
        // invalidates all iterators.  The behavior is undefined unless
        // '24 > time.hour()', 'firstDate <= lastDate', and 'firstDate' and
        // 'lastDate' are within the valid range of this timetable.

    void reset();
        // Reset this timetable to the default constructed (empty) state.  All
        // associated iterators are invalidated.

    void setInitialTransitionCode(int code);
        // Set the transition code in effect at the start of this timetable to
        // the specified 'code'.  The behavior is undefined unless
        // '0 <= code || k_UNSET_TRANSITION_CODE == code'.

    void setValidRange(const Date& firstDate, const Date& lastDate);
        // Set the range of this timetable using the specified 'firstDate' and
        // 'lastDate' as, respectively, the first date and the last date of the
        // timetable.  Any transitions, and associated transition codes, that
        // are outside of the new range are removed.  The removal of a
        // transition invalidates all iterators.  The behavior is undefined
        // unless 'firstDate <= lastDate'.

                                  // Aspects

    void swap(Timetable& other);
        // Efficiently exchange the value of this object with the value of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    // ACCESSORS
    const_iterator begin() const;
        // Return an iterator referring to the first transition in this
        // timetable, or the past-the-end iterator if this timetable is empty.
        // The iterator remains valid as long as this timetable exists, and the
        // number of transitions within this timetable does not change.

    const_iterator end() const;
        // Return the past-the-end iterator for this timetable.  The iterator
        // remains valid as long as this timetable exists, and the number of
        // transitions within this timetable does not change.

    const Date& firstDate() const;
        // Return a 'const' reference to the earliest date in the valid range
        // of this timetable.  The behavior is undefined if this timetable does
        // not have a valid range (i.e., it is in the default constructed
        // (empty) state).

    int initialTransitionCode() const;
        // Return the transition code that is in effect at the start of this
        // timetable (see 'setInitialTransitionCode').

    bool isInRange(const Date& date) const;
        // Return 'true' if the specified 'date' is within the valid range of
        // this timetable, and 'false' otherwise.

    const Date& lastDate() const;
        // Return a 'const' reference to the latest date in the valid range of
        // this timetable.  The behavior is undefined if this timetable does
        // not have a valid range (i.e., it is in the default constructed
        // (empty) state).

    int length() const;
        // Return the number of days in the valid range of this timetable,
        // which is defined to be 0 if this timetable is empty, and
        // 'lastDate() - firstDate() + 1' otherwise.

    int transitionCodeInEffect(const Date& date, const Time& time) const;
        // Return the transition code associated with the latest transition
        // that occurs on or before the specified 'date' and 'time' in this
        // timetable.  If this timetable has no such transition, return
        // 'initialTransitionCode()'.  The behavior is undefined unless
        // '24 > time.hour()' and 'date' is within the valid range of this
        // timetable.

    int transitionCodeInEffect(const Datetime& datetime) const;
        // Return the transition code associated with the latest transition
        // that occurs on or before the specified 'datetime' in this timetable.
        // If this timetable has no such transition, return
        // 'initialTransitionCode()'.  The behavior is undefined unless
        // '24 > datetime.hour()' and 'datetime.date()' is within the valid
        // range of this timetable.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to supply memory.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level' and return a
        // reference to the modifiable 'stream'.  If 'level' is specified,
        // optionally specify 'spacesPerLevel', 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.
};

// FREE OPERATORS
bool operator==(const Timetable& lhs, const Timetable& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' timetables have the same
    // value, and 'false' otherwise.  Two timetables have the same value if
    // they have the same initial transition code, the same valid range (or are
    // both empty), the same number of transitions, and each corresponding pair
    // of transitions have the same value.

bool operator!=(const Timetable& lhs, const Timetable& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' timetables do not have
    // the same value, and 'false' otherwise.  Two timetables do not have the
    // same value if they do not have the same initial transition code, do not
    // have the same valid range (and are not both empty), do not have the same
    // number of transitions, or, for at least one corresponding pair of
    // transitions, do not have the same value.

bsl::ostream& operator<<(bsl::ostream& stream, const Timetable& timetable);
    // Write the value of the specified 'timetable' to the specified output
    // 'stream', and return a reference to the modifiable 'stream'.

// FREE FUNCTIONS
void swap(Timetable& a, Timetable& b);
    // Exchange the values of the specified 'a' and 'b' objects.  This function
    // provides the no-throw exception-safety guarantee if the two objects were
    // created with the same allocator and the basic guarantee otherwise.

// HASH SPECIALIZATIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const Timetable& 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 'Timetable'.

                      // =============================
                      // class Timetable_ConstIterator
                      // =============================

class Timetable_ConstIterator {
    // Provide read-only, sequential access in increasing (chronological) order
    // to the transitions in a 'Timetable' object.

    // DATA
    const Timetable                 *d_timetable_p;      // pointer to the
                                                         // 'Timetable' into
                                                         // which this iterator
                                                         // references

    bsl::size_t                      d_dayIndex;         // index of the
                                                         // referenced daily
                                                         // timetable

    bsl::size_t                      d_transitionIndex;  // index of the
                                                         // referenced
                                                         // transition in the
                                                         // referenced daily
                                                         // timetable

    mutable TimetableTransition_Ref  d_ref;              // cached value used
                                                         // for the return
                                                         // value of
                                                         // 'operator->()'

    // FRIENDS
    friend class Timetable;

    friend bool operator==(const Timetable_ConstIterator&,
                           const Timetable_ConstIterator&);
    friend bool operator!=(const Timetable_ConstIterator&,
                           const Timetable_ConstIterator&);

  private:
    // PRIVATE CREATORS
    Timetable_ConstIterator(const Timetable& timetable,
                            bsl::size_t      dayIndex,
                            bsl::size_t      transitionIndex);
        // Create a transition iterator for the specified 'timetable' that
        // refers to the transition at the specified 'transitionIndex' on the
        // day at the specified 'dayIndex' in 'timetable'.

  public:
    // TYPES
    typedef TimetableTransition      value_type;
    typedef TimetableTransition_Ref *pointer;
    typedef TimetableTransition      reference;
        // The star operator returns a 'TimetableTransition' *by* *value*.

    // CREATORS
    Timetable_ConstIterator();
        // Create a default iterator.  Note that the behavior of most methods
        // is undefined when used on a default-constructed iterator.

    Timetable_ConstIterator(const Timetable_ConstIterator& original);
        // Create an iterator having the value of the specified 'original'
        // iterator.

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

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

    Timetable_ConstIterator& operator++();
        // Advance this iterator to refer to the next transition in the
        // associated timetable, and return a reference providing modifiable
        // access to this object.  The behavior is undefined unless, on entry,
        // this iterator references a valid transition.

    Timetable_ConstIterator& operator--();
        // Regress this iterator to refer to the previous transition in the
        // associated timetable, and return a reference providing modifiable
        // access to this object.  The behavior is undefined unless, on entry,
        // this iterator references a valid transition that is not the first
        // transition of the associated timetable.

    // ACCESSORS
    TimetableTransition operator*() const;
        // Return, *by* *value*, a 'TimetableTransition' object representing
        // the transition referenced by this iterator.  The behavior is
        // undefined unless this iterator references a valid transition in the
        // associated timetable.

    const TimetableTransition_Ref *operator->() const;
        // Return a proxy to the transition referenced by this iterator.  The
        // behavior is undefined unless this iterator references a valid
        // transition in the associated timetable.
};

// FREE OPERATORS
Timetable_ConstIterator operator++(Timetable_ConstIterator& iterator, int);
    // Advance the specified 'iterator' to refer to the next transition in the
    // referenced timetable, and return an iterator referring to the original
    // element (*before* the advancement).  The behavior is undefined unless,
    // on entry, 'iterator' references a valid transition.

Timetable_ConstIterator operator--(Timetable_ConstIterator& iterator, int);
    // Regress the specified 'iterator' to refer to the previous transition in
    // the referenced timetable, and return an iterator referring to the
    // original element (*before* the decrementation).  The behavior is
    // undefined unless, on entry, 'iterator' references a valid transition
    // that is not the first transition of the associated timetable.

bool operator==(const Timetable_ConstIterator& lhs,
                const Timetable_ConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators have the same
    // value, and 'false' otherwise.  Two 'Timetable_ConstIterator' iterators
    // have the same value if they refer to the same timetable and the same
    // transition.

bool operator!=(const Timetable_ConstIterator& lhs,
                const Timetable_ConstIterator& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators do not have the
    // same value, and 'false' otherwise.  Two 'Timetable_ConstIterator'
    // iterators do not have the same value if they do not refer to the same
    // timetable, or do not refer to the same transition.

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

                        // -------------------------
                        // class TimetableTransition
                        // -------------------------

// PRIVATE CREATORS
inline
TimetableTransition::TimetableTransition()
: d_datetime(Date())
, d_code(k_UNSET_TRANSITION_CODE)
{
}

inline
TimetableTransition::TimetableTransition(const Datetime& datetime, int code)
: d_datetime(datetime)
, d_code(code)
{
    BSLS_ASSERT(24 > datetime.hour());

    BSLS_ASSERT(0 <= code || k_UNSET_TRANSITION_CODE == code);
}

// CREATORS
inline
TimetableTransition::TimetableTransition(const TimetableTransition& original)
: d_datetime(original.d_datetime)
, d_code(original.d_code)
{
}

// MANIPULATORS
inline
TimetableTransition& TimetableTransition::operator=(
                                                const TimetableTransition& rhs)
{
    d_datetime = rhs.d_datetime;
    d_code     = rhs.d_code;

    return *this;
}

// ACCESSORS
inline
const Datetime& TimetableTransition::datetime() const
{
    return d_datetime;
}

inline
int TimetableTransition::code() const
{
    return d_code;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const TimetableTransition& lhs,
                      const TimetableTransition& rhs)
{
    return lhs.datetime() == rhs.datetime() && lhs.code() == rhs.code();
}

inline
bool bdlt::operator!=(const TimetableTransition& lhs,
                      const TimetableTransition& rhs)
{
    return lhs.datetime() != rhs.datetime() || lhs.code() != rhs.code();
}

inline
bool bdlt::operator<(const TimetableTransition& lhs,
                     const TimetableTransition& rhs)
{
    return lhs.datetime() < rhs.datetime()
        || (lhs.datetime() == rhs.datetime() && lhs.code() < rhs.code());
}

inline
bool bdlt::operator<(const TimetableTransition& lhs, const Datetime& rhs)
{
    BSLS_ASSERT(24 > rhs.hour());

    return lhs.datetime() < rhs;
}

inline
bool bdlt::operator<(const Datetime& lhs, const TimetableTransition& rhs)
{
    BSLS_ASSERT(24 > lhs.hour());

    return lhs < rhs.datetime();
}

// HASH SPECIALIZATIONS
template <class HASHALG>
inline
void bdlt::hashAppend(HASHALG& hashAlg, const TimetableTransition& object)
{
    using ::BloombergLP::bslh::hashAppend;

    hashAppend(hashAlg, object.datetime());
    hashAppend(hashAlg, object.code());
}

namespace bdlt {

                      // -----------------------------
                      // class TimetableTransition_Ref
                      // -----------------------------

// PRIVATE CREATORS
inline
TimetableTransition_Ref::TimetableTransition_Ref()
: TimetableTransition()
{
}

// CREATORS
inline
TimetableTransition_Ref::TimetableTransition_Ref(
                                         const TimetableTransition& transition)
: TimetableTransition(transition)
{
}

inline
TimetableTransition_Ref::TimetableTransition_Ref(
                                       const TimetableTransition_Ref& original)
: TimetableTransition(original)
{
}

// MANIPULATORS
inline
TimetableTransition_Ref& TimetableTransition_Ref::operator=(
                                                const TimetableTransition& rhs)
{
    d_datetime = rhs.d_datetime;
    d_code     = rhs.d_code;

    return *this;
}

                  // -------------------------------------
                  // class Timetable_CompactableTransition
                  // -------------------------------------

// CREATORS
inline
Timetable_CompactableTransition::Timetable_CompactableTransition()
: d_time(0)
, d_code(k_UNSET_TRANSITION_CODE)
{
}

inline
Timetable_CompactableTransition::Timetable_CompactableTransition(
                                                              const Time& time,
                                                              int         code)
: d_time(time)
, d_code(code)
{
    BSLS_ASSERT(24 > time.hour());

    BSLS_ASSERT(0 <= code || k_UNSET_TRANSITION_CODE == code);
}

inline
Timetable_CompactableTransition::Timetable_CompactableTransition(
                               const Timetable_CompactableTransition& original)
: d_time(original.d_time)
, d_code(original.d_code)
{
}

// MANIPULATORS
inline
Timetable_CompactableTransition& Timetable_CompactableTransition::operator=(
                                    const Timetable_CompactableTransition& rhs)
{
    d_time = rhs.d_time;
    d_code = rhs.d_code;

    return *this;
}

// ACCESSORS
inline
const Time& Timetable_CompactableTransition::time() const
{
    return d_time;
}

inline
int Timetable_CompactableTransition::code() const
{
    return d_code;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const Timetable_CompactableTransition& lhs,
                      const Timetable_CompactableTransition& rhs)
{
    return lhs.time() == rhs.time() && lhs.code() == rhs.code();
}

inline
bool bdlt::operator!=(const Timetable_CompactableTransition& lhs,
                      const Timetable_CompactableTransition& rhs)
{
    return lhs.time() != rhs.time() || lhs.code() != rhs.code();
}

inline
bool bdlt::operator<(const Timetable_CompactableTransition& lhs,
                     const Timetable_CompactableTransition& rhs)
{
    return lhs.time() < rhs.time()
        || (lhs.time() == rhs.time() && lhs.code() < rhs.code());
}

inline
bool bdlt::operator<(const Timetable_CompactableTransition& lhs,
                     const Time&                            rhs)
{
    BSLS_ASSERT(24 > rhs.hour());

    return lhs.time() < rhs;
}

inline
bool bdlt::operator<(const Time&                            lhs,
                     const Timetable_CompactableTransition& rhs)
{
    BSLS_ASSERT(24 > lhs.hour());

    return lhs < rhs.time();
}

// HASH SPECIALIZATIONS
template <class HASHALG>
inline
void bdlt::hashAppend(HASHALG&                               hashAlg,
                      const Timetable_CompactableTransition& object)
{
    using ::BloombergLP::bslh::hashAppend;

    hashAppend(hashAlg, object.time());
    hashAppend(hashAlg, object.code());
}

namespace bdlt {

                           // -------------------
                           // class Timetable_Day
                           // -------------------

// CREATORS
inline
Timetable_Day::Timetable_Day(bslma::Allocator *basicAllocator)
: d_initialTransitionCode(k_UNSET_TRANSITION_CODE)
, d_transitions(basicAllocator)
{
}

inline
Timetable_Day::Timetable_Day(const Timetable_Day&  original,
                             bslma::Allocator     *basicAllocator)
: d_initialTransitionCode(original.d_initialTransitionCode)
, d_transitions(original.d_transitions, basicAllocator)
{
}

// MANIPULATORS
inline
Timetable_Day& Timetable_Day::operator=(const Timetable_Day& rhs)
{
    d_initialTransitionCode = rhs.d_initialTransitionCode;
    d_transitions           = rhs.d_transitions;

    return *this;
}

inline
bool Timetable_Day::removeAllTransitions()
{
    int code = finalTransitionCode();

    d_transitions.clear();

    return code != d_initialTransitionCode;
}

inline
bool Timetable_Day::setInitialTransitionCode(int code)
{
    BSLS_ASSERT(0 <= code || k_UNSET_TRANSITION_CODE == code);

    bool rv = d_initialTransitionCode != code && d_transitions.empty();

    d_initialTransitionCode = code;

    return rv;
}

// ACCESSORS
inline
int Timetable_Day::finalTransitionCode() const
{
    bsl::vector<Timetable_CompactableTransition>::const_reverse_iterator iter =
                                                        d_transitions.rbegin();

    return iter != d_transitions.rend()
         ? iter->d_code
         : d_initialTransitionCode;
}

inline
int Timetable_Day::initialTransitionCode() const
{
    return d_initialTransitionCode;
}

inline
bsl::size_t Timetable_Day::size() const
{
    return d_transitions.size();
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const Timetable_Day& lhs, const Timetable_Day& rhs)
{
    return lhs.d_initialTransitionCode == rhs.d_initialTransitionCode
        && lhs.d_transitions           == rhs.d_transitions;
}

inline
bool bdlt::operator!=(const Timetable_Day& lhs, const Timetable_Day& rhs)
{
    return lhs.d_initialTransitionCode != rhs.d_initialTransitionCode
        || lhs.d_transitions           != rhs.d_transitions;
}

inline
bool bdlt::operator<(const Timetable_Day& lhs, const Timetable_Day& rhs)
{
    return lhs.d_initialTransitionCode < rhs.d_initialTransitionCode
        || (   lhs.d_initialTransitionCode == rhs.d_initialTransitionCode
            && lhs.d_transitions < rhs.d_transitions);
}

// HASH SPECIALIZATIONS
template <class HASHALG>
inline
void bdlt::hashAppend(HASHALG& hashAlg, const Timetable_Day& object)
{
    using ::BloombergLP::bslh::hashAppend;

    hashAppend(hashAlg, object.d_initialTransitionCode);
    hashAppend(hashAlg, object.d_transitions);
}

namespace bdlt {

                             // ---------------
                             // class Timetable
                             // ---------------

// MANIPULATORS
inline
Timetable& Timetable::operator=(const Timetable& rhs)
{
    Timetable(rhs, allocator()).swap(*this);

    return *this;
}

inline
void Timetable::addTransition(const Datetime& datetime, int code)
{
    addTransition(datetime.date(), datetime.time(), code);
}

inline
void Timetable::removeAllTransitions()
{
    Date firstDate = d_firstDate;
    Date lastDate  = d_lastDate;

    d_firstDate = Date(9999, 12, 31);
    d_lastDate  = Date(   1,  1,  1);

    d_timetable.removeAll();

    setValidRange(firstDate, lastDate);
}

inline
void Timetable::removeTransition(const Datetime& datetime)
{
    removeTransition(datetime.date(), datetime.time());
}

inline
void Timetable::reset()
{
    d_initialTransitionCode = k_UNSET_TRANSITION_CODE;

    d_firstDate = Date(9999, 12, 31);
    d_lastDate  = Date(   1,  1,  1);

    d_timetable.removeAll();
}

                                  // Aspects

inline
void Timetable::swap(Timetable& other)
{
    // Member 'swap' is undefined for objects with non-equal allocators.

    BSLS_ASSERT(allocator() == other.allocator());

    bslalg::SwapUtil::swap(&d_initialTransitionCode,
                           &other.d_initialTransitionCode);

    bslalg::SwapUtil::swap(&d_firstDate, &other.d_firstDate);
    bslalg::SwapUtil::swap(&d_lastDate,  &other.d_lastDate);
    bslalg::SwapUtil::swap(&d_timetable, &other.d_timetable);
}

// ACCESSORS
inline
Timetable::const_iterator Timetable::end() const
{
    return Timetable_ConstIterator(*this, d_timetable.length(), 0);
}

inline
const Date& Timetable::firstDate() const
{
    BSLS_ASSERT(0 < length());

    return d_firstDate;
}

inline
int Timetable::initialTransitionCode() const
{
    return d_initialTransitionCode;
}

inline
bool Timetable::isInRange(const Date& date) const
{
    return date >= d_firstDate && date <= d_lastDate;
}

inline
const Date& Timetable::lastDate() const
{
    BSLS_ASSERT(0 < length());

    return d_lastDate;
}

inline
int Timetable::length() const
{
    return d_firstDate <= d_lastDate ? d_lastDate - d_firstDate + 1 : 0;
}

inline
int Timetable::transitionCodeInEffect(const Date& date, const Time& time) const
{
    BSLS_ASSERT(24 > time.hour());
    BSLS_ASSERT(isInRange(date));

    bsl::size_t          index = date - d_firstDate;
    const Timetable_Day& daily = d_timetable[index];

    return daily.transitionCodeInEffect(time);
}

inline
int Timetable::transitionCodeInEffect(const Datetime& datetime) const
{
    return transitionCodeInEffect(datetime.date(), datetime.time());
}

                                  // Aspects

inline
bslma::Allocator *Timetable::allocator() const
{
    return d_timetable.allocator();
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlt::operator==(const Timetable& lhs, const Timetable& rhs)
{
    return lhs.d_initialTransitionCode == rhs.d_initialTransitionCode
        && lhs.d_firstDate             == rhs.d_firstDate
        && lhs.d_lastDate              == rhs.d_lastDate
        && lhs.d_timetable             == rhs.d_timetable;
}

inline
bool bdlt::operator!=(const Timetable& lhs, const Timetable& rhs)
{
    return lhs.d_initialTransitionCode != rhs.d_initialTransitionCode
        || lhs.d_firstDate             != rhs.d_firstDate
        || lhs.d_lastDate              != rhs.d_lastDate
        || lhs.d_timetable             != rhs.d_timetable;
}

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

// FREE FUNCTIONS
inline
void bdlt::swap(Timetable& a, Timetable& b)
{
    if (a.allocator() == b.allocator()) {
        a.swap(b);

        return;                                                       // RETURN
    }

    Timetable futureA(b, a.allocator());
    Timetable futureB(a, b.allocator());

    futureA.swap(a);
    futureB.swap(b);
}

// HASH SPECIALIZATIONS
template <class HASHALG>
inline
void bdlt::hashAppend(HASHALG& hashAlg, const Timetable& object)
{
    using ::BloombergLP::bslh::hashAppend;

    hashAppend(hashAlg, object.d_firstDate);
    hashAppend(hashAlg, object.d_lastDate);
    hashAppend(hashAlg, object.d_initialTransitionCode);
    hashAppend(hashAlg, object.d_timetable);
}

namespace bdlt {

                      // -----------------------------
                      // class Timetable_ConstIterator
                      // -----------------------------

// PRIVATE CREATORS
inline
Timetable_ConstIterator::Timetable_ConstIterator(
                                              const Timetable& timetable,
                                              bsl::size_t      dayIndex,
                                              bsl::size_t      transitionIndex)
: d_timetable_p(&timetable)
, d_dayIndex(dayIndex)
, d_transitionIndex(transitionIndex)
{
}

// CREATORS
inline
Timetable_ConstIterator::Timetable_ConstIterator()
: d_timetable_p(0)
, d_dayIndex(0)
, d_transitionIndex(0)
{
}

inline
Timetable_ConstIterator::Timetable_ConstIterator(
                                       const Timetable_ConstIterator& original)
: d_timetable_p(original.d_timetable_p)
, d_dayIndex(original.d_dayIndex)
, d_transitionIndex(original.d_transitionIndex)
{
}

// MANIPULATORS
inline
Timetable_ConstIterator& Timetable_ConstIterator::
                                  operator=(const Timetable_ConstIterator& rhs)
{
    d_timetable_p     = rhs.d_timetable_p;
    d_dayIndex        = rhs.d_dayIndex;
    d_transitionIndex = rhs.d_transitionIndex;

    return *this;
}

// ACCESSORS
inline
TimetableTransition Timetable_ConstIterator::operator*() const
{
    BSLS_ASSERT(d_timetable_p);
    BSLS_ASSERT(d_dayIndex
                          < static_cast<bsl::size_t>(d_timetable_p->length()));
    BSLS_ASSERT(d_transitionIndex
                              < d_timetable_p->d_timetable[d_dayIndex].size());

    const Timetable_CompactableTransition& transition =
                          d_timetable_p->d_timetable[d_dayIndex].d_transitions[
                                                            d_transitionIndex];
    return TimetableTransition(
            Datetime(d_timetable_p->firstDate() + static_cast<int>(d_dayIndex),
                     transition.time()),
            transition.code());
}

inline
const TimetableTransition_Ref *Timetable_ConstIterator::operator->() const
{
    BSLS_ASSERT(d_timetable_p);
    BSLS_ASSERT(d_dayIndex
                          < static_cast<bsl::size_t>(d_timetable_p->length()));

    d_ref = this->operator*();
    return &d_ref;
}

}  // close package namespace

// FREE OPERATORS
inline
bdlt::Timetable_ConstIterator bdlt::operator++(
                                             Timetable_ConstIterator& iterator,
                                             int)
{
    const Timetable_ConstIterator curr = iterator;
    ++iterator;
    return curr;
}

inline
bdlt::Timetable_ConstIterator bdlt::operator--(
                                             Timetable_ConstIterator& iterator,
                                             int)
{
    const Timetable_ConstIterator curr = iterator;
    --iterator;
    return curr;
}

inline
bool bdlt::operator==(const Timetable_ConstIterator& lhs,
                      const Timetable_ConstIterator& rhs)
{
    return lhs.d_timetable_p     == rhs.d_timetable_p
        && lhs.d_dayIndex        == rhs.d_dayIndex
        && lhs.d_transitionIndex == rhs.d_transitionIndex;
}

inline
bool bdlt::operator!=(const Timetable_ConstIterator& lhs,
                      const Timetable_ConstIterator& rhs)
{
    return lhs.d_timetable_p     != rhs.d_timetable_p
        || lhs.d_dayIndex        != rhs.d_dayIndex
        || lhs.d_transitionIndex != rhs.d_transitionIndex;
}

}  // close enterprise namespace

// TRAITS

namespace BloombergLP {
namespace bslma {

template <>
struct UsesBslmaAllocator<bdlt::Timetable_Day> : bsl::true_type {};

template <>
struct UsesBslmaAllocator<bdlt::Timetable> : bsl::true_type {};

}  // close namespace bslma
}  // close enterprise namespace

#endif

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