// baltzo_zoneinfo.h                                                  -*-C++-*-
#ifndef INCLUDED_BALTZO_ZONEINFO
#define INCLUDED_BALTZO_ZONEINFO

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

//@PURPOSE: Provide a value type to represent a time zone.
//
//@CLASSES:
//  baltzo::Zoneinfo: information about a time zone
//  baltzo::ZoneinfoTransition: attributes representing a time transition
//
//@SEE_ALSO: baltzo_localtimedescriptor, baltzo_zoneinfoutil
//
//@DESCRIPTION: This component provides a *value* *semantic* type,
// 'baltzo::Zoneinfo', that represents the information about a specific time
// zone contained in the Zoneinfo database.  In addition, this component
// provides an unconstrained *in-core* *value-semantic* type
// 'baltzo::ZoneinfoTransition' that can be used to characterize a transition
// for time zones.
//
///'baltzo::ZoneinfoTransition'
///----------------------------
// A 'baltzo::ZoneinfoTransition' contains:
//..
//  Name        Type
//  ----------  --------------------------
//  utcTime     bdlt::EpochUtil::TimeT64
//  descriptor  baltzo::LocalTimeDescriptor
//..
//: o 'utcTime': UTC time when a transition occurs
//:
//: o 'descriptor': local time value corresponding to the time transition
//
// For example, in New York on March 14, 2011, at the instant 1 a.m., clocks
// are set forward by an hour to mark the transition from Eastern Standard Time
// to Eastern Daylight Time.  This change can be represented by a
// 'baltzo::ZoneinfoTransition' object whose 'utcTime' refers to March 14,
// 2011, 1am and whose 'descriptor' describes Eastern Daylight Time (i.e.,
// description is "EDT", 'dstInEffectFlag' is 'true', and 'utcOffsetInSeconds'
// is -14,400 (-4 * 60 * 60)).
//
///'baltzo::Zoneinfo'
///------------------
// A 'baltzo::Zoneinfo' contains:
//
//: o the time zone identifier (e.g., "America/New_York" or "Asia/Tokyo")
//:
//: o the ordered sequence of 'baltzo::ZoneinfoTransition' objects,
//:   representing the various transitions from UTC for this time zone.
//:
//: o an optional POSIX-like TZ environment string used to represent
//:   far-reaching times past the end of the explicit time zone data.
//
// A 'baltzo::Zoneinfo' object also provides the method
// 'findTransitionForUtcTime' that allows a client to find, in the sequence of
// transitions, the appropriate transition whose local-time descriptor
// describes the properties of local time, at a specified UTC time, for the
// time zone in question.  Note that, even though this information is
// sufficient for converting local date and time, to their corresponding values
// in other time zones, clients are encouraged to use the utilities provided in
// 'baltzo_timezoneutil'.  Also note that, 'baltzo::Zoneinfo' objects are
// typically populated by the client through the 'baltzo::Loader' protocol, and
// not directly.
//
///Zoneinfo Database
///-----------------
// This database, also referred to as either the TZ database or the Olson
// database (after its creator, Arthur Olson), is a standard, public-domain
// time-zone information distribution used by many software systems (including
// a number of Unix variants and the Java Runtime Environment).  Information
// about the Zoneinfo database -- including the time zone rules for the
// supported time zones, and source code for the 'zic' compiler (for compiling
// those rules into the binary representation used by this component) -- can be
// found online at 'http://www.iana.org/time-zones/repository/tz-link.html'.
// This time zone information can be used to perform the conversion of dates
// and times from UTC to their corresponding dates and times in a given time
// zone and vice-versa.  (See 'baltzo_zoneinfobinaryreader' for more
// information about the binary file format.)
//
///posixExtendedRangeDescription
///-----------------------------
// This string may be populated with a POSIX-like TZ string that describes
// rules for local time are handled before the first and after the last
// local-time transitions maintained by this object.  Typically this is used
// for computing local time values far in the future.  The rules for the
// encoded string can be found online at
// 'http://www.ibm.com/developerworks/aix/library/au-aix-posix/'.
//
///Usage
///-----
// The following usage examples illustrate how to populate a 'baltzo::Zoneinfo'
// object and use it to transform a UTC time into a local time.
//
///Example 1: Populate a 'baltzo::Zoneinfo'
/// - - - - - - - - - - - - - - - - - - - -
// Suppose we want to represent the time-zone information for New York, in 2010
// using a 'baltzo::Zoneinfo' object.  In order to do so, we need to provide
// the UTC date-times (transitions) after which the time zone changes its
// offset from UTC, or daylight-saving Time starts or ends.
//
// First, we define two times "Mar 14, 2010 6:00 AM" and "Nov 07, 2010 7:00 AM"
// representing respectively the UTC time at which New York transitions to
// Eastern Daylight-saving Time (EDT) and Eastern Standard Time (EST) in 2010:
//..
//  bdlt::Datetime edtDatetime(2010, 03, 14, 2, 0);
//  bdlt::Datetime estDatetime(2010, 11, 07, 2, 0);
//..
// Then, we create two local-time descriptors that hold the offset from UTC and
// DST status for EST and EDT in New York in 2010, in terms of their
// 'offsetFromUtcInSeconds', 'dstInEffectFlag' and 'description' attributes:
//..
//  const baltzo::LocalTimeDescriptor est(-5 * 60 * 60, false, "EST");
//  const baltzo::LocalTimeDescriptor edt(-4 * 60 * 60, true,  "EDT");
//..
// Note that these descriptors will be associated with the created transition
// times, to reflect how local time in New York changes its offset from UTC and
// DST status after specific times.
//
// Next, we create an empty 'baltzo::Zoneinfo' object that will be populated
// with the information necessary to describe the time zone for New York:
//..
//  baltzo::Zoneinfo newYorkTimeZone;
//..
// Then, before being able to associate the transition times with their
// corresponding descriptors, we need translate the transition times to
// 'bdlt::EpochUtil::TimeT64':
//..
//  bdlt::EpochUtil::TimeT64 edtTransitionTime =
//                              bdlt::EpochUtil::convertToTimeT64(edtDatetime);
//  bdlt::EpochUtil::TimeT64 estTransitionTime =
//                              bdlt::EpochUtil::convertToTimeT64(estDatetime);
//..
// Now, we associate the created descriptors with the transitions we indicated
// previously and add them to 'newYorkTimeZone' using the 'addTransition'
// method:
//..
//  newYorkTimeZone.addTransition(edtTransitionTime, edt);
//  newYorkTimeZone.addTransition(estTransitionTime, est);
//..
// Note that this insertion operation maintains the transitions in order of
// transition time, and therefore inserting transitions out-of-order, while not
// illegal, is very inefficient.
//
// Finally we verify that the 'newYorkTimeZone' contains the transitions we
// indicated:
//..
//  {
//      baltzo::Zoneinfo::TransitionConstIterator it =
//                                          newYorkTimeZone.beginTransitions();
//
//      assert(it->utcTime()    == edtTransitionTime);
//      assert(it->descriptor() == edt);
//      ++it;
//      assert(it->utcTime()    == estTransitionTime);
//      assert(it->descriptor() == est);
//  }
//..
// Notice that the iterator refers to a 'baltzo::ZoneinfoTransition' object.
//
///Example 2: Converting UTC to Local Time
///- - - - - - - - - - - - - - - - - - - -
// Suppose that we want to program a function, 'utcToLocalTime' to perform the
// conversion from a UTC time value to the corresponding local time value, in a
// given time zone, and return the computed local time:
//
// First we declare the function 'utcToLocalTime' and its contract:
//..
//  static bdlt::DatetimeTz utcToLocalTime(const bdlt::Datetime&   utcTime,
//                                        const baltzo::Zoneinfo& timeZone)
//  {
//      // Return the 'bdlt::DatetimeTz' value representing the date, time and
//      // offset from UTC (rounded to the minute) value of the local time,
//      // corresponding to the specified 'utcTime' in the specified
//      // 'timeZone'.  The behavior is undefined if the 'utcTime' precedes the
//      // time of the first transition contained in 'timeZone' and
//      // '0 < timeZone.numTransitions()'.
//..
// Then, we check the precondition of 'utcToLocalTime', by checking that
// 'timeZone' contains at least one transitions and comparing 'utcTime' to the
// first transition time in 'timeZone':
//..
//      BSLS_ASSERT(0 < timeZone.numTransitions());
//      BSLS_ASSERT(timeZone.firstTransition().utcTime() <=
//                                 bdlt::EpochUtil::convertToTimeT64(utcTime));
//..
// Next, we obtain the appropriate 'baltzo::ZoneinfoTransition' object,
// invoking the method 'findTransitionForUtcTime' on 'timeZone':
//..
//      baltzo::Zoneinfo::TransitionConstIterator it =
//                                  timeZone.findTransitionForUtcTime(utcTime);
//..
// Then, we access the descriptor associated with the transition to which 'it'
// refers, and calculate the offset from UTC rounded to the minute:
//..
//      const baltzo::ZoneinfoTransition& transition = *it;
//      const int offsetInMinutes =
//                           transition.descriptor().utcOffsetInSeconds() / 60;
//..
// Now, we apply the obtained 'offsetInMinutes' to the originally specified
// 'utcTime' obtaining the corresponding local time in the specified
// 'timeZone':
//..
//      bdlt::Datetime temp(utcTime);
//      temp.addMinutes(offsetInMinutes);
//..
// Finally, return the local time value together with its offset from UTC:
//..
//      return bdlt::DatetimeTz(temp, offsetInMinutes);
//  }
//..
// Suppose, now, we want to convert UTC time to the corresponding local time in
// New York.  We can do so using the previously defined function
// 'utcToLocalTime' and reusing the 'baltzo::Zoneinfo' object,
// 'newYorkTimeZone' of Example 1.
//
// First, we define 'bdlt::Datetime' object representing the UTC time "Apr 10,
// 2010 12:00":
//..
//  bdlt::Datetime utcDatetime(2010, 04, 10, 12, 0, 0);
//..
// Then, we invoke 'utcToLocalTime' passing 'newYorkTimeZone' as a time zone
// and save the result:
//..
//  bdlt::DatetimeTz nyDatetime = utcToLocalTime(utcDatetime, newYorkTimeZone);
//..
// Finally, we compute the New York local time corresponding to 'utcDatetime',
// verify that "April 10, 2010 8:00" is the computed time:
//..
//  const bdlt::Datetime expectedTime(2010, 4, 10, 8, 0, 0);
//  assert(-4 * 60      == nyDatetime.offset());
//  assert(expectedTime == nyDatetime.localDatetime());
//..

#include <balscm_version.h>

#include <baltzo_localtimedescriptor.h>

#include <bdlt_datetime.h>
#include <bdlt_epochutil.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 <bsls_types.h>

#include <bsl_cstring.h>
#include <bsl_iosfwd.h>
#include <bsl_set.h>
#include <bsl_string.h>
#include <bsl_vector.h>

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

namespace BloombergLP {
namespace baltzo {

                          // ========================
                          // class ZoneinfoTransition
                          // ========================

class ZoneinfoTransition {
    // This class is an unconstrained *in-core* value-semantic class that
    // characterizes a transition when the local time value of a time-zone
    // changes.  The salient attributes of this type are the 'utcTime'
    // (representing seconds from UTC), and 'descriptor' representing the local
    // time value after the transition.

    // DATA
    bdlt::EpochUtil::TimeT64   d_utcTime;       // UTC time (representing in
                                                // seconds from epoch) when the
                                                // time transition occurs

    const LocalTimeDescriptor *d_descriptor_p;  // pointer to the descriptor
                                                // associated with this
                                                // transition (held, not owned)

    // FRIENDS
    friend class Zoneinfo;

    // PRIVATE CREATORS
    ZoneinfoTransition(bdlt::EpochUtil::TimeT64   utcTime,
                       const LocalTimeDescriptor *descriptor);
        // Create a 'ZoneinfoTransition' object having the specified 'utcTime'
        // and, 'descriptor' attribute values.  The behavior is undefined
        // unless 'descriptor' remains valid for the lifetime of this object.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(ZoneinfoTransition,
                                   bslmf::IsBitwiseMoveable);

    // CREATORS
    ~ZoneinfoTransition();
        // Destroy this object.

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

    bdlt::EpochUtil::TimeT64 utcTime() const;
        // Return the value of the 'utcTime' 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 ZoneinfoTransition& lhs, const ZoneinfoTransition& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'ZoneinfoTransition' objects have the
    // same value if the corresponding value of their 'utcTime' attribute is
    // the same and both refer to the same 'descriptor' address.

bool operator!=(const ZoneinfoTransition& lhs, const ZoneinfoTransition& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'ZoneinfoTransition' objects do
    // not have the same value if the corresponding value of their 'utcTime' is
    // not the same or if they do not refer to the same 'descriptor' address.

bool operator<(const ZoneinfoTransition& lhs, const ZoneinfoTransition& rhs);
    // Return 'true' if the value of the specified 'lhs' is less than (ordered
    // before) the value of the specified 'rhs'.  Note that the value of 'lhs'
    // is less than the value of 'rhs' if the value of the 'utcTime' attribute
    // of 'lhs' is less than the value of the 'utcTime' attribute of 'rhs'.

bsl::ostream& operator<<(bsl::ostream&             stream,
                         const ZoneinfoTransition& 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)' with the attribute names elided.

                               // ==============
                               // class Zoneinfo
                               // ==============

class Zoneinfo {
    // This class is a value-semantic type holding a structured representation
    // of the information contained in an Zoneinfo (or "Olson") time zone
    // database for a *single* locale (e.g., "America/New_York").  The salient
    // attributes of this type are the string identifier and the ordered
    // sequence of 'ZoneinfoTransition' objects.

    // PRIVATE TYPES
    class DescriptorLess {
        // This 'class' is a private functor that provides a comparator
        // predicate for the type 'LocalTimeDescriptor', so that it can be
        // stored in associative containers such as 'bsl::set'.

      public:
        bool operator()(const LocalTimeDescriptor& lhs,
                        const LocalTimeDescriptor& rhs) const;
            // Return 'true' if the value of the specified 'lhs' is less than
            // (ordered before) the value of the specified 'rhs'.  Note that
            // the value of 'lhs' is less than the value of 'rhs' if the value
            // of the corresponding 'utcOffsetInSeconds', 'description', and
            // 'dstInEffectFlag' attributes of 'lhs' when incrementally
            // compared one at a time in that order is less than the attribute
            // value of 'rhs'.  Also note that the comparison moves to the next
            // attribute only when the corresponding attribute values compare
            // equal.
    };

    typedef bsl::vector<ZoneinfoTransition> TransitionSequence;
        // Alias for the sequence of transitions that characterize a 'Zoneinfo'
        // object.

    typedef bsl::set<LocalTimeDescriptor, DescriptorLess> DescriptorSet;
        // Alias for the set of unique local-time descriptors that are managed
        // by a 'Zoneinfo' object.

    // DATA
    bsl::string         d_identifier;
                          // this time zone's id

    DescriptorSet       d_descriptors;
                          // set of local time descriptors for this time zone
                          // (e.g., 'EST')

    TransitionSequence  d_transitions;
                          // transitions, from one local time descriptor to
                          // another (e.g., 'EST' to 'EDT'), ordered by the
                          // time the transition occurred (or will occur)

    bsl::string         d_posixExtendedRangeDescription;
                          // optional POSIX-like TZ environment string
                          // representing far-reaching times

    // FRIENDS
    friend bool operator==(const Zoneinfo&, const Zoneinfo&);

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

    typedef TransitionSequence::const_iterator TransitionConstIterator;
        // Alias for a bi-directional 'const' iterator over the sequence of
        // transitions maintained by a 'Zoneinfo' object.

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

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

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

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

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

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

    Zoneinfo& operator=(bslmf::MovableRef<Zoneinfo> 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 addTransition(bdlt::EpochUtil::TimeT64   utcTime,
                       const LocalTimeDescriptor& descriptor);
        // Add to this object a transition occurring at the specified 'utcTime'
        // when the local time in the described time-zone adopts the
        // characteristics of the specified 'descriptor'.  If a transition at
        // 'utcTime' is already present, replace it's local-time descriptor
        // with 'descriptor'.

    void setIdentifier(const bsl::string_view&  value);
    void setIdentifier(const char              *value);
        // Set the 'identifier' attribute of this object to the specified
        // 'value'.

    void setPosixExtendedRangeDescription(const bsl::string_view&  value);
    void setPosixExtendedRangeDescription(const char              *value);
        // Set the 'posixExtendedRangeDescription' attribute of this object,
        // used to describe local time transitions far in the future, to the
        // specified 'value' (see {posixExtendedRangeDescription}).

    void swap(Zoneinfo& 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 the allocator used by this object to supply memory.  Note
        // that if no allocator was supplied at construction the currently
        // installed default allocator is used.

    TransitionConstIterator findTransitionForUtcTime(
                                          const bdlt::Datetime& utcTime) const;
        // Return an iterator providing non-modifiable access to the transition
        // that holds the local-time descriptor associated with the specified
        // 'utcTime'.  The behavior is undefined unless 'numTransitions() > 0'
        // and 'utcTime' is at or after the transition returned by
        // 'firstTransition'.

    const ZoneinfoTransition& firstTransition() const;
        // Return a reference providing non-modifiable access to the first
        // transition contained in this object.  The behavior is undefined
        // unless 'numTransitions() > 0'.

    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 bsl::string& identifier() const;
        // Return a reference providing non-modifiable access to the
        // 'identifier' attribute of this object.

    const bsl::string& posixExtendedRangeDescription() const;
        // Return a reference providing non-modifiable access to the
        // 'posixExtendedRangeDescription' attribute of this object, used to
        // describe local time transitions far in the future (see
        // {posixExtendedRangeDescription}).

    bsl::size_t numTransitions() const;
        // Return the number of transitions maintained by this zone info.

    TransitionConstIterator beginTransitions() const;
        // Return an iterator providing non-modifiable access to the first
        // transition in the ordered sequence of transitions maintained by this
        // object.  Note that if 'beginTransitions() == endTransitions()' then
        // there are no transitions stored by this object.

    TransitionConstIterator endTransitions() const;
        // Return an iterator providing non-modifiable access to the one-past
        // the last transition in the ordered sequence of transitions that is
        // associated with this object.  Note that if
        // 'beginTransitions() == endTransitions()' then there are no
        // transitions stored by this object.

    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 Zoneinfo& lhs, const Zoneinfo& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'Zoneinfo' objects have the same
    // value if the corresponding value of their 'identifier' attribute is the
    // same and if both store the same sequence of transitions, ordered by
    // time.

bool operator!=(const Zoneinfo& lhs, const Zoneinfo& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'Zoneinfo' objects do not have
    // the same value if their corresponding 'identifier' attribute does not
    // have the same value, or if both do *not* store the same sequence of
    // transitions, ordered by time.

bsl::ostream& operator<<(bsl::ostream& stream, const Zoneinfo& 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, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

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

// PRIVATE CREATORS
inline
ZoneinfoTransition::ZoneinfoTransition(bdlt::EpochUtil::TimeT64   utcTime,
                                       const LocalTimeDescriptor *descriptor)
: d_utcTime(utcTime)
, d_descriptor_p(descriptor)
{
    BSLS_ASSERT(descriptor);
}

// CREATORS
inline
ZoneinfoTransition::~ZoneinfoTransition()
{
    BSLS_ASSERT(d_descriptor_p);
}

// ACCESSORS
inline
const LocalTimeDescriptor& ZoneinfoTransition::descriptor() const
{
    return *d_descriptor_p;
}

inline
bdlt::EpochUtil::TimeT64 ZoneinfoTransition::utcTime() const
{
    return d_utcTime;
}

}  // close package namespace

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

inline
bool baltzo::operator!=(const ZoneinfoTransition& lhs,
                        const ZoneinfoTransition& rhs)
{
    return  !(lhs == rhs);
}

inline
bool baltzo::operator<(const ZoneinfoTransition& lhs,
                       const ZoneinfoTransition& rhs)
{
    return lhs.utcTime() < rhs.utcTime();
}

namespace baltzo {

                               // --------------
                               // class Zoneinfo
                               // --------------

// CREATORS
inline
Zoneinfo::Zoneinfo()
: d_identifier()
, d_descriptors()
, d_transitions()
, d_posixExtendedRangeDescription()
{
}

inline
Zoneinfo::Zoneinfo(const allocator_type& allocator)
: d_identifier(allocator)
, d_descriptors(allocator)
, d_transitions(allocator)
, d_posixExtendedRangeDescription(allocator)
{
}

// MANIPULATORS
inline
Zoneinfo& Zoneinfo::operator=(const Zoneinfo& rhs)
{
    Zoneinfo(rhs, get_allocator()).swap(*this);
    return *this;
}

inline
void Zoneinfo::setIdentifier(const bsl::string_view& value)
{
    BSLS_ASSERT(0 != value.data());

    d_identifier = value;
}

inline
void Zoneinfo::setIdentifier(const char *value)
{
    BSLS_ASSERT(value);

    d_identifier = value;
}

inline
void Zoneinfo::setPosixExtendedRangeDescription(const bsl::string_view& value)
{
    BSLS_ASSERT(0 != value.data());

    d_posixExtendedRangeDescription.assign(value.begin(), value.end());
}

inline
void Zoneinfo::setPosixExtendedRangeDescription(const char *value)
{
    BSLS_ASSERT(value);

    d_posixExtendedRangeDescription = value;
}

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

    bslalg::SwapUtil::swap(&d_identifier,  &other.d_identifier);
    bslalg::SwapUtil::swap(&d_descriptors, &other.d_descriptors);
    bslalg::SwapUtil::swap(&d_transitions, &other.d_transitions);
    bslalg::SwapUtil::swap(&d_posixExtendedRangeDescription,
                           &other.d_posixExtendedRangeDescription);
}

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

inline
const ZoneinfoTransition& Zoneinfo::firstTransition() const
{
    BSLS_ASSERT(numTransitions() > 0);

    return d_transitions.front();
}

inline
Zoneinfo::allocator_type Zoneinfo::get_allocator() const
{
    return d_identifier.get_allocator();
}

inline
const bsl::string& Zoneinfo::identifier() const
{
    return d_identifier;
}

inline
const bsl::string& Zoneinfo::posixExtendedRangeDescription() const
{
    return d_posixExtendedRangeDescription;
}

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

inline
Zoneinfo::TransitionConstIterator Zoneinfo::beginTransitions() const
{
    return d_transitions.begin();
}

inline
Zoneinfo::TransitionConstIterator Zoneinfo::endTransitions() const
{
    return d_transitions.end();
}

}  // close package namespace

// FREE OPERATORS
inline
bool baltzo::operator==(const Zoneinfo& lhs, const Zoneinfo& rhs)
{
    return lhs.identifier() == rhs.identifier()
        && lhs.posixExtendedRangeDescription() ==
                                            rhs.posixExtendedRangeDescription()
        && lhs.numTransitions() == rhs.numTransitions()
        && bsl::equal(lhs.d_transitions.begin(),
                      lhs.d_transitions.end(),
                      rhs.d_transitions.begin());
}

inline
bool baltzo::operator!=(const Zoneinfo& lhs, const Zoneinfo& rhs)
{
    return !(lhs == rhs);
}

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