// baltzo_localtimeperiod.h                                           -*-C++-*-
#ifndef INCLUDED_BALTZO_LOCALTIMEPERIOD
#define INCLUDED_BALTZO_LOCALTIMEPERIOD

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

//@PURPOSE: Provide a type describing local time over a time period.
//
//@CLASSES:
//  baltzo::LocalTimePeriod: time descriptor and period of applicability
//
//@SEE_ALSO:  baltzo_localtimedescriptor, baltzo_zoneinfoutil
//
//@DESCRIPTION: This component provides a single, complex-constrained
// (value-semantic) attribute class, 'baltzo::LocalTimePeriod', that describes
// a period of time over which a local time description (UTC offset, DST
// status, and a descriptive string) is in effect.
//
///Attributes
///----------
//..
//  Name            Type                        Default  Simple Constraints
//  -------------   --------------------------  -------  ------------------
//  descriptor      baltzo::LocalTimeDescriptor  default  none
//  utcStartTime    bdlt::Datetime               default  none
//  utcEndTime      bdlt::Datetime               default  none
//
//  Complex Constraints
//  -----------------------------------------------------------------
//  'utcStartTime == utcEndTime ||
//  (utcStartTime != bdlt::Datetime() && utcEndTime != bdlt::Datetime()
//   && utcStartTime < utcEndTime)'
//..
//: o 'localTimeDescriptor': a description of local time that applies during
//:   the interval defined by 'startUtcTime' and 'endUtcTime'.
//:
//: o 'utcStartTime': UTC representation of the start of the time interval over
//:   which 'localTimeDescriptor' applies.
//:
//: o 'utcEndTime': UTC representation of the moment immediately after the end
//:   of the time interval over which 'localTimeDescriptor' applies.
//
// For example, in New York in 2010, the local time was Eastern Daylight Time
// ("EDT") from March 14, 2010 to November 7, 2010, and during Eastern Daylight
// Time, Daylight-Saving Time (DST) was in effect, and the offset from UTC was
// -4 hours.  We can represent this information using a
// 'baltzo::LocalTimePeriod' object whose 'utcStartTime' is "Mar 14, 2010 07:00
// UTC" (2AM EST), 'utcEndTime' is "Nov 7, 2010 06:00 UTC" (1AM EST, what would
// have been 2AM EDT), and 'localTimeDescriptor' has a 'description' of "EDT",
// 'dstInEffectFlag' of 'true', and a 'utcOffsetInSeconds' of -14,400
// (-4 * 60 * 60).
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Converting a UTC Time to a Local Time
/// - - - - - - - - - - - - - - - - - - - - - - - -
// In this example we illustrate how to use a local time period to convert a
// UTC time to the corresponding local time in some time zone.
//
// First, we define a function that performs a conversion from UTC time to
// local time:
//..
//  int convertUtcToLocalTime(bdlt::Datetime                 *result,
//                            const bdlt::Datetime&           utcTime,
//                            const baltzo::LocalTimePeriod&  period)
//      // Load into the specified 'result' the date-time value corresponding
//      // to the specified 'utcTime' in the local time described by the
//      // specified 'period'.  Return 0 on success, and a non-zero value if
//      // 'utcTime < period.utcStartTime()' or
//      // 'utcTime >= period.utcEndTime()'.
//  {
//      BSLS_ASSERT(result);
//
//      if (utcTime <  period.utcStartTime() ||
//          utcTime >= period.utcEndTime()) {
//          return 1;                                                 // RETURN
//      }
//
//      *result = utcTime;
//      result->addSeconds(period.descriptor().utcOffsetInSeconds());
//      return 0;
//  }
//..
// Then, we create a 'baltzo::LocalTimePeriod' object, 'edt2010', that
// describes New York Daylight-Saving Time in 2010:
//..
//  enum { NEW_YORK_DST_OFFSET = -4 * 60 * 60 };  // -4 hours in seconds
//
//  baltzo::LocalTimeDescriptor edt(NEW_YORK_DST_OFFSET, true, "EDT");
//
//  baltzo::LocalTimePeriod     edt2010(edt,
//                                      bdlt::Datetime(2010,  3, 14, 7),
//                                      bdlt::Datetime(2010, 11,  7, 6));
//
//  assert(bdlt::Datetime(2010,  3, 14, 7) == edt2010.utcStartTime());
//  assert(bdlt::Datetime(2010, 11,  7, 6) == edt2010.utcEndTime());
//  assert("EDT" == edt2010.descriptor().description());
//  assert(true  == edt2010.descriptor().dstInEffectFlag());
//  assert(NEW_YORK_DST_OFFSET == edt2010.descriptor().utcOffsetInSeconds());
//..
// Next, we create a 'bdlt::Datetime', 'utcDatetime', representing the (UTC)
// time "Jul 20, 2010 11:00":
//..
//  bdlt::Datetime utcDatetime(2010, 7, 20, 11, 0, 0);
//..
// Now, we use the 'convertUtcToLocalTime' function we defined earlier to
// convert 'utcDatetime' into its local time in Eastern Daylight Time (as
// described by 'edt2010'):
//..
//  bdlt::Datetime localDatetime;
//  int            status = convertUtcToLocalTime(&localDatetime,
//                                                utcDatetime,
//                                                edt2010);
//  if (0 != status) {
//      // The conversion failed so return an error code.
//
//      return 1;                                                     // RETURN
//  }
//..
// Finally, we verify that the result corresponds to the expected local time in
// New York, "Jul 20, 2010 7:00":
//..
//  assert(bdlt::Datetime(2010, 7, 20, 7) == localDatetime);
//..

#include <balscm_version.h>

#include <baltzo_localdatetime.h>
#include <baltzo_localtimedescriptor.h>

#include <bdlt_datetime.h>

#include <bslalg_swaputil.h>

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

#include <bslmf_isbitwisemoveable.h>
#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>

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

#include <bsl_iosfwd.h>

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

namespace BloombergLP {
namespace baltzo {

                           // =====================
                           // class LocalTimePeriod
                           // =====================

class LocalTimePeriod {
    // This complex-constrained (value-semantic) attribute class describes a
    // range of time in which a particular local time description (offset, DST
    // status, and a descriptive string) is in effect.  See the Attributes
    // section under @DESCRIPTION in the component-level documentation.  Note
    // that the class invariants are identically the constraints on the
    // attributes.

    // DATA
    LocalTimeDescriptor d_descriptor;    // local time descriptor for this
                                         // period

    bdlt::Datetime      d_utcStartTime;  // start of this period

    bdlt::Datetime      d_utcEndTime;    // end of this period

  public:
    // TYPES
    typedef bsl::allocator<char> allocator_type;

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(LocalTimePeriod, bslma::UsesBslmaAllocator);
    BSLMF_NESTED_TRAIT_DECLARATION(LocalTimePeriod, bslmf::IsBitwiseMoveable);

    // CLASS METHODS
    static bool isValidUtcStartAndEndTime(const bdlt::Datetime& utcStartTime,
                                          const bdlt::Datetime& utcEndTime);
        // Return 'true' if the specified 'utcStartTime' and 'utcEndTime' have
        // the same value, or if 'utcStartTime' and 'utcEndTime' are comparable
        // (i.e., neither has the value of a default-constructed
        // 'bdlt::DateTime' object) and 'utcStartTime < utcEndTime'; return
        // 'false' otherwise.

    // CREATORS
    LocalTimePeriod();
    explicit LocalTimePeriod(const allocator_type& allocator);
        // Create a 'LocalTimePeriod' object having the (default) attribute
        // values:
        //..
        //  descriptor()   == LocalTimeDescriptor()
        //  utcStartTime() == bdlt::Datetime()
        //  utcEndTime()   == bdlt::Datetime()
        //..
        // Optionally specify an 'allocator' (e.g., the address of a
        // 'bslma::Allocator' object) to supply memory; otherwise, the default
        // allocator is used.

    LocalTimePeriod(const LocalTimeDescriptor& descriptor,
                    const bdlt::Datetime&      utcStartTime,
                    const bdlt::Datetime&      utcEndTime,
                    const allocator_type&      allocator = allocator_type());
        // Create a 'LocalTimePeriod' object having the specified 'descriptor',
        // 'utcStartTime', and 'utcEndTime' attribute values.  Optionally
        // specify an 'allocator' (e.g., the address of a 'bslma::Allocator'
        // object) to supply memory; otherwise, the default allocator is used.
        // The behavior is undefined unless 'utcStartTime == utcEndTime', or if
        // 'utcStartTime' and 'utcEndTime' are comparable (i.e., neither equals
        // the a default constructed 'bdlt::DateTime' object) unless
        // 'utcStartTime < utcEndTime'.  (See the 'isValidUtcStartAndEndTime'
        // method.)

    LocalTimePeriod(const LocalTimePeriod& original,
                    const allocator_type&  allocator = allocator_type());
        // Create a 'LocalTimePeriod' object with the same value as the
        // specified 'original' object.  Optionally specify an 'allocator'
        // (e.g., the address of a 'bslma::Allocator' object) to supply memory;
        // otherwise, the default allocator is used.

    LocalTimePeriod(bslmf::MovableRef<LocalTimePeriod> original)
                                                         BSLS_KEYWORD_NOEXCEPT;
        // Create a 'LocalTimePeriod' object having the same value and the same
        // allocator as the specified 'original' object.  The value of
        // 'original' becomes unspecified but valid, and its allocator remains
        // unchanged.

    LocalTimePeriod(bslmf::MovableRef<LocalTimePeriod> original,
                    const allocator_type&              allocator);
        // Create a 'LocalTimePeriod' object having the same value as the
        // specified 'original' object, using the specified 'allocator' (e.g.,
        // the address of a 'bslma::Allocator' object) to supply memory.  The
        // allocator of 'original' remains unchanged.  If 'original' and the
        // newly created object have the same allocator then the value of
        // 'original' becomes unspecified but valid, and no exceptions will be
        // thrown; otherwise 'original' is unchanged and an exception may be
        // thrown.

    ~LocalTimePeriod();
        // Destroy this object.

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

    LocalTimePeriod& operator=(bslmf::MovableRef<LocalTimePeriod> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a non-'const' reference to this object.  The allocators of
        // this object and 'rhs' both remain unchanged.  If 'rhs' and this
        // object have the same allocator then the value of 'rhs' becomes
        // unspecified but valid, and no exceptions will be thrown; otherwise
        // 'rhs' is unchanged (and an exception may be thrown).

    void setDescriptor(const LocalTimeDescriptor& value);
        // Set the 'descriptor' attribute to the specified 'value'.

    void setUtcStartAndEndTime(const bdlt::Datetime& utcStartTime,
                               const bdlt::Datetime& utcEndTime);
        // Use the specified 'utcStartTime' and 'utcEndTime' to set the
        // 'utcStartTime' and 'utcEndTime' attributes of this object
        // respectively.  The behavior is undefined unless
        // 'utcStartTime == utcEndTime', or if 'utcStartTime' and 'utcEndTime'
        // are comparable (i.e., neither equals a default constructed
        // 'bdlt::DateTime' object) unless 'utcStartTime < utcEndTime'.  (See
        // the 'isValidUtcStartAndEndTime' method.)

    void swap(LocalTimePeriod& 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
    bslma::Allocator *allocator() const;
        // !DEPRECATED!: Use 'get_allocator()' instead.
        //
        // Return 'get_allocator().mechanism()'.

    const LocalTimeDescriptor& descriptor() const;
        // Return a reference providing non-modifiable access to the
        // 'descriptor' attribute of this object.

    allocator_type get_allocator() const;
        // Return the allocator used by this object to supply memory.  Note
        // that if no allocator was supplied at construction the default
        // allocator in effect at construction is used.

    const bdlt::Datetime& utcEndTime() const;
        // Return a reference providing non-modifiable access to the
        // 'utcEndTime' attribute of this object.

    const bdlt::Datetime& utcStartTime() const;
        // Return a reference providing non-modifiable access to the
        // 'utcStartTime' attribute of this object.

                        // Aspects

    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 the format
        // is not fully specified, and can change without notice.
};

// FREE OPERATORS
bool operator==(const LocalTimePeriod& lhs, const LocalTimePeriod& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'LocalTimePeriod' objects have the
    // same value if each of their corresponding 'descriptor', 'utcStartTime',
    // and 'utcEndTime' attributes have the same value.

bool operator!=(const LocalTimePeriod& lhs, const LocalTimePeriod& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'LocalTimePeriod' objects do not
    // have the same value if the corresponding values of their 'descriptor',
    // 'utcStartTime', or 'utcEndTime' attributes are not the same.

std::ostream& operator<<(bsl::ostream&          stream,
                         const LocalTimePeriod& object);
    // Write the value of the specified '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 and can change
    // without notice.  Also note that this method has the same behavior as
    // 'object.print(stream, 0, -1)'.

// FREE FUNCTIONS
void swap(LocalTimePeriod& a, LocalTimePeriod& 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.

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

                           // ---------------------
                           // class LocalTimePeriod
                           // ---------------------

// CLASS METHODS
inline
bool LocalTimePeriod::isValidUtcStartAndEndTime(
                                            const bdlt::Datetime& utcStartTime,
                                            const bdlt::Datetime& utcEndTime)
{
    bdlt::Datetime defaultObj;
    return utcStartTime == utcEndTime
        || (utcStartTime != defaultObj && utcEndTime != defaultObj
            && utcStartTime < utcEndTime);
}

// CREATORS
inline
LocalTimePeriod::LocalTimePeriod()
: d_descriptor()
, d_utcStartTime()
, d_utcEndTime()
{
}

inline
LocalTimePeriod::LocalTimePeriod(const allocator_type& allocator)
: d_descriptor(allocator)
, d_utcStartTime()
, d_utcEndTime()
{
}

inline
LocalTimePeriod::LocalTimePeriod(const LocalTimeDescriptor& descriptor,
                                 const bdlt::Datetime&      utcStartTime,
                                 const bdlt::Datetime&      utcEndTime,
                                 const allocator_type&      allocator)
: d_descriptor(descriptor, allocator)
, d_utcStartTime(utcStartTime)
, d_utcEndTime(utcEndTime)
{
    BSLS_ASSERT(isValidUtcStartAndEndTime(d_utcStartTime, d_utcEndTime));
}

inline
LocalTimePeriod::LocalTimePeriod(const LocalTimePeriod& original,
                                 const allocator_type&  allocator)
: d_descriptor(original.d_descriptor, allocator)
, d_utcStartTime(original.d_utcStartTime)
, d_utcEndTime(original.d_utcEndTime)
{
}

inline
LocalTimePeriod::LocalTimePeriod(bslmf::MovableRef<LocalTimePeriod> original)
                                                          BSLS_KEYWORD_NOEXCEPT
: d_descriptor(bslmf::MovableRefUtil::move(
                         bslmf::MovableRefUtil::access(original).d_descriptor))
, d_utcStartTime(bslmf::MovableRefUtil::access(original).d_utcStartTime)
, d_utcEndTime(bslmf::MovableRefUtil::access(original).d_utcEndTime)
{
}

inline
LocalTimePeriod::LocalTimePeriod(bslmf::MovableRef<LocalTimePeriod> original,
                                 const allocator_type&              allocator)
: d_descriptor(bslmf::MovableRefUtil::move(
                         bslmf::MovableRefUtil::access(original).d_descriptor),
                         allocator)
, d_utcStartTime(bslmf::MovableRefUtil::access(original).d_utcStartTime)
, d_utcEndTime(bslmf::MovableRefUtil::access(original).d_utcEndTime)
{
}

inline
LocalTimePeriod::~LocalTimePeriod()
{
    BSLS_ASSERT(isValidUtcStartAndEndTime(d_utcStartTime, d_utcEndTime));
}

// MANIPULATORS
inline
LocalTimePeriod& LocalTimePeriod::operator=(const LocalTimePeriod& rhs)
{
    d_descriptor   = rhs.d_descriptor;    // must be first
    d_utcStartTime = rhs.d_utcStartTime;
    d_utcEndTime   = rhs.d_utcEndTime;
    return *this;
}

inline
LocalTimePeriod& LocalTimePeriod::operator=(
                                        bslmf::MovableRef<LocalTimePeriod> rhs)
{
    LocalTimePeriod& lvalue = rhs;

    // Move 'd_descriptor' first for strong exception guarantee.

    d_descriptor   = bslmf::MovableRefUtil::move(lvalue.d_descriptor);
    d_utcStartTime = bslmf::MovableRefUtil::move(lvalue.d_utcStartTime);
    d_utcEndTime   = bslmf::MovableRefUtil::move(lvalue.d_utcEndTime);

    return *this;
}

inline
void LocalTimePeriod::setDescriptor(const LocalTimeDescriptor& value)
{
    d_descriptor = value;
}

inline
void LocalTimePeriod::setUtcStartAndEndTime(const bdlt::Datetime& utcStartTime,
                                            const bdlt::Datetime& utcEndTime)
{
    BSLS_ASSERT(isValidUtcStartAndEndTime(utcStartTime, utcEndTime));

    d_utcStartTime = utcStartTime;
    d_utcEndTime   = utcEndTime;
}

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

    BSLS_ASSERT(get_allocator() == other.get_allocator());

    bslalg::SwapUtil::swap(&d_descriptor,   &other.d_descriptor);
    bslalg::SwapUtil::swap(&d_utcStartTime, &other.d_utcStartTime);
    bslalg::SwapUtil::swap(&d_utcEndTime,   &other.d_utcEndTime);
}

// ACCESSORS
inline
bslma::Allocator *LocalTimePeriod::allocator() const
{
    return get_allocator().mechanism();
}

inline
const LocalTimeDescriptor& LocalTimePeriod::descriptor() const
{
    return d_descriptor;
}

inline
LocalTimePeriod::allocator_type LocalTimePeriod::get_allocator() const
{
    return d_descriptor.description().get_allocator();
}

inline
const bdlt::Datetime& LocalTimePeriod::utcEndTime() const
{
    return d_utcEndTime;
}

inline
const bdlt::Datetime& LocalTimePeriod::utcStartTime() const
{
    return d_utcStartTime;
}

}  // close package namespace

// FREE OPERATORS
inline
bool baltzo::operator==(const LocalTimePeriod& lhs, const LocalTimePeriod& rhs)
{
    return lhs.descriptor()   == rhs.descriptor()
        && lhs.utcStartTime() == rhs.utcStartTime()
        && lhs.utcEndTime()   == rhs.utcEndTime();
}

inline
bool baltzo::operator!=(const LocalTimePeriod& lhs, const LocalTimePeriod& rhs)
{
    return lhs.descriptor()   != rhs.descriptor()
        || lhs.utcStartTime() != rhs.utcStartTime()
        || lhs.utcEndTime()   != rhs.utcEndTime();
}

inline
std::ostream& baltzo::operator<<(bsl::ostream&          stream,
                                 const LocalTimePeriod& object)
{
    return object.print(stream, 0, -1);
}

}  // 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 ----------------------------------