// balber_berutil.h                                                   -*-C++-*-
#ifndef INCLUDED_BALBER_BERUTIL
#define INCLUDED_BALBER_BERUTIL

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

//@PURPOSE: Provide functions to encode and decode simple types in BER format.
//
//@CLASSES:
//   balber::BerUtil: namespace of utility functions for BER
//
//@SEE_ALSO: balber_berencoder, balber_berdecoder
//
//@DESCRIPTION: This component provides utility functions for encoding and
// decoding of primitive BER constructs, such as tag identifier octets, length
// octets, fundamental C++ types.  The encoding and decoding of 'bsl::string'
// and BDE date/time types is also implemented.
//
// These utility functions operate on 'bsl::streambuf' for buffer management.
//
// More information about BER constructs can be found in the BER specification
// (X.690).  A copy of the specification can be found at the URL:
//: o http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
//
// Note that this is a low-level component that only encodes and decodes
// primitive constructs.  Clients should use the 'balber_berencoder' and
// 'balber_berdecoder' components (which use this component in the
// implementation) to encode and decode well-formed BER messages.
//
///Terminology
///-----------
// The documentation of this component occasionally uses the following
// terminology as shorthand:
//
//: *date-and-time* *type*:
//:   A data type provided by BDE for the representation of a date and/or time
//:   value.  The date-and-time types are: 'bdlt::Date', 'bdlt::DateTz',
//:   'bdlt::Datetime', 'bdlt::DatetimeTz', 'bdlt::Time', and 'bdlt::TimeTz'.
//:   Note that under this definition, the time-zone-aware types provided by
//:   BDE, such as 'baltzo::LocalDatetime', are not date-and-time types.
//:
//: *date-and-time* *value*:
//:   The value associated with an object of a date-and-time type.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1:  Reading and Writing Identifier Octets
///- - - - - - - - - - - - - - - - - - - - - - - - -
// The following snippets of code illustrate the usage of this component.  Due
// to the low-level nature of this component, an extended usage example is not
// necessary.
//
// Suppose we wanted to write the identifier octets for a BER tag having the
// following properties:
//..
//    Tag Class:   Context-specific
//    Tag Type:    Primitive
//    Tag Number:  31
//..
// According to the BER specification, this should generate two octets
// containing the values 0x9F and 0x1F.  The following function demonstrates
// this:
//..
//  bdlsb::MemOutStreamBuf osb;
//
//  balber::BerConstants::TagClass tagClass  =
//                                    balber::BerConstants::e_CONTEXT_SPECIFIC;
//  balber::BerConstants::TagType  tagType   =
//                                           balber::BerConstants::e_PRIMITIVE;
//  int                            tagNumber = 31;
//
//  int retCode = balber::BerUtil::putIdentifierOctets(&osb,
//                                                     tagClass,
//                                                     tagType,
//                                                     tagNumber);
//  assert(0    == retCode);
//  assert(2    == osb.length());
//  assert(0x9F == (unsigned char)osb.data()[0]);
//  assert(0x1F == (unsigned char)osb.data()[1]);
//..
// The next part of the function will read the identifier octets from the
// stream and verify its contents:
//..
//  bdlsb::FixedMemInStreamBuf isb(osb.data(), osb.length());
//
//  balber::BerConstants::TagClass tagClassIn;
//  balber::BerConstants::TagType  tagTypeIn;
//  int                            tagNumberIn;
//  int                            numBytesConsumed = 0;
//
//  retCode = balber::BerUtil::getIdentifierOctets(&isb,
//                                                 &tagClassIn,
//                                                 &tagTypeIn,
//                                                 &tagNumberIn,
//                                                 &numBytesConsumed);
//  assert(0         == retCode);
//  assert(2         == numBytesConsumed);
//  assert(tagClass  == tagClassIn);
//  assert(tagType   == tagTypeIn);
//  assert(tagNumber == tagNumberIn);
//..

#include <balscm_version.h>

#include <balber_berconstants.h>
#include <balber_berdecoderoptions.h>
#include <balber_berencoderoptions.h>

#include <bdldfp_decimal.h>

#include <bdlt_date.h>
#include <bdlt_datetime.h>
#include <bdlt_datetimetz.h>
#include <bdlt_datetz.h>
#include <bdlt_iso8601util.h>
#include <bdlt_prolepticdateimputil.h>
#include <bdlt_time.h>
#include <bdlt_timetz.h>

#include <bdlb_float.h>
#include <bdlb_variant.h>

#include <bslmf_assert.h>

#include <bsla_nodiscard.h>
#include <bsla_unreachable.h>

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

#include <bsl_streambuf.h>
#include <bsl_string.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace balber {

                               // ==============
                               // struct BerUtil
                               // ==============

struct BerUtil {
    // This utility contains functions to encode and decode primitive BER
    // constructs and simple value semantic types.  By convention, all
    // functions return 0 on success, and a non-zero value otherwise.  Also by
    // convention, all the "get" functions take an 'accumNumBytesConsumed';
    // each of the functions will add to this variable the number of bytes
    // consumed within the scope of the function.

    enum {
        k_INDEFINITE_LENGTH = -1  // used to indicate that the length is
                                  // indefinite

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
        ,
        BDEM_INDEFINITE_LENGTH = k_INDEFINITE_LENGTH,
        INDEFINITE_LENGTH      = k_INDEFINITE_LENGTH
#endif  // BDE_OMIT_INTERNAL_DEPRECATED
    };

    // CLASS METHODS
    static int getEndOfContentOctets(bsl::streambuf *streamBuf,
                                     int            *accumNumBytesConsumed);
        // Decode the "end-of-content" octets (two consecutive zero-octets)
        // from the specified 'streamBuf' and add the number of bytes consumed
        // (which is always 2) to the specified 'accumNumBytesConsumed'.
        // Return 0 on success, and a non-zero value otherwise.

    static int getIdentifierOctets(
                                bsl::streambuf         *streamBuf,
                                BerConstants::TagClass *tagClass,
                                BerConstants::TagType  *tagType,
                                int                    *tagNumber,
                                int                    *accumNumBytesConsumed);
        // Decode the identifier octets from the specified 'streamBuf' and load
        // the tag class, tag type, and tag number to the specified 'tagClass',
        // 'tagType', and 'tagNumber' respectively.  Add the number of bytes
        // consumed to the specified 'accumNumBytesConsumed'.  Return
        // 0 on success, and a non-zero value otherwise.

    static int getLength(bsl::streambuf *streamBuf,
                         int            *result,
                         int            *accumNumBytesConsumed);
        // Decode the length octets from the specified 'streamBuf' and load the
        // result to the specified 'result'.  If the length is indefinite
        // (i.e., contents will be terminated by "end-of-content" octets) then
        // 'result' will be set to 'k_INDEFINITE_LENGTH'.  Add the number of
        // bytes consumed to the specified 'accumNumBytesConsumed'.  Return 0
        // on success, and a non-zero value otherwise.

    template <typename TYPE>
    static int getValue(
                      bsl::streambuf           *streamBuf,
                      TYPE                     *value,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
        // Decode the specified 'value' from the specified 'streamBuf',
        // consuming exactly the specified 'length' bytes.  Return 0 on
        // success, and a non-zero value otherwise.  Optionally specify
        // decoding 'options' to control aspects of the decoding.  Note that
        // the value consists of the contents bytes only (no length prefix).
        // Also note that only fundamental C++ types, 'bsl::string', and BDE
        // date/time types are supported.

    template <typename TYPE>
    static int getValue(
                      bsl::streambuf           *streamBuf,
                      TYPE                     *value,
                      int                      *accumNumBytesConsumed,
                      const BerDecoderOptions&  options = BerDecoderOptions());
        // Decode the specified 'value' from the specified 'streamBuf' and add
        // the number of bytes consumed to the specified
        // 'accumNumBytesConsumed'.  Return 0 on success, and a non-zero value
        // otherwise.  Optionally specify decoding 'options' to control aspects
        // of the decoding.  Note that the value consists of the length and
        // contents primitives.  Also note that only fundamental C++ types,
        // 'bsl::string', and BDE date/time types are supported.

    static int putEndOfContentOctets(bsl::streambuf *streamBuf);
        // Encode the "end-of-content" octets (two consecutive zero-octets) to
        // the specified 'streamBuf'.  The "end-of-content" octets act as the
        // termination bytes for objects that have indefinite length.  Return 0
        // on success, and a non-zero value otherwise.

    static int putIdentifierOctets(bsl::streambuf         *streamBuf,
                                   BerConstants::TagClass  tagClass,
                                   BerConstants::TagType   tagType,
                                   int                     tagNumber);
        // Encode the identifier octets for the specified 'tagClass', 'tagType'
        // and 'tagNumber' to the specified 'streamBuf'.  Return 0 on success,
        // and a non-zero value otherwise.

    static int putIndefiniteLengthOctet(bsl::streambuf *streamBuf);
        // Encode the "indefinite-length" octet onto the specified 'streamBuf'.
        // This octet signifies that the length of the contents is indefinite
        // (i.e., contents will be terminated by end of content octets).
        // Return 0 on success, and a non-zero value otherwise.

    static int putLength(bsl::streambuf *streamBuf, int length);
        // Encode the specified 'length' to the specified 'streamBuf'.  Return
        // 0 on success, and a non-zero value otherwise.  The behavior is
        // undefined unless '0 <= length'.

    template <typename TYPE>
    static int putValue(bsl::streambuf          *streamBuf,
                        const TYPE&              value,
                        const BerEncoderOptions *options = 0);
        // Encode the specified 'value' to the specified 'streamBuf'.  Return 0
        // on success, and a non-zero value otherwise.  Note that the value
        // consists of the length and contents primitives.  Also note that only
        // fundamental C++ types, 'bsl::string', 'bslstl::StringRef' and BDE
        // date/time types are supported.
};

///Implementation Note
///-------------------
// The following utility structs used in the implementation of 'BerUtil' are
// provided in reverse dependency order.  This means that low-level utilities
// appear first, and higher-level utilities later.  No utility uses another
// that appears later.
//
// Each utility provides type aliases for the lower-level utilities used in its
// implementation.  This set of type aliases also serves as a manifest of the
// utility's dependencies.

                          // ========================
                          // struct BerUtil_Constants
                          // ========================

struct BerUtil_Constants {
    // This component-private utility 'struct' provides a namespace for a set
    // of constants used to calculate quantities needed by BER encoders and
    // decoders.  For example, this struct provides a named constant for the
    // number of bits in a byte, which is used in downstream calculations.

    // TYPES
    enum { k_NUM_BITS_PER_OCTET = 8 };
};

                        // ============================
                        // struct BerUtil_StreambufUtil
                        // ============================

struct BerUtil_StreambufUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to perform input and output operations on
    // 'bsl::streambuf' objects.  Note that these functions are intended to
    // adapt the standard stream-buffer operations to a BDE-style interface.

    // CLASS METHODS
    static int peekChar(char *value, bsl::streambuf *streamBuf);
        // Read the next byte from the specified 'streamBuf' without advancing
        // the read position and load that byte into the specified 'value'.
        // Return 0 on success, and a non-zero value otherwise.  If this
        // operation is not successful, the value of '*value' is unchanged.
        // This operation fails if the input sequence of 'streamBuf' is at its
        // end.

    static int getChars(char           *buffer,
                        bsl::streambuf *streamBuf,
                        int             bufferLength);
        // Read the specified 'bufferLength' number of bytes from the input
        // sequence of the specified 'streamBuf', as if by a call to
        // 'streamBuf->sgetn(buffer, bufferLength)', and load the bytes into
        // successive elements of the specified 'buffer', starting at the first
        // element.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if 'length' bytes are successfully read from the
        // input sequence of the 'streamBuf' without the read position becoming
        // unavailable.  If less than 'bufferLength' bytes are read, the number
        // of bytes loaded into 'buffer' is not specified.  The behavior is
        // undefined unless '0 <= bufferLength' and 'buffer' is the address of
        // a sequence of at least 'bufferLength' bytes.

    static int putChars(bsl::streambuf *streamBuf,
                        const char     *buffer,
                        int             bufferLength);
        // Write the first specified 'bufferLength' number of bytes from the
        // specified 'buffer' to the specified 'streamBuf', as if by a call to
        // 'streamBuf->sputn(buffer, bufferLength)'.  Return 0 on success, and
        // a non-zero value otherwise.
};

                      // ================================
                      // struct BerUtil_IdentifierImpUtil
                      // ================================

struct BerUtil_IdentifierImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER identifier octet
    // encoding and decoding.

    // TYPES
    typedef BerUtil_Constants Constants;
        // 'Constants' is an alias to a namespace for a suite of
        // general-purpose constants that occur when encoding or decoding BER
        // data.

  private:
    // PRIVATE TYPES
    enum {
        k_TAG_CLASS_MASK  = 0xC0,  // 0xC0 = 0b1100'0000
        k_TAG_TYPE_MASK   = 0x20,  // 0x20 = 0b0010'0000
        k_TAG_NUMBER_MASK = 0x1F,  // 0x1F = 0b0001'1111
        // The first octet in a sequence of one or more BER identifier
        // octets encodes 3 quantities: the tag class, the tag type, and
        // the leading bits of the tag number.  These quantities are
        // encoded according to the packing suggested by the above 3 masks.

        k_MAX_TAG_NUMBER_IN_ONE_OCTET = 30,
        // The last 5 bits of the first octet in a sequence of one or more
        // BER identifier octets encodes one of 32 different values.
        // Values 0 through 30 indicate the tag number of the contents is
        // the corresponding value.  The value 31 indicates that the next
        // octet is the first byte in a possibly multi-byte encoding of an
        // 8-bit VLQ.

        k_NUM_VALUE_BITS_IN_TAG_OCTET = 7,
        // BER identifier octets (after the first octet) encode an 8-bit
        // VLQ unsigned integer value that indicates the tag number of the
        // contents.  The most significant bit of this octet indicates
        // whether or not the octet is the last one in the VLQ sequence, or
        // if another VLQ octet follows.

        k_MAX_TAG_NUMBER_OCTETS =
            (sizeof(int) * Constants::k_NUM_BITS_PER_OCTET) /
                k_NUM_VALUE_BITS_IN_TAG_OCTET +
            1,
        // This component restricts the maximum supported number of octets
        // used to represent the tag number to 4.  This means that there
        // are at most '4 * 7 = 28' bits used to encode such a tag number.

        k_CHAR_MSB_MASK = 0x80,  // 0x80 = 0b1000'0000
        // An 8-bit mask for the most significant bit of an octet.

        k_SEVEN_BITS_MASK = 0x7F  // 0x7F = 0b0111'1111
        // An 8-bit mask for all but the most significant bit of an octet.
    };

  public:
    // CLASS METHODS
    static int getIdentifierOctets(
                                 BerConstants::TagClass *tagClass,
                                 BerConstants::TagType  *tagType,
                                 int                    *tagNumber,
                                 int                    *accumNumBytesConsumed,
                                 bsl::streambuf         *streamBuf);
        // Decode the identifier octets from the specified 'streamBuf' and load
        // the tag class, tag type, and tag number to the specified 'tagClass',
        // 'tagType', and 'tagNumber', respectively.  Add the number of bytes
        // consumed to the specified 'accumNumBytesConsumed'.  Return 0 on
        // success, and a non-zero value otherwise.

    static int putIdentifierOctets(bsl::streambuf         *streamBuf,
                                   BerConstants::TagClass  tagClass,
                                   BerConstants::TagType   tagType,
                                   int                     tagNumber);
        // Encode the identifier octets for the specified 'tagClass', 'tagType'
        // and 'tagNumber', in that order, to the specified 'streamBuf'.
        // Return 0 on success, and a non-zero value otherwise.
};

                      // ================================
                      // struct BerUtil_RawIntegerImpUtil
                      // ================================

struct BerUtil_RawIntegerImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER integer encoding.  This
    // 'struct' is separate from 'BerUtil_IntegerImpUtil' to break a dependency
    // cycle between 'BerUtil_IntegerImpUtil' and 'BerUtil_LengthImpUtil'.

    // TYPES
    typedef BerUtil_Constants Constants;
        // 'Constants' is an alias to a namespace for a suite of
        // general-purpose constants that occur when encoding or decoding BER
        // data.

    // CLASS METHODS
    template <class INTEGRAL_TYPE>
    static int putIntegerGivenLength(bsl::streambuf *streamBuf,
                                     INTEGRAL_TYPE   value,
                                     int             length);
        // Encode the octets used in the BER encoding of the specified 'value'
        // of the specified 'INTEGRAL_TYPE' to the specified 'streamBuf', using
        // exactly the specified 'length' number of octets.  Return 0 on
        // success, and a non-zero value otherwise.  The behavior is undefined
        // unless 'INTEGRAL_TYPE' is fundamental integral type and exactly
        // 'length' number of octets is used in the BER encoding of the
        // specified 'value'.
};

                        // ============================
                        // struct BerUtil_LengthImpUtil
                        // ============================

struct BerUtil_LengthImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER length quantity encoding
    // and decoding.

    // TYPES
    typedef BerUtil_Constants Constants;
        // 'Constants' is an alias to a namespace for a suite of
        // general-purpose constants that occur when encoding or decoding BER
        // data.

    typedef BerUtil_RawIntegerImpUtil RawIntegerUtil;
        // 'RawIntegerUtil' is an alias to a namespace for a suite of functions
        // used to implement integer encoding.

  private:
    // PRIVATE TYPES
    enum {
        k_INDEFINITE_LENGTH = -1,
        // constant used to indicate that a calculated length quantity is
        // indefinite

        k_INDEFINITE_LENGTH_OCTET = 0x80,  // 0x80 = 0b1000'0000
        // value of the (singular) length octet used to indicate that
        // the length of a sequence of BER octets will be determined by
        // seeking forward until and end-of-contents pair of octets is
        // encountered

        k_LONG_FORM_LENGTH_FLAG_MASK = 0x80,  // 0x80 = 0b1000'0000
        // mask used to determine if the higher-order bit of a length
        // octet indicates that the next octet in the sequence is part of
        // the VLQ-encoding of the length or if the current octet is the
        // final octet of the length octets

        k_LONG_FORM_LENGTH_VALUE_MASK = 0x7F  // 0x7F = 0b0111'1111
        // mask used to retrieve the bits of a non-final length octet
        // that contribute to the BLQ-encoding of the length
    };

  public:
    // CLASS METHODS

    // Length Decoding Functions

    static int getLength(int            *result,
                         int            *accumNumBytesConsumed,
                         bsl::streambuf *streamBuf);
        // Decode the length octets from the specified 'streamBuf' and load the
        // result to the specified 'result'.  If the length is indefinite
        // (i.e., contents will be terminated by "end-of-content" octets) then
        // 'result' will be set to 'k_INDEFINITE_LENGTH'.  Add the number of
        // bytes consumed to the specified 'accumNumBytesConsumed'.  Return 0
        // on success, and a non-zero value otherwise.

    static int getEndOfContentOctets(int            *accumNumBytesConsumed,
                                     bsl::streambuf *streamBuf);
        // Decode the "end-of-content" octets (two consecutive zero-octets)
        // from the specified 'streamBuf' and add the number of bytes consumed
        // (which is always 2) to the specified 'accumNumBytesConsumed'.
        // Return 0 on success, and a non-zero value otherwise.

    // Length Encoding Functions

    static int putLength(bsl::streambuf *streamBuf, int length);
        // Encode the specified 'length' length octets to the specified
        // 'streamBuf'.  Return 0 on success, and a non-zero value otherwise.
        // The behavior is undefined unless '0 <= length'.

    static int putIndefiniteLengthOctet(bsl::streambuf *streamBuf);
        // Encode the "indefinite-length" octet onto the specified 'streamBuf'.
        // This octet signifies that the length of the contents is indefinite
        // (i.e., contents will be terminated by end of content octets).
        // Return 0 on success, and a non-zero value otherwise.

    static int putEndOfContentOctets(bsl::streambuf *streamBuf);
        // Encode the identifier octets for the specified 'tagClass', 'tagType'
        // and 'tagNumber' to the specified 'streamBuf'.  Return 0 on success,
        // and a non-zero value otherwise.
};

                       // =============================
                       // struct BerUtil_BooleanImpUtil
                       // =============================

struct BerUtil_BooleanImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for boolean values.  Within the definition of this 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690.

    // TYPES
    typedef BerUtil_LengthImpUtil LengthUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

    // CLASS METHODS

    // Decoding

    static int getBoolValue(bool           *value,
                            bsl::streambuf *streamBuf,
                            int             length);
        // Decode to the specified 'value' from the specified 'streamBuf',
        // consuming exactly the specified 'length' bytes.  Return 0 on
        // success, and a non-zero value otherwise.  This operations succeeds
        // if 'length' bytes are successfully read from the 'streamBuf' and
        // they contain a valid representation of the contents octets for a
        // BER-encoded boolean value according to the specification.

    // Encoding

    static int putBoolValue(bsl::streambuf *streamBuf, bool value);
        // Encode the specified 'value' to the specified 'streamBuf'.  Return 0
        // on success and a non-zero value otherwise.  The 'value' is encoded
        // as the sequence of contents octets for a BER-encoded boolean value
        // according to the specification.  This operation succeeds if all of
        // the contents octets are successfully written to the 'streamBuf'.
};

                       // =============================
                       // struct BerUtil_IntegerImpUtil
                       // =============================

struct BerUtil_IntegerImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for integer values.  Within the definition of this 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690.

    // TYPES
    typedef BerUtil_Constants Constants;
        // 'Constants' is an alias to a namespace for a suite of
        // general-purpose constants that occur when encoding or decoding BER
        // data.

    typedef BerUtil_LengthImpUtil LengthImpUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

    typedef BerUtil_RawIntegerImpUtil RawIntegerUtil;
        // 'RawIntegerUtil' is an alias to a namespace for a suite of low-level
        // functions used to implement BER encoding operations for integer
        // values.

    typedef BerUtil_StreambufUtil StreambufUtil;
        // 'StreambufUtil' is an alias to a namespace for a suite of functions
        // used to implement input and output operations on 'bsl::streambuf'
        // objects.

    // CLASS DATA
    static const int k_40_BIT_INTEGER_LENGTH = 5;
        // Number of octets used to encode a signed integer value in 40 bits.

    // CLASS METHODS
    static int getNumOctetsToStream(short value);
    static int getNumOctetsToStream(int value);
    static int getNumOctetsToStream(long long value);
        // Return the number of octets required to provide a BER encoding of
        // the specified 'value' according to the specification.

    template <class INTEGRAL_TYPE>
    static int getNumOctetsToStream(INTEGRAL_TYPE value);
        // Return the number of octets required to provide a BER encoding of
        // the specified 'value' according to the specification.  The program
        // is ill-formed unless the specified 'INTEGRAL_TYPE' is a fundamental
        // integral type.

    static int getIntegerValue(long long      *value,
                               bsl::streambuf *streamBuf,
                               int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the contents octets of a
        // BER-encoded integer value according to the specification.  Return 0
        // if successful, and a non-zero value otherwise.

    template <class INTEGRAL_TYPE>
    static int getIntegerValue(INTEGRAL_TYPE  *value,
                               bsl::streambuf *streamBuf,
                               int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the contents octets of BER-encoded
        // integer value according to the specification.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes read contain a valid representation of the contents octets
        // of an integer value according to the specification.  The program is
        // ill-formed unless the specified 'INTEGRAL_TYPE' is a fundamental
        // integral type.

    static int get40BitIntegerValue(bsls::Types::Int64 *value,
                                    bsl::streambuf     *streamBuf);
        // Read 5 bytes from the input sequence of the specified 'streamBuf'
        // and load to the specified 'value' the interpretation of those bytes
        // as a 40-bit, signed, 2's-complement, big-endian integer.  Return 0
        // if successful, and a non-zero value otherwise.  The operation
        // succeeds if all 5 bytes are successfully read from the input
        // sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes read contain a valid representation of a
        // 40-bit, signed, 2's-complement, big-endian integer.

    template <class INTEGRAL_TYPE>
    static int putIntegerValue(bsl::streambuf *streamBuf, INTEGRAL_TYPE value);
        // Write the length and contents octets of the BER encoding of the
        // specified integer 'value' (as defined in the specification) to the
        // output sequence of the specified 'streamBuf'.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if all bytes corresponding to the length and contents octets are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.  The program is ill-formed unless the specified
        // 'INTEGRAL_TYPE' is a fundamental integral type.

    static int put40BitIntegerValue(bsl::streambuf     *streamBuf,
                                    bsls::Types::Int64  value);
        // Write the 5 octets that comprise the 40-bit, signed, 2's-complement,
        // bit-endian representation of the specified integer 'value' to the
        // specified 'streamBuf'.  Return 0 if successful, and a non-zero value
        // otherwise.  The operation succeeds if all bytes corresponding to the
        // representation of the 'value' are written to the 'streamBuf' without
        // the write position becoming unavailable.  The behavior is undefined
        // unless the 'value' is in the half-open interval
        // '[-549755813888, 549755813888)'.

    template <class INTEGRAL_TYPE>
    static int putIntegerGivenLength(bsl::streambuf *streamBuf,
                                     INTEGRAL_TYPE   value,
                                     int             length);
        // Write exactly the specified 'length' number of contents octets of
        // the BER encoding of the specified integer 'value' (as defined in the
        // specification) to the output sequence of the specified 'streamBuf'.
        // Return 0 if successful, and a non-zero value otherwise.  The
        // operation succeeds if all bytes corresponding to the contents octets
        // are written to the 'streamBuf' without the write position becoming
        // unavailable.  The behavior is undefined unless there are exactly
        // 'length' number of contents octets used to encode the integer
        // 'value' according to the specification.  The program is ill-formed
        // unless the specified 'INTEGRAL_TYPE' is a fundamental integral type.
};

                      // ===============================
                      // struct BerUtil_CharacterImpUtil
                      // ===============================

struct BerUtil_CharacterImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for byte values.  Within the definition of this 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690.

    // TYPES
    typedef BerUtil_IntegerImpUtil IntegerUtil;
        // 'IntegerUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for integer
        // values.

    typedef BerUtil_LengthImpUtil LengthUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

    // CLASS METHODS

    // Decoding

    static int getCharValue(char           *value,
                            bsl::streambuf *streamBuf,
                            int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the value of the contents octets of
        // a BER-encoded integer according to the specification.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes read contain a valid representation of the contents octets
        // of an integer value according to the specification.  Note that the
        // signedness of the interpreted integer value is the same as the
        // signedness of 'char' according to the current platform.

    static int getSignedCharValue(signed char    *value,
                                  bsl::streambuf *streamBuf,
                                  int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the value of the contents octets of
        // a BER-encoded integer according to the specification.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes read contain a valid representation of the contents octets
        // of an integer value according to the specification.

    static int getUnsignedCharValue(unsigned char  *value,
                                    bsl::streambuf *streamBuf,
                                    int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the value of the contents octets of
        // a BER-encoded integer according to the specification.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes read contain a valid representation of the contents octets
        // of an integer value according to the specification.

    // Encoding

    static int putCharValue(bsl::streambuf *streamBuf, char value);
        // Write the length and contents octets of the BER encoding of the
        // specified integer 'value' (as defined in the specification) to the
        // output sequence of the specified 'streamBuf'.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if all bytes corresponding to the length and contents octets are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putSignedCharValue(bsl::streambuf *streamBuf,
                                  signed char     value);
        // Write the length and contents octets of the BER encoding of the
        // specified integer 'value' (as defined in the specification) to the
        // output sequence of the specified 'streamBuf'.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if all bytes corresponding to the length and contents octets are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putUnsignedCharValue(bsl::streambuf *streamBuf,
                                    unsigned char   value);
        // Write the length and contents octets of the BER encoding of the
        // specified integer 'value' (as defined in the specification) to the
        // output sequence of the specified 'streamBuf'.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if all bytes corresponding to the length and contents octets are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.
};

                    // ===================================
                    // struct BerUtil_FloatingPointImpUtil
                    // ===================================

struct BerUtil_FloatingPointImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for floating point number values.  Within the definition of
    // this 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690,
    //:   and
    //:
    //: *the* *floating* *point* *specification*:
    //:   Refers to the 2008 revision of the IEE 754 Standard for
    //:   Floating-Point Arithemtic.

    // TYPES
    typedef BerUtil_IntegerImpUtil IntegerUtil;
        // 'IntegerUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for integer
        // values.

    typedef BerUtil_LengthImpUtil LengthUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

  private:
    // PRIVATE TYPES
    enum {
        k_MAX_MULTI_WIDTH_ENCODING_SIZE = 8,

        k_BINARY_NEGATIVE_NUMBER_ID = 0xC0,
        k_BINARY_POSITIVE_NUMBER_ID = 0x80,

        k_REAL_BINARY_ENCODING = 0x80,

        k_DOUBLE_EXPONENT_SHIFT              = 52,
        k_DOUBLE_OUTPUT_LENGTH               = 10,
        k_DOUBLE_EXPONENT_MASK_FOR_TWO_BYTES = 0x7FF,
        k_DOUBLE_NUM_EXPONENT_BITS           = 11,
        k_DOUBLE_NUM_MANTISSA_BITS           = 52,
        k_DOUBLE_NUM_EXPONENT_BYTES          = 2,
        k_DOUBLE_NUM_MANTISSA_BYTES          = 7,
        k_DOUBLE_BIAS                        = 1023,

        k_POSITIVE_ZERO_LEN    = 0,
        k_NEGATIVE_ZERO_LEN    = 1,

        k_POSITIVE_INFINITY_ID = 0x40,
        k_NEGATIVE_INFINITY_ID = 0x41,
        k_NAN_ID               = 0x42,
        k_NEGATIVE_ZERO_ID     = 0x43,

        k_DOUBLE_INFINITY_EXPONENT_ID = 0x7FF,
        k_INFINITY_MANTISSA_ID        = 0,

        k_REAL_SIGN_MASK            = 0x40,  // 0x40 = 0b0100'0000
        k_REAL_BASE_MASK            = 0x20,  // 0x20 = 0b0010'0000
        k_REAL_SCALE_FACTOR_MASK    = 0x0C,  // 0x0C = 0b0000'1100
        k_REAL_EXPONENT_LENGTH_MASK = 0x03,  // 0x03 = 0b0000'0011

        k_BER_RESERVED_BASE       = 3,
        k_REAL_BASE_SHIFT         = 4,
        k_REAL_SCALE_FACTOR_SHIFT = 2,

        k_REAL_MULTIPLE_EXPONENT_OCTETS = 4
    };

    // PRIVATE CLASS METHODS

    // Utilities

    static void assembleDouble(double    *value,
                               long long  exponent,
                               long long  mantissa,
                               int        sign);
        // Load to the specified 'value' the value of the "binary64" object
        // having the specified 'exponent' value, the bits of the specified
        // 'mantissa' interpreted as the digits of the mantissa, and the value
        // of the specified 'sign' interpreted as the sign bit, according to
        // the floating point specification.  The behavior is undefined unless
        // 'exponent' is in the range '[-1023, 1023]', 'mantissa' is in the
        // range '[-9007199254740991, 9007199254740991]', and 'sign' is 0 or 1.
        // The program is ill-formed unless the platform uses the "binary64"
        // interchange format encoding defined in the floating point
        // specification as the object representation for 'double' values.

    static void normalizeMantissaAndAdjustExp(long long *mantissa,
                                              int       *exponent,
                                              bool       denormalized);
        // Normalize the specified '*mantissa' value by adjusting the implicit
        // decimal point to after the rightmost 1 bit in the mantissa.  If
        // 'false == denormalized' prepend the implicit 1 in the mantissa
        // before adjusting the implicit decimal point.  Multiply the
        // '*exponent' value by 2 to the power of the number of places the
        // implicit decimal point moves.

    static void parseDouble(int       *exponent,
                            long long *mantissa,
                            int       *sign,
                            double     value);
        // Parse the specified 'value' and populate the specified 'exponent',
        // 'mantissa', and 'sign' values from the exponent, mantissa, and sign
        // of the 'value', respectively.

  public:
    // CLASS METHODS

    // Decoding

    static int getFloatValue(float          *value,
                             bsl::streambuf *streamBuf,
                             int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the contents octets of a
        // BER-encoded real value according to the specification.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes read contain a valid representation of the contents octets
        // of a real value according to the specification.

    static int getDoubleValue(double         *value,
                              bsl::streambuf *streamBuf,
                              int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the contents octets of a
        // BER-encoded real value according to the specification.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes read contain a valid representation of the contents octets
        // of a real value according to the specification.

    static int getDecimal64Value(bdldfp::Decimal64 *value,
                                 bsl::streambuf    *streamBuf,
                                 int                length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the contents octets of an encoded
        // 64-bit decimal value.  Return 0 if successful, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes read contain a
        // valid representation of the contents octets of an encoded 64-bit
        // decimal value.  See the package-level documentation of {'balber'}
        // for the definition of the format used to encode 64-bit decimal
        // values.

    // Encoding

    static int putFloatValue(bsl::streambuf         *streamBuf,
                            float                    value,
                            const BerEncoderOptions *options = 0);
        // Write the length and contents octets of the BER encoding of the
        // specified real 'value' (as defined in the specification) to the
        // output sequence of the specified 'streamBuf'.  Optionally specify
        // 'options', which will indicate whether '-0.0f' will be preserved or
        // encoded as '+0.0f'.  Return 0 if successful, and a non-zero value
        // otherwise.  The operation succeeds if all bytes corresponding to the
        // length and contents octets are written to the 'streamBuf' without
        // the write position becoming unavailable.

    static int putDoubleValue(bsl::streambuf          *streamBuf,
                              double                   value,
                              const BerEncoderOptions *options = 0);
        // Write the length and contents octets of the BER encoding of the
        // specified real 'value' (as defined in the specification) to the
        // output sequence of the specified 'streamBuf'.  Optionally specify
        // 'options', which will indicate whether '-0.0' will be preserved or
        // encoded as '+0.0'.  Return 0 if successful, and a non-zero value
        // otherwise.  The operation succeeds if all bytes corresponding to the
        // length and contents octets are written to the 'streamBuf' without
        // the write position becoming unavailable.

    static int putDecimal64Value(bsl::streambuf    *streamBuf,
                                 bdldfp::Decimal64  value);
        // Write the length and contents octets of the encoding of the BER
        // encoding of the specified 'value' to the output sequence of the
        // specified 'streamBuf'.  Return 0 if successful, and a non-zero value
        // otherwise.  The operation succeeds if all bytes corresponding to the
        // length and contents octets are written to the 'streamBuf' without
        // the write position becoming unavailable.  See the package-level
        // documentation of {'balber'} for the definition of the format used to
        // encode 64-bit decimal values.
};

                        // ============================
                        // struct BerUtil_StringImpUtil
                        // ============================

struct BerUtil_StringImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for string values.  Within the definition of this 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690.

    // TYPES
    typedef BerUtil_LengthImpUtil LengthUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

  public:
    // CLASS METHODS

    // Utilities

    static int putRawStringValue(bsl::streambuf *streamBuf,
                                 const char     *string,
                                 int             stringLength);
        // Write the length and contents octets of the BER encoding of the
        // specified byte 'string' having the specified 'stringLength' (as
        // defined in the specification) to the output sequence of the
        // specified 'streamBuf'.  Return 0 if successful, and a non-zero value
        // otherwise.  The operation succeeds if all bytes corresponding to the
        // length and contents octets are written to the 'streamBuf' without
        // the write position becoming unavailable.

    static int putChars(bsl::streambuf *streamBuf, char value, int numChars);
        // Write the specified 'numChars' number of bytes having the specified
        // 'value' to the output sequence of the specified 'streamBuf'.  Return
        // 0 if successful, and a non-zero value otherwise.  The operation
        // succeeds if all 'numChars' bytes are written to the 'streamBuf'
        // without the write position becoming unavailable.

    // 'bsl::string' Decoding

    static int getStringValue(bsl::string              *value,
                              bsl::streambuf           *streamBuf,
                              int                       length,
                              const BerDecoderOptions&  options);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // interpretation of those bytes as the value of the contents octets of
        // a BER-encoded character string (more specifically, an unrestricted
        // character string) according to the specification, unless an
        // alternate value is indicated by the specified 'options', in which
        // case, the alternate value is loaded.  If the 'DefaultEmptyStrings'
        // attribute of the 'options' is 'true' and the witnessed BER-encoded
        // character string represents the empty string value, the alternate
        // value is the current '*value', otherwise there is no alternate
        // value.  Return 0 if successful, and a non-zero value otherwise.  The
        // operation succeeds if 'length' bytes are successfully read from the
        // input sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes read contain a valid representation of
        // the contents octets of a character string value according to the
        // specification.

    // 'bsl::string' Encoding

    static int putStringValue(bsl::streambuf     *streamBuf,
                              const bsl::string&  value);
        // Write the length and contents octets of the BER encoding of the
        // specified character string 'value' (as defined in the specification)
        // to the output sequence of the specified 'streamBuf'.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if all bytes corresponding to the length and contents octets are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.

    // 'bslstl::StringRef' Encoding

    static int putStringRefValue(bsl::streambuf           *streamBuf,
                                 const bslstl::StringRef&  value);
        // Write the length and contents octets of the BER encoding of the
        // specified character string 'value' (as defined in the specification)
        // to the output sequence of the specified 'streamBuf'.  Return 0 if
        // successful, and a non-zero value otherwise.  The operation succeeds
        // if all bytes corresponding to the length and contents octets are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.
};

                       // =============================
                       // struct BerUtil_Iso8601ImpUtil
                       // =============================

struct BerUtil_Iso8601ImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for date and time values in the ISO 8601 format.  See the
    // component-level documentation of {'bdlt_iso8601util'} for a complete
    // description of the ISO 8601 format used by the functions provided by
    // this 'struct'.

    // TYPES
    typedef BerUtil_StringImpUtil StringUtil;
        // 'StringUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoder and decoding operations for string
        // values.

  private:
    // PRIVATE CLASS METHODS
    template <class TYPE>
    static int getValue(TYPE *value, bsl::streambuf *streamBuf, int length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // value represented by the interpretation of the bytes as an ISO 8601
        // date/time value.  The specified 'TYPE' defines the expected ISO 8601
        // date/time format, which is the format corresponding to the 'TYPE' as
        // specified in {'bdlt_iso8601util'}.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of the expected ISO 8601
        // date/time format.  The program is ill-formed unless 'TYPE' is one
        // of: 'bdlt::Date', 'bdlt::DateTz', 'bdlt::Datetime',
        // 'bdlt::DatetimeTz', 'bdlt::Time', or 'bdlt::TimeTz'.

    template <class TYPE>
    static int putValue(bsl::streambuf          *streamBuf,
                        const TYPE&              value,
                        const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf'.  If the specified
        // 'options' is 0, use 3 decimal places of fractional second precision,
        // otherwise use the number of decimal places specified by the
        // 'datetimeFractionalSecondPrecision' attribute of the 'options'.
        // Return 0 on success and a non-zero value otherwise.  The operation
        // succeeds if all bytes of the ISO 8601 representation of the 'value'
        // are written to the 'streamBuf' without the write position becoming
        // unavailable.  The program is ill-formed unless 'TYPE' is one of
        // 'bdlt::Date', 'bdlt::DateTz', 'bdlt::Datetime', 'bdlt::DatetimeTz',
        // 'bdlt::Time', or 'bdlt::TimeTz'.

  public:
    // CLASS METHODS

    // Decoding

    static int getDateValue(bdlt::Date     *value,
                            bsl::streambuf *streamBuf,
                            int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 date.  Return 0 on success, and a non-zero value otherwise.
        // The operation succeeds if 'length' bytes are successfully read from
        // the input sequence of the 'streamBuf' without the read position
        // becoming unavailable, and the bytes contain a valid representation
        // of an ISO 8601 date.

    static int getDateTzValue(bdlt::DateTz   *value,
                              bsl::streambuf *streamBuf,
                              int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 date and time zone.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of an ISO 8601 date and time zone.

    static int getDatetimeValue(bdlt::Datetime *value,
                                bsl::streambuf *streamBuf,
                                int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 date and time.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of an ISO 8601 date and time.

    static int getDatetimeTzValue(bdlt::DatetimeTz *value,
                                  bsl::streambuf   *streamBuf,
                                  int               length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 date, time, and time zone.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if 'length' bytes
        // are successfully read from the input sequence of the 'streamBuf'
        // without the read position becoming unavailable, and the bytes
        // contain a valid representation of an ISO 8601 date, time, and time
        // zone.

    static int getTimeValue(bdlt::Time     *value,
                            bsl::streambuf *streamBuf,
                            int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 time.  Return 0 on success, and a non-zero value otherwise.
        // The operation succeeds if 'length' bytes are successfully read from
        // the input sequence of the 'streamBuf' without the read position
        // becoming unavailable, and the bytes contain a valid representation
        // of an ISO 8601 time.

    static int getTimeTzValue(bdlt::TimeTz   *value,
                              bsl::streambuf *streamBuf,
                              int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 time and time zone.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of an ISO 8601 time and time zone.

    // Encoding

    static int putDateValue(bsl::streambuf          *streamBuf,
                            const bdlt::Date&        value,
                            const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf'.  Return 0 on success
        // and a non-zero value otherwise.  The operation succeeds if all bytes
        // of the ISO 8601 representation of the 'value' are written to the
        // 'streamBuf' without the write position becoming unavailable.

    static int putDateTzValue(bsl::streambuf          *streamBuf,
                              const bdlt::DateTz&      value,
                              const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf'.  Return 0 on success
        // and a non-zero value otherwise.  The operation succeeds if all bytes
        // of the ISO 8601 representation of the 'value' are written to the
        // 'streamBuf' without the write position becoming unavailable.

    static int putDatetimeValue(bsl::streambuf          *streamBuf,
                                const bdlt::Datetime&    value,
                                const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf'.  If the specified
        // 'options' is 0, use 3 decimal places of fractional second precision,
        // otherwise use the number of decimal places specified by the
        // 'datetimeFractionalSecondPrecision' attribute of the 'options'.
        // Return 0 on success and a non-zero value otherwise.  The operation
        // succeeds if all bytes of the ISO 8601 representation of the 'value'
        // are written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putDatetimeTzValue(bsl::streambuf          *streamBuf,
                                  const bdlt::DatetimeTz&  value,
                                  const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf'.  If the specified
        // 'options' is 0, use 3 decimal places of fractional second precision,
        // otherwise use the number of decimal places specified by the
        // 'datetimeFractionalSecondPrecision' attribute of the 'options'.
        // Return 0 on success and a non-zero value otherwise.  The operation
        // succeeds if all bytes of the ISO 8601 representation of the 'value'
        // are written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putTimeValue(bsl::streambuf          *streamBuf,
                            const bdlt::Time&        value,
                            const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf'.  If the specified
        // 'options' is 0, use 3 decimal places of fractional second precision,
        // otherwise use the number of decimal places specified by the
        // 'datetimeFractionalSecondPrecision' attribute of the 'options'.
        // Return 0 on success and a non-zero value otherwise.  The operation
        // succeeds if all bytes of the ISO 8601 representation of the 'value'
        // are written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putTimeTzValue(bsl::streambuf          *streamBuf,
                              const bdlt::TimeTz&      value,
                              const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf'.  If the specified
        // 'options' is 0, use 3 decimal places of fractional second precision,
        // otherwise use the number of decimal places specified by the
        // 'datetimeFractionalSecondPrecision' attribute of the 'options'.
        // Return 0 on success and a non-zero value otherwise.  The operation
        // succeeds if all bytes of the ISO 8601 representation of the 'value'
        // are written to the 'streamBuf' without the write position becoming
        // unavailable.
};

                    // ====================================
                    // struct BerUtil_TimezoneOffsetImpUtil
                    // ====================================

struct BerUtil_TimezoneOffsetImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions and constants used by 'BerUtil' to encode and decode
    // time-zone values.

    // TYPES
    enum {
        k_MIN_OFFSET = -1439,
        // The minimum number of minutes in a valid time-zone offset

        k_MAX_OFFSET = 1439,
        // The maximum number of minutes in a valid time-zone offset

        k_TIMEZONE_LENGTH = 2
        // The number of octets used in the encoding of a time-zone offset
        // value.  This number is constant: all time-zone values are
        // encoded using 2 octets regardless of numeric value.
    };

    // CLASS METHODS
    static bool isValidTimezoneOffsetInMinutes(int value);
        // Return 'true' if the specified 'value' is a valid time-zone offset,
        // and return 'false' otherwise.  A time-zone offset is valid if it is
        // greater than or equal to 'k_MIN_OFFSET' and less than or equal to
        // 'k_MAX_OFFSET'.

    static int getTimezoneOffsetInMinutes(int            *value,
                                          bsl::streambuf *streamBuf);
        // Read from the specified 'streamBuf' and load to the specified
        // 'value' of the time-zone offset.

    static int getTimezoneOffsetInMinutesIfValid(int            *value,
                                                 bsl::streambuf *streamBuf);
        // Read a time zone offset value from the specified 'streamBuf'.  If
        // the offset is greater than or equal to 'k_MIN_OFFSET' and less than
        // or equal to 'k_MAX_OFFSET' then load the value of the offset to the
        // specified 'value' and return zero, otherwise do not modify the value
        // addressed by 'value' and return non-zero.

    static int putTimezoneOffsetInMinutes(bsl::streambuf *streamBuf,
                                          int             value);
        // Write to the specified 'streamBuf' the value of the specified
        // time-zone offset 'value'.  The behavior is undefined unless
        // 'k_MIN_OFFSET <= value' and 'value <= k_MAX_OFFSET'.
};

                     // ==================================
                     // struct BerUtil_DateAndTimeEncoding
                     // ==================================

struct BerUtil_DateAndTimeEncoding {
    // This component-private 'struct' provides a namespace for enumerating the
    // union of the sets of date and time formats used to encode and decode all
    // date and time types supported by 'BerUtil'.

    // TYPES
    enum Value {
        e_ISO8601_DATE,
        e_ISO8601_DATETZ,
        e_ISO8601_DATETIME,
        e_ISO8601_DATETIMETZ,
        e_ISO8601_TIME,
        e_ISO8601_TIMETZ,
        e_COMPACT_BINARY_DATE,
        e_COMPACT_BINARY_DATETZ,
        e_COMPACT_BINARY_DATETIME,
        e_COMPACT_BINARY_DATETIMETZ,
        e_COMPACT_BINARY_TIME,
        e_COMPACT_BINARY_TIMETZ,
        e_EXTENDED_BINARY_DATETIME,
        e_EXTENDED_BINARY_DATETIMETZ,
        e_EXTENDED_BINARY_TIME,
        e_EXTENDED_BINARY_TIMETZ
    };

    // TYPES
    enum {
        k_EXTENDED_BINARY_MIN_BDE_VERSION = 35500
        // the minimum BDE version number in which this component supports
        // encoding and decoding date and time types using their
        // respective extended-binary-encoding formats.
    };
};

                 // =========================================
                 // struct BerUtil_ExtendedBinaryEncodingUtil
                 // =========================================

struct BerUtil_ExtendedBinaryEncodingUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to determine if a particular date and/or
    // time value should be encoded using its corresponding
    // extended-binary-encoding format, its corresponding
    // compact-binary-encoding format, or neither format.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    // CLASS METHODS
    static bool useExtendedBinaryEncoding(const bdlt::Time&        value,
                                          const BerEncoderOptions *options);
    static bool useExtendedBinaryEncoding(const bdlt::TimeTz&      value,
                                          const BerEncoderOptions *options);
    static bool useExtendedBinaryEncoding(const bdlt::Datetime&    value,
                                          const BerEncoderOptions *options);
    static bool useExtendedBinaryEncoding(const bdlt::DatetimeTz&  value,
                                          const BerEncoderOptions *options);
        // Return 'true' if the specified 'value' must be encoded using its
        // corresponding extended-binary-encoding format according to the
        // specified 'options', and return 'false' otherwise.

    static bool useBinaryEncoding(const BerEncoderOptions *options);
        // Return 'true' if a date and/or time value must be encoded using
        // either its corresponding extended-binary-encoding format or its
        // corresponding compact-binary-encoding format according to the
        // specified 'options', and return 'false' otherwise.  Note that, for
        // any given 'value' and 'options', the 'value' must be encoded using
        // its corresponding compact-binary-encoding format if
        // 'useExtendedBinaryEncoding(value, options)' returns 'false' and
        // 'useBinaryEncoding(options)' returns 'true'.
};

                    // ====================================
                    // struct BerUtil_DateAndTimeHeaderType
                    // ====================================

struct BerUtil_DateAndTimeHeaderType {
    // This component-private 'struct' provides a namespace for enumerating the
    // set of "header type" values that may be encoded in the 2-byte header of
    // an extended-binary-encoding formatted date-and-time value.

    // TYPES
    enum Value {
        e_NOT_EXTENDED_BINARY,
        // header-type value that indicates the encoded value is in either
        // its corresponding compact-binary encoding or its corresponding
        // ISO 8601 encoding

        e_EXTENDED_BINARY_WITHOUT_TIMEZONE,
        // header-type value that indicates the encoded value is in its
        // corresponding extended-binary encoding and does not carry a
        // time-zone offset value

        e_EXTENDED_BINARY_WITH_TIMEZONE
        // header-type value that indicates the encoded value is in its
        // corresponding extended-binary encoding and carries a time-zone
        // offset value
    };
};

                      // ===============================
                      // class BerUtil_DateAndTimeHeader
                      // ===============================

class BerUtil_DateAndTimeHeader {
    // This component-private, in-core, value-semantic attribute class provides
    // a representation of the information available in the first two bytes of
    // any extended-binary-encoding formatted data.  All extended-binary
    // encoding schemes for date-and-time types contain a 2-byte header in the
    // same format, which can be unambiguously distinguished from the first 2
    // bytes of a date-and-time type in its corresponding
    // compact-binary-encoding format or its ISO 8601 format.

  public:
    // TYPES
    typedef BerUtil_DateAndTimeHeaderType Type;
        // 'Type' is an alias to a namespace for enumerating the set of "header
        // type" values that may be encoded in the 2-byte header of an
        // extended-binary-encoding formatted date-and-time value.

    typedef BerUtil_TimezoneOffsetImpUtil TimezoneUtil;
        // 'TimezoneUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for time-zone
        // offset values.

  private:
    // DATA
    Type::Value d_type;
        // date-and-time header type

    int         d_timezoneOffsetInMinutes;
        // offset in minutes from UTC indicated by the date-and-time header if
        // the header contains a time-zone offset, and 0 otherwise

  public:
    // CREATORS
    BerUtil_DateAndTimeHeader();
        // Create a 'BerUtil_DateAndTimeHeader' object having a 'type'
        // attribute with the 'Type::e_NOT_EXTENDED_BINARY' value and a
        // 'timezoneOffsetInMinutes' attribute with the 0 value.

    //! BerUtil_DateAndTimeHeader(
        //!               const BerUtil_DateAndTimeHeader& original) = default;
        // Create a 'BerUtil_DateAndTimeHeader' object having the same value as
        // the specified 'original' object.

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

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

    void makeNotExtendedBinary();
        // Set the 'type' attribute of this object to the
        // 'Type::e_NOT_EXTENDED_BINARY' value and the
        // 'timezoneOffsetInMinutes' attribute of this object to the 0 value.

    void makeExtendedBinaryWithoutTimezone();
        // Set the 'type' attribute of this object to the
        // 'Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE' value and the
        // 'timezoneOffsetInMinutes' attribute of this object to the 0 value.

    void makeExtendedBinaryWithTimezone(int offset);
        // Set the 'type' attribute of this object to the
        // 'Type::e_EXTENDED_BINARY_WITH_TIMEZONE' value and the
        // 'timezoneOffsetInMinutes' attribute of this object to the specified
        // 'offset'.  The behavior is undefined unless
        // 'TimezoneUtil::k_MIN_OFFSET <= offset' and
        // 'TimezoneUtil::k_MAX_OFFSET >= offset'.

    // ACCESSORS
    bool isExtendedBinary() const;
        // Return 'true' if the 'type' attribute of this object is
        // 'Type::e_EXTENDED_BINARY_WITH_TIMEZONE' or
        // 'Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE', and 'false' otherwise.

    bool isExtendedBinaryWithoutTimezone() const;
        // Return 'true' if the 'type' attribute of this object is
        // 'Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE', and 'false' otherwise.

    bool isExtendedBinaryWithTimezone() const;
        // Return 'true' if the 'type' attribute of this object is
        // 'Type::e_EXTENDED_BINARY_WITH_TIMEZONE', and 'false' otherwise.

    int timezoneOffsetInMinutes() const;
        // Return the value of the 'timezoneOffsetInMinutes' attribute of this
        // object.
};

                  // =======================================
                  // struct BerUtil_DateAndTimeHeaderImpUtil
                  // =======================================

struct BerUtil_DateAndTimeHeaderImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions used by 'BerUtil' to implement encoding and decoding
    // operations for the 2-byte header of extended-binary-encoding formatted
    // date-and-time value.

    // TYPES
    typedef BerUtil_DateAndTimeHeader Header;
        // 'Header' is an alias to an in-core, value-semantic attribute class
        // that represents the range of valid values of the 2-byte header of
        // extended-binary-encoding formatted date-and-time values.

    typedef BerUtil_DateAndTimeHeaderType Type;
        // 'Type' is an alias to a namespace for enumerating the set of "header
        // type" values that may be encoded in the 2-byte header of an
        // extended-binary-encoding formatted date-and-time value.

    typedef BerUtil_StreambufUtil StreambufUtil;
        // 'StreambufUtil' is an alias to a namespace for a suite of functions
        // used to implement input and output operations on 'bsl::streambuf'
        // objects.

    typedef BerUtil_TimezoneOffsetImpUtil TimezoneUtil;
        // 'TimezoneUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for time-zone
        // offset values.

    // CLASS DATA
    static const int k_HEADER_LENGTH = 2;
        // Number of octets used to encode an extended-binary-encoding header.

    // CLASS METHODS
    static bool isReserved(unsigned char firstByte);
        // Return 'true' if the specified 'firstByte' of an encoded
        // date-and-time value indicates it is in a format reserved for future
        // use, and return 'false' otherwise.  Note that this may indicate the
        // value was encoded incorrectly or using a newer version of this
        // component.

    static bool isExtendedBinary(unsigned char firstByte);
        // Return 'true' if the specified 'firstByte' of an encoded
        // date-and-time value indicates it is in the extended-binary-encoding
        // format, and return 'false' otherwise.

    static bool isExtendedBinaryWithoutTimezone(unsigned char firstByte);
        // Return 'true' if the specified 'firstByte' of an encoded
        // date-and-time value indicates it is in the extended-binary-encoding
        // format and does not carry a time-zone offset value, and return
        // 'false' otherwise.

    static bool isExtendedBinaryWithTimezone(unsigned char firstByte);
        // Return 'true' if the specified 'firstByte' if an encoded
        // date-and-time value indicates is is in the extended-binary-encoding
        // format and carries a time-zone offset value, and return 'false'
        // otherwise.

    static void detectTypeIfNotReserved(bool          *reserved,
                                        Type::Value   *type,
                                        unsigned char  firstByte);
        // If the specified 'firstByte' of an encoded date-and-time value
        // indicates it is in a compact-binary-encoding format or an ISO 8601
        // format, load the value 'Type::e_NOT_EXTENDED_BINARY' to the
        // specified 'type' and 'false' to the specified 'reserved' flag.  If
        // it indicates it is in an extended-binary format that carries a
        // time-zone offset value, load the value
        // 'Type::e_EXTENDED_BINARY_WITH_TIMEZONE' to the 'type' and 'false' to
        // 'reserved'.  If it indicates it is in an extended-binary format that
        // does not carry a time-zone offset value, load the value
        // 'Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE' to the 'type' and 'false'
        // to 'reserved'.  Otherwise, load the value 'true' to 'reserved' and
        // leave the 'type' in a valid but unspecified state.  Note that this
        // operation has a wide contract because all possible values of
        // 'firstByte' can be interpreted to indicate one of the conditions
        // described above.

    static void detectType(Type::Value *type, unsigned char firstByte);
        // If the specified 'firstByte' of an encoded date-and-time value
        // indicates it is in a compact-binary-encoding format or an ISO 8601
        // format, load the value 'Type::e_NOT_EXTENDED_BINARY' to the
        // specified 'type'.  If it indicates it is in an extended-binary
        // format that carries a time-zone offset value, load the value
        // 'Type::e_EXTENDED_BINARY_WITH_TIMEZONE' to the 'type'.  If it
        // indicates it is in an extended-binary format that does not carry a
        // time-zone offset value, load the value
        // 'Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE' to the 'type'.  The
        // behavior is undefined unless 'isReserved(firstByte)' returns
        // 'false'.

    static int getValueIfNotReserved(Header *value, bsl::streambuf *streamBuf);
        // Read 2 bytes from the input sequence of the specified 'streamBuf'
        // and load to the specified 'value' the interpretation of those bytes
        // as an extended-binary header value, if that header indicates the
        // value is not in a format reserved for future use.  Return 0 on
        // success, and a non-zero value otherwise.

    static int getValueIfNotReserved(Header        *value,
                                     unsigned char  headerByte0,
                                     unsigned char  headerByte1);
        // Load to the specified 'value' the interpretation of the specified
        // 'headerByte0' and 'headerByte1' as the 2 bytes that comprise an
        // encoded extended-binary header value if that value indicates it is
        // not in a format reserved for future use.  Return 0 on success, and a
        // non-zero value otherwise.

    static int getValue(Header *value, bsl::streambuf *streamBuf);
        // Read 2 bytes from the input sequence of the specified 'streamBuf'
        // and load to the specified 'value' the interpretation of those bytes
        // as an extended-binary header value Return 0 on success, and a
        // non-zero value otherwise.  The behavior is undefined if 2 bytes are
        // successfully read from the 'streamBuf', but the interpretation of
        // those bytes as an extended-binary header indicates the value is in a
        // format reserved for future use.

    static int getValue(Header        *value,
                        unsigned char  headerByte0,
                        unsigned char  headerByte1);
        // Load to the specified 'value' the interpretation of the specified
        // 'headerByte0' and 'headerByte1' as the 2 bytes that comprise an
        // encoded extended-binary header value.  Return 0 on success, and a
        // non-zero value otherwise.  The behavior is undefined if the
        // interpretation of the 2 bytes as an extended-binary header indicates
        // the value is in a format reserved for future use.

    static int putExtendedBinaryWithoutTimezoneValue(
                                                    bsl::streambuf *streamBuf);
        // Write a representation of an extended-binary header value that does
        // not carry a time-zone offset value to the specified 'streamBuf'.
        // Return 0 on success, and a non-zero value otherwise.

    static int putExtendedBinaryWithTimezoneValue(
                                      bsl::streambuf *streamBuf,
                                      int             timezoneOffsetInMinutes);
        // Write a representation of an extended-binary header value that
        // carries the specified 'timezoneOffsetInMinutes' time-zone offset
        // value to the specified 'streamBuf'.  Return 0 on success, and a
        // non-zero value otherwise.
};

                        // ===========================
                        // struct BerUtil_DateEncoding
                        // ===========================

struct BerUtil_DateEncoding {
    // This component-private 'struct' provides a namespace for enumerating the
    // set of formats that may be used by 'BerUtil' to encode and decode values
    // of 'bdlt::Date' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_DATE        = Encoding::e_ISO8601_DATE,
        e_COMPACT_BINARY_DATE = Encoding::e_COMPACT_BINARY_DATE
    };
};

                       // =============================
                       // struct BerUtil_DateTzEncoding
                       // =============================

struct BerUtil_DateTzEncoding {
    // This component-private 'struct' provides a namespace for enumerating the
    // set of formats that may be used by 'BerUtil' to encode and decode values
    // of 'bdlt::DateTz' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_DATETZ        = Encoding::e_ISO8601_DATETZ,
        e_COMPACT_BINARY_DATE   = Encoding::e_COMPACT_BINARY_DATE,
        e_COMPACT_BINARY_DATETZ = Encoding::e_COMPACT_BINARY_DATETZ
    };
};

                    // ===================================
                    // struct BerUtil_DateOrDateTzEncoding
                    // ===================================

struct BerUtil_DateOrDateTzEncoding {
    // This component-private 'struct' provides a namespace for enumerating the
    // set of formats that may be used by 'BerUtil' to encode and decode values
    // of 'bdlb::Variant2<bdlt::Date, bdlt::DateTz>' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_DATE          = Encoding::e_ISO8601_DATE,
        e_ISO8601_DATETZ        = Encoding::e_ISO8601_DATETZ,
        e_COMPACT_BINARY_DATE   = Encoding::e_COMPACT_BINARY_DATE,
        e_COMPACT_BINARY_DATETZ = Encoding::e_COMPACT_BINARY_DATETZ
    };
};

                         // ==========================
                         // struct BerUtil_DateImpUtil
                         // ==========================

struct BerUtil_DateImpUtil {
    // This component-private 'struct' provides a namespace for a suite of
    // functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for date values.  Within the definition of this 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690,
    //:   and
    //:
    //: *the* *default* *set* *of* *options*:
    //:   Refers to a 'balber::BerEncoderOptions' value having a
    //:   'datetimeFractionalSecondPrecision' attribute of 3 and a
    //:   'encodeDateAndTimeTypesAsBinary' attribute of 'false'.
    //
    // See the package level documentation of {'balber'} for a definition of
    // the compact and extended binary formats for date and time values.

    // TYPES
    typedef BerUtil_DateAndTimeHeaderImpUtil DateAndTimeHeaderUtil;
        // 'DateAndTimeHeaderUtil' is an alias to a namespace for a suite of
        // functions used to implement encoding and decoding operations for the
        // 2-byte header of an extended-binary-encoding formatted date-and-time
        // value.

    typedef BerUtil_DateEncoding DateEncoding;
        // 'DateEncoding' is an alias to a namespace for enumerating the set of
        // formats that may be used by 'BerUtil' to encode and decode values of
        // 'bdlt::Date' type.

    typedef BerUtil_DateTzEncoding DateTzEncoding;
        // 'DateEncoding' is an alias to a namespace for enumerating the set of
        // formats that may be used by 'BerUtil' to encode and decode values of
        // 'bdlt::DateTz' type.

    typedef BerUtil_DateOrDateTzEncoding DateOrDateTzEncoding;
        // 'DateEncoding' is an alias to a namespace for enumerating the set of
        // formats that may be used by 'BerUtil' to decode to values of
        // 'bdlb::Variant2<bdlt::Date, bdlt::DateTz>' type.

    typedef BerUtil_IntegerImpUtil IntegerUtil;
        // 'IntegerUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for integer
        // values.

    typedef BerUtil_Iso8601ImpUtil Iso8601Util;
        // 'Iso8601Util' is an alias to a namespace for a suite of functions
        // used to implementing the encoding and decoding of date and time
        // values using the ISO 8601 format.

    typedef BerUtil_LengthImpUtil LengthUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

    typedef BerUtil_StreambufUtil StreambufUtil;
        // 'StreambufUtil' is an alias to a namespace for a suite of functions
        // used to implement input and output operations on 'bsl::streambuf'
        // objects.

    typedef BerUtil_StringImpUtil StringUtil;
        // 'StringUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for string values.

    typedef BerUtil_TimezoneOffsetImpUtil TimezoneUtil;
        // 'TimezoneUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for time-zone
        // offset values.

    typedef bdlb::Variant2<bdlt::Date, bdlt::DateTz> DateOrDateTz;
        // 'DateOrDateTz' is a convenient alias for
        // 'bdlb::Variant2<bdlt::Date, bdlt::DateTz>'.

    enum {
        k_COMPACT_BINARY_DATE_EPOCH = 737425
        // The serial date of January 1st, 2020.  Note that the serial date
        // of a date is defined as the number of days between that date and
        // January 1st year 1 in the Proleptic Gregorian calendar.
    };

  private:
    // PRIVATE TYPES
    enum {
        k_MAX_ISO8601_DATE_LENGTH = bdlt::Iso8601Util::k_DATE_STRLEN,
        // the maximum number of content octets used by 'BerUtil' to encode
        // a date value using the ISO 8601 format

        k_MAX_ISO8601_DATETZ_LENGTH = bdlt::Iso8601Util::k_DATETZ_STRLEN,
        // the maximum number of content octets used by 'BerUtil' to
        // encode a date and time zone value using the ISO 8601 format

        k_MAX_COMPACT_BINARY_DATE_LENGTH = 3,
        // the maximum number of content octets used by 'BerUtil' to
        // encode a date value using the compact-binary format

        k_MIN_COMPACT_BINARY_DATETZ_LENGTH =
            k_MAX_COMPACT_BINARY_DATE_LENGTH + 1,
        // the minimum number of content octets used by 'BerUtil' to
        // encode a date and time zone value using the compact-binary
        // format

        k_MAX_COMPACT_BINARY_DATETZ_LENGTH = 5
        // the maximum number of content octets used by 'BerUtil' to
        // encode a date and time zone value using the compact-binary
        // format
    };

    // PRIVATE CLASS METHODS

    // 'bdlt::Date' Decoding

    static int detectDateEncoding(DateEncoding::Value *encoding,
                                  int                  length,
                                  unsigned char        firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::Date' value given the specified
        // 'length' and 'firstByte' of the encoded representation.  Return 0 on
        // success, -1 if the format is reserved for future use, and some other
        // non-zero value otherwise.

    static int getIso8601DateValue(bdlt::Date     *value,
                                   bsl::streambuf *streamBuf,
                                   int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 date.  Return 0 on success, and a non-zero value otherwise.
        // The operation succeeds if 'length' bytes are successfully read from
        // the input sequence of the 'streamBuf' without the read position
        // becoming unavailable, and the bytes contain a valid representation
        // of an ISO 8601 date.

    static int getCompactBinaryDateValue(bdlt::Date     *value,
                                         bsl::streambuf *streamBuf,
                                         int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as a
        // compact-binary date.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of a compact-binary date.

    // 'bdlt::Date' Encoding

    static DateEncoding::Value selectDateEncoding(
                                             const bdlt::Date&        value,
                                             const BerEncoderOptions *options);
        // Determine the format that should be used to encode the specified
        // 'value' given the 'value' and the specified 'options'.  If 'options'
        // is 0, the default set of options is used.  Return an enumerator
        // identifying the selected format.

    static int putIso8601DateValue(bsl::streambuf          *streamBuf,
                                   const bdlt::Date&        value,
                                   const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if all bytes of the ISO 8601 representation of
        // the 'value' are written to the 'streamBuf' without the write
        // position becoming unavailable.

    static int putCompactBinaryDateValue(bsl::streambuf          *streamBuf,
                                         const bdlt::Date&        value,
                                         const BerEncoderOptions *options);
        // Write the compact-binary date representation of the specified
        // 'value' to the output sequence of the specified 'streamBuf'
        // according to the specified 'options'.  If 'options' is 0, the
        // default set of options is used.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if all bytes of the
        // compact-binary date representation of the 'value' are written to the
        // 'streamBuf' without the write position becoming unavailable.

    // 'bdlt::DateTz' Decoding

    static int detectDateTzEncoding(DateTzEncoding::Value *encoding,
                                    int                    length,
                                    unsigned char          firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::DateTz' value given the specified
        // 'length' and 'firstByte' of the encoded representation.  Return 0 on
        // success, -1 if the format is reserved for future use, and some other
        // non-zero value otherwise.

    static int getIso8601DateTzValue(bdlt::DateTz   *value,
                                     bsl::streambuf *streamBuf,
                                     int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time zone value represented by the interpretation of the
        // read bytes as an ISO 8601 date and time zone.  Return 0 on success,
        // and a non-zero value otherwise.  The operation succeeds if 'length'
        // bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of an ISO 8601 date and time
        // zone.

    static int getCompactBinaryDateValue(bdlt::DateTz   *value,
                                         bsl::streambuf *streamBuf,
                                         int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time zone value represented by the interpretation of the
        // read bytes as a compact-binary date.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if 'length' bytes
        // are successfully read from the input sequence of the 'streamBuf'
        // without the read position becoming unavailable, and the bytes
        // contain a valid representation of a compact-binary date.

    static int getCompactBinaryDateTzValue(bdlt::DateTz   *value,
                                           bsl::streambuf *streamBuf,
                                           int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time zone value represented by the interpretation of the
        // read bytes as a compact-binary date and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a compact-binary date.

    // 'bdlt::DateTz' Encoding

    static DateTzEncoding::Value selectDateTzEncoding(
                                             const bdlt::DateTz&      value,
                                             const BerEncoderOptions *options);
        // Determine the format that should be used to encode the specified
        // 'value' given the 'value' and the specified 'options'.  If 'options'
        // is 0, the default set of options is used.  Return an enumerator
        // identifying the selected format.

    static int putIso8601DateTzValue(bsl::streambuf          *streamBuf,
                                     const bdlt::DateTz&      value,
                                     const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if all bytes of the ISO 8601 representation of
        // the 'value' are written to the 'streamBuf' without the write
        // position becoming unavailable.

    static int putCompactBinaryDateValue(bsl::streambuf          *streamBuf,
                                         const bdlt::DateTz&      value,
                                         const BerEncoderOptions *options);
        // Write the compact-binary date representation of the specified
        // 'value' to the output sequence of the specified 'streamBuf'
        // according to the specified 'options'.  If 'options' is 0, the
        // default set of options is used.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if all bytes of the
        // compact-binary date representation of the 'value' are written to the
        // 'streamBuf' without the write position becoming unavailable.  The
        // behavior is undefined unless the 'offset' of the 'value' is 0.

    static int putCompactBinaryDateTzValue(bsl::streambuf          *streamBuf,
                                           const bdlt::DateTz&      value,
                                           const BerEncoderOptions *options);
        // Write the compact-binary date and time zone representation of the
        // specified 'value' to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if all bytes of
        // the compact-binary date representation of the 'value' are written to
        // the 'streamBuf' without the write position becoming unavailable.

    // Variant Decoding

    static int detectDateOrDateTzEncoding(
                                 DateOrDateTzEncoding::Value *encoding,
                                 int                          length,
                                 unsigned char                firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::Date' or 'bdlt::DateTz' value given
        // the specified 'length' and 'firstByte' of the encoded
        // representation.  Return 0 on success, -1 if the format is reserved
        // for future use, and some other non-zero value otherwise.

    static int getIso8601DateValue(DateOrDateTz   *value,
                                   bsl::streambuf *streamBuf,
                                   int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 date.  Return 0 on success, and a non-zero value otherwise.
        // The operation succeeds if 'length' bytes are successfully read from
        // the input sequence of the 'streamBuf' without the read position
        // becoming unavailable, and the bytes contain a valid representation
        // of an ISO 8601 date.

    static int getIso8601DateTzValue(DateOrDateTz   *value,
                                     bsl::streambuf *streamBuf,
                                     int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as an
        // ISO 8601 date and time zone.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of an ISO 8601 date and time zone.

    static int getCompactBinaryDateValue(DateOrDateTz   *value,
                                         bsl::streambuf *streamBuf,
                                         int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as a
        // compact-binary date.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of a compact-binary date.

    static int getCompactBinaryDateTzValue(DateOrDateTz   *value,
                                           bsl::streambuf *streamBuf,
                                           int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time zone value represented by the interpretation of the
        // read bytes as a compact-binary date and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a compact-binary date and
        // time zone.

  public:
    // CLASS METHODS

    // Utilities

    static void dateToDaysSinceEpoch(bsls::Types::Int64 *daysSinceEpoch,
                                     const bdlt::Date&   date);
        // Load to the specified 'daysSinceEpoch' the number of days between
        // the compact-binary date epoch and the specified 'date'. The
        // compact-binary date epoch is the date defined by the
        // 'k_COMPACT_BINARY_DATE_EPOCH' serial date.  Note that this quantity
        // may be negative if the specified 'date' occurs before the
        // compact-binary date epoch.

    static int daysSinceEpochToDate(bdlt::Date         *date,
                                    bsls::Types::Int64  daysSinceEpoch);
        // Load to the specified 'date' the date represented by the serial date
        // indicated by adding the specified 'daysSinceEpoch' to
        // 'k_COMPACT_BINARY_DATE_EPOCH'.  Return 0 on success, and a non-zero
        // value otherwise.  This operation succeeds if the resulting value
        // represents a date in the range '[0001JAN01 .. 9999DEC31]'.  Note
        // that 'daysSinceEpoch' may be negative to indicate a serial date that
        // occurs before 'k_COMPACT_BINARY_DATE_EPOCH'.

    // 'bdlt::Date' Decoding

    static int getDateValue(bdlt::Date     *date,
                            bsl::streambuf *streamBuf,
                            int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented those bytes.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if 'length' bytes
        // are successfully read from the input sequence of the 'streamBuf'
        // without the read position becoming unavailable, and the bytes
        // contain a valid representation of a date value.  See the
        // package-level documentation of {'balber'} for a description of the
        // decision procedure used to detect the encoding format for a
        // 'bdlt::Date' value.

    // 'bdlt::Date' Encoding

    static int putDateValue(bsl::streambuf          *streamBuf,
                            const bdlt::Date&        value,
                            const BerEncoderOptions *options);
        // Write a representation of the specified 'value' date to the output
        // sequence of the specified 'streamBuf' according to the specified
        // 'options'.  If 'options' is 0, the default set of options is used.
        // Return 0 on success, and a non-zero value otherwise.  This operation
        // succeeds if all bytes in the representation of the 'value' are
        // written to the output sequence of the 'streamBuf' without the write
        // position becoming unavailable.  See the class documentation for a
        // description of the default options.  See the package-level
        // documentation of {'balber'} for a description of the decision
        // procedure used to select an encoding format for the 'value'.

    // 'bdlt::DateTz' Decoding

    static int getDateTzValue(bdlt::DateTz   *value,
                              bsl::streambuf *streamBuf,
                              int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time zone value represented by those bytes.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a date and time zone value.
        // See the package-level documentation of {'balber'} for a description
        // of the decision procedure used to detect the encoding format for a
        // 'bdlt::DateTz' value.

    // 'bdlt::DateTz' Encoding

    static int putDateTzValue(bsl::streambuf          *streamBuf,
                              const bdlt::DateTz&      date,
                              const BerEncoderOptions *options);
        // Write a representation of the specified 'value' date and time zone
        // to the output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  This
        // operation succeeds if all bytes in the representation of the 'value'
        // are written to the output sequence of the 'streamBuf' without the
        // write position becoming unavailable.  See the class documentation
        // for a description of the default options.  See the package-level
        // documentation of {'balber'} for a description of the decision
        // procedure used to select an encoding format for the 'value'.

    // Variant Decoding

    static int getDateOrDateTzValue(DateOrDateTz   *value,
                                    bsl::streambuf *streamBuf,
                                    int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and optional time zone value represented by those bytes.
        // Return 0 on success, and a non-zero value otherwise.  The operation
        // succeeds if 'length' bytes are successfully read from the input
        // sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes contain a valid representation of a date
        // and optional time zone value.  See the package-level documentation
        // of {'balber'} for a description of the decision procedure used to
        // detect the encoding format for a 'DateOrDateTz' value.
};

                        // ===========================
                        // struct BerUtil_TimeEncoding
                        // ===========================

struct BerUtil_TimeEncoding {
    // This component-private utility 'struct' provides a namespace for
    // enumerating the set of formats that may be used by 'BerUtil' to encode
    // and decode values of 'bdlt::Time' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_TIME         = Encoding::e_ISO8601_TIME,
        e_COMPACT_BINARY_TIME  = Encoding::e_COMPACT_BINARY_TIME,
        e_EXTENDED_BINARY_TIME = Encoding::e_EXTENDED_BINARY_TIME
    };

    enum {
        k_EXTENDED_BINARY_MIN_BDE_VERSION =
            Encoding::k_EXTENDED_BINARY_MIN_BDE_VERSION
    };
};

                       // =============================
                       // struct BerUtil_TimeTzEncoding
                       // =============================

struct BerUtil_TimeTzEncoding {
    // This component-private utility 'struct' provides a namespace for
    // enumerating the set of formats that may be used by 'BerUtil' to encode
    // and decode values of 'bdlt::TimeTz' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_TIMETZ         = Encoding::e_ISO8601_TIMETZ,
        e_COMPACT_BINARY_TIME    = Encoding::e_COMPACT_BINARY_TIME,
        e_COMPACT_BINARY_TIMETZ  = Encoding::e_COMPACT_BINARY_TIMETZ,
        e_EXTENDED_BINARY_TIMETZ = Encoding::e_EXTENDED_BINARY_TIMETZ
    };

    enum {
        k_EXTENDED_BINARY_MIN_BDE_VERSION =
            Encoding::k_EXTENDED_BINARY_MIN_BDE_VERSION
    };
};

                    // ===================================
                    // struct BerUtil_TimeOrTimeTzEncoding
                    // ===================================

struct BerUtil_TimeOrTimeTzEncoding {
    // This component-private utility 'struct' provides a namespace for
    // enumerating the set of formats that may be used by 'BerUtil' to decode
    // values of 'bdlb::Variant2<bdlt::Time, bdlt::TimeTz>' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_TIME           = Encoding::e_ISO8601_TIME,
        e_ISO8601_TIMETZ         = Encoding::e_ISO8601_TIMETZ,
        e_COMPACT_BINARY_TIME    = Encoding::e_COMPACT_BINARY_TIME,
        e_COMPACT_BINARY_TIMETZ  = Encoding::e_COMPACT_BINARY_TIMETZ,
        e_EXTENDED_BINARY_TIME   = Encoding::e_EXTENDED_BINARY_TIME,
        e_EXTENDED_BINARY_TIMETZ = Encoding::e_EXTENDED_BINARY_TIMETZ
    };
};

                         // ==========================
                         // struct BerUtil_TimeImpUtil
                         // ==========================

struct BerUtil_TimeImpUtil {
    // This component-private 'struct' provides a namespace for a suite of
    // functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for time values.  Within the definition of this 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690,
    //:   and
    //:
    //: *the* *default* *set* *of* *options*:
    //:   Refers to a 'balber::BerEncoderOptions' value having a
    //:   'datetimeFractionalSecondPrecision' attribute of 3 and a
    //:   'encodeDateAndTimeTypesAsBinary' attribute of 'false'.
    //
    // See the package level documentation of {'balber'} for a definition of
    // the compact and extended binary formats for date and time values.

    // TYPES
    typedef BerUtil_ExtendedBinaryEncodingUtil ExtendedBinaryEncodingUtil;
        // 'DateAndTimeHeaderUtil' is an alias to a namespace for a suite of
        // functions used to implement encoding and decoding operations for the
        // 2-byte header of an extended-binary-encoding formatted date-and-time
        // value.

    typedef BerUtil_DateAndTimeHeader DateAndTimeHeader;
        // 'Header' is an alias to an in-core, value-semantic attribute class
        // that represents the range of valid values of the 2-byte header of
        // extended-binary-encoding formatted date-and-time values.

    typedef BerUtil_DateAndTimeHeaderType DateAndTimeHeaderType;
        // 'Type' is an alias to a namespace for enumerating the set of "header
        // type" values that may be encoded in the 2-byte header of an
        // extended-binary-encoding formatted date-and-time value.

    typedef BerUtil_DateAndTimeHeaderImpUtil DateAndTimeHeaderUtil;
        // 'DateAndTimeHeaderUtil' is an alias to a namespace for a suite of
        // functions used to implement encoding and decoding operations for the
        // 2-byte header of an extended-binary-encoding formatted date-and-time
        // value.

    typedef BerUtil_IntegerImpUtil IntegerUtil;
        // 'IntegerUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for integer
        // values.

    typedef BerUtil_Iso8601ImpUtil Iso8601Util;
        // 'Iso8601Util' is an alias to a namespace for a suite of functions
        // used to implementing the encoding and decoding of date and time
        // values using the ISO 8601 format.

    typedef BerUtil_LengthImpUtil LengthUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

    typedef BerUtil_StringImpUtil StringUtil;
        // 'StringUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for string values.

    typedef BerUtil_StreambufUtil StreambufUtil;
        // 'StreambufUtil' is an alias to a namespace for a suite of functions
        // used to implement input and output operations on 'bsl::streambuf'
        // objects.

    typedef BerUtil_TimeEncoding TimeEncoding;
        // 'TimeEncoding' is an alias to a namespace for enumerating the set of
        // formats that may be used by 'BerUtil' to encode and decode values of
        // 'bdlt::Time' type.

    typedef BerUtil_TimeTzEncoding TimeTzEncoding;
        // 'TimeTzEncoding' is an alias to a namespace for enumerating the set
        // of formats that may be used by 'BerUtil' to encode and decode values
        // of 'bdlt::TimeTz' type.

    typedef BerUtil_TimeOrTimeTzEncoding TimeOrTimeTzEncoding;
        // 'TimeOrTimeTzEncoding' is an alias to a namespace for enumerating
        // the set of formats that may be used by 'BerUtil' to decode to values
        // of 'bdlb::Variant2<bdlt::Time, bdlt::TimeTz>' type.

    typedef BerUtil_TimezoneOffsetImpUtil TimezoneUtil;
        // 'TimezoneUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for time-zone
        // offset values.

    typedef bdlb::Variant2<bdlt::Time, bdlt::TimeTz> TimeOrTimeTz;
        // 'TimeOrTimeTz' is a convenient alias for
        // 'bdlb::Variant2<bdlt::Time, bdlt::TimeTz>'.

  private:
    // PRIVATE TYPES
    enum {
        k_EXTENDED_BINARY_TIME_LENGTH =
            +DateAndTimeHeaderUtil::k_HEADER_LENGTH +
            +IntegerUtil::k_40_BIT_INTEGER_LENGTH,  // = 7
        // the number of content octets used by 'BerUtil' to encode
        // a time value using the extended-binary time and time zone format

        k_EXTENDED_BINARY_TIMETZ_LENGTH =
            +DateAndTimeHeaderUtil::k_HEADER_LENGTH +
            +IntegerUtil::k_40_BIT_INTEGER_LENGTH,  // = 7
        // the number of contents octets used by 'BerUtil' to encode
        // a time and time zone value using the extended-binary time and
        // time zone format

        k_MAX_ISO8601_TIME_LENGTH = bdlt::Iso8601Util::k_TIME_STRLEN,
        // the maximum number of content octets used by 'BerUtil' to encode
        // a time value using the ISO 8601 format

        k_MAX_ISO8601_TIMETZ_LENGTH = bdlt::Iso8601Util::k_TIMETZ_STRLEN,
        // the maximum number of content octets used by 'BerUtil to encode
        // a time and time zone value using the ISO 8601 format

        k_MIN_COMPACT_BINARY_TIME_LENGTH = 1,
        // the minimum number of content octets used by 'BerUtil' to encode
        // a time value using the compact-binary time format

        k_MAX_COMPACT_BINARY_TIME_LENGTH = 4,
        // the maximum number of content octets used by 'BerUtil' to encode
        // a time value using the compact-binary time format

        k_MIN_COMPACT_BINARY_TIMETZ_LENGTH =
            k_MAX_COMPACT_BINARY_TIME_LENGTH + 1,
        // the minimum number of content octets used by 'BerUtil' to encode
        // a time and time zone value using the compact-binary time and
        // time zone format

        k_MAX_COMPACT_BINARY_TIMETZ_LENGTH = 6
        // the maximum number of content octets used by 'BerUtil' to encode
        // a time and time zone value using the compact-binary time and
        // time zone format
    };

    // PRIVATE CLASS METHODS

    // 'bdlt::Time' Decoding

    static int detectTimeEncoding(TimeEncoding::Value *encoding,
                                  int                  length,
                                  unsigned char        firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::Time' value given the specified
        // 'length' and 'firstByte' of the encoded representation.  Return 0 on
        // success, -1 if the format is reserved for future use, and some other
        // non-zero value otherwise.

    static int getIso8601TimeValue(bdlt::Time     *value,
                                   bsl::streambuf *streamBuf,
                                   int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as an
        // ISO 8601 time.  Return 0 on success, and a non-zero value otherwise.
        // The operation succeeds if 'length' bytes are successfully read from
        // the input sequence of the 'streamBuf' without the read position
        // becoming unavailable, and the bytes contain a valid representation
        // of an ISO 8601 time.

    static int getCompactBinaryTimeValue(bdlt::Time     *value,
                                         bsl::streambuf *streamBuf,
                                         int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as a
        // compact-binary time.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of a compact-binary time.

    static int getExtendedBinaryTimeValue(bdlt::Time     *value,
                                          bsl::streambuf *streamBuf,
                                          int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as an
        // extended-binary time.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of a extended-binary time.

    // 'bdlt::Time' Encoding

    static TimeEncoding::Value selectTimeEncoding(
                                             const bdlt::Time&        value,
                                             const BerEncoderOptions *options);
        // Determine the format that should be used to encode the specified
        // 'value' given the 'value' and the specified 'options'.  If 'options'
        // is 0, the default set of options is used.  Return an enumerator
        // identifying the selected format.

    static int putIso8601TimeValue(bsl::streambuf          *streamBuf,
                                   const bdlt::Time&        value,
                                   const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if all bytes of the ISO 8601 representation of
        // the 'value' are written to the 'streamBuf' without the write
        // position becoming unavailable.

    static int putCompactBinaryTimeValue(bsl::streambuf          *streamBuf,
                                         const bdlt::Time&        value,
                                         const BerEncoderOptions *options);
        // Write the compact-binary time representation of the specified
        // 'value' to the output sequence of the specified 'streamBuf'
        // according to the specified 'options'.  If 'options' is 0, the
        // default set of options is used.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if all bytes of the
        // compact-binary time representation of the 'value' are written to the
        // 'streamBuf' without the write position becoming unavailable.

    static int putExtendedBinaryTimeValue(bsl::streambuf          *streamBuf,
                                          const bdlt::Time&        value,
                                          const BerEncoderOptions *options);
        // Write the extended-binary time representation of the specified
        // 'value' to the output sequence of the specified 'streamBuf'
        // according to the specified 'options'.  If 'options' is 0, the
        // default set of options is used.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if all bytes of the
        // extended-binary time representation of the 'value' are written to
        // the 'streamBuf' without the write position becoming unavailable.

    // 'bdlt::TimeTz' Decoding

    static int detectTimeTzEncoding(TimeTzEncoding::Value *encoding,
                                    int                    length,
                                    unsigned char          firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::Time' value given the specified
        // 'length' and 'firstByte' of the encoded representation.  Return 0 on
        // success, -1 if the format is reserved for future use, and some other
        // non-zero value otherwise.

    static int getIso8601TimeTzValue(bdlt::TimeTz   *value,
                                     bsl::streambuf *streamBuf,
                                     int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and time zone value represented by the interpretation of the
        // read bytes as an ISO 8601 time and time zone.  Return 0 on success,
        // and a non-zero value otherwise.  The operation succeeds if 'length'
        // bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of an ISO 8601 time and time
        // zone.

    static int getCompactBinaryTimeValue(bdlt::TimeTz   *value,
                                         bsl::streambuf *streamBuf,
                                         int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as a
        // compact-binary time.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of a compact-binary time.

    static int getCompactBinaryTimeTzValue(bdlt::TimeTz   *value,
                                           bsl::streambuf *streamBuf,
                                           int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and time zone value represented by the interpretation of the
        // read bytes as a compact-binary time and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a compact-binary time and
        // time zone.

    static int getExtendedBinaryTimeTzValue(bdlt::TimeTz   *value,
                                            bsl::streambuf *streamBuf,
                                            int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and time zone value represented by the interpretation of the
        // read bytes as an extended-binary time and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of an extended-binary time and
        // time zone.

    // 'bdlt::TimeTz' Encoding

    static TimeTzEncoding::Value selectTimeTzEncoding(
                                             const bdlt::TimeTz&      value,
                                             const BerEncoderOptions *options);
        // Determine the format that should be used to encode the specified
        // 'value' given the 'value' and the specified 'options'.  If 'options'
        // is 0, the default set of options is used.  Return an enumerator
        // identifying the selected format.

    static int putIso8601TimeTzValue(bsl::streambuf          *streamBuf,
                                     const bdlt::TimeTz&      value,
                                     const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if all bytes of the ISO 8601 representation of
        // the 'value' are written to the 'streamBuf' without the write
        // position becoming unavailable.

    static int putCompactBinaryTimeValue(bsl::streambuf          *streamBuf,
                                         const bdlt::TimeTz&      value,
                                         const BerEncoderOptions *options);
        // Write the compact-binary time representation of the specified
        // 'value' to the output sequence of the specified 'streamBuf'
        // according to the specified 'options'.  If 'options' is 0, the
        // default set of options is used.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if all bytes of the
        // compact-binary time representation of the 'value' are written to the
        // 'streamBuf' without the write position becoming unavailable.  The
        // behavior is undefined unless the 'offset' of the 'value' is 0.

    static int putCompactBinaryTimeTzValue(bsl::streambuf          *streamBuf,
                                           const bdlt::TimeTz&      value,
                                           const BerEncoderOptions *options);
        // Write the compact-binary date and time zone representation of the
        // specified 'value' to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if all bytes of
        // the compact-binary date representation of the 'value' are written to
        // the 'streamBuf' without the write position becoming unavailable.

    static int putExtendedBinaryTimeTzValue(bsl::streambuf          *streamBuf,
                                            const bdlt::TimeTz&      value,
                                            const BerEncoderOptions *options);
        // Write the extended-binary date and time zone representation of the
        // specified 'value' to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if all bytes of
        // the extended-binary date representation of the 'value' are written
        // to the 'streamBuf' without the write position becoming unavailable.

    // Variant Decoding

    static int detectTimeOrTimeTzEncoding(
                                       TimeOrTimeTzEncoding::Value *encoding,
                                       int                          length,
                                       unsigned char                firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::Time' or 'bdlt::TimeTz' value given
        // the specified 'length' and 'firstByte' of the encoded
        // representation.  Return 0 on success, -1 if the format is reserved
        // for future use, and some other non-zero value otherwise.

    static int getIso8601TimeValue(TimeOrTimeTz   *value,
                                   bsl::streambuf *streamBuf,
                                   int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as an
        // ISO 8601 time.  Return 0 on success, and a non-zero value otherwise.
        // The operation succeeds if 'length' bytes are successfully read from
        // the input sequence of the 'streamBuf' without the read position
        // becoming unavailable, and the bytes contain a valid representation
        // of an ISO 8601 time.

    static int getIso8601TimeTzValue(TimeOrTimeTz   *value,
                                     bsl::streambuf *streamBuf,
                                     int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and time zone value represented by the interpretation of the
        // read bytes as an ISO 8601 time and time zone.  Return 0 on success,
        // and a non-zero value otherwise.  The operation succeeds if 'length'
        // bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of an ISO 8601 time and time
        // zone.

    static int getCompactBinaryTimeValue(TimeOrTimeTz   *value,
                                         bsl::streambuf *streamBuf,
                                         int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as a
        // compact-binary time.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of a compact-binary time.

    static int getCompactBinaryTimeTzValue(TimeOrTimeTz   *value,
                                           bsl::streambuf *streamBuf,
                                           int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and time zone value represented by the interpretation of the
        // read bytes as a compact-binary time and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a compact-binary time and
        // time zone.

    static int getExtendedBinaryTimeValue(TimeOrTimeTz   *value,
                                          bsl::streambuf *streamBuf,
                                          int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as a
        // extended-binary time.  Return 0 on success, and a non-zero value
        // otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of an extended-binary time.

    static int getExtendedBinaryTimeTzValue(TimeOrTimeTz   *value,
                                            bsl::streambuf *streamBuf,
                                            int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and time zone value represented by the interpretation of the
        // read bytes as an extended-binary time and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of an extended-binary time and
        // time zone.

  public:
    // CLASS METHODS

    // Utilities

    static void timeToMillisecondsSinceMidnight(
                                  int               *millisecondsSinceMidnight,
                                  const bdlt::Time&  time);
        // Load to the specified 'millisecondsSinceMidnight' the number of
        // milliseconds in the specified 'time' value.

    static void timeToMicrosecondsSinceMidnight(
                                 bsls::Types::Int64 *microsecondsSinceMidnight,
                                 const bdlt::Time&   time);
        // Load to the specified 'microsecondsSinceMidnight' the number of
        // microseconds in the specified 'time' value.

    static int millisecondsSinceMidnightToTime(
                                        bdlt::Time *time,
                                        int         millisecondsSinceMidnight);
        // Load to the specified 'time' the time value represented by the
        // specified 'millisecondsSinceMidnight'.

    static int microsecondsSinceMidnightToTime(
                                bdlt::Time         *time,
                                bsls::Types::Int64  microsecondsSinceMidnight);
        // Load to the specified 'time' the time value represented by the
        // specified 'microsecondsSinceMidnight'.

    // 'bdlt::Time' Decoding

    static int getTimeValue(bdlt::Time     *value,
                            bsl::streambuf *streamBuf,
                            int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented those bytes.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if 'length' bytes
        // are successfully read from the input sequence of the 'streamBuf'
        // without the read position becoming unavailable, and the bytes
        // contain a valid representation of a time value.  See the
        // package-level documentation of {'balber'} for a description of the
        // decision procedure used to detect the encoding format for a
        // 'bdlt::Time' value.

    // 'bdlt::Time' Encoding

    static int putTimeValue(bsl::streambuf          *streamBuf,
                            const bdlt::Time&        value,
                            const BerEncoderOptions *options);
        // Write a representation of the specified time 'value' to the output
        // sequence of the specified 'streamBuf' according to the specified
        // 'options'.  If 'options' is 0, the default set of options is used.
        // Return 0 on success, and a non-zero value otherwise.  This operation
        // succeeds if all bytes in the representation of the 'value' are
        // written to the output sequence of the 'streamBuf' without the write
        // position becoming unavailable.  See the class documentation for a
        // description of the default options.  See the package-level
        // documentation of {'balber'} for a description of the decision
        // procedure used to select an encoding format for the 'value'.

    // 'bdlt::TimeTz' Decoding

    static int getTimeTzValue(bdlt::TimeTz   *value,
                              bsl::streambuf *streamBuf,
                              int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and time zone value represented by those bytes.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a time and time zone value.
        // See the package-level documentation of {'balber'} for a description
        // of the decision procedure used to detect the encoding format for a
        // 'bdlt::TimeTz' value.

    // 'bdlt::TimeTz' Encoding

    static int putTimeTzValue(bsl::streambuf          *streamBuf,
                              const bdlt::TimeTz&      value,
                              const BerEncoderOptions *options);
        // Write a representation of the specified time and time-zone 'value'
        // to the output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  This
        // operation succeeds if all bytes in the representation of the 'value'
        // are written to the output sequence of the 'streamBuf' without the
        // write position becoming unavailable.  See the class documentation
        // for a description of the default options.  See the package-level
        // documentation of {'balber'} for a description of the decision
        // procedure used to select an encoding format for the 'value'.

    // Variant Decoding

    static int getTimeOrTimeTzValue(TimeOrTimeTz   *value,
                                    bsl::streambuf *streamBuf,
                                    int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time and optional time zone value represented by those bytes.
        // Return 0 on success, and a non-zero value otherwise.  The operation
        // succeeds if 'length' bytes are successfully read from the input
        // sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes contain a valid representation of a time
        // and optional time zone value.  See the package-level documentation
        // of {'balber'} for a description of the decision procedure used to
        // detect the encoding format for a 'TimeOrTimeTz' value.
};

                      // ===============================
                      // struct BerUtil_DatetimeEncoding
                      // ===============================

struct BerUtil_DatetimeEncoding {
    // This component-private utility 'struct' provides a namespace for
    // enumerating the set of formats that may be used by 'BerUtil' to encode
    // and decode values of 'bdlt::Datetime' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_DATETIME          = Encoding::e_ISO8601_DATETIME,
        e_COMPACT_BINARY_DATETIME   = Encoding::e_COMPACT_BINARY_DATETIME,
        e_COMPACT_BINARY_DATETIMETZ = Encoding::e_COMPACT_BINARY_DATETIMETZ,
        e_EXTENDED_BINARY_DATETIME  = Encoding::e_EXTENDED_BINARY_DATETIMETZ
    };

    enum {
        k_EXTENDED_BINARY_MIN_BDE_VERSION =
            Encoding::k_EXTENDED_BINARY_MIN_BDE_VERSION
    };
};

                     // =================================
                     // struct BerUtil_DatetimeTzEncoding
                     // =================================

struct BerUtil_DatetimeTzEncoding {
    // This component-private utility 'struct' provides a namespace for
    // enumerating the set of formats that may be used by 'BerUtil' to encode
    // and decode values of 'bdlt::DatetimeTz' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_DATETIMETZ         = Encoding::e_ISO8601_DATETIMETZ,
        e_COMPACT_BINARY_DATETIME    = Encoding::e_COMPACT_BINARY_DATETIME,
        e_COMPACT_BINARY_DATETIMETZ  = Encoding::e_COMPACT_BINARY_DATETIMETZ,
        e_EXTENDED_BINARY_DATETIMETZ = Encoding::e_EXTENDED_BINARY_DATETIMETZ
    };

    enum {
        k_EXTENDED_BINARY_MIN_BDE_VERSION =
            Encoding::k_EXTENDED_BINARY_MIN_BDE_VERSION
    };
};

                // ===========================================
                // struct BerUtil_DatetimeOrDatetimeTzEncoding
                // ===========================================

struct BerUtil_DatetimeOrDatetimeTzEncoding {
    // This component-private utility 'struct' provides a namespace for
    // enumerating the set of formats that may be used by 'BerUtil' to decode
    // to values of 'bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz>' type.

    // TYPES
    typedef BerUtil_DateAndTimeEncoding Encoding;
        // 'Encoding' is an alias to a namespace for enumerating the union of
        // the sets of date and time formats used to encode and decode all date
        // and time types supported by 'BerUtil'.

    enum Value {
        e_ISO8601_DATETIME           = Encoding::e_ISO8601_DATETIME,
        e_ISO8601_DATETIMETZ         = Encoding::e_ISO8601_DATETIMETZ,
        e_COMPACT_BINARY_DATETIME    = Encoding::e_COMPACT_BINARY_DATETIME,
        e_COMPACT_BINARY_DATETIMETZ  = Encoding::e_COMPACT_BINARY_DATETIMETZ,
        e_EXTENDED_BINARY_DATETIME   = Encoding::e_EXTENDED_BINARY_DATETIME,
        e_EXTENDED_BINARY_DATETIMETZ = Encoding::e_EXTENDED_BINARY_DATETIMETZ
    };
};

                       // ==============================
                       // struct BerUtil_DatetimeImpUtil
                       // ==============================

struct BerUtil_DatetimeImpUtil {
    // This component-private 'struct' provides a namespace for a suite of
    // functions used by 'BerUtil' to implement BER encoding and decoding
    // operations for date and time values.  Within the definition of this
    // 'struct':
    //
    //: *the* *specification*:
    //:   Refers to the August 2015 revision of the ITU-T Recommendation X.690,
    //:   and
    //:
    //: *the* *default* *set* *of* *options*:
    //:   Refers to a 'balber::BerEncoderOptions' value having a
    //:   'datetimeFractionalSecondPrecision' attribute of 3 and a
    //:   'encodeDateAndTimeTypesAsBinary' attribute of 'false'.
    //
    // See the package level documentation of {'balber'} for a definition of
    // the compact and extended binary formats for date and time values.

    // TYPES
    typedef BerUtil_Constants Constants;
        // 'Constants' is an alias to a namespace for a suite of
        // general-purpose constants that occur when encoding or decoding BER
        // data.

    typedef BerUtil_ExtendedBinaryEncodingUtil ExtendedBinaryEncodingUtil;
        // 'DateAndTimeHeaderUtil' is an alias to a namespace for a suite of
        // functions used to implement encoding and decoding operations for the
        // 2-byte header of an extended-binary-encoding formatted date-and-time
        // value.

    typedef BerUtil_DateAndTimeHeader DateAndTimeHeader;
        // 'Header' is an alias to an in-core, value-semantic attribute class
        // that represents the range of valid values of the 2-byte header of
        // extended-binary-encoding formatted date-and-time values.

    typedef BerUtil_DateAndTimeHeaderImpUtil DateAndTimeHeaderUtil;
        // 'DateAndTimeHeaderUtil' is an alias to a namespace for a suite of
        // functions used to implement encoding and decoding operations for the
        // 2-byte header of an extended-binary-encoding formatted date-and-time
        // value.

    typedef BerUtil_DateImpUtil DateUtil;
        // 'DateUtil' is an alias to a namespace for a suite of functions used
        // to implement BER encoding and decoding operations for date values.

    typedef BerUtil_DatetimeEncoding DatetimeEncoding;
        // 'DatetimeEncoding' is an alias to a namespace for enumerating the
        // set of formats that may be used by 'BerUtil' to encode and decode
        // values of 'bdlt::Datetime' type.

    typedef BerUtil_DatetimeTzEncoding DatetimeTzEncoding;
        // 'DatetimeTzEncoding' is an alias to a namespace for enumerating the
        // set of formats that may be used by 'BerUtil' to encode and decode
        // values of 'bdlt::DatetimeTz' type.

    typedef BerUtil_DatetimeOrDatetimeTzEncoding DatetimeOrDatetimeTzEncoding;
        // 'DatetimeOrDatetimeTzEncoding' is an alias to a namespace for
        // enumerating the set of formats that may be used by 'BerUtil' to
        // decode to values of
        // 'bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz>' type.

    typedef BerUtil_IntegerImpUtil IntegerUtil;
        // 'IntegerUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for integer
        // values.

    typedef BerUtil_Iso8601ImpUtil Iso8601Util;
        // 'Iso8601Util' is an alias to a namespace for a suite of functions
        // used to implementing the encoding and decoding of date and time
        // values using the ISO 8601 format.

    typedef BerUtil_LengthImpUtil LengthUtil;
        // 'LengthUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for length
        // quantities.

    typedef BerUtil_StreambufUtil StreambufUtil;
        // 'StreambufUtil' is an alias to a namespace for a suite of functions
        // used to implement input and output operations on 'bsl::streambuf'
        // objects.

    typedef BerUtil_StringImpUtil StringUtil;
        // 'StringUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for string values.

    typedef BerUtil_TimeImpUtil TimeUtil;
        // 'DateUtil' is an alias to a namespace for a suite of functions used
        // to implement BER encoding and decoding operations for time values.

    typedef BerUtil_TimezoneOffsetImpUtil TimezoneUtil;
        // 'TimezoneUtil' is an alias to a namespace for a suite of functions
        // used to implement BER encoding and decoding operations for time-zone
        // offset values.

    typedef bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz>
        DatetimeOrDatetimeTz;
        // 'DatetimeOrDatetimeTz' is a convenient alias for
        // 'bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz>'.

  private:
    // PRIVATE TYPES
    enum {
        k_EXTENDED_BINARY_SERIAL_DATE_LENGTH = 3,

        k_EXTENDED_BINARY_DATETIME_LENGTH =
            DateAndTimeHeaderUtil::k_HEADER_LENGTH +
            k_EXTENDED_BINARY_SERIAL_DATE_LENGTH +
            IntegerUtil::k_40_BIT_INTEGER_LENGTH,  // = 10
        // the number of content octets used by 'BerUtil' to encode a date
        // and time value using the extended-binary date and time format

        k_EXTENDED_BINARY_DATETIMETZ_LENGTH =
            DateAndTimeHeaderUtil::k_HEADER_LENGTH +
            k_EXTENDED_BINARY_SERIAL_DATE_LENGTH +
            IntegerUtil::k_40_BIT_INTEGER_LENGTH,  // = 10
        // the number of contents octets used by 'BerUtil' to encode a
        // date, time, and time zone value using the extended-binary date,
        // time, and time zone format

        k_MAX_ISO8601_DATETIME_LENGTH = bdlt::Iso8601Util::k_DATETIME_STRLEN,
        // the maximum number of content octets used by 'BerUtil' to
        // encode a date and time value using the ISO 8601 format

        k_MAX_ISO8601_DATETIMETZ_LENGTH =
            bdlt::Iso8601Util::k_DATETIMETZ_STRLEN,
        // the maximum number of content octets used by 'BerUtil' to
        // encode a date, time, and time zone value using the ISO 8601
        // format

        k_MAX_COMPACT_BINARY_DATETIME_LENGTH = 6,
        // the maximum number of content octets used by 'BerUtil' to encode
        // a date and time value using the compact-binary date and time
        // format

        k_MIN_COMPACT_BINARY_DATETIMETZ_LENGTH =
            k_MAX_COMPACT_BINARY_DATETIME_LENGTH + 1,
        // the minimum number of content octets used by 'BerUtil' to
        // encode a date, time, and time zone value using the
        // compact-binary date, time, and time zone format

        k_MAX_COMPACT_BINARY_DATETIMETZ_LENGTH = 9
        // the maximum number of content octets used by 'BerUtil' to
        // encode a date, time, and time zone value using the
        // compact-binary date, time, and time zone format
    };

    // PRIVATE CLASS METHODS

    // Utilities

    static void datetimeToMillisecondsSinceEpoch(
                                 bsls::Types::Int64    *millisecondsSinceEpoch,
                                 const bdlt::Datetime&  value);
        // Load to the specified 'millisecondsFromEpoch' the number of
        // milliseconds between the start of the day on the compact-binary date
        // epoch and the specified 'value'.  The compact-binary date epoch is
        // the date defined by the 'DateUtil::k_COMPACT_BINARY_DATE_EPOCH'
        // serial date.  Note that this quantity may be negative if the
        // specified 'value' occurs before the compact-binary date epoch.

    static int millisecondsSinceEpochToDatetime(
                                   bdlt::Datetime     *value,
                                   bsls::Types::Int64  millisecondsSinceEpoch);
        // Load to the specified 'value' the date and time represented by the
        // specified 'millisecondsSinceEpoch' number of milliseconds from the
        // compact-binary date epoch.  The compact-binary date epoch is the
        // date defined by the 'DateUtil::k_COMPACT_BINARY_DATE_EPOCH' serial
        // date.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if the resulting date and time is a valid
        // 'bdlt::Datetime' value.  Note that 'millisecondsSinceEpoch' may be
        // negative to indicate a date and time that occurs before the
        // compact-binary date epoch.

    // 'bdlt::Datetime' Decoding

    static int detectDatetimeEncoding(DatetimeEncoding::Value *encoding,
                                      int                      length,
                                      unsigned char            firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::Datetime' value given the specified
        // 'length' and 'firstByte' of the encoded representation.  Return 0 on
        // success, -1 if the format is reserved for future use, and some other
        // non-zero value otherwise.

    static int getIso8601DatetimeValue(bdlt::Datetime *value,
                                       bsl::streambuf *streamBuf,
                                       int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time value represented by the interpretation of the read
        // bytes as an ISO 8601 date and time.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if 'length' bytes
        // are successfully read from the input sequence of the 'streamBuf'
        // without the read position becoming unavailable, and the bytes
        // contain a valid representation of an ISO 8601 date and time.

    static int getCompactBinaryDatetimeValue(bdlt::Datetime *value,
                                             bsl::streambuf *streamBuf,
                                             int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time value represented by the interpretation of the read
        // bytes as a compact-binary date and time.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if 'length' bytes
        // are successfully read from the input sequence of the 'streamBuf'
        // without the read position becoming unavailable, and the bytes
        // contain a valid representation of a compact-binary date and time.

    static int getCompactBinaryDatetimeTzValue(bdlt::Datetime *value,
                                               bsl::streambuf *streamBuf,
                                               int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time value represented by the interpretation of the read
        // bytes as a compact-binary date, time, and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a compact-binary date, time,
        // and time zone.

    static int getExtendedBinaryDatetimeValue(bdlt::Datetime *value,
                                              bsl::streambuf *streamBuf,
                                              int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time value represented by the interpretation of the read
        // bytes as an extended-binary date, time, and time zone.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // 'length' bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of an extended-binary date,
        // time, and time zone.

    // 'bdlt::Datetime' Encoding

    static DatetimeEncoding::Value selectDatetimeEncoding(
                                       bsls::Types::Int64      *serialDatetime,
                                       int                     *length,
                                       const bdlt::Datetime&    value,
                                       const BerEncoderOptions *options);
        // Determine the format that should be used to encode the specified
        // 'value' given the 'value' and the specified 'options'.  Load to the
        // specified 'serialDatetime' the number of milliseconds since the
        // start of the day on the compact-binary date epoch to the 'value',
        // and load to the specified 'length' the number of contents octets
        // that would be used by the BER encoding of 'serialDatetime' according
        // to the specification.  If 'options' is 0, the default set of options
        // is used.  Return an enumerator identifying the selected format.
        // Note that the 'serialDatetime' and 'length' of a date and time value
        // are frequently used as arguments to date and time encoding
        // operations defined in this 'struct'.

    static int putIso8601DatetimeValue(bsl::streambuf          *streamBuf,
                                       const bdlt::Datetime&    value,
                                       const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if all bytes of the ISO 8601 representation of
        // the 'value' are written to the 'streamBuf' without the write
        // position becoming unavailable.

    static int putCompactBinaryDatetimeValue(
                                       bsl::streambuf          *streamBuf,
                                       bsls::Types::Int64       serialDatetime,
                                       int                      length,
                                       const BerEncoderOptions *options);
        // Write the specified 'length' number of octets of the compact-binary
        // date and time representation of the specified 'serialDatetime'
        // number of milliseconds from the start of the day on the
        // compact-binary serial epoch to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if all 'length'
        // bytes of the representation of the 'serialDatetime' are written to
        // the 'streamBuf' without the write position becoming unavailable.

    static int putCompactBinaryDatetimeValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::Datetime&    value,
                                            const BerEncoderOptions *options);
        // Write the compact-binary date and time representation of the
        // specified 'value' to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if all bytes of
        // the compact-binary date and time representation of the 'value' are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putCompactBinaryDatetimeTzValue(
                                       bsl::streambuf          *streamBuf,
                                       bsls::Types::Int64       serialDatetime,
                                       int                      length,
                                       const BerEncoderOptions *options);
        // Write the specified 'length' number of octets of the compact-binary
        // date, time, and time zone representation of the specified
        // 'serialDatetime' number of milliseconds from the start of the day on
        // the compact-binary serial epoch to the output sequence of the
        // specified 'streamBuf' according to the specified 'options'.  If
        // 'options' is 0, the default set of options is used.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // all 'length' bytes of the representation of the 'serialDatetime' are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putExtendedBinaryDatetimeValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::Datetime&    value,
                                            const BerEncoderOptions *options);
        // Write the extended-binary date and time representation of the
        // specified 'value' to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if all bytes of
        // the extended-binary date and time representation of the 'value' are
        // written to the 'streamBuf' without the write position becoming
        // unavailable.

    // 'bdlt::DatetimeTz' Decoding

    static int detectDatetimeTzEncoding(
                                   DatetimeTzEncoding::Value *encoding,
                                   int                        length,
                                   unsigned char              firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::DatetimeTz' value given the specified
        // 'length' and 'firstByte' of the encoded representation.  Return 0 on
        // success, -1 if the format is reserved for future use, and some other
        // non-zero value otherwise.

    static int getIso8601DatetimeTzValue(bdlt::DatetimeTz *value,
                                         bsl::streambuf   *streamBuf,
                                         int               length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date, time, and time zone value represented by the interpretation of
        // the read bytes as an ISO 8601 date, time, and time zone.  Return 0
        // on success, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes contain a valid representation of an ISO 8601 date, time,
        // and time zone .

    static int getCompactBinaryDatetimeValue(bdlt::DatetimeTz *value,
                                             bsl::streambuf   *streamBuf,
                                             int               length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date value represented by the interpretation of the read bytes as a
        // compact-binary date and time.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of a compact-binary date and time.

    static int getCompactBinaryDatetimeTzValue(bdlt::DatetimeTz *value,
                                               bsl::streambuf   *streamBuf,
                                               int               length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date, time, and time zone value represented by the interpretation of
        // the read bytes as a compact-binary date, time, and time zone.
        // Return 0 on success, and a non-zero value otherwise.  The operation
        // succeeds if 'length' bytes are successfully read from the input
        // sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes contain a valid representation of a
        // compact-binary date, time, and time zone.

    static int getExtendedBinaryDatetimeTzValue(bdlt::DatetimeTz *value,
                                                bsl::streambuf   *streamBuf,
                                                int               length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time zone value represented by the interpretation of the
        // read bytes as an extended-binary date, time, and time zone.  Return
        // 0 on success, and a non-zero value otherwise.  The operation
        // succeeds if 'length' bytes are successfully read from the input
        // sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes contain a valid representation of an
        // extended-binary date, time, and time zone.

    // 'bdlt::DatetimeTz' Encoding

    static DatetimeTzEncoding::Value selectDatetimeTzEncoding(
                                       bsls::Types::Int64      *serialDatetime,
                                       int                     *length,
                                       const bdlt::DatetimeTz&  value,
                                       const BerEncoderOptions *options);
        // Determine the format that should be used to encode the specified
        // 'value' given the 'value' and the specified 'options'.  If 'options'
        // is 0, the default set of options is used.  Return an enumerator
        // identifying the selected format.

    static int putIso8601DatetimeTzValue(bsl::streambuf          *streamBuf,
                                         const bdlt::DatetimeTz&  value,
                                         const BerEncoderOptions *options);
        // Write the ISO 8601 representation of the specified 'value' to the
        // output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if all bytes of the ISO 8601 representation of
        // the 'value' are written to the 'streamBuf' without the write
        // position becoming unavailable.

    static int putCompactBinaryDatetimeTzValue(
                              bsl::streambuf          *streamBuf,
                              int                      timezoneOffsetInMinutes,
                              bsls::Types::Int64       serialDatetime,
                              int                      serialDatetimeLength,
                              const BerEncoderOptions *options);
        // Write the specified 'serialDatetimeLength' number of octets of the
        // compact-binary date, time, and time zone representation using the
        // specified the date and time defined by the specified
        // 'serialDatetime' number of milliseconds from the start of the day on
        // the compact-binary serial epoch and the specified
        // 'timezoneOffsetInMinutes' time zone to the output sequence of the
        // specified 'streamBuf' according to the specified 'options'.  If
        // 'options' is 0, the default set of options is used.  Return 0 on
        // success, and a non-zero value otherwise.  The operation succeeds if
        // all 'length' bytes of the compact-binary date, time, and time zone
        // representation of the 'serialDatetime' and 'timezoneOffsetInMinutes'
        // are written to the 'streamBuf' without the write position becoming
        // unavailable.

    static int putExtendedBinaryDatetimeTzValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::DatetimeTz&  value,
                                            const BerEncoderOptions *options);
        // Write the extended-binary date, time, and time zone representation
        // of the specified 'value' to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  The operation succeeds if all bytes of
        // the extended-binary date, time, and time zone representation of the
        // 'value' are written to the 'streamBuf' without the write position
        // becoming unavailable.

    // Variant Decoding

    static int detectDatetimeOrDatetimeTzEncoding(
                               DatetimeOrDatetimeTzEncoding::Value *encoding,
                               int                                  length,
                               unsigned char                        firstByte);
        // Load to the specified 'encoding' the enumerator that describes the
        // format used to encode a 'bdlt::Datetime' or 'bdlt::DatetimeTz' value
        // given the specified 'length' and 'firstByte' of the encoded
        // representation.  Return 0 on success, -1 if the format is reserved
        // for future use, and some other non-zero value otherwise.

    static int getIso8601DatetimeValue(DatetimeOrDatetimeTz *value,
                                       bsl::streambuf       *streamBuf,
                                       int                   length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date, time, and optional time zone value represented by the
        // interpretation of the read bytes as an ISO 8601 date and time.
        // Return 0 on success, and a non-zero value otherwise.  The operation
        // succeeds if 'length' bytes are successfully read from the input
        // sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes contain a valid representation of an ISO
        // 8601 date and time.

    static int getIso8601DatetimeTzValue(DatetimeOrDatetimeTz *value,
                                         bsl::streambuf       *streamBuf,
                                         int                   length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date, time, and optional time zone value represented by the
        // interpretation of the read bytes as an ISO 8601 date, time, and time
        // zone.  Return 0 on success, and a non-zero value otherwise.  The
        // operation succeeds if 'length' bytes are successfully read from the
        // input sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes contain a valid representation of an ISO
        // 8601 date, time, and time zone.

    static int getCompactBinaryDatetimeValue(DatetimeOrDatetimeTz *value,
                                             bsl::streambuf       *streamBuf,
                                             int                   length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date, time, and optional time zone value represented by the
        // interpretation of the read bytes as a compact-binary date and time.
        // Return 0 on success, and a non-zero value otherwise.  The operation
        // succeeds if 'length' bytes are successfully read from the input
        // sequence of the 'streamBuf' without the read position becoming
        // unavailable, and the bytes contain a valid representation of a
        // compact-binary date and time.

    static int getCompactBinaryDatetimeTzValue(DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date, time, and optional time zone value represented by the
        // interpretation of the read bytes as a compact-binary date, time, and
        // time zone.  Return 0 on success, and a non-zero value otherwise.
        // The operation succeeds if 'length' bytes are successfully read from
        // the input sequence of the 'streamBuf' without the read position
        // becoming unavailable, and the bytes contain a valid representation
        // of a compact-binary date, time, and time zone.

    static int getExtendedBinaryDatetimeValue(DatetimeOrDatetimeTz *value,
                                              bsl::streambuf       *streamBuf,
                                              int                   length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as a
        // extended-binary date and time.  Return 0 on success, and a non-zero
        // value otherwise.  The operation succeeds if 'length' bytes are
        // successfully read from the input sequence of the 'streamBuf' without
        // the read position becoming unavailable, and the bytes contain a
        // valid representation of an extended-binary date and time.

    static int getExtendedBinaryDatetimeTzValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // time value represented by the interpretation of the read bytes as a
        // extended-binary date, time, and time zone.  Return 0 on success, and
        // a non-zero value otherwise.  The operation succeeds if 'length'
        // bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of an extended-binary date,
        // time, and time zone.

  public:
    // CLASS METHODS

    // 'bdlt::Datetime' Decoding

    static int getDatetimeValue(bdlt::Datetime *value,
                                bsl::streambuf *streamBuf,
                                int             length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date and time value represented those bytes.  Return 0 on success,
        // and a non-zero value otherwise.  The operation succeeds if 'length'
        // bytes are successfully read from the input sequence of the
        // 'streamBuf' without the read position becoming unavailable, and the
        // bytes contain a valid representation of a date and time value.  See
        // the package-level documentation of
        // {'balber'} for a description of the decision procedure used to
        // detect the encoding format for a 'bdlt::Datetime' value.

    // 'bdlt::Datetime' Encoding

    static int putDatetimeValue(bsl::streambuf          *streamBuf,
                                const bdlt::Datetime&    value,
                                const BerEncoderOptions *options);
        // Write a representation of the specified 'value' date and time to the
        // output sequence of the specified 'streamBuf' according to the
        // specified 'options'.  If 'options' is 0, the default set of options
        // is used.  Return 0 on success, and a non-zero value otherwise.  This
        // operation succeeds if all bytes in the representation of the 'value'
        // are written to the output sequence of the 'streamBuf' without the
        // write position becoming unavailable.  See the class documentation
        // for a description of the default options.  See the package-level
        // documentation of {'balber'} for a description of the decision
        // procedure used to select an encoding format for the 'value'.

    // 'bdlt::DatetimeTz' Decoding

    static int getDatetimeTzValue(bdlt::DatetimeTz *value,
                                  bsl::streambuf   *streamBuf,
                                  int               length);
        // Read the specified 'length' number of bytes from the input sequence
        // of the specified 'streamBuf' and load to the specified 'value' the
        // date, time, and time zone value represented those bytes.  Return 0
        // on success, and a non-zero value otherwise.  The operation succeeds
        // if 'length' bytes are successfully read from the input sequence of
        // the 'streamBuf' without the read position becoming unavailable, and
        // the bytes contain a valid representation of a date, time, and time
        // zone value.  See the package-level documentation of {'balber'} for a
        // description of the decision procedure used to detect the encoding
        // format for a 'bdlt::Datetime' value.

    // 'bdlt::DatetimeTz' Encoding

    static int putDatetimeTzValue(bsl::streambuf          *streamBuf,
                                  const bdlt::DatetimeTz&  value,
                                  const BerEncoderOptions *options);
        // Write a representation of the specified 'value' date, time, and time
        // zone to the output sequence of the specified 'streamBuf' according
        // to the specified 'options'.  If 'options' is 0, the default set of
        // options is used.  Return 0 on success, and a non-zero value
        // otherwise.  This operation succeeds if all bytes in the
        // representation of the 'value' are written to the output sequence of
        // the 'streamBuf' without the write position becoming unavailable.
        // See the class documentation for a description of the default
        // options.  See the package-level documentation of {'balber'} for a
        // description of the decision procedure used to select an encoding
        // format for the 'value'.

    // Variant Decoding

    static int getDatetimeOrDatetimeTzValue(DatetimeOrDatetimeTz *value,
                                            bsl::streambuf       *streamBuf,
                                            int                   length);
        // Write a representation of the specified 'value' date, time, and
        // optional time zone to the output sequence of the specified
        // 'streamBuf' according to the specified 'options'.  If 'options' is
        // 0, the default set of options is used.  Return 0 on success, and a
        // non-zero value otherwise.  This operation succeeds if all bytes in
        // the representation of the 'value' are written to the output sequence
        // of the 'streamBuf' without the write position becoming unavailable.
        // See the class documentation for a description of the default
        // options.  See the package-level documentation of {'balber'} for a
        // description of the decision procedure used to select an encoding
        // format for the 'value'.
};

                       // ==============================
                       // struct BerUtil_GetValueImpUtil
                       // ==============================

struct BerUtil_GetValueImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions that define the overload set for the implementation of
    // 'balber::BerUtil::getValue'.  The set of types used for the 'value'
    // parameters in the overload set of 'getValue' in this 'struct' define the
    // set of types that 'balber::BerUtil::getValue' supports.

    // TYPES
    typedef BerUtil_BooleanImpUtil BooleanUtil;
        // 'BooleanUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for boolean values.

    typedef BerUtil_CharacterImpUtil CharacterUtil;
        // 'CharacterUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for byte values.

    typedef BerUtil_DateImpUtil DateUtil;
        // 'DateUtil' is an alias to a namespace for a suite of functions used
        // by 'BerUtil' to implement BER encoding and decoding operations for
        // date values.

    typedef BerUtil_DatetimeImpUtil DatetimeUtil;
        // 'DatetimeUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for date and time values.

    typedef BerUtil_FloatingPointImpUtil FloatingPointUtil;
        // 'FloatingPointUtil' is an alias to a namespace for a suite of
        // functions used by 'BerUtil' to implement BER encoding and decoding
        // operations for floating-point number values.

    typedef BerUtil_IntegerImpUtil IntegerUtil;
        // 'IntegerUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for integer values.

    typedef BerUtil_StringImpUtil StringUtil;
        // 'StringUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for string values.

    typedef BerUtil_TimeImpUtil TimeUtil;
        // 'TimeUtil' is an alias to a namespace for a suite of functions used
        // by 'BerUtil' to implement BER encoding and decoding operations for
        // time values.

    typedef bdlb::Variant2<bdlt::Date, bdlt::DateTz> DateOrDateTz;
        // 'DateOrDateTz' is a convenient alias for
        // 'bdlb::Variant2<bdlt::Date, bdlt::DateTz>'.

    typedef bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz>
        DatetimeOrDatetimeTz;
        // 'DatetimeOrDatetimeTz' is a convenient alias for
        // 'bdlb::Variant2<bdlt::Datetime, bdlt::DatetimeTz>'.

    typedef bdlb::Variant2<bdlt::Time, bdlt::TimeTz> TimeOrTimeTz;
        // 'TimeOrTimeTz' is a convenient alias for
        // 'bdlb::Variant2<bdlt::Time, bdlt::TimeTz>'.

    // CLASS METHODS
    template <typename TYPE>
    static int getValue(
                      TYPE                     *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bool                     *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      char                     *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      unsigned char            *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      signed char              *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      float                    *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      double                   *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bdldfp::Decimal64        *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bsl::string              *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bdlt::Date               *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bdlt::DateTz             *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      DateOrDateTz             *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bdlt::Datetime           *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bdlt::DatetimeTz         *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      DatetimeOrDatetimeTz     *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bdlt::Time               *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      bdlt::TimeTz             *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
    static int getValue(
                      TimeOrTimeTz             *value,
                      bsl::streambuf           *streamBuf,
                      int                       length,
                      const BerDecoderOptions&  options = BerDecoderOptions());
        // Decode the specified 'value' from the specified 'streamBuf',
        // consuming exactly the specified 'length' bytes.  Return 0 on
        // success, and a non-zero value otherwise.  Optionally specify
        // decoding 'options' to control aspects of the decoding.  Note that
        // the value consists of the contents of the bytes only (no length
        // prefix).  Also note that only fundamental C++ types, 'bsl::string',
        // and BDE date/time types are supported.
};

                       // ==============================
                       // struct BerUtil_PutValueImpUtil
                       // ==============================

struct BerUtil_PutValueImpUtil {
    // This component-private utility 'struct' provides a namespace for a suite
    // of functions that define the overload set for the implementation of
    // 'balber::BerUtil::putValue'.  The set of types used for the 'value'
    // parameters in the overload set of 'putValue' in this 'struct' define the
    // set of types that 'balber::BerUtil::putValue' supports.

    // TYPES
    typedef BerUtil_BooleanImpUtil BooleanUtil;
        // 'BooleanUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for boolean values.

    typedef BerUtil_CharacterImpUtil CharacterUtil;
        // 'CharacterUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for byte values.

    typedef BerUtil_DateImpUtil DateUtil;
        // 'DateUtil' is an alias to a namespace for a suite of functions used
        // by 'BerUtil' to implement BER encoding and decoding operations for
        // date values.

    typedef BerUtil_DatetimeImpUtil DatetimeUtil;
        // 'DatetimeUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for date and time values.

    typedef BerUtil_FloatingPointImpUtil FloatingPointUtil;
        // 'FloatingPointUtil' is an alias to a namespace for a suite of
        // functions used by 'BerUtil' to implement BER encoding and decoding
        // operations for floating-point number values.

    typedef BerUtil_IntegerImpUtil IntegerUtil;
        // 'IntegerUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for integer values.

    typedef BerUtil_StringImpUtil StringUtil;
        // 'StringUtil' is an alias to a namespace for a suite of functions
        // used by 'BerUtil' to implement BER encoding and decoding operations
        // for string values.

    typedef BerUtil_TimeImpUtil TimeUtil;
        // 'TimeUtil' is an alias to a namespace for a suite of functions used
        // by 'BerUtil' to implement BER encoding and decoding operations for
        // time values.

    // CLASS METHODS
    template <typename TYPE>
    static int putValue(bsl::streambuf          *streamBuf,
                        const TYPE&              value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        bool                     value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        char                     value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        unsigned char            value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        signed char              value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        float                    value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        double                   value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        bdldfp::Decimal64        value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        const bsl::string&       value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf           *streamBuf,
                        const bslstl::StringRef&  value,
                        const BerEncoderOptions  *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        const bdlt::Date&        value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        const bdlt::DateTz&      value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        const bdlt::Datetime&    value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        const bdlt::DatetimeTz&  value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        const bdlt::Time&        value,
                        const BerEncoderOptions *options);
    static int putValue(bsl::streambuf          *streamBuf,
                        const bdlt::TimeTz&      value,
                        const BerEncoderOptions *options);
        // Encode the specified 'value' to the specified 'streamBuf'.  Return 0
        // on success, and a non-zero value otherwise.  Note that the value
        // consists of the length and contents primitives.  Also note that only
        // fundamental C++ types, 'bsl::string', 'bslstl::StringRef' and BDE
        // date/time types are supported.
};

                             // ==================
                             // struct BerUtil_Imp
                             // ==================

struct BerUtil_Imp {
    // This component-private utility 'struct' exists to provide
    // backwards-compatability for external components that depend upon the
    // facilities provided by this 'struct'.

    // CLASS METHODS
    static int putStringValue(bsl::streambuf *streamBuf,
                              const char     *string,
                              int             stringLength);
        // Write the length and contents octets of the BER encoding of the
        // specified character 'string' having the specified 'stringLength' (as
        // defined in the specification) to the output sequence of the
        // specified 'streamBuf'.  Return 0 if successful, and a non-zero value
        // otherwise.  The operation succeeds if and only if all bytes
        // corresponding to the length and contents octets are written to the
        // 'streamBuf' without the write position becoming unavailable.
};

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

                               // --------------
                               // struct BerUtil
                               // --------------

// CLASS METHODS
inline
int BerUtil::getEndOfContentOctets(bsl::streambuf *streamBuf,
                                   int            *accumNumBytesConsumed)
{
    return BerUtil_LengthImpUtil::getEndOfContentOctets(accumNumBytesConsumed,
                                                        streamBuf);
}

inline
int BerUtil::getLength(bsl::streambuf *streamBuf,
                       int            *result,
                       int            *accumNumBytesConsumed)
{
    return BerUtil_LengthImpUtil::getLength(
        result, accumNumBytesConsumed, streamBuf);
}

template <class TYPE>
inline
int BerUtil::getValue(bsl::streambuf           *streamBuf,
                      TYPE                     *value,
                      int                       length,
                      const BerDecoderOptions&  options)
{
    return BerUtil_GetValueImpUtil::getValue(
        value, streamBuf, length, options);
}

template <typename TYPE>
inline
int BerUtil::getValue(bsl::streambuf           *streamBuf,
                      TYPE                     *value,
                      int                      *accumNumBytesConsumed,
                      const BerDecoderOptions&  options)
{
    int length;
    if (BerUtil_LengthImpUtil::getLength(
            &length, accumNumBytesConsumed, streamBuf)) {
        return -1;                                                    // RETURN
    }

    if (BerUtil::getValue(streamBuf, value, length, options)) {
        return -1;                                                    // RETURN
    }

    *accumNumBytesConsumed += length;
    return 0;
}

inline
int BerUtil::putEndOfContentOctets(bsl::streambuf *streamBuf)
{
    return BerUtil_LengthImpUtil::putEndOfContentOctets(streamBuf);
}

inline
int BerUtil::putIndefiniteLengthOctet(bsl::streambuf *streamBuf)
{
    return BerUtil_LengthImpUtil::putIndefiniteLengthOctet(streamBuf);
}

inline
int BerUtil::putLength(bsl::streambuf *streamBuf, int length)
{
    return BerUtil_LengthImpUtil::putLength(streamBuf, length);
}

template <typename TYPE>
inline
int BerUtil::putValue(bsl::streambuf          *streamBuf,
                      const TYPE&              value,
                      const BerEncoderOptions *options)
{
    return BerUtil_PutValueImpUtil::putValue(streamBuf, value, options);
}

                        // ----------------------------
                        // struct BerUtil_StreambufUtil
                        // ----------------------------

// CLASS METHODS
inline
int BerUtil_StreambufUtil::peekChar(char *value, bsl::streambuf *streamBuf)
{
    const bsl::streambuf::int_type byte = streamBuf->sgetc();
    if (bsl::streambuf::traits_type::eof() == byte) {
        return -1;                                                    // RETURN
    }

    *value = bsl::streambuf::traits_type::to_char_type(byte);
    return 0;
}

inline
int BerUtil_StreambufUtil::getChars(char           *buffer,
                                    bsl::streambuf *streamBuf,
                                    int             bufferLength)
{
    const bsl::streamsize numCharsRead =
        streamBuf->sgetn(buffer, static_cast<bsl::streamsize>(bufferLength));

    if (numCharsRead != static_cast<bsl::streamsize>(bufferLength)) {
        return -1;                                                    // RETURN
    }

    return 0;
}

inline
int BerUtil_StreambufUtil::putChars(bsl::streambuf *streamBuf,
                                    const char     *buffer,
                                    int             bufferLength)
{
    const bsl::streamsize numCharsWritten =
        streamBuf->sputn(buffer, static_cast<bsl::streamsize>(bufferLength));

    if (numCharsWritten != bufferLength) {
        return -1;                                                    // RETURN
    }

    return 0;
}

                      // --------------------------------
                      // struct BerUtil_RawIntegerImpUtil
                      // --------------------------------

// CLASS METHODS
template <typename TYPE>
int BerUtil_RawIntegerImpUtil::putIntegerGivenLength(bsl::streambuf *streamBuf,
                                                     TYPE            value,
                                                     int             length)
{
    enum { k_BDEM_SUCCESS = 0, k_BDEM_FAILURE = -1 };

    if (length <= 0) {
        return k_BDEM_FAILURE;                                        // RETURN
    }

    static const bool isUnsigned = (TYPE(-1) > TYPE(0));

    if (isUnsigned && (unsigned)length == sizeof(TYPE) + 1) {
        static const TYPE SGN_BIT = static_cast<TYPE>(
            static_cast<TYPE>(1)
            << (sizeof(TYPE) * Constants::k_NUM_BITS_PER_OCTET - 1));
        // Length may be one greater than 'sizeof(TYPE)' only if type is
        // unsigned and the high bit (normally the sign bit) is set.  In this
        // case, a leading zero octet is emitted.

        if (!(value & SGN_BIT)) {
            return k_BDEM_FAILURE;                                    // RETURN
        }

        if (0 != streamBuf->sputc(0)) {
            return k_BDEM_FAILURE;                                    // RETURN
        }

        --length;
    }

    if (static_cast<unsigned>(length) > sizeof(TYPE)) {
        return k_BDEM_FAILURE;                                        // RETURN
    }

#if BSLS_PLATFORM_IS_BIG_ENDIAN
    return length == streamBuf->sputn(
                         static_cast<char *>(static_cast<void *>(&value)) +
                             sizeof(TYPE) - length,
                         length)
               ? k_BDEM_SUCCESS
               : k_BDEM_FAILURE;
#else

    char *dst = static_cast<char *>(static_cast<void *>(&value)) + length;
    for (; length > 0; --length) {
        unsigned char c = *--dst;
        if (c != streamBuf->sputc(c)) {
            return k_BDEM_FAILURE;                                    // RETURN
        }
    }

    return k_BDEM_SUCCESS;

#endif
}

                       // -----------------------------
                       // struct BerUtil_BooleanImpUtil
                       // -----------------------------

// Decoding

inline
int BerUtil_BooleanImpUtil::getBoolValue(bool           *value,
                                         bsl::streambuf *streamBuf,
                                         int             length)
{
    if (1 != length) {
        return -1;                                                    // RETURN
    }

    int intValue = streamBuf->sbumpc();
    if (bsl::streambuf::traits_type::eof() == intValue) {
        return -1;                                                    // RETURN
    }

    *value = 0 != intValue;

    return 0;
}

// Encoding

inline
int BerUtil_BooleanImpUtil::putBoolValue(bsl::streambuf *streamBuf, bool value)
{
    // It has been observed in practice that 'value' may refer to uninitialized
    // or overwritten memory, in which case its value may neither be 'true' (1)
    // nor 'false' (0).  We assert here to ensure that users get a useful error
    // message.  Note that we assert (rather than returning an error code), as
    // it is undefined behavior to examine the value of such an uninitialized
    // 'bool'.  Also note that gcc complains about this assert when used with
    // the '-Wlogical-op' flag.  Therefore, to silence this warning/error we
    // cast the 'bool' value to a 'char *' and check the value referred to by
    // the 'char *'.

    BSLMF_ASSERT(sizeof(bool) == sizeof(char));
    BSLS_ASSERT(0 == *static_cast<char *>(static_cast<void *>(&value)) ||
                1 == *static_cast<char *>(static_cast<void *>(&value)));

    typedef bsl::streambuf::char_type char_type;

    if (0 != LengthUtil::putLength(streamBuf, 1)) {
        return -1;                                                    // RETURN
    }

    if (static_cast<int>(value) !=
        streamBuf->sputc(static_cast<char_type>(value ? 1 : 0))) {
        return -1;                                                    // RETURN
    }

    return 0;
}

                       // -----------------------------
                       // struct BerUtil_IntegerImpUtil
                       // -----------------------------

// CLASS METHODS
template <typename TYPE>
int BerUtil_IntegerImpUtil::getNumOctetsToStream(TYPE value)
{
    int numBytes = sizeof(TYPE);

    BSLMF_ASSERT(sizeof(TYPE) > 1);

    // The 2 double casts to 'TYPE' in this function are necessary because if
    // the type is 64 bits the innermost cast is need to widen the constant
    // before the shift, and the outermost cast is needed if the type is
    // narrower than 'int'.

    static const TYPE NEG_MASK = static_cast<TYPE>(
        static_cast<TYPE>(0xff80)
        << ((sizeof(TYPE) - 2) * Constants::k_NUM_BITS_PER_OCTET));
    if (0 == value) {
        numBytes = 1;
    }
    else if (value > 0) {
        static const TYPE SGN_BIT = static_cast<TYPE>(
            static_cast<TYPE>(1)
            << (sizeof(TYPE) * Constants::k_NUM_BITS_PER_OCTET - 1));
        if (value & SGN_BIT) {
            // If 'value > 0' but the high bit (sign bit) is set, then this is
            // an unsigned value and a leading zero byte must be emitted to
            // prevent the value from looking like a negative value on the
            // wire.  The leading zero is followed by all of the bytes of the
            // unsigned value.

            return static_cast<int>(sizeof(TYPE) + 1);                // RETURN
        }

        // This mask zeroes out the most significant byte and the first bit of
        // the next byte.

        static const TYPE POS_MASK = TYPE(~NEG_MASK);
        while ((value & POS_MASK) == value) {
            value = static_cast<TYPE>(value << 8);

            // shift out redundant high-order 0x00

            --numBytes;
        }
    }
    else {  // 0 > value
        while ((value | NEG_MASK) == value) {
            value = static_cast<TYPE>(value << 8);

            // shift out redundant high-order 0xFF

            --numBytes;
        }
    }

    BSLS_ASSERT(numBytes > 0);
    return numBytes;
}

template <typename TYPE>
int BerUtil_IntegerImpUtil::getIntegerValue(TYPE           *value,
                                            bsl::streambuf *streamBuf,
                                            int             length)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    enum { k_SIGN_BIT_MASK = 0x80 };

    static const bool isUnsigned = (TYPE(-1) > TYPE(0));

    if (isUnsigned && static_cast<unsigned>(length) == sizeof(TYPE) + 1) {
        // Length of an unsigned is allowed to be one larger then
        // 'sizeof(TYPE)' only if first byte is zero.  (This is so that large
        // unsigned numbers do not appear as negative numbers in the BER
        // stream).  Remove the leading zero byte.

        if (0 != streamBuf->sbumpc()) {
            // First byte was not zero.  Fail.

            return k_FAILURE;                                         // RETURN
        }

        --length;
    }

    if (static_cast<unsigned>(length) > sizeof(TYPE)) {
        // Overflow.

        return k_FAILURE;                                             // RETURN
    }

    *value = static_cast<TYPE>(streamBuf->sgetc() & k_SIGN_BIT_MASK ? -1 : 0);

    for (int i = 0; i < length; ++i) {
        int nextOctet = streamBuf->sbumpc();
        if (bsl::streambuf::traits_type::eof() == nextOctet) {
            return k_FAILURE;                                         // RETURN
        }

        const unsigned long long mask =
            (1ull << ((sizeof(TYPE) - 1) * Constants::k_NUM_BITS_PER_OCTET)) -
            1;
        *value = static_cast<TYPE>((*value & mask)
                                   << Constants::k_NUM_BITS_PER_OCTET);
        *value =
            static_cast<TYPE>(*value | static_cast<unsigned char>(nextOctet));
    }

    return k_SUCCESS;
}

inline
int BerUtil_IntegerImpUtil::get40BitIntegerValue(bsls::Types::Int64 *value,
                                                 bsl::streambuf     *streamBuf)
{
    char bytes[5];
    if (0 != StreambufUtil::getChars(bytes, streamBuf, sizeof(bytes))) {
        return -1;                                                    // RETURN
    }

    const bsls::Types::Uint64 byte0 =
        static_cast<bsls::Types::Uint64>(static_cast<unsigned char>(bytes[0]))
        << (8 * 4);

    const bsls::Types::Uint64 byte1 =
        static_cast<bsls::Types::Uint64>(static_cast<unsigned char>(bytes[1]))
        << (8 * 3);

    const bsls::Types::Uint64 byte2 =
        static_cast<bsls::Types::Uint64>(static_cast<unsigned char>(bytes[2]))
        << (8 * 2);

    const bsls::Types::Uint64 byte3 =
        static_cast<bsls::Types::Uint64>(static_cast<unsigned char>(bytes[3]))
        << (8 * 1);

    const bsls::Types::Uint64 byte4 =
        static_cast<bsls::Types::Uint64>(static_cast<unsigned char>(bytes[4]))
        << (8 * 0);

    const bsls::Types::Uint64 unsignedValue =
        byte0 + byte1 + byte2 + byte3 + byte4;

    *value = static_cast<bsls::Types::Int64>(unsignedValue);

    return 0;
}

template <typename TYPE>
int BerUtil_IntegerImpUtil::putIntegerValue(bsl::streambuf *streamBuf,
                                            TYPE            value)
{
    typedef bsl::streambuf::char_type char_type;

    const int length = getNumOctetsToStream(value);
    if (length != streamBuf->sputc(static_cast<char_type>(length))) {
        return -1;                                                    // RETURN
    }

    return putIntegerGivenLength(streamBuf, value, length);
}

template <typename TYPE>
int BerUtil_IntegerImpUtil::putIntegerGivenLength(bsl::streambuf *streamBuf,
                                                  TYPE            value,
                                                  int             length)
{
    return RawIntegerUtil::putIntegerGivenLength(streamBuf, value, length);
}

inline
int BerUtil_IntegerImpUtil::put40BitIntegerValue(bsl::streambuf     *streamBuf,
                                                 bsls::Types::Int64  value)
    ///Implementation Note
    ///-------------------
    // This implementation requires the platform use a 2's complement
    // representation for signed integer values.
{
    BSLS_ASSERT(-549755813888ll <= value);
    BSLS_ASSERT(549755813888ll > value);

    const bsls::Types::Uint64 unsignedValue = value;

    const char bytes[5] = {
        static_cast<char>(
            static_cast<unsigned char>((unsignedValue >> (8 * 4)) & 0xFF)),
        static_cast<char>(
            static_cast<unsigned char>((unsignedValue >> (8 * 3)) & 0xFF)),
        static_cast<char>(
            static_cast<unsigned char>((unsignedValue >> (8 * 2)) & 0xFF)),
        static_cast<char>(
            static_cast<unsigned char>((unsignedValue >> (8 * 1)) & 0xFF)),
        static_cast<char>(
            static_cast<unsigned char>((unsignedValue >> (8 * 0)) & 0xFF))};

    return StreambufUtil::putChars(streamBuf, bytes, sizeof(bytes));
}

                      // -------------------------------
                      // struct BerUtil_CharacterImpUtil
                      // -------------------------------

// CLASS METHODS

// Decoding

inline
int BerUtil_CharacterImpUtil::getCharValue(char           *value,
                                           bsl::streambuf *streamBuf,
                                           int             length)
{
    switch (length) {
      case 1: {
        ; // do nothing
      } break;
      case 2: {
        if (0 != streamBuf->sbumpc()) {
            // see 'getIntegerValue', if this 'char' had been encoded as
            // 'unsigned' there might be a leading 0 which is acceptable, but
            // any other value for the first byte is invalid

            return -1;                                                // RETURN
        }
      } break;
      default: {
        return -1;                                                    // RETURN
      }
    }

    int valueOctet = streamBuf->sbumpc();
    if (bsl::streambuf::traits_type::eof() == valueOctet) {
        return -1;                                                    // RETURN
    }

    *value = static_cast<char>(valueOctet);
    return 0;
}

inline
int BerUtil_CharacterImpUtil::getSignedCharValue(signed char    *value,
                                                 bsl::streambuf *streamBuf,
                                                 int             length)
{
    char temp;
    if (0 != getCharValue(&temp, streamBuf, length)) {
        return -1;                                                    // RETURN
    }

    *value = static_cast<signed char>(temp);
    return 0;
}

inline
int BerUtil_CharacterImpUtil::getUnsignedCharValue(unsigned char  *value,
                                                   bsl::streambuf *streamBuf,
                                                   int             length)
{
    short temp;
    if (IntegerUtil::getIntegerValue(&temp, streamBuf, length)) {
        return -1;                                                    // RETURN
    }

    *value = static_cast<unsigned char>(temp);
    return 0;
}

// Encoding

inline
int BerUtil_CharacterImpUtil::putCharValue(bsl::streambuf *streamBuf,
                                           char            value)
{
    if (0 != LengthUtil::putLength(streamBuf, 1)) {
        return -1;                                                    // RETURN
    }

    if (static_cast<unsigned char>(value) != streamBuf->sputc(value)) {
        return -1;                                                    // RETURN
    }

    return 0;
}

inline
int BerUtil_CharacterImpUtil::putSignedCharValue(bsl::streambuf *streamBuf,
                                                 signed char     value)
{
    return putCharValue(streamBuf, static_cast<char>(value));
}

inline
int BerUtil_CharacterImpUtil::putUnsignedCharValue(bsl::streambuf *streamBuf,
                                                   unsigned char   value)
{
    return IntegerUtil::putIntegerValue(streamBuf,
                                        static_cast<unsigned short>(value));
}

                    // -----------------------------------
                    // struct BerUtil_FloatingPointImpUtil
                    // -----------------------------------

// CLASS METHODS

// Decoding

inline
int BerUtil_FloatingPointImpUtil::getFloatValue(float          *value,
                                                bsl::streambuf *streamBuf,
                                                int             length)
{
    double tentativeValue;
    if (0 != getDoubleValue(&tentativeValue, streamBuf, length)) {
        return -1;                                                    // RETURN
    }

    *value = static_cast<float>(tentativeValue);

    return 0;
}

// Encoding

inline
int BerUtil_FloatingPointImpUtil::putFloatValue(
                                            bsl::streambuf          *streamBuf,
                                            float                    value,
                                            const BerEncoderOptions *options)
{
    return putDoubleValue(streamBuf,
                          static_cast<double>(value),
                          options);
}

                        // ----------------------------
                        // struct BerUtil_StringImpUtil
                        // ----------------------------

// CLASS METHODS

// Encoding Utilities

inline
int BerUtil_StringImpUtil::putRawStringValue(bsl::streambuf *streamBuf,
                                             const char     *value,
                                             int             valueLength)
{
    if (0 != LengthUtil::putLength(streamBuf, valueLength)) {
        return -1;                                                    // RETURN
    }

    if (valueLength != streamBuf->sputn(value, valueLength)) {
        return -1;                                                    // RETURN
    }

    return 0;
}

// 'bsl::string' Encoding

inline
int BerUtil_StringImpUtil::putStringValue(bsl::streambuf     *streamBuf,
                                          const bsl::string&  value)
{
    return putRawStringValue(
        streamBuf, value.data(), static_cast<int>(value.length()));
}

// 'bslstl::StringRef' Encoding

inline
int BerUtil_StringImpUtil::putStringRefValue(
                                           bsl::streambuf           *streamBuf,
                                           const bslstl::StringRef&  value)
{
    return putRawStringValue(
        streamBuf, value.data(), static_cast<int>(value.length()));
}

                       // -----------------------------
                       // struct BerUtil_Iso8601ImpUtil
                       // -----------------------------

// PRIVATE CLASS METHODS
template <class TYPE>
int BerUtil_Iso8601ImpUtil::getValue(TYPE           *value,
                                     bsl::streambuf *streamBuf,
                                     int             length)
{
    if (length <= 0) {
        return -1;                                                    // RETURN
    }

    char               localBuf[32];  // for common case where length < 32
    bsl::vector<char>  vecBuf;        // for length >= 32
    char              *buf;

    if (length < 32) {
        buf = localBuf;
    }
    else {
        vecBuf.resize(length);
        buf = &vecBuf[0];  // First byte of contiguous string
    }

    const bsl::streamsize bytesConsumed = streamBuf->sgetn(buf, length);
    if (static_cast<int>(bytesConsumed) != length) {
        return -1;                                                    // RETURN
    }

    return bdlt::Iso8601Util::parse(value, buf, length);
}

template <class TYPE>
int BerUtil_Iso8601ImpUtil::putValue(bsl::streambuf          *streamBuf,
                                     const TYPE&              value,
                                     const BerEncoderOptions *options)
{
    char                           buf[bdlt::Iso8601Util::k_MAX_STRLEN];
    bdlt::Iso8601UtilConfiguration config;
    int                            datetimeFractionalSecondPrecision =
        options ? options->datetimeFractionalSecondPrecision() : 6;
    config.setFractionalSecondPrecision(datetimeFractionalSecondPrecision);
    int len = bdlt::Iso8601Util::generate(buf, sizeof(buf), value, config);

    return StringUtil::putStringRefValue(streamBuf,
                                         bslstl::StringRef(buf, len));
}

                 // --------------------------------------
                 // struct BerUtil_ExtendedBinaryEncodingUtil
                 // --------------------------------------

// CLASS METHODS
inline
bool BerUtil_ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(
                                              const bdlt::Time&        value,
                                              const BerEncoderOptions *options)
{
    if (!options) {
        // If encoding options are not specified, by default the ISO8601 format
        // is used.

        return false;                                                 // RETURN
    }

    const bool useBinaryEncoding = options->encodeDateAndTimeTypesAsBinary();

    const bool useMicrosecondPrecision =
        (6 == options->datetimeFractionalSecondPrecision());

    const bool compactBinaryEncodingOfValueIsBuggy = (bdlt::Time() == value);
    // The compact binary encoding format ambiguously encodes both the
    // '00:00:00' time value and the '24:00:00' time value to the same bit
    // pattern.  The decoder treats this bit pattern as the '00:00:00' time
    // value.  The extended binary encoding format does not have this
    // limitation.  Therefore, if the extended binary format is allowed, it
    // should be used to encode default time values even if the fractional
    // second precision is not 6.

    const bool useExtendedBinaryEncodingIfAllowed =
        useMicrosecondPrecision || compactBinaryEncodingOfValueIsBuggy;

    const bool extendedBinaryEncodingIsAllowed =
        (options->bdeVersionConformance() >=
         Encoding::k_EXTENDED_BINARY_MIN_BDE_VERSION);

    return useBinaryEncoding && useExtendedBinaryEncodingIfAllowed &&
           extendedBinaryEncodingIsAllowed;
}

inline
bool BerUtil_ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(
                                              const bdlt::TimeTz&      value,
                                              const BerEncoderOptions *options)
{
    return useExtendedBinaryEncoding(value.localTime(), options);
}

inline
bool BerUtil_ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(
                                              const bdlt::Datetime&    value,
                                              const BerEncoderOptions *options)
{
    return useExtendedBinaryEncoding(value.time(), options);
}

inline
bool BerUtil_ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(
                                              const bdlt::DatetimeTz&  value,
                                              const BerEncoderOptions *options)
{
    return useExtendedBinaryEncoding(value.localDatetime().time(), options);
}

inline
bool BerUtil_ExtendedBinaryEncodingUtil::useBinaryEncoding(
                                              const BerEncoderOptions *options)
{
    return options && options->encodeDateAndTimeTypesAsBinary();
}

                      // -------------------------------
                      // class BerUtil_DateAndTimeHeader
                      // -------------------------------

// CREATORS
inline
BerUtil_DateAndTimeHeader::BerUtil_DateAndTimeHeader()
: d_type(Type::e_NOT_EXTENDED_BINARY)
, d_timezoneOffsetInMinutes(0)
{
}

// MANIPULATORS
inline
void BerUtil_DateAndTimeHeader::makeNotExtendedBinary()
{
    d_type                    = Type::e_NOT_EXTENDED_BINARY;
    d_timezoneOffsetInMinutes = 0;
}

inline
void BerUtil_DateAndTimeHeader::makeExtendedBinaryWithoutTimezone()
{
    d_type                    = Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE;
    d_timezoneOffsetInMinutes = 0;
}

inline
void BerUtil_DateAndTimeHeader::makeExtendedBinaryWithTimezone(int offset)
{
    BSLS_ASSERT(TimezoneUtil::isValidTimezoneOffsetInMinutes(offset));

    d_type                    = Type::e_EXTENDED_BINARY_WITH_TIMEZONE;
    d_timezoneOffsetInMinutes = offset;
}

// ACCESSORS
inline
bool BerUtil_DateAndTimeHeader::isExtendedBinary() const
{
    return (Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE == d_type) ||
           (Type::e_EXTENDED_BINARY_WITH_TIMEZONE == d_type);
}

inline
bool BerUtil_DateAndTimeHeader::isExtendedBinaryWithoutTimezone() const
{
    return Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE == d_type;
}

inline
bool BerUtil_DateAndTimeHeader::isExtendedBinaryWithTimezone() const
{
    return Type::e_EXTENDED_BINARY_WITH_TIMEZONE == d_type;
}

inline
int BerUtil_DateAndTimeHeader::timezoneOffsetInMinutes() const
{
    return d_timezoneOffsetInMinutes;
}

                  // ---------------------------------------
                  // struct BerUtil_DateAndTimeHeaderImpUtil
                  // ---------------------------------------

// CLASS METHODS
inline
bool BerUtil_DateAndTimeHeaderImpUtil::isReserved(unsigned char firstByte)
{
    // A date-and-time header has a reserved bit pattern if:
    //..
    //  o the first bit is 1, and any of the following are true:
    //    o the second bit is 1
    //    o the third bit is 1
    //..

    const bool bit0 = firstByte & 0x80;
    const bool bit1 = firstByte & 0x40;
    const bool bit2 = firstByte & 0x20;

    return bit0 && (bit1 || bit2);
}

inline
bool BerUtil_DateAndTimeHeaderImpUtil::isExtendedBinary(
                                                       unsigned char firstByte)
{
    const bool bit0 = firstByte & 0x80;
    const bool bit1 = firstByte & 0x40;
    const bool bit2 = firstByte & 0x20;

    return bit0 && !(bit1 || bit2);
}

inline
bool BerUtil_DateAndTimeHeaderImpUtil::isExtendedBinaryWithoutTimezone(
                                                       unsigned char firstByte)
{
    const bool bit0 = firstByte & 0x80;
    const bool bit1 = firstByte & 0x40;
    const bool bit2 = firstByte & 0x20;
    const bool bit3 = firstByte & 0x10;

    return bit0 && !(bit1 || bit2) && !bit3;
}

inline
bool BerUtil_DateAndTimeHeaderImpUtil::isExtendedBinaryWithTimezone(
                                                       unsigned char firstByte)
{
    const bool bit0 = firstByte & 0x80;
    const bool bit1 = firstByte & 0x40;
    const bool bit2 = firstByte & 0x20;
    const bool bit3 = firstByte & 0x10;

    return bit0 && !(bit1 || bit2) && bit3;
}

inline
void BerUtil_DateAndTimeHeaderImpUtil::detectTypeIfNotReserved(
                                                      bool          *reserved,
                                                      Type::Value   *type,
                                                      unsigned char  firstByte)
{
    if (isReserved(firstByte)) {
        *reserved = true;
        return;                                                       // RETURN
    }

    *reserved = false;
    detectType(type, firstByte);
}

inline
void BerUtil_DateAndTimeHeaderImpUtil::detectType(Type::Value   *type,
                                                  unsigned char  firstByte)
{
    BSLS_ASSERT_OPT(!isReserved(firstByte));

    const bool bit0 = firstByte & 0x80;
    const bool bit3 = firstByte & 0x10;

    if (bit0 & bit3) {
        *type = Type::e_EXTENDED_BINARY_WITH_TIMEZONE;
        return;                                                       // RETURN
    }

    if (bit0 & !bit3) {
        *type = Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE;
        return;                                                       // RETURN
    }

    *type = Type::e_NOT_EXTENDED_BINARY;
}

inline
int BerUtil_DateAndTimeHeaderImpUtil::getValueIfNotReserved(
                                                     Header         *value,
                                                     bsl::streambuf *streamBuf)
{
    char headerBytes[2];
    if (0 !=
        StreambufUtil::getChars(headerBytes, streamBuf, sizeof(headerBytes))) {
        return -1;                                                    // RETURN
    }

    const unsigned char headerByte0 =
        static_cast<unsigned char>(headerBytes[0]);
    const unsigned char headerByte1 =
        static_cast<unsigned char>(headerBytes[1]);

    return getValueIfNotReserved(value, headerByte0, headerByte1);
}

inline
int BerUtil_DateAndTimeHeaderImpUtil::getValueIfNotReserved(
                                                    Header        *value,
                                                    unsigned char  headerByte0,
                                                    unsigned char  headerByte1)
{
    if (isReserved(headerByte0)) {
        return -1;                                                    // RETURN
    }

    return getValue(value, headerByte0, headerByte1);
}

inline
int BerUtil_DateAndTimeHeaderImpUtil::getValue(Header         *value,
                                               bsl::streambuf *streamBuf)
{
    char headerBytes[2];
    if (0 !=
        StreambufUtil::getChars(headerBytes, streamBuf, sizeof(headerBytes))) {
        return -1;                                                    // RETURN
    }

    const unsigned char headerByte0 =
        static_cast<unsigned char>(headerBytes[0]);
    const unsigned char headerByte1 =
        static_cast<unsigned char>(headerBytes[1]);

    return getValue(value, headerByte0, headerByte1);
}

inline
int BerUtil_DateAndTimeHeaderImpUtil::getValue(Header        *value,
                                               unsigned char  headerByte0,
                                               unsigned char  headerByte1)
{
    BSLS_ASSERT_OPT(!isReserved(headerByte0));

    Type::Value type;
    detectType(&type, headerByte0);

    switch (type) {
      case Type::e_NOT_EXTENDED_BINARY: {
        value->makeNotExtendedBinary();
        return 0;                                                     // RETURN
      } break;
      case Type::e_EXTENDED_BINARY_WITHOUT_TIMEZONE: {
        // If the header indicates that it does not carry time-zone
        // information, and the low 12 bits of the header are non-zero, then
        // the header is malformed.

        if ((headerByte0 & 0x0F) | (headerByte1 & 0xFF)) {
            return -1;                                                // RETURN
        }

        value->makeExtendedBinaryWithoutTimezone();
        return 0;                                                     // RETURN
      } break;
      case Type::e_EXTENDED_BINARY_WITH_TIMEZONE: {
        enum { k_TIMEZONE_SIGN_BIT = 0x08 };

        // If the time-zone offset's sign bit is 1, then sign-extend the low
        // nibble of the first byte, which encodes the 4 hi bits of the
        // 12-bit, 2's-complement number that represents the time-zone offset
        // in minutes.

        const unsigned char timezoneOffsetHi =
            (headerByte0 & k_TIMEZONE_SIGN_BIT)
                ? ((0x0F & headerByte0) | 0xF0)
                : ((0x0F & headerByte0) | 0x00);

        const signed char signedTimezoneOffsetHi =
            static_cast<signed char>(timezoneOffsetHi);

        const unsigned char timezoneOffsetLo = headerByte1;

        const int timezoneOffset =
            (static_cast<int>(signedTimezoneOffsetHi) << 8) |
            (static_cast<int>(timezoneOffsetLo) << 0);

        if (!TimezoneUtil::isValidTimezoneOffsetInMinutes(timezoneOffset)) {
            return -1;                                                // RETURN
        }

        value->makeExtendedBinaryWithTimezone(timezoneOffset);
        return 0;                                                     // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

inline
int BerUtil_DateAndTimeHeaderImpUtil::putExtendedBinaryWithoutTimezoneValue(
                                                     bsl::streambuf *streamBuf)
{
    static const char header[k_HEADER_LENGTH] = {'\x80', '\x00'};
    return StreambufUtil::putChars(streamBuf, header, k_HEADER_LENGTH);
}

inline
int BerUtil_DateAndTimeHeaderImpUtil::putExtendedBinaryWithTimezoneValue(
                                       bsl::streambuf *streamBuf,
                                       int             timezoneOffsetInMinutes)
{
    BSLS_ASSERT(
        TimezoneUtil::isValidTimezoneOffsetInMinutes(timezoneOffsetInMinutes));

    const unsigned short offsetWord =
        static_cast<unsigned short>(timezoneOffsetInMinutes);

    static const unsigned short k_OFFSET_MASK = 0x0FFF;

    const unsigned short headerWord = 0x9000 | (offsetWord & k_OFFSET_MASK);

    const char header[k_HEADER_LENGTH] = {
        static_cast<char>((headerWord >> 8) & 0xFF),
        static_cast<char>((headerWord >> 0) & 0xFF)};

    return StreambufUtil::putChars(streamBuf, header, k_HEADER_LENGTH);
}

                         // --------------------------
                         // struct BerUtil_DateImpUtil
                         // --------------------------

// PRIVATE CLASS METHODS

// 'bdlt::Date' Decoding

inline
int BerUtil_DateImpUtil::detectDateEncoding(DateEncoding::Value *encoding,
                                            int                  length,
                                            unsigned char        firstByte)
{
    if (k_MAX_COMPACT_BINARY_DATE_LENGTH >= length) {
        *encoding = DateEncoding::e_COMPACT_BINARY_DATE;
        return 0;                                                     // RETURN
    }

    BSLS_ASSERT_SAFE(k_MAX_COMPACT_BINARY_DATE_LENGTH < length);

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    *encoding = DateEncoding::e_ISO8601_DATE;
    return 0;
}

// 'bdlt::Date' Encoding

inline
BerUtil_DateEncoding::Value BerUtil_DateImpUtil::selectDateEncoding(
                                              const bdlt::Date&,
                                              const BerEncoderOptions *options)
{
    if (options && options->encodeDateAndTimeTypesAsBinary()) {
        return DateEncoding::e_COMPACT_BINARY_DATE;                   // RETURN
    }

    return DateEncoding::e_ISO8601_DATE;
}

// 'bdlt::DateTz' Decoding

inline
int BerUtil_DateImpUtil::detectDateTzEncoding(DateTzEncoding::Value *encoding,
                                              int                    length,
                                              unsigned char          firstByte)
{
    BSLMF_ASSERT(k_MAX_COMPACT_BINARY_DATE_LENGTH <
                 k_MIN_COMPACT_BINARY_DATETZ_LENGTH);

    if (k_MIN_COMPACT_BINARY_DATETZ_LENGTH > length) {
        *encoding = DateTzEncoding::e_COMPACT_BINARY_DATE;
        return 0;                                                     // RETURN
    }

    if (k_MAX_COMPACT_BINARY_DATETZ_LENGTH >= length) {
        *encoding = DateTzEncoding::e_COMPACT_BINARY_DATETZ;
        return 0;                                                     // RETURN
    }

    BSLS_ASSERT_SAFE(k_MAX_COMPACT_BINARY_DATETZ_LENGTH < length);

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    *encoding = DateTzEncoding::e_ISO8601_DATETZ;
    return 0;
}

// 'bdlt::DateTz' Encoding

inline
BerUtil_DateTzEncoding::Value BerUtil_DateImpUtil::selectDateTzEncoding(
                                              const bdlt::DateTz&      value,
                                              const BerEncoderOptions *options)
{
    if (options && options->encodeDateAndTimeTypesAsBinary() &&
        0 == value.offset()) {
        return DateTzEncoding::e_COMPACT_BINARY_DATE;                 // RETURN
    }

    if (options && options->encodeDateAndTimeTypesAsBinary()) {
        return DateTzEncoding::e_COMPACT_BINARY_DATETZ;               // RETURN
    }

    return DateTzEncoding::e_ISO8601_DATETZ;
}

// Variant Decoding

inline
int BerUtil_DateImpUtil::detectDateOrDateTzEncoding(
                                        DateOrDateTzEncoding::Value *encoding,
                                        int                          length,
                                        unsigned char                firstByte)
{
    if (k_MAX_COMPACT_BINARY_DATE_LENGTH >= length) {
        *encoding = DateOrDateTzEncoding::e_COMPACT_BINARY_DATE;
        return 0;                                                     // RETURN
    }

    BSLS_ASSERT_SAFE(k_MAX_COMPACT_BINARY_DATE_LENGTH < length);

    if (k_MAX_COMPACT_BINARY_DATETZ_LENGTH >= length) {
        *encoding = DateOrDateTzEncoding::e_COMPACT_BINARY_DATETZ;
        return 0;                                                     // RETURN
    }

    BSLS_ASSERT_SAFE(k_MAX_COMPACT_BINARY_DATETZ_LENGTH < length);

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    if (k_MAX_ISO8601_DATE_LENGTH >= length) {
        *encoding = DateOrDateTzEncoding::e_ISO8601_DATE;
        return 0;                                                     // RETURN
    }

    BSLS_ASSERT_SAFE(k_MAX_ISO8601_DATE_LENGTH < length);

    *encoding = DateOrDateTzEncoding::e_ISO8601_DATETZ;
    return 0;
}

inline
int BerUtil_DateImpUtil::getIso8601DateValue(DateOrDateTz   *value,
                                             bsl::streambuf *streamBuf,
                                             int             length)
{
    value->createInPlace<bdlt::Date>();
    return getIso8601DateValue(&value->the<bdlt::Date>(), streamBuf, length);
}

inline
int BerUtil_DateImpUtil::getIso8601DateTzValue(DateOrDateTz   *value,
                                               bsl::streambuf *streamBuf,
                                               int             length)
{
    value->createInPlace<bdlt::DateTz>();
    return getIso8601DateTzValue(
        &value->the<bdlt::DateTz>(), streamBuf, length);
}

inline
int BerUtil_DateImpUtil::getCompactBinaryDateValue(DateOrDateTz   *value,
                                                   bsl::streambuf *streamBuf,
                                                   int             length)
{
    value->createInPlace<bdlt::Date>();
    return getCompactBinaryDateValue(
        &value->the<bdlt::Date>(), streamBuf, length);
}

inline
int BerUtil_DateImpUtil::getCompactBinaryDateTzValue(DateOrDateTz   *value,
                                                     bsl::streambuf *streamBuf,
                                                     int             length)
{
    value->createInPlace<bdlt::DateTz>();
    return getCompactBinaryDateTzValue(
        &value->the<bdlt::DateTz>(), streamBuf, length);
}

// CLASS METHODS

// Variant Decoding

inline
int BerUtil_DateImpUtil::getDateOrDateTzValue(DateOrDateTz   *value,
                                              bsl::streambuf *streamBuf,
                                              int             length)
{
    char firstByte;
    if (0 != StreambufUtil::peekChar(&firstByte, streamBuf)) {
        return -1;                                                    // RETURN
    }

    DateOrDateTzEncoding::Value encoding;
    int rc = detectDateOrDateTzEncoding(&encoding, length, firstByte);

    if (0 != rc) {
        return -1;                                                    // RETURN
    }

    switch (encoding) {
      case DateOrDateTzEncoding::e_ISO8601_DATE: {
        return getIso8601DateValue(value, streamBuf, length);         // RETURN
      } break;
      case DateOrDateTzEncoding::e_ISO8601_DATETZ: {
        return getIso8601DateTzValue(value, streamBuf, length);       // RETURN
      } break;
      case DateOrDateTzEncoding::e_COMPACT_BINARY_DATE: {
        return getCompactBinaryDateValue(value, streamBuf, length);   // RETURN
      } break;
      case DateOrDateTzEncoding::e_COMPACT_BINARY_DATETZ: {
        return getCompactBinaryDateTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

                         // --------------------------
                         // struct BerUtil_TimeImpUtil
                         // --------------------------

// PRIVATE CLASS METHODS

// Utilities

inline
void BerUtil_DateImpUtil::dateToDaysSinceEpoch(
                                            bsls::Types::Int64 *daysSinceEpoch,
                                            const bdlt::Date&   date)
{
    const int serialDate = bdlt::ProlepticDateImpUtil::ymdToSerial(
        date.year(), date.month(), date.day());

    *daysSinceEpoch = serialDate - k_COMPACT_BINARY_DATE_EPOCH;
}

inline
int BerUtil_DateImpUtil::daysSinceEpochToDate(
                                            bdlt::Date         *date,
                                            bsls::Types::Int64  daysSinceEpoch)
{
    const bsls::Types::Int64 serialDate =
        daysSinceEpoch + k_COMPACT_BINARY_DATE_EPOCH;

    if (!bdlt::ProlepticDateImpUtil::isValidSerial(
            static_cast<int>(serialDate))) {
        return -1;                                                    // RETURN
    }

    int year;
    int month;
    int day;
    bdlt::ProlepticDateImpUtil::serialToYmd(
        &year, &month, &day, static_cast<int>(serialDate));

    date->setYearMonthDay(year, month, day);
    return 0;
}

// 'bdlt::Time' Decoding

inline
int BerUtil_TimeImpUtil::detectTimeEncoding(TimeEncoding::Value *encoding,
                                            int                  length,
                                            unsigned char        firstByte)
{
    if (k_MAX_COMPACT_BINARY_TIME_LENGTH >= length) {
        *encoding = TimeEncoding::e_COMPACT_BINARY_TIME;
        return 0;                                                     // RETURN
    }

    BSLS_ASSERT_SAFE(k_MAX_COMPACT_BINARY_TIME_LENGTH < length);

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinary(firstByte)) {
        *encoding = TimeEncoding::e_EXTENDED_BINARY_TIME;
        return 0;                                                     // RETURN
    }

    *encoding = TimeEncoding::e_ISO8601_TIME;
    return 0;
}

inline
int BerUtil_TimeImpUtil::getIso8601TimeValue(bdlt::Time     *value,
                                             bsl::streambuf *streamBuf,
                                             int             length)
{
    return Iso8601Util::getTimeValue(value, streamBuf, length);
}

// 'bdlt::Time' Encoding

inline
BerUtil_TimeEncoding::Value BerUtil_TimeImpUtil::selectTimeEncoding(
                                              const bdlt::Time&        value,
                                              const BerEncoderOptions *options)
{
    if (ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(value,
                                                              options)) {
        return TimeEncoding::e_EXTENDED_BINARY_TIME;                  // RETURN
    }

    if (ExtendedBinaryEncodingUtil::useBinaryEncoding(options)) {
        return TimeEncoding::e_COMPACT_BINARY_TIME;                   // RETURN
    }

    return TimeEncoding::e_ISO8601_TIME;
}

inline
int BerUtil_TimeImpUtil::putIso8601TimeValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::Time&        value,
                                            const BerEncoderOptions *options)
{
    return Iso8601Util::putTimeValue(streamBuf, value, options);
}

// 'bdlt::Time' Decoding

inline
int BerUtil_TimeImpUtil::detectTimeTzEncoding(TimeTzEncoding::Value *encoding,
                                              int                    length,
                                              unsigned char          firstByte)
{
    BSLMF_ASSERT(k_MAX_COMPACT_BINARY_TIME_LENGTH <
                 k_MIN_COMPACT_BINARY_TIMETZ_LENGTH);

    BSLMF_ASSERT(k_MIN_COMPACT_BINARY_TIMETZ_LENGTH <=
                 k_MAX_COMPACT_BINARY_TIMETZ_LENGTH);

    if (k_MAX_COMPACT_BINARY_TIME_LENGTH >= length) {
        *encoding = TimeTzEncoding::e_COMPACT_BINARY_TIME;
        return 0;                                                     // RETURN
    }

    if (k_MAX_COMPACT_BINARY_TIMETZ_LENGTH >= length) {
        *encoding = TimeTzEncoding::e_COMPACT_BINARY_TIMETZ;
        return 0;                                                     // RETURN
    }

    BSLS_ASSERT(k_MAX_COMPACT_BINARY_TIMETZ_LENGTH < length);

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinary(firstByte)) {
        *encoding = TimeTzEncoding::e_EXTENDED_BINARY_TIMETZ;
        return 0;                                                     // RETURN
    }

    *encoding = TimeTzEncoding::e_ISO8601_TIMETZ;
    return 0;
}

inline
int BerUtil_TimeImpUtil::getIso8601TimeTzValue(bdlt::TimeTz   *value,
                                               bsl::streambuf *streamBuf,
                                               int             length)
{
    return Iso8601Util::getTimeTzValue(value, streamBuf, length);
}

// 'bdlt::TimeTz' Encoding

inline
BerUtil_TimeTzEncoding::Value BerUtil_TimeImpUtil::selectTimeTzEncoding(
                                              const bdlt::TimeTz&      value,
                                              const BerEncoderOptions *options)
{
    if (ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(value,
                                                              options)) {
        return TimeTzEncoding::e_EXTENDED_BINARY_TIMETZ;              // RETURN
    }

    if (ExtendedBinaryEncodingUtil::useBinaryEncoding(options) &&
        (0 == value.offset())) {
        return TimeTzEncoding::e_COMPACT_BINARY_TIME;                 // RETURN
    }

    if (ExtendedBinaryEncodingUtil::useBinaryEncoding(options)) {
        return TimeTzEncoding::e_COMPACT_BINARY_TIMETZ;               // RETURN
    }

    return TimeTzEncoding::e_ISO8601_TIMETZ;
}

inline
int BerUtil_TimeImpUtil::putIso8601TimeTzValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::TimeTz&      value,
                                            const BerEncoderOptions *options)
{
    return Iso8601Util::putTimeTzValue(streamBuf, value, options);
}

// Variant Decoding

inline
int BerUtil_TimeImpUtil::detectTimeOrTimeTzEncoding(
                                        TimeOrTimeTzEncoding::Value *encoding,
                                        int                          length,
                                        unsigned char                firstByte)
{
    if (k_MAX_COMPACT_BINARY_TIME_LENGTH >= length) {
        *encoding = TimeOrTimeTzEncoding::e_COMPACT_BINARY_TIME;
        return 0;                                                     // RETURN
    }

    if (k_MAX_COMPACT_BINARY_TIMETZ_LENGTH >= length) {
        *encoding = TimeOrTimeTzEncoding::e_COMPACT_BINARY_TIMETZ;
        return 0;                                                     // RETURN
    }

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinaryWithoutTimezone(firstByte)) {
        *encoding = TimeOrTimeTzEncoding::e_EXTENDED_BINARY_TIME;
        return 0;                                                     // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinaryWithTimezone(firstByte)) {
        *encoding = TimeOrTimeTzEncoding::e_EXTENDED_BINARY_TIMETZ;
        return 0;                                                     // RETURN
    }

    if (k_MAX_ISO8601_TIME_LENGTH >= length) {
        *encoding = TimeOrTimeTzEncoding::e_ISO8601_TIME;
        return 0;                                                     // RETURN
    }

    *encoding = TimeOrTimeTzEncoding::e_ISO8601_TIMETZ;
    return 0;
}

inline
int BerUtil_TimeImpUtil::getIso8601TimeValue(TimeOrTimeTz   *value,
                                             bsl::streambuf *streamBuf,
                                             int             length)
{
    value->createInPlace<bdlt::Time>();
    return getIso8601TimeValue(&value->the<bdlt::Time>(), streamBuf, length);
}

inline
int BerUtil_TimeImpUtil::getIso8601TimeTzValue(TimeOrTimeTz   *value,
                                               bsl::streambuf *streamBuf,
                                               int             length)
{
    value->createInPlace<bdlt::TimeTz>();
    return getIso8601TimeTzValue(
        &value->the<bdlt::TimeTz>(), streamBuf, length);
}

inline
int BerUtil_TimeImpUtil::getCompactBinaryTimeValue(TimeOrTimeTz   *value,
                                                   bsl::streambuf *streamBuf,
                                                   int             length)
{
    value->createInPlace<bdlt::Time>();
    return getCompactBinaryTimeValue(
        &value->the<bdlt::Time>(), streamBuf, length);
}

inline
int BerUtil_TimeImpUtil::getCompactBinaryTimeTzValue(TimeOrTimeTz   *value,
                                                     bsl::streambuf *streamBuf,
                                                     int             length)
{
    value->createInPlace<bdlt::TimeTz>();
    return getCompactBinaryTimeTzValue(
        &value->the<bdlt::TimeTz>(), streamBuf, length);
}

inline
int BerUtil_TimeImpUtil::getExtendedBinaryTimeValue(TimeOrTimeTz   *value,
                                                    bsl::streambuf *streamBuf,
                                                    int             length)
{
    value->createInPlace<bdlt::Time>();
    return getExtendedBinaryTimeValue(
        &value->the<bdlt::Time>(), streamBuf, length);
}

inline
int BerUtil_TimeImpUtil::getExtendedBinaryTimeTzValue(
                                                     TimeOrTimeTz   *value,
                                                     bsl::streambuf *streamBuf,
                                                     int             length)
{
    value->createInPlace<bdlt::TimeTz>();
    return getExtendedBinaryTimeTzValue(
        &value->the<bdlt::TimeTz>(), streamBuf, length);
}

// CLASS METHODS

// Utilities

inline
void BerUtil_TimeImpUtil::timeToMillisecondsSinceMidnight(
                                  int               *millisecondsSinceMidnight,
                                  const bdlt::Time&  time)
{
    const bdlt::Time defaultTime;
    *millisecondsSinceMidnight =
        static_cast<int>((time - defaultTime).totalMilliseconds());
}

inline
void BerUtil_TimeImpUtil::timeToMicrosecondsSinceMidnight(
                                 bsls::Types::Int64 *millisecondsSinceMidnight,
                                 const bdlt::Time&   time)
{
    typedef bdlt::TimeUnitRatio Ratio;
    typedef bsls::Types::Int64  Int64;

    *millisecondsSinceMidnight =
        static_cast<Int64>(time.hour()) * Ratio::k_MICROSECONDS_PER_HOUR +
        static_cast<Int64>(time.minute()) * Ratio::k_MICROSECONDS_PER_MINUTE +
        static_cast<Int64>(time.second()) * Ratio::k_MICROSECONDS_PER_SECOND +
        static_cast<Int64>(time.millisecond()) *
            Ratio::k_MICROSECONDS_PER_MILLISECOND +
        static_cast<Int64>(time.microsecond());
}

inline
int BerUtil_TimeImpUtil::millisecondsSinceMidnightToTime(
                                         bdlt::Time *time,
                                         int         millisecondsSinceMidnight)
{
    typedef bdlt::TimeUnitRatio Ratio;

    static const int k_MIN_NUM_MILLISECONDS = 0;
    static const int k_MAX_NUM_MILLISECONDS =
        24 * Ratio::k_MILLISECONDS_PER_HOUR;

    if (k_MIN_NUM_MILLISECONDS > millisecondsSinceMidnight) {
        return -1;                                                    // RETURN
    }

    if (k_MAX_NUM_MILLISECONDS < millisecondsSinceMidnight) {
        return -1;                                                    // RETURN
    }

    const int serialTime = millisecondsSinceMidnight;

    const int hour = serialTime / Ratio::k_MILLISECONDS_PER_HOUR_32;

    const int minute =
        (serialTime - hour * Ratio::k_MILLISECONDS_PER_HOUR_32) /
        Ratio::k_MILLISECONDS_PER_MINUTE_32;

    const int second = (serialTime - hour * Ratio::k_MILLISECONDS_PER_HOUR_32 -
                        minute * Ratio::k_MILLISECONDS_PER_MINUTE_32) /
                       Ratio::k_MILLISECONDS_PER_SECOND_32;

    const int millisecond =
        (serialTime - hour * Ratio::k_MILLISECONDS_PER_HOUR_32 -
         minute * Ratio::k_MILLISECONDS_PER_MINUTE_32 -
         second * Ratio::k_MILLISECONDS_PER_SECOND_32);

    time->setTime(hour, minute, second, millisecond);
    return 0;
}

inline
int BerUtil_TimeImpUtil::microsecondsSinceMidnightToTime(
                                 bdlt::Time         *time,
                                 bsls::Types::Int64  microsecondsSinceMidnight)
{
    typedef bdlt::TimeUnitRatio Ratio;

    static const bsls::Types::Int64 k_MIN_NUM_MICROSECONDS = 0;
    static const bsls::Types::Int64 k_MAX_NUM_MICROSECONDS =
        24 * Ratio::k_MICROSECONDS_PER_HOUR;

    if (k_MIN_NUM_MICROSECONDS > microsecondsSinceMidnight) {
        return -1;                                                    // RETURN
    }

    if (k_MAX_NUM_MICROSECONDS < microsecondsSinceMidnight) {
        return -1;                                                    // RETURN
    }

    const bsls::Types::Int64 serialTime = microsecondsSinceMidnight;

    const int hour =
        static_cast<int>(serialTime / Ratio::k_MICROSECONDS_PER_HOUR);

    const int minute =
        static_cast<int>((serialTime - hour * Ratio::k_MICROSECONDS_PER_HOUR) /
                         Ratio::k_MICROSECONDS_PER_MINUTE);

    const int second =
        static_cast<int>((serialTime - hour * Ratio::k_MICROSECONDS_PER_HOUR -
                          minute * Ratio::k_MICROSECONDS_PER_MINUTE) /
                         Ratio::k_MICROSECONDS_PER_SECOND);

    const int millisecond =
        static_cast<int>((serialTime - hour * Ratio::k_MICROSECONDS_PER_HOUR -
                          minute * Ratio::k_MICROSECONDS_PER_MINUTE -
                          second * Ratio::k_MICROSECONDS_PER_SECOND) /
                         Ratio::k_MICROSECONDS_PER_MILLISECOND);

    const int microsecond = static_cast<int>(
        (serialTime - hour * Ratio::k_MICROSECONDS_PER_HOUR -
         minute * Ratio::k_MICROSECONDS_PER_MINUTE -
         second * Ratio::k_MICROSECONDS_PER_SECOND -
         millisecond * Ratio::k_MICROSECONDS_PER_MILLISECOND));

    time->setTime(hour, minute, second, millisecond, microsecond);
    return 0;
}

// 'bdlt::Time' Decoding

inline
int BerUtil_TimeImpUtil::getTimeValue(bdlt::Time     *value,
                                      bsl::streambuf *streamBuf,
                                      int             length)
{
    char firstByte;
    if (0 != StreambufUtil::peekChar(&firstByte, streamBuf)) {
        return -1;                                                    // RETURN
    }

    TimeEncoding::Value encoding;
    int rc = detectTimeEncoding(&encoding, length, firstByte);

    if (0 != rc) {
        return -1;                                                    // RETURN
    }

    switch (encoding) {
      case TimeEncoding::e_ISO8601_TIME: {
        return getIso8601TimeValue(value, streamBuf, length);         // RETURN
      } break;
      case TimeEncoding::e_COMPACT_BINARY_TIME: {
        return getCompactBinaryTimeValue(value, streamBuf, length);   // RETURN
      } break;
      case TimeEncoding::e_EXTENDED_BINARY_TIME: {
        return getExtendedBinaryTimeValue(value, streamBuf, length);  // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// 'bdlt::Time' Encoding

inline
int BerUtil_TimeImpUtil::putTimeValue(bsl::streambuf          *streamBuf,
                                      const bdlt::Time&        value,
                                      const BerEncoderOptions *options)
{
    switch (selectTimeEncoding(value, options)) {
      case TimeEncoding::e_ISO8601_TIME: {
        return putIso8601TimeValue(streamBuf, value, options);        // RETURN
      } break;
      case TimeEncoding::e_COMPACT_BINARY_TIME: {
        return putCompactBinaryTimeValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
      case TimeEncoding::e_EXTENDED_BINARY_TIME: {
        return putExtendedBinaryTimeValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// 'bdlt::TimeTz' Decoding

inline
int BerUtil_TimeImpUtil::getTimeTzValue(bdlt::TimeTz   *value,
                                        bsl::streambuf *streamBuf,
                                        int             length)
{
    char firstByte;
    if (0 != StreambufUtil::peekChar(&firstByte, streamBuf)) {
        return -1;                                                    // RETURN
    }

    TimeTzEncoding::Value encoding;
    int rc = detectTimeTzEncoding(&encoding, length, firstByte);

    if (0 != rc) {
        return -1;                                                    // RETURN
    }

    switch (encoding) {
      case TimeTzEncoding::e_ISO8601_TIMETZ: {
        return getIso8601TimeTzValue(value, streamBuf, length);       // RETURN
      } break;
      case TimeTzEncoding::e_COMPACT_BINARY_TIME: {
        return getCompactBinaryTimeValue(value, streamBuf, length);   // RETURN
      } break;
      case TimeTzEncoding::e_COMPACT_BINARY_TIMETZ: {
        return getCompactBinaryTimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case TimeTzEncoding::e_EXTENDED_BINARY_TIMETZ: {
        return getExtendedBinaryTimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// 'bdlt::TimeTz' Encoding

inline
int BerUtil_TimeImpUtil::putTimeTzValue(bsl::streambuf          *streamBuf,
                                        const bdlt::TimeTz&      value,
                                        const BerEncoderOptions *options)
{
    switch (selectTimeTzEncoding(value, options)) {
      case TimeTzEncoding::e_ISO8601_TIMETZ: {
        return putIso8601TimeTzValue(streamBuf, value, options);      // RETURN
      } break;
      case TimeTzEncoding::e_COMPACT_BINARY_TIME: {
        return putCompactBinaryTimeValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
      case TimeTzEncoding::e_COMPACT_BINARY_TIMETZ: {
        return putCompactBinaryTimeTzValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
      case TimeTzEncoding::e_EXTENDED_BINARY_TIMETZ: {
        return putExtendedBinaryTimeTzValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// Variant Decoding

inline
int BerUtil_TimeImpUtil::getTimeOrTimeTzValue(TimeOrTimeTz   *value,
                                              bsl::streambuf *streamBuf,
                                              int             length)
{
    char firstByte;
    if (0 != StreambufUtil::peekChar(&firstByte, streamBuf)) {
        return -1;                                                    // RETURN
    }

    TimeOrTimeTzEncoding::Value encoding;
    int rc = detectTimeOrTimeTzEncoding(&encoding, length, firstByte);

    if (0 != rc) {
        return -1;                                                    // RETURN
    }

    switch (encoding) {
      case TimeOrTimeTzEncoding::e_ISO8601_TIME: {
        return getIso8601TimeValue(value, streamBuf, length);         // RETURN
      } break;
      case TimeOrTimeTzEncoding::e_ISO8601_TIMETZ: {
        return getIso8601TimeTzValue(value, streamBuf, length);       // RETURN
      } break;
      case TimeOrTimeTzEncoding::e_COMPACT_BINARY_TIME: {
        return getCompactBinaryTimeValue(value, streamBuf, length);   // RETURN
      } break;
      case TimeOrTimeTzEncoding::e_COMPACT_BINARY_TIMETZ: {
        return getCompactBinaryTimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case TimeOrTimeTzEncoding::e_EXTENDED_BINARY_TIME: {
        return getExtendedBinaryTimeValue(value, streamBuf, length);  // RETURN
      } break;
      case TimeOrTimeTzEncoding::e_EXTENDED_BINARY_TIMETZ: {
        return getExtendedBinaryTimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

                       // ------------------------------
                       // struct BerUtil_DatetimeImpUtil
                       // ------------------------------

// PRIVATE CLASS METHODS

// 'bdlt::Datetime' Decoding

inline
int BerUtil_DatetimeImpUtil::detectDatetimeEncoding(
                                            DatetimeEncoding::Value *encoding,
                                            int                      length,
                                            unsigned char            firstByte)
{
    if (k_MAX_COMPACT_BINARY_DATETIME_LENGTH >= length) {
        *encoding = DatetimeEncoding::e_COMPACT_BINARY_DATETIME;
        return 0;                                                     // RETURN
    }

    if (k_MAX_COMPACT_BINARY_DATETIMETZ_LENGTH >= length) {
        *encoding = DatetimeEncoding::e_COMPACT_BINARY_DATETIMETZ;
        return 0;                                                     // RETURN
    }

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinary(firstByte)) {
        *encoding = DatetimeEncoding::e_EXTENDED_BINARY_DATETIME;
        return 0;                                                     // RETURN
    }

    *encoding = DatetimeEncoding::e_ISO8601_DATETIME;
    return 0;
}

// 'bdlt::Datetime' Encoding

inline
BerUtil_DatetimeEncoding::Value
BerUtil_DatetimeImpUtil::selectDatetimeEncoding(
                                 bsls::Types::Int64      *serialDatetime,
                                 int                     *serialDatetimeLength,
                                 const bdlt::Datetime&    value,
                                 const BerEncoderOptions *options)
{
    if (ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(value,
                                                              options)) {
        return DatetimeEncoding::e_EXTENDED_BINARY_DATETIME;          // RETURN
    }

    if (ExtendedBinaryEncodingUtil::useBinaryEncoding(options)) {
        bsls::Types::Int64 serialDatetimeValue;
        datetimeToMillisecondsSinceEpoch(&serialDatetimeValue, value);

        const int serialDatetimeLengthValue =
            IntegerUtil::getNumOctetsToStream(serialDatetimeValue);

        if (k_MIN_COMPACT_BINARY_DATETIMETZ_LENGTH <=
            serialDatetimeLengthValue) {
            *serialDatetime       = serialDatetimeValue;
            *serialDatetimeLength = serialDatetimeLengthValue;
            return DatetimeEncoding::e_COMPACT_BINARY_DATETIMETZ;     // RETURN
        }

        *serialDatetime       = serialDatetimeValue;
        *serialDatetimeLength = serialDatetimeLengthValue;
        return DatetimeEncoding::e_COMPACT_BINARY_DATETIME;           // RETURN
    }

    return DatetimeEncoding::e_ISO8601_DATETIME;
}

// 'bdlt::DatetimeTz' Decoding

inline
int BerUtil_DatetimeImpUtil::detectDatetimeTzEncoding(
                                          DatetimeTzEncoding::Value *encoding,
                                          int                        length,
                                          unsigned char              firstByte)
{
    if (k_MAX_COMPACT_BINARY_DATETIME_LENGTH >= length) {
        *encoding = DatetimeTzEncoding::e_COMPACT_BINARY_DATETIME;
        return 0;                                                     // RETURN
    }

    if (k_MAX_COMPACT_BINARY_DATETIMETZ_LENGTH >= length) {
        *encoding = DatetimeTzEncoding::e_COMPACT_BINARY_DATETIMETZ;
        return 0;                                                     // RETURN
    }

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinary(firstByte)) {
        *encoding = DatetimeTzEncoding::e_EXTENDED_BINARY_DATETIMETZ;
        return 0;                                                     // RETURN
    }

    *encoding = DatetimeTzEncoding::e_ISO8601_DATETIMETZ;
    return 0;
}

// 'bdlt::DatetimeTz' Encoding

inline
BerUtil_DatetimeTzEncoding::Value
BerUtil_DatetimeImpUtil::selectDatetimeTzEncoding(
                                       bsls::Types::Int64      *serialDatetime,
                                       int                     *length,
                                       const bdlt::DatetimeTz&  value,
                                       const BerEncoderOptions *options)
{
    if (ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding(value,
                                                              options)) {
        return DatetimeTzEncoding::e_EXTENDED_BINARY_DATETIMETZ;      // RETURN
    }

    if (ExtendedBinaryEncodingUtil::useBinaryEncoding(options)) {
        bsls::Types::Int64 serialDatetimeValue;
        datetimeToMillisecondsSinceEpoch(&serialDatetimeValue,
                                         value.localDatetime());

        const int lengthValue =
            IntegerUtil::getNumOctetsToStream(serialDatetimeValue);

        if (0 == value.offset() &&
            k_MIN_COMPACT_BINARY_DATETIMETZ_LENGTH > lengthValue) {
            *serialDatetime = serialDatetimeValue;
            *length         = lengthValue;
            return DatetimeTzEncoding::e_COMPACT_BINARY_DATETIME;     // RETURN
        }

        *serialDatetime = serialDatetimeValue;
        *length         = lengthValue;
        return DatetimeTzEncoding::e_COMPACT_BINARY_DATETIMETZ;       // RETURN
    }

    return DatetimeTzEncoding::e_ISO8601_DATETIMETZ;
}

// Variant Decoding

inline
int BerUtil_DatetimeImpUtil::detectDatetimeOrDatetimeTzEncoding(
                                DatetimeOrDatetimeTzEncoding::Value *encoding,
                                int                                  length,
                                unsigned char                        firstByte)
{
    BSLS_ASSERT(0 < length);

    if (k_MAX_COMPACT_BINARY_DATETIME_LENGTH >= length) {
        *encoding = DatetimeOrDatetimeTzEncoding::e_COMPACT_BINARY_DATETIME;
        return 0;                                                     // RETURN
    }

    if (k_MAX_COMPACT_BINARY_DATETIMETZ_LENGTH >= length) {
        *encoding = DatetimeOrDatetimeTzEncoding::e_COMPACT_BINARY_DATETIMETZ;
        return 0;                                                     // RETURN
    }

    if (DateAndTimeHeaderUtil::isReserved(firstByte)) {
        return -1;                                                    // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinaryWithoutTimezone(firstByte)) {
        *encoding = DatetimeOrDatetimeTzEncoding::e_EXTENDED_BINARY_DATETIME;
        return 0;                                                     // RETURN
    }

    if (DateAndTimeHeaderUtil::isExtendedBinaryWithTimezone(firstByte)) {
        *encoding = DatetimeOrDatetimeTzEncoding::e_EXTENDED_BINARY_DATETIMETZ;
        return 0;                                                     // RETURN
    }

    if (k_MAX_ISO8601_DATETIME_LENGTH >= length) {
        *encoding = DatetimeOrDatetimeTzEncoding::e_ISO8601_DATETIME;
        return 0;                                                     // RETURN
    }

    *encoding = DatetimeOrDatetimeTzEncoding::e_ISO8601_DATETIMETZ;
    return 0;
}

inline
int BerUtil_DatetimeImpUtil::getIso8601DatetimeValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length)
{
    value->createInPlace<bdlt::Datetime>();
    return getIso8601DatetimeValue(
        &value->the<bdlt::Datetime>(), streamBuf, length);
}

inline
int BerUtil_DatetimeImpUtil::getIso8601DatetimeTzValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length)
{
    value->createInPlace<bdlt::DatetimeTz>();
    return getIso8601DatetimeTzValue(
        &value->the<bdlt::DatetimeTz>(), streamBuf, length);
}

inline
int BerUtil_DatetimeImpUtil::getCompactBinaryDatetimeValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length)
{
    value->createInPlace<bdlt::Datetime>();
    return getCompactBinaryDatetimeValue(
        &value->the<bdlt::Datetime>(), streamBuf, length);
}

inline
int BerUtil_DatetimeImpUtil::getCompactBinaryDatetimeTzValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length)
{
    value->createInPlace<bdlt::DatetimeTz>();
    return getCompactBinaryDatetimeTzValue(
        &value->the<bdlt::DatetimeTz>(), streamBuf, length);
}

inline
int BerUtil_DatetimeImpUtil::getExtendedBinaryDatetimeValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length)
{
    value->createInPlace<bdlt::Datetime>();
    return getExtendedBinaryDatetimeValue(
        &value->the<bdlt::Datetime>(), streamBuf, length);
}

inline
int BerUtil_DatetimeImpUtil::getExtendedBinaryDatetimeTzValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length)
{
    value->createInPlace<bdlt::DatetimeTz>();
    return getExtendedBinaryDatetimeTzValue(
        &value->the<bdlt::DatetimeTz>(), streamBuf, length);
}

// CLASS METHODS

// 'bdlt::Datetime' Encoding

inline
int BerUtil_DatetimeImpUtil::putDatetimeValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::Datetime&    value,
                                            const BerEncoderOptions *options)
{
    const bdlt::Time& time = value.time();
    bdlt::Date        date = value.date();

    if (0 != date.addDaysIfValid(0) ||
        !bdlt::Time::isValid(
            time.hour(), time.minute(), time.second(), time.millisecond())) {
        return -1;                                                    // RETURN
    }

    bsls::Types::Int64 serialDatetime;
    int                length;

    switch (selectDatetimeEncoding(&serialDatetime, &length, value, options)) {
      case DatetimeEncoding::e_ISO8601_DATETIME: {
        return putIso8601DatetimeValue(streamBuf, value, options);    // RETURN
      } break;
      case DatetimeEncoding::e_COMPACT_BINARY_DATETIME: {
        return putCompactBinaryDatetimeValue(
            streamBuf, serialDatetime, length, options);              // RETURN
      } break;
      case DatetimeEncoding::e_COMPACT_BINARY_DATETIMETZ: {
        return putCompactBinaryDatetimeTzValue(
            streamBuf, serialDatetime, length, options);              // RETURN
      } break;
      case DatetimeEncoding::e_EXTENDED_BINARY_DATETIME: {
        return putExtendedBinaryDatetimeValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// 'bdlt::Datetime' Decoding

inline
int BerUtil_DatetimeImpUtil::getDatetimeValue(bdlt::Datetime *value,
                                              bsl::streambuf *streamBuf,
                                              int             length)
{
    char firstByte;
    if (0 != StreambufUtil::peekChar(&firstByte, streamBuf)) {
        return -1;                                                    // RETURN
    }

    DatetimeEncoding::Value encoding;
    int rc = detectDatetimeEncoding(&encoding, length, firstByte);

    if (0 != rc) {
        return -1;                                                    // RETURN
    }

    switch (encoding) {
      case DatetimeEncoding::e_ISO8601_DATETIME: {
        return getIso8601DatetimeValue(value, streamBuf, length);     // RETURN
      } break;
      case DatetimeEncoding::e_COMPACT_BINARY_DATETIME: {
        return getCompactBinaryDatetimeValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case DatetimeEncoding::e_COMPACT_BINARY_DATETIMETZ: {
        return getCompactBinaryDatetimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case DatetimeEncoding::e_EXTENDED_BINARY_DATETIME: {
        return getExtendedBinaryDatetimeValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// 'bdlt::DatetimeTz' Encoding

inline
int BerUtil_DatetimeImpUtil::putDatetimeTzValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::DatetimeTz&  value,
                                            const BerEncoderOptions *options)
{
    // Applications can create invalid 'bdlt::DatetimeTz' objects in optimized
    // build modes.  As this function assumes that 'value' is valid, it is
    // possible to encode an invalid 'bdlt::DatetimeTz' without returning an
    // error.  Decoding the corresponding output can result in hard-to-trace
    // decoding errors.  So to identify such errors early, we return an error
    // if 'value' is not valid.

    const bdlt::DateTz& dateTz = value.dateTz();
    const bdlt::TimeTz& timeTz = value.timeTz();
    if (0 != dateTz.localDate().addDaysIfValid(0) ||
        !bdlt::DateTz::isValid(dateTz.localDate(), dateTz.offset()) ||
        !bdlt::TimeTz::isValid(timeTz.utcTime(), timeTz.offset())) {
        return -1;                                                    // RETURN
    }

    bsls::Types::Int64 serialDatetime;
    int                serialDatetimeLength;

    switch (selectDatetimeTzEncoding(
        &serialDatetime, &serialDatetimeLength, value, options)) {
      case DatetimeTzEncoding::e_ISO8601_DATETIMETZ: {
        return putIso8601DatetimeTzValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
      case DatetimeTzEncoding::e_COMPACT_BINARY_DATETIME: {
        return putCompactBinaryDatetimeValue(
            streamBuf, serialDatetime, serialDatetimeLength, options);
                                                                      // RETURN
      } break;
      case DatetimeTzEncoding::e_COMPACT_BINARY_DATETIMETZ: {
        return putCompactBinaryDatetimeTzValue(streamBuf,
                                               value.offset(),
                                               serialDatetime,
                                               serialDatetimeLength,
                                               options);
                                                                      // RETURN
      } break;
      case DatetimeTzEncoding::e_EXTENDED_BINARY_DATETIMETZ: {
        return putExtendedBinaryDatetimeTzValue(streamBuf, value, options);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// 'bdlt::DatetimeTz' Decoding

inline
int BerUtil_DatetimeImpUtil::getDatetimeTzValue(bdlt::DatetimeTz *value,
                                                bsl::streambuf   *streamBuf,
                                                int               length)
{
    char firstByte;
    if (0 != StreambufUtil::peekChar(&firstByte, streamBuf)) {
        return -1;                                                    // RETURN
    }

    DatetimeTzEncoding::Value encoding;
    int rc = detectDatetimeTzEncoding(&encoding, length, firstByte);

    if (0 != rc) {
        return -1;                                                    // RETURN
    }

    switch (encoding) {
      case DatetimeTzEncoding::e_ISO8601_DATETIMETZ: {
        return getIso8601DatetimeTzValue(value, streamBuf, length);   // RETURN
      } break;
      case DatetimeTzEncoding::e_COMPACT_BINARY_DATETIME: {
        return getCompactBinaryDatetimeValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case DatetimeTzEncoding::e_COMPACT_BINARY_DATETIMETZ: {
        return getCompactBinaryDatetimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case DatetimeTzEncoding::e_EXTENDED_BINARY_DATETIMETZ: {
        return getExtendedBinaryDatetimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

// Variant Decoding

inline
int BerUtil_DatetimeImpUtil::getDatetimeOrDatetimeTzValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length)
{
    char firstByte;
    if (0 != StreambufUtil::peekChar(&firstByte, streamBuf)) {
        return -1;                                                    // RETURN
    }

    DatetimeOrDatetimeTzEncoding::Value encoding;
    int rc = detectDatetimeOrDatetimeTzEncoding(&encoding, length, firstByte);

    if (0 != rc) {
        return -1;                                                    // RETURN
    }

    switch (encoding) {
      case DatetimeOrDatetimeTzEncoding::e_ISO8601_DATETIME: {
        return getIso8601DatetimeValue(value, streamBuf, length);     // RETURN
      } break;
      case DatetimeOrDatetimeTzEncoding::e_ISO8601_DATETIMETZ: {
        return getIso8601DatetimeTzValue(value, streamBuf, length);   // RETURN
      } break;
      case DatetimeOrDatetimeTzEncoding::e_COMPACT_BINARY_DATETIME: {
        return getCompactBinaryDatetimeValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case DatetimeOrDatetimeTzEncoding::e_COMPACT_BINARY_DATETIMETZ: {
        return getCompactBinaryDatetimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case DatetimeOrDatetimeTzEncoding::e_EXTENDED_BINARY_DATETIME: {
        return getExtendedBinaryDatetimeValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
      case DatetimeOrDatetimeTzEncoding::e_EXTENDED_BINARY_DATETIMETZ: {
        return getExtendedBinaryDatetimeTzValue(value, streamBuf, length);
                                                                      // RETURN
      } break;
    }

    BSLS_ASSERT_OPT(!"Reachable");
#if BSLA_UNREACHABLE_IS_ACTIVE
    BSLA_UNREACHABLE;
#else
    return -1;                                                        // RETURN
#endif
}

                       // ------------------------------
                       // struct BerUtil_GetValueImpUtil
                       // ------------------------------

// CLASS METHODS
template <typename TYPE>
int BerUtil_GetValueImpUtil::getValue(TYPE                     *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return IntegerUtil::getIntegerValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bool                     *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return BooleanUtil::getBoolValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(char                     *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return CharacterUtil::getCharValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(unsigned char            *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return CharacterUtil::getUnsignedCharValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(signed char              *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return CharacterUtil::getSignedCharValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(float                    *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return FloatingPointUtil::getFloatValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(double                   *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return FloatingPointUtil::getDoubleValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bdldfp::Decimal64        *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return FloatingPointUtil::getDecimal64Value(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bsl::string              *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&  options)
{
    return StringUtil::getStringValue(value, streamBuf, length, options);
}

inline
int BerUtil_GetValueImpUtil::getValue(bdlt::Date               *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return DateUtil::getDateValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bdlt::DateTz             *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return DateUtil::getDateTzValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(DateOrDateTz             *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return DateUtil::getDateOrDateTzValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bdlt::Datetime           *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return DatetimeUtil::getDatetimeValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bdlt::DatetimeTz         *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return DatetimeUtil::getDatetimeTzValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(DatetimeOrDatetimeTz     *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return DatetimeUtil::getDatetimeOrDatetimeTzValue(
        value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bdlt::Time               *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return TimeUtil::getTimeValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(bdlt::TimeTz             *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return TimeUtil::getTimeTzValue(value, streamBuf, length);
}

inline
int BerUtil_GetValueImpUtil::getValue(TimeOrTimeTz             *value,
                                      bsl::streambuf           *streamBuf,
                                      int                       length,
                                      const BerDecoderOptions&)
{
    return TimeUtil::getTimeOrTimeTzValue(value, streamBuf, length);
}

                       // ------------------------------
                       // struct BerUtil_PutValueImpUtil
                       // ------------------------------

// CLASS METHODS
template <class TYPE>
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const TYPE&              value,
                                      const BerEncoderOptions *)
{
    return IntegerUtil::putIntegerValue(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      bool                     value,
                                      const BerEncoderOptions *)
{
    return BooleanUtil::putBoolValue(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      char                     value,
                                      const BerEncoderOptions *)
{
    return CharacterUtil::putCharValue(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      unsigned char            value,
                                      const BerEncoderOptions *)
{
    return CharacterUtil::putUnsignedCharValue(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      signed char              value,
                                      const BerEncoderOptions *)
{
    return CharacterUtil::putSignedCharValue(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      float                    value,
                                      const BerEncoderOptions *options)
{
    return FloatingPointUtil::putFloatValue(streamBuf,
                                            value,
                                            options);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      double                   value,
                                      const BerEncoderOptions *options)
{
    return FloatingPointUtil::putDoubleValue(streamBuf,
                                             value,
                                             options);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      bdldfp::Decimal64        value,
                                      const BerEncoderOptions *)
{
    return FloatingPointUtil::putDecimal64Value(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const bsl::string&       value,
                                      const BerEncoderOptions *)
{
    return StringUtil::putStringValue(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf           *streamBuf,
                                      const bslstl::StringRef&  value,
                                      const BerEncoderOptions  *)
{
    return StringUtil::putStringRefValue(streamBuf, value);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const bdlt::Date&        value,
                                      const BerEncoderOptions *options)
{
    return DateUtil::putDateValue(streamBuf, value, options);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const bdlt::DateTz&      value,
                                      const BerEncoderOptions *options)
{
    return DateUtil::putDateTzValue(streamBuf, value, options);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const bdlt::Datetime&    value,
                                      const BerEncoderOptions *options)
{
    return DatetimeUtil::putDatetimeValue(streamBuf, value, options);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const bdlt::DatetimeTz&  value,
                                      const BerEncoderOptions *options)
{
    return DatetimeUtil::putDatetimeTzValue(streamBuf, value, options);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const bdlt::Time&        value,
                                      const BerEncoderOptions *options)
{
    return TimeUtil::putTimeValue(streamBuf, value, options);
}

inline
int BerUtil_PutValueImpUtil::putValue(bsl::streambuf          *streamBuf,
                                      const bdlt::TimeTz&      value,
                                      const BerEncoderOptions *options)
{
    return TimeUtil::putTimeTzValue(streamBuf, value, options);
}

                             // ------------------
                             // struct BerUtil_Imp
                             // ------------------

// CLASS METHODS
inline
int BerUtil_Imp::putStringValue(bsl::streambuf *streamBuf,
                                const char     *string,
                                int             stringLength)
{
    typedef BerUtil_StringImpUtil StringUtil;
    return StringUtil::putRawStringValue(streamBuf, string, stringLength);
}

}  // close package namespace
}  // close enterprise namespace

#endif

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