// baltzo_localtimedescriptor.h                                       -*-C++-*-
#ifndef INCLUDED_BALTZO_LOCALTIMEDESCRIPTOR
#define INCLUDED_BALTZO_LOCALTIMEDESCRIPTOR

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

//@PURPOSE: Provide an attribute class for characterizing local time values.
//
//@CLASSES:
//  baltzo::LocalTimeDescriptor: attributes characterizing a local time
//
//@SEE_ALSO: baltzo_zoneinfo
//
//@DESCRIPTION: This component provides a single, simply constrained
// (value-semantic) attribute class, 'baltzo::LocalTimeDescriptor', that is
// used to characterize subsets of local time values within time zones.  Note
// that this class is consistent with the "local-time types" found in the
// "Zoneinfo" representation of a time zone (see 'baltzo_zoneinfo').
//
///Attributes
///----------
//..
//  Name                Type         Default  Simple Constraints
//  ------------------  -----------  -------  ------------------
//  description         bsl::string  ""       none
//  dstInEffectFlag     bool         false    none
//  utcOffsetInSeconds  int          0        [-86399 .. 86399]
//..
//: o 'description': non-canonical, non-localized name (intended for
//:   debugging).
//:
//: o 'dstInEffectFlag': 'true' if the described local times are
//:   Daylight-Saving-Time (DST) values.
//:
//: o 'utcOffsetInSeconds': offset from UTC of the described local times.
//
// For example, in New York on January 1, 2011, the local time is Eastern
// Standard Time, Daylight-Saving Time (DST) is not in effect, and the offset
// from UTC is -5 hours.  We can represent this information using a
// 'baltzo::LocalTimeDescriptor' object whose 'description' is "EST",
// 'dstInEffectFlag' is 'false', and 'utcOffsetInSeconds' is -18,000 (-5 * 60
// * 60).  Note that 'description' is *not* canonical, and is intended for
// development and debugging only.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Converting Between UTC and Local Times
///- - - - - - - - - - - - - - - - - - - - - - - - -
// When using the "Zoneinfo" database, we want to represent and access the
// local time information contained in the "Zoneinfo" binary data files.  Once
// we have obtained this information, we can use it to convert times from one
// time zone to another.  The following code illustrates how to perform such
// conversions using 'baltzo::LocalTimeDescriptor'.
//
// First, we define a 'baltzo::LocalTimeDescriptor' object that characterizes
// the local time in effect for New York Daylight-Saving Time in 2010:
//..
//  enum { NEW_YORK_DST_OFFSET = -4 * 60 * 60 };  // -4 hours in seconds
//
//  baltzo::LocalTimeDescriptor newYorkDst(NEW_YORK_DST_OFFSET, true, "EDT");
//
//  assert(NEW_YORK_DST_OFFSET == newYorkDst.utcOffsetInSeconds());
//  assert(               true == newYorkDst.dstInEffectFlag());
//  assert(              "EDT" == newYorkDst.description());
//..
// Then, we create a 'bdlt::Datetime' representing the time
// "Jul 20, 2010 11:00" in New York:
//..
//  bdlt::Datetime newYorkDatetime(2010, 7, 20, 11, 0, 0);
//..
// Next, we convert 'newYorkDatetime' to its corresponding UTC value using the
// 'newYorkDst' descriptor (created above); note that, when converting from a
// local time to a UTC time, the *signed* offset from UTC is *subtracted* from
// the local time:
//..
//  bdlt::Datetime utcDatetime = newYorkDatetime;
//  utcDatetime.addSeconds(-newYorkDst.utcOffsetInSeconds());
//..
// Then, we verify that the result corresponds to the expected UTC time,
// "Jul 20, 2010 15:00":
//..
//  assert(bdlt::Datetime(2010, 7, 20, 15, 0, 0) == utcDatetime);
//..
// Next, we define a 'baltzo::LocalTimeDescriptor' object that describes the
// local time in effect for Rome in the summer of 2010:
//..
//  enum { ROME_DST_OFFSET = 2 * 60 * 60 };  // 2 hours in seconds
//
//  baltzo::LocalTimeDescriptor romeDst(ROME_DST_OFFSET, true, "CEST");
//
//  assert(ROME_DST_OFFSET == romeDst.utcOffsetInSeconds());
//  assert(           true == romeDst.dstInEffectFlag());
//  assert(         "CEST" == romeDst.description());
//..
// Now, we convert 'utcDatetime' to its corresponding local-time value in Rome
// using the 'romeDst' descriptor (created above):
//..
//  bdlt::Datetime romeDatetime = utcDatetime;
//  romeDatetime.addSeconds(romeDst.utcOffsetInSeconds());
//..
// Notice that, when converting from UTC time to local time, the signed offset
// from UTC is *added* to UTC time rather than subtracted.
//
// Finally, we verify that the result corresponds to the expected local time,
// "Jul 20, 2010 17:00":
//..
//  assert(bdlt::Datetime(2010, 7, 20, 17, 0, 0) == romeDatetime);
//..

#include <balscm_version.h>

#include <bslalg_swaputil.h>

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

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

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

#include <bsl_algorithm.h>
#include <bsl_iosfwd.h>
#include <bsl_string.h>

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

namespace BloombergLP {
namespace baltzo {

                         // =========================
                         // class LocalTimeDescriptor
                         // =========================

class LocalTimeDescriptor {
    // This simply constrained (value-semantic) attribute class characterizes a
    // subset of local time values.  See the Attributes section under
    // @DESCRIPTION in the component-level documentation for information on the
    // class attributes.  Note that the class invariants are identically the
    // constraints on the individual attributes.

    // DATA
    int         d_utcOffsetInSeconds;  // *signed* offset *from* UTC

    bool        d_dstInEffectFlag;     // 'true' if Daylight-Saving Time is in
                                       // effect, and 'false' otherwise

    bsl::string d_description;         // *non-canonical* identifier for this
                                       // descriptor

    // FRIENDS
    friend void swap(LocalTimeDescriptor&, LocalTimeDescriptor&);

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

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(LocalTimeDescriptor,
                                   bslma::UsesBslmaAllocator);

    BSLMF_NESTED_TRAIT_DECLARATION(LocalTimeDescriptor,
                                   bslmf::IsBitwiseMoveable);

    // CLASS METHODS
    static bool isValidUtcOffsetInSeconds(int value);
        // Return 'true' if the specified 'value' is in the range
        // '[-86399 .. 86399]', and 'false' otherwise.

    // CREATORS
    LocalTimeDescriptor();
    explicit LocalTimeDescriptor(const allocator_type& allocator);
        // Create a 'LocalTimeDescriptor' object having the (default)
        // attribute values:
        //..
        //  utcOffsetInSeconds() == 0
        //  dstInEffectFlag()    == false
        //  description()        == ""
        //..
        // Optionally specify an 'allocator' (e.g., the address of a
        // 'bslma::Allocator' object) to supply memory; otherwise, the default
        // allocator is used.

    LocalTimeDescriptor(int                      utcOffsetInSeconds,
                        bool                     dstInEffectFlag,
                        const bsl::string_view&  description,
                        const allocator_type&    allocator = allocator_type());
        // Create a 'LocalTimeDescriptor' object having the specified
        // 'utcOffsetInSeconds', 'dstInEffectFlag', and 'description'
        // 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
        // '-86339 <= utcOffsetInSeconds <= 86399'.

    LocalTimeDescriptor(const LocalTimeDescriptor& original,
                        const allocator_type&      allocator=allocator_type());
        // Create a 'LocalTimeDescriptor' object having 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.

    LocalTimeDescriptor(bslmf::MovableRef<LocalTimeDescriptor> original)
                                                         BSLS_KEYWORD_NOEXCEPT;
        // Create a 'LocalTimeDescriptor' 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.

    LocalTimeDescriptor(bslmf::MovableRef<LocalTimeDescriptor> original,
                        const allocator_type&                  allocator);
        // Create a 'LocalTimeDescriptor' 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.

    ~LocalTimeDescriptor();
        // Destroy this object.

    // MANIPULATORS
    LocalTimeDescriptor& operator=(const LocalTimeDescriptor& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a non-'const' reference to this object.

    LocalTimeDescriptor& operator=(bslmf::MovableRef<LocalTimeDescriptor> 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 setDescription(const bsl::string_view& value);
        // Set the 'description' attribute of this object to the specified
        // 'value'.  Note that 'value' is not canonical, and is intended for
        // debugging only.

    void setDstInEffectFlag(bool value);
        // Set the 'dstInEffectFlag' attribute of this object to the specified
        // 'value'.  Note that 'true' implies Daylight-Saving Time (DST) is in
        // effect.

    void setUtcOffsetInSeconds(int value);
        // Set the 'utcOffsetInSeconds' attribute of this object to the
        // specified 'value'.  The behavior is undefined unless
        // '-86399 <= value <= 86399'.

                                  // Aspects

    void swap(LocalTimeDescriptor& 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 bsl::string& description() const;
        // Return a 'const' reference to the 'description' attribute of this
        // object.  Note that 'description' is not canonical, and is intended
        // for debugging only.

    bool dstInEffectFlag() const;
        // Return the value of the 'dstInEffectFlag' attribute of this object.
        // Note that 'true' implies Daylight-Saving Time (DST) is in effect.

    int utcOffsetInSeconds() const;
        // Return the value of the 'utcOffsetInSeconds' attribute of this
        // object.  Note that this value is in the range '[-86399 .. 86399]'.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // !DEPRECATED!: Use 'get_allocator()' instead.
        //
        // Return 'get_allocator().mechanism()'.

    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.

    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 non-'const' 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 LocalTimeDescriptor& lhs,
                const LocalTimeDescriptor& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'LocalTimeDescriptor' objects have
    // the same value if all of the corresponding values of their
    // 'utcOffsetInSeconds', 'dstInEffectFlag', and 'description' attributes
    // are the same.

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

bsl::ostream& operator<<(bsl::ostream&              stream,
                         const LocalTimeDescriptor& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a non-'const' 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)', but with the attribute names
    // elided.

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

// CLASS METHODS
inline
bool LocalTimeDescriptor::isValidUtcOffsetInSeconds(int value)
{
    return value >= -86399 && value <= 86399;
}

// CREATORS
inline
LocalTimeDescriptor::LocalTimeDescriptor()
: d_utcOffsetInSeconds(0)
, d_dstInEffectFlag(false)
, d_description()
{
}

inline
LocalTimeDescriptor::LocalTimeDescriptor(const allocator_type& allocator)
: d_utcOffsetInSeconds(0)
, d_dstInEffectFlag(false)
, d_description(allocator)
{
}

inline
LocalTimeDescriptor::LocalTimeDescriptor(
                                   int                      utcOffsetInSeconds,
                                   bool                     dstInEffectFlag,
                                   const bsl::string_view&  description,
                                   const allocator_type&    allocator)
: d_utcOffsetInSeconds(utcOffsetInSeconds)
, d_dstInEffectFlag(dstInEffectFlag)
, d_description(description.begin(), description.end(), allocator)
{
    BSLS_ASSERT(isValidUtcOffsetInSeconds(utcOffsetInSeconds));
}

inline
LocalTimeDescriptor::LocalTimeDescriptor(const LocalTimeDescriptor& original,
                                         const allocator_type&      allocator)
: d_utcOffsetInSeconds(original.d_utcOffsetInSeconds)
, d_dstInEffectFlag(original.d_dstInEffectFlag)
, d_description(original.d_description, allocator)
{
}

inline
LocalTimeDescriptor::LocalTimeDescriptor(
         bslmf::MovableRef<LocalTimeDescriptor> original) BSLS_KEYWORD_NOEXCEPT
: d_utcOffsetInSeconds(bslmf::MovableRefUtil::move(
      bslmf::MovableRefUtil::access(original).d_utcOffsetInSeconds)),
  d_dstInEffectFlag(bslmf::MovableRefUtil::move(
      bslmf::MovableRefUtil::access(original).d_dstInEffectFlag)),
  d_description(bslmf::MovableRefUtil::move(
      bslmf::MovableRefUtil::access(original).d_description))
{
}

inline
LocalTimeDescriptor::LocalTimeDescriptor(
                        bslmf::MovableRef<LocalTimeDescriptor> original,
                        const allocator_type&                  allocator)
: d_utcOffsetInSeconds(bslmf::MovableRefUtil::move(
      bslmf::MovableRefUtil::access(original).d_utcOffsetInSeconds))
, d_dstInEffectFlag(bslmf::MovableRefUtil::move(
      bslmf::MovableRefUtil::access(original).d_dstInEffectFlag))
, d_description(bslmf::MovableRefUtil::move(
      bslmf::MovableRefUtil::access(original).d_description), allocator)
{
}


inline
LocalTimeDescriptor::~LocalTimeDescriptor()
{
    BSLS_ASSERT(isValidUtcOffsetInSeconds(d_utcOffsetInSeconds));
}

// MANIPULATORS
inline
LocalTimeDescriptor& LocalTimeDescriptor::operator=(
                                                const LocalTimeDescriptor& rhs)
{
    d_description        = rhs.d_description;         // first for strong
                                                      // exception guarantee
    d_utcOffsetInSeconds = rhs.d_utcOffsetInSeconds;
    d_dstInEffectFlag    = rhs.d_dstInEffectFlag;
    return *this;
}

inline
LocalTimeDescriptor& LocalTimeDescriptor::operator=(
                                    bslmf::MovableRef<LocalTimeDescriptor> rhs)
{
    // Move 'd_description' first for strong exception guarantee.

    d_description = bslmf::MovableRefUtil::move(
        bslmf::MovableRefUtil::access(rhs).d_description);

    d_utcOffsetInSeconds = bslmf::MovableRefUtil::move(
        bslmf::MovableRefUtil::access(rhs).d_utcOffsetInSeconds);

    d_dstInEffectFlag = bslmf::MovableRefUtil::move(
        bslmf::MovableRefUtil::access(rhs).d_dstInEffectFlag);

    return *this;
}


inline
void LocalTimeDescriptor::setDescription(const bsl::string_view& value)
{
    d_description.assign(value.begin(), value.end());
}

inline
void LocalTimeDescriptor::setDstInEffectFlag(bool value)
{
    d_dstInEffectFlag = value;
}

inline
void LocalTimeDescriptor::setUtcOffsetInSeconds(int value)
{
    BSLS_ASSERT(isValidUtcOffsetInSeconds(value));

    d_utcOffsetInSeconds = value;
}

                                  // Aspects

inline
void LocalTimeDescriptor::swap(LocalTimeDescriptor& other)
{
    BSLS_ASSERT(get_allocator() == other.get_allocator());

    bslalg::SwapUtil::swap(&d_description,        &other.d_description);
    bslalg::SwapUtil::swap(&d_dstInEffectFlag,    &other.d_dstInEffectFlag);
    bslalg::SwapUtil::swap(&d_utcOffsetInSeconds, &other.d_utcOffsetInSeconds);
}

// ACCESSORS
inline
const bsl::string& LocalTimeDescriptor::description() const
{
    return d_description;
}

inline
bool LocalTimeDescriptor::dstInEffectFlag() const
{
    return d_dstInEffectFlag;
}

inline
int LocalTimeDescriptor::utcOffsetInSeconds() const
{
    return d_utcOffsetInSeconds;
}

                                  // Aspects

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

inline
LocalTimeDescriptor::allocator_type LocalTimeDescriptor::get_allocator() const
{
    return d_description.get_allocator();
}

}  // close package namespace

// FREE OPERATORS
inline
bool baltzo::operator==(const LocalTimeDescriptor& lhs,
                        const LocalTimeDescriptor& rhs)
{
    return lhs.utcOffsetInSeconds() == rhs.utcOffsetInSeconds()
        && lhs.dstInEffectFlag()    == rhs.dstInEffectFlag()
        && lhs.description()        == rhs.description();
}

inline
bool baltzo::operator!=(const LocalTimeDescriptor& lhs,
                        const LocalTimeDescriptor& rhs)
{
    return lhs.utcOffsetInSeconds() != rhs.utcOffsetInSeconds()
        || lhs.dstInEffectFlag()    != rhs.dstInEffectFlag()
        || lhs.description()        != rhs.description();
}

// FREE FUNCTIONS
inline
void baltzo::swap(LocalTimeDescriptor& a, LocalTimeDescriptor& b)
{
    bslalg::SwapUtil::swap(&a.d_description,        &b.d_description);
    bslalg::SwapUtil::swap(&a.d_dstInEffectFlag,    &b.d_dstInEffectFlag);
    bslalg::SwapUtil::swap(&a.d_utcOffsetInSeconds, &b.d_utcOffsetInSeconds);
}

}  // close enterprise namespace

#endif

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