// 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.  For `bsl::string_view` and
// `bslstl::StringRef` types only encoding is supported.
//
// 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:
// * 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_string_view.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace balber {

                               // ==============
                               // 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.
struct BerUtil {

    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

    /// 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 getEndOfContentOctets(bsl::streambuf *streamBuf,
                                     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 getIdentifierOctets(
                                bsl::streambuf         *streamBuf,
                                BerConstants::TagClass *tagClass,
                                BerConstants::TagType  *tagType,
                                int                    *tagNumber,
                                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.
    static int getLength(bsl::streambuf *streamBuf,
                         int            *result,
                         int            *accumNumBytesConsumed);

    /// 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                       length,
                      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.
    template <typename TYPE>
    static int getValue(
                      bsl::streambuf           *streamBuf,
                      TYPE                     *value,
                      int                      *accumNumBytesConsumed,
                      const BerDecoderOptions&  options = BerDecoderOptions());

    /// 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 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.
    static int putIdentifierOctets(bsl::streambuf         *streamBuf,
                                   BerConstants::TagClass  tagClass,
                                   BerConstants::TagType   tagType,
                                   int                     tagNumber);

    /// 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 putIndefiniteLengthOctet(bsl::streambuf *streamBuf);

    /// 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`.
    static int putLength(bsl::streambuf *streamBuf, int length);

    /// 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`, `bsl::string_view`, `bslstl::StringRef`, and
    /// BDE date/time types are supported.
    template <typename TYPE>
    static int putValue(bsl::streambuf          *streamBuf,
                        const TYPE&              value,
                        const BerEncoderOptions *options = 0);
};

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

/// 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.
struct BerUtil_Constants {

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

                        // ============================
                        // 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.
struct BerUtil_StreambufUtil {

    // CLASS METHODS

    /// 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 peekChar(char *value, bsl::streambuf *streamBuf);

    /// 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 getChars(char           *buffer,
                        bsl::streambuf *streamBuf,
                        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.
    static int putChars(bsl::streambuf *streamBuf,
                        const char     *buffer,
                        int             bufferLength);
};

                      // ================================
                      // 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.
struct BerUtil_IdentifierImpUtil {

    // TYPES

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

  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

    /// 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 getIdentifierOctets(
                                 BerConstants::TagClass *tagClass,
                                 BerConstants::TagType  *tagType,
                                 int                    *tagNumber,
                                 int                    *accumNumBytesConsumed,
                                 bsl::streambuf         *streamBuf);

    /// 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.
    static int putIdentifierOctets(bsl::streambuf         *streamBuf,
                                   BerConstants::TagClass  tagClass,
                                   BerConstants::TagType   tagType,
                                   int                     tagNumber);
};

                      // ================================
                      // 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`.
struct BerUtil_RawIntegerImpUtil {

    // TYPES

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

    // CLASS METHODS

    /// 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`.
    template <class INTEGRAL_TYPE>
    static int putIntegerGivenLength(bsl::streambuf *streamBuf,
                                     INTEGRAL_TYPE   value,
                                     int             length);
};

                        // ============================
                        // 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.
struct BerUtil_LengthImpUtil {

    // TYPES

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

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

  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

    /// 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 getLength(int            *result,
                         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.
    static int getEndOfContentOctets(int            *accumNumBytesConsumed,
                                     bsl::streambuf *streamBuf);

    // Length Encoding Functions

    /// 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 putLength(bsl::streambuf *streamBuf, int length);

    /// 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 putIndefiniteLengthOctet(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.
    static int putEndOfContentOctets(bsl::streambuf *streamBuf);
};

                       // =============================
                       // 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.
struct BerUtil_BooleanImpUtil {

    // TYPES

    /// `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_LengthImpUtil LengthUtil;

    // CLASS METHODS

    // Decoding

    /// 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.
    static int getBoolValue(bool           *value,
                            bsl::streambuf *streamBuf,
                            int             length);

    // Encoding

    /// 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`.
    static int putBoolValue(bsl::streambuf *streamBuf, bool value);
};

                       // =============================
                       // 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.
struct BerUtil_IntegerImpUtil {

    // TYPES

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

    /// `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_LengthImpUtil LengthImpUtil;

    /// `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_RawIntegerImpUtil RawIntegerUtil;

    /// `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_StreambufUtil StreambufUtil;

    // CLASS DATA

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

    // CLASS METHODS

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

    /// 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.
    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 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.
    template <class INTEGRAL_TYPE>
    static int getIntegerValue(INTEGRAL_TYPE  *value,
                               bsl::streambuf *streamBuf,
                               int             length);

    /// 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.
    static int get40BitIntegerValue(bsls::Types::Int64 *value,
                                    bsl::streambuf     *streamBuf);

    /// 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.
    template <class INTEGRAL_TYPE>
    static int putIntegerValue(bsl::streambuf *streamBuf, INTEGRAL_TYPE 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)`.
    static int put40BitIntegerValue(bsl::streambuf     *streamBuf,
                                    bsls::Types::Int64  value);

    /// 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.
    template <class INTEGRAL_TYPE>
    static int putIntegerGivenLength(bsl::streambuf *streamBuf,
                                     INTEGRAL_TYPE   value,
                                     int             length);
};

                      // ===============================
                      // 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.
struct BerUtil_CharacterImpUtil {

    // TYPES

    /// `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_IntegerImpUtil IntegerUtil;

    /// `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_LengthImpUtil LengthUtil;

    // CLASS METHODS

    // Decoding

    /// 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 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.
    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);

    // Encoding

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

                    // ===================================
                    // 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
///
/// * **the floating point specification**:
///   > Refers to the 2008 revision of the IEE 754 Standard for
///   > Floating-Point Arithemtic.
struct BerUtil_FloatingPointImpUtil {

    // TYPES

    /// `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_IntegerImpUtil IntegerUtil;

    /// `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_LengthImpUtil LengthUtil;

  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

    /// 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 assembleDouble(double    *value,
                               long long  exponent,
                               long long  mantissa,
                               int        sign);

    /// 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 normalizeMantissaAndAdjustExp(long long *mantissa,
                                              int       *exponent,
                                              bool       denormalized);

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

  public:
    // CLASS METHODS

    // Decoding

    /// 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 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 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.
    static int getDecimal64Value(bdldfp::Decimal64 *value,
                                 bsl::streambuf    *streamBuf,
                                 int                length);

    // Encoding

    /// 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 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.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 putDoubleValue(bsl::streambuf          *streamBuf,
                              double                   value,
                              const BerEncoderOptions *options = 0);

    /// 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.
    static int putDecimal64Value(bsl::streambuf    *streamBuf,
                                 bdldfp::Decimal64  value);
};

                        // ============================
                        // 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.
struct BerUtil_StringImpUtil {

    // TYPES

    /// `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_LengthImpUtil LengthUtil;

  public:
    // CLASS METHODS

    // Utilities

    /// 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 putRawStringValue(bsl::streambuf *streamBuf,
                                 const char     *string,
                                 int             stringLength);

    /// 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.
    static int putChars(bsl::streambuf *streamBuf, char value, int numChars);

    // 'bsl::string' Decoding

    /// 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.
    static int getStringValue(bsl::string              *value,
                              bsl::streambuf           *streamBuf,
                              int                       length,
                              const BerDecoderOptions&  options);

    // 'bsl::string' Encoding

    /// 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.
    static int putStringValue(bsl::streambuf     *streamBuf,
                              const bsl::string&  value);

    // 'bsl::string_view' Encoding

    /// 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.
    static int putStringViewValue(bsl::streambuf          *streamBuf,
                                  const bsl::string_view&  value);

    // 'bslstl::StringRef' Encoding

    /// 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.
    static int putStringRefValue(bsl::streambuf           *streamBuf,
                                 const bslstl::StringRef&  value);
};

                       // =============================
                       // 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`.
struct BerUtil_Iso8601ImpUtil {

    // TYPES

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

  private:
    // PRIVATE CLASS METHODS

    /// 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 getValue(TYPE *value, bsl::streambuf *streamBuf, int length);

    /// 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`.
    template <class TYPE>
    static int putValue(bsl::streambuf          *streamBuf,
                        const TYPE&              value,
                        const BerEncoderOptions *options);

  public:
    // CLASS METHODS

    // Decoding

    /// 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 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 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 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.  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 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, 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 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 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 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 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 getTimeTzValue(bdlt::TimeTz   *value,
                              bsl::streambuf *streamBuf,
                              int             length);

    // Encoding

    /// 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 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`.  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 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);
};

                    // ====================================
                    // 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.
struct BerUtil_TimezoneOffsetImpUtil {

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

    /// 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 bool isValidTimezoneOffsetInMinutes(int value);

    /// Read from the specified `streamBuf` and load to the specified `value`
    /// of the time-zone offset.
    static int getTimezoneOffsetInMinutes(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 getTimezoneOffsetInMinutesIfValid(int            *value,
                                                 bsl::streambuf *streamBuf);

    /// 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`.
    static int putTimezoneOffsetInMinutes(bsl::streambuf *streamBuf,
                                          int             value);
};

                     // ==================================
                     // 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`.
struct BerUtil_DateAndTimeEncoding {

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

/// 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.
struct BerUtil_ExtendedBinaryEncodingUtil {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

    // CLASS METHODS

    /// 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 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 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`.
    static bool useBinaryEncoding(const BerEncoderOptions *options);
};

                    // ====================================
                    // 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.
struct BerUtil_DateAndTimeHeaderType {

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

/// 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.
class BerUtil_DateAndTimeHeader {

  public:
    // TYPES

    /// `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_DateAndTimeHeaderType Type;

    /// `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 BerUtil_TimezoneOffsetImpUtil TimezoneUtil;

  private:
    // DATA

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

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

  public:
    // CREATORS

    /// 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();

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

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

    // MANIPULATORS

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

    /// 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 makeNotExtendedBinary();

    /// 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 makeExtendedBinaryWithoutTimezone();

    /// 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`.
    void makeExtendedBinaryWithTimezone(int offset);

    // ACCESSORS

    /// 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 isExtendedBinary() const;

    /// Return `true` if the `type` attribute of this object is
    /// `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_WITH_TIMEZONE`, and `false` otherwise.
    bool isExtendedBinaryWithTimezone() const;

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

                  // =======================================
                  // 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.
struct BerUtil_DateAndTimeHeaderImpUtil {

    // TYPES

    /// `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_DateAndTimeHeader Header;

    /// `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_DateAndTimeHeaderType Type;

    /// `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_StreambufUtil StreambufUtil;

    /// `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 BerUtil_TimezoneOffsetImpUtil TimezoneUtil;

    // CLASS DATA

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

    // CLASS METHODS

    /// 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 isReserved(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 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 does not carry a time-zone offset value, and return
    /// `false` otherwise.
    static bool isExtendedBinaryWithoutTimezone(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 bool isExtendedBinaryWithTimezone(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 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`.  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 void detectType(Type::Value *type, unsigned char firstByte);

    /// 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, bsl::streambuf *streamBuf);

    /// 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 getValueIfNotReserved(Header        *value,
                                     unsigned char  headerByte0,
                                     unsigned char  headerByte1);

    /// 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, bsl::streambuf *streamBuf);

    /// 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 getValue(Header        *value,
                        unsigned char  headerByte0,
                        unsigned char  headerByte1);

    /// 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 putExtendedBinaryWithoutTimezoneValue(
                                                    bsl::streambuf *streamBuf);

    /// 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.
    static int putExtendedBinaryWithTimezoneValue(
                                      bsl::streambuf *streamBuf,
                                      int             timezoneOffsetInMinutes);
};

                        // ===========================
                        // 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.
struct BerUtil_DateEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

                       // =============================
                       // 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.
struct BerUtil_DateTzEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
struct BerUtil_DateOrDateTzEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
///
/// * **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.
struct BerUtil_DateImpUtil {

    // TYPES

    /// `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_DateAndTimeHeaderImpUtil DateAndTimeHeaderUtil;

    /// `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_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::DateTz` 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 decode to values of
    /// `bdlb::Variant2<bdlt::Date, bdlt::DateTz>` type.
    typedef BerUtil_DateOrDateTzEncoding DateOrDateTzEncoding;

    /// `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_IntegerImpUtil IntegerUtil;

    /// `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_Iso8601ImpUtil Iso8601Util;

    /// `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_LengthImpUtil LengthUtil;

    /// `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_StreambufUtil StreambufUtil;

    /// `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_StringImpUtil StringUtil;

    /// `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 BerUtil_TimezoneOffsetImpUtil TimezoneUtil;

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

    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

    /// 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 detectDateEncoding(DateEncoding::Value *encoding,
                                  int                  length,
                                  unsigned char        firstByte);

    /// 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 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 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 getCompactBinaryDateValue(bdlt::Date     *value,
                                         bsl::streambuf *streamBuf,
                                         int             length);

    // 'bdlt::Date' Encoding

    /// 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 DateEncoding::Value selectDateEncoding(
                                             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 putIso8601DateValue(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.
    static int putCompactBinaryDateValue(bsl::streambuf          *streamBuf,
                                         const bdlt::Date&        value,
                                         const BerEncoderOptions *options);

    // 'bdlt::DateTz' Decoding

    /// 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 detectDateTzEncoding(DateTzEncoding::Value *encoding,
                                    int                    length,
                                    unsigned char          firstByte);

    /// 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 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 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 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 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.
    static int getCompactBinaryDateTzValue(bdlt::DateTz   *value,
                                           bsl::streambuf *streamBuf,
                                           int             length);

    // 'bdlt::DateTz' Encoding

    /// 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 DateTzEncoding::Value selectDateTzEncoding(
                                             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 putIso8601DateTzValue(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 putCompactBinaryDateValue(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.
    static int putCompactBinaryDateTzValue(bsl::streambuf          *streamBuf,
                                           const bdlt::DateTz&      value,
                                           const BerEncoderOptions *options);

    // Variant Decoding

    /// 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 detectDateOrDateTzEncoding(
                                 DateOrDateTzEncoding::Value *encoding,
                                 int                          length,
                                 unsigned char                firstByte);

    /// 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 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 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 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 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 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 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.
    static int getCompactBinaryDateTzValue(DateOrDateTz   *value,
                                           bsl::streambuf *streamBuf,
                                           int             length);

  public:
    // CLASS METHODS

    // Utilities

    /// 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 void dateToDaysSinceEpoch(bsls::Types::Int64 *daysSinceEpoch,
                                     const bdlt::Date&   date);

    /// 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`.
    static int daysSinceEpochToDate(bdlt::Date         *date,
                                    bsls::Types::Int64  daysSinceEpoch);

    // 'bdlt::Date' Decoding

    /// 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.
    static int getDateValue(bdlt::Date     *date,
                            bsl::streambuf *streamBuf,
                            int             length);

    // 'bdlt::Date' Encoding

    /// 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`.
    static int putDateValue(bsl::streambuf          *streamBuf,
                            const bdlt::Date&        value,
                            const BerEncoderOptions *options);

    // 'bdlt::DateTz' Decoding

    /// 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.
    static int getDateTzValue(bdlt::DateTz   *value,
                              bsl::streambuf *streamBuf,
                              int             length);

    // 'bdlt::DateTz' Encoding

    /// 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`.
    static int putDateTzValue(bsl::streambuf          *streamBuf,
                              const bdlt::DateTz&      date,
                              const BerEncoderOptions *options);

    // Variant Decoding

    /// 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.
    static int getDateOrDateTzValue(DateOrDateTz   *value,
                                    bsl::streambuf *streamBuf,
                                    int             length);
};

                        // ===========================
                        // 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.
struct BerUtil_TimeEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
struct BerUtil_TimeTzEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
struct BerUtil_TimeOrTimeTzEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
///
/// * **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.
struct BerUtil_TimeImpUtil {

    // TYPES

    /// `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_ExtendedBinaryEncodingUtil ExtendedBinaryEncodingUtil;

    /// `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_DateAndTimeHeader DateAndTimeHeader;

    /// `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_DateAndTimeHeaderType DateAndTimeHeaderType;

    /// `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_DateAndTimeHeaderImpUtil DateAndTimeHeaderUtil;

    /// `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_IntegerImpUtil IntegerUtil;

    /// `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_Iso8601ImpUtil Iso8601Util;

    /// `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_LengthImpUtil LengthUtil;

    /// `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_StringImpUtil StringUtil;

    /// `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_StreambufUtil StreambufUtil;

    /// `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_TimeEncoding TimeEncoding;

    /// `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_TimeTzEncoding TimeTzEncoding;

    /// `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_TimeOrTimeTzEncoding TimeOrTimeTzEncoding;

    /// `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 BerUtil_TimezoneOffsetImpUtil TimezoneUtil;

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

  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

    /// 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 detectTimeEncoding(TimeEncoding::Value *encoding,
                                  int                  length,
                                  unsigned char        firstByte);

    /// 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 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 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 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 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.
    static int getExtendedBinaryTimeValue(bdlt::Time     *value,
                                          bsl::streambuf *streamBuf,
                                          int             length);

    // 'bdlt::Time' Encoding

    /// 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 TimeEncoding::Value selectTimeEncoding(
                                             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 putIso8601TimeValue(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 putCompactBinaryTimeValue(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.
    static int putExtendedBinaryTimeValue(bsl::streambuf          *streamBuf,
                                          const bdlt::Time&        value,
                                          const BerEncoderOptions *options);

    // 'bdlt::TimeTz' Decoding

    /// 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 detectTimeTzEncoding(TimeTzEncoding::Value *encoding,
                                    int                    length,
                                    unsigned char          firstByte);

    /// 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 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 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 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 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 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 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.
    static int getExtendedBinaryTimeTzValue(bdlt::TimeTz   *value,
                                            bsl::streambuf *streamBuf,
                                            int             length);

    // 'bdlt::TimeTz' Encoding

    /// 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 TimeTzEncoding::Value selectTimeTzEncoding(
                                             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 putIso8601TimeTzValue(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 putCompactBinaryTimeValue(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 putCompactBinaryTimeTzValue(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.
    static int putExtendedBinaryTimeTzValue(bsl::streambuf          *streamBuf,
                                            const bdlt::TimeTz&      value,
                                            const BerEncoderOptions *options);

    // Variant Decoding

    /// 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 detectTimeOrTimeTzEncoding(
                                       TimeOrTimeTzEncoding::Value *encoding,
                                       int                          length,
                                       unsigned char                firstByte);

    /// 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 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 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 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 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 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 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 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 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 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 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.
    static int getExtendedBinaryTimeTzValue(TimeOrTimeTz   *value,
                                            bsl::streambuf *streamBuf,
                                            int             length);

  public:
    // CLASS METHODS

    // Utilities

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

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

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

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

    // 'bdlt::Time' Decoding

    /// 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.
    static int getTimeValue(bdlt::Time     *value,
                            bsl::streambuf *streamBuf,
                            int             length);

    // 'bdlt::Time' Encoding

    /// 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`.
    static int putTimeValue(bsl::streambuf          *streamBuf,
                            const bdlt::Time&        value,
                            const BerEncoderOptions *options);

    // 'bdlt::TimeTz' Decoding

    /// 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.
    static int getTimeTzValue(bdlt::TimeTz   *value,
                              bsl::streambuf *streamBuf,
                              int             length);

    // 'bdlt::TimeTz' Encoding

    /// 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`.
    static int putTimeTzValue(bsl::streambuf          *streamBuf,
                              const bdlt::TimeTz&      value,
                              const BerEncoderOptions *options);

    // Variant Decoding

    /// 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.
    static int getTimeOrTimeTzValue(TimeOrTimeTz   *value,
                                    bsl::streambuf *streamBuf,
                                    int             length);
};

                      // ===============================
                      // 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.
struct BerUtil_DatetimeEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
struct BerUtil_DatetimeTzEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
struct BerUtil_DatetimeOrDatetimeTzEncoding {

    // TYPES

    /// `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`.
    typedef BerUtil_DateAndTimeEncoding Encoding;

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

/// 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.
///
/// * **the default set of optionsi**:
///   > 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.
struct BerUtil_DatetimeImpUtil {

    // TYPES

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

    /// `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_ExtendedBinaryEncodingUtil ExtendedBinaryEncodingUtil;

    /// `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_DateAndTimeHeader DateAndTimeHeader;

    /// `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_DateAndTimeHeaderImpUtil DateAndTimeHeaderUtil;

    /// `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_DateImpUtil DateUtil;

    /// `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_DatetimeEncoding DatetimeEncoding;

    /// `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_DatetimeTzEncoding DatetimeTzEncoding;

    /// `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_DatetimeOrDatetimeTzEncoding DatetimeOrDatetimeTzEncoding;

    /// `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_IntegerImpUtil IntegerUtil;

    /// `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_Iso8601ImpUtil Iso8601Util;

    /// `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_LengthImpUtil LengthUtil;

    /// `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_StreambufUtil StreambufUtil;

    /// `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_StringImpUtil StringUtil;

    /// `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_TimeImpUtil TimeUtil;

    /// `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 BerUtil_TimezoneOffsetImpUtil TimezoneUtil;

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

  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

    /// 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 void datetimeToMillisecondsSinceEpoch(
                                 bsls::Types::Int64    *millisecondsSinceEpoch,
                                 const bdlt::Datetime&  value);

    /// 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.
    static int millisecondsSinceEpochToDatetime(
                                   bdlt::Datetime     *value,
                                   bsls::Types::Int64  millisecondsSinceEpoch);

    // 'bdlt::Datetime' Decoding

    /// 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 detectDatetimeEncoding(DatetimeEncoding::Value *encoding,
                                      int                      length,
                                      unsigned char            firstByte);

    /// 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 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 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 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, 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 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 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.
    static int getExtendedBinaryDatetimeValue(bdlt::Datetime *value,
                                              bsl::streambuf *streamBuf,
                                              int             length);

    // 'bdlt::Datetime' Encoding

    /// 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 DatetimeEncoding::Value selectDatetimeEncoding(
                                       bsls::Types::Int64      *serialDatetime,
                                       int                     *length,
                                       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 putIso8601DatetimeValue(bsl::streambuf          *streamBuf,
                                       const bdlt::Datetime&    value,
                                       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,
                                       bsls::Types::Int64       serialDatetime,
                                       int                      length,
                                       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 putCompactBinaryDatetimeValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::Datetime&    value,
                                            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 putCompactBinaryDatetimeTzValue(
                                       bsl::streambuf          *streamBuf,
                                       bsls::Types::Int64       serialDatetime,
                                       int                      length,
                                       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.
    static int putExtendedBinaryDatetimeValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::Datetime&    value,
                                            const BerEncoderOptions *options);

    // 'bdlt::DatetimeTz' Decoding

    /// 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 detectDatetimeTzEncoding(
                                   DatetimeTzEncoding::Value *encoding,
                                   int                        length,
                                   unsigned char              firstByte);

    /// 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 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 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 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, 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 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 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.
    static int getExtendedBinaryDatetimeTzValue(bdlt::DatetimeTz *value,
                                                bsl::streambuf   *streamBuf,
                                                int               length);

    // 'bdlt::DatetimeTz' Encoding

    /// 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 DatetimeTzEncoding::Value selectDatetimeTzEncoding(
                                       bsls::Types::Int64      *serialDatetime,
                                       int                     *length,
                                       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 putIso8601DatetimeTzValue(bsl::streambuf          *streamBuf,
                                         const bdlt::DatetimeTz&  value,
                                         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 putCompactBinaryDatetimeTzValue(
                              bsl::streambuf          *streamBuf,
                              int                      timezoneOffsetInMinutes,
                              bsls::Types::Int64       serialDatetime,
                              int                      serialDatetimeLength,
                              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.
    static int putExtendedBinaryDatetimeTzValue(
                                            bsl::streambuf          *streamBuf,
                                            const bdlt::DatetimeTz&  value,
                                            const BerEncoderOptions *options);

    // Variant Decoding

    /// 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 detectDatetimeOrDatetimeTzEncoding(
                               DatetimeOrDatetimeTzEncoding::Value *encoding,
                               int                                  length,
                               unsigned char                        firstByte);

    /// 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 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, 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 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 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 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, 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 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
    /// 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 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, 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.
    static int getExtendedBinaryDatetimeTzValue(
                                               DatetimeOrDatetimeTz *value,
                                               bsl::streambuf       *streamBuf,
                                               int                   length);

  public:
    // CLASS METHODS

    // 'bdlt::Datetime' Decoding

    /// 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.
    static int getDatetimeValue(bdlt::Datetime *value,
                                bsl::streambuf *streamBuf,
                                int             length);

    // 'bdlt::Datetime' Encoding

    /// 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`.
    static int putDatetimeValue(bsl::streambuf          *streamBuf,
                                const bdlt::Datetime&    value,
                                const BerEncoderOptions *options);

    // 'bdlt::DatetimeTz' Decoding

    /// 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.
    static int getDatetimeTzValue(bdlt::DatetimeTz *value,
                                  bsl::streambuf   *streamBuf,
                                  int               length);

    // 'bdlt::DatetimeTz' Encoding

    /// 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`.
    static int putDatetimeTzValue(bsl::streambuf          *streamBuf,
                                  const bdlt::DatetimeTz&  value,
                                  const BerEncoderOptions *options);

    // Variant Decoding

    /// 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`.
    static int getDatetimeOrDatetimeTzValue(DatetimeOrDatetimeTz *value,
                                            bsl::streambuf       *streamBuf,
                                            int                   length);
};

                       // ==============================
                       // 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.
struct BerUtil_GetValueImpUtil {

    // TYPES

    /// `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_BooleanImpUtil BooleanUtil;

    /// `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_CharacterImpUtil CharacterUtil;

    /// `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_DateImpUtil DateUtil;

    /// `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_DatetimeImpUtil DatetimeUtil;

    /// `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_FloatingPointImpUtil FloatingPointUtil;

    /// `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_IntegerImpUtil IntegerUtil;

    /// `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_StringImpUtil StringUtil;

    /// `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 BerUtil_TimeImpUtil TimeUtil;

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

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

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

    // CLASS METHODS

    /// 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.
    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());
};

                       // ==============================
                       // 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.
struct BerUtil_PutValueImpUtil {

    // TYPES

    /// `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_BooleanImpUtil BooleanUtil;

    /// `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_CharacterImpUtil CharacterUtil;

    /// `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_DateImpUtil DateUtil;

    /// `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_DatetimeImpUtil DatetimeUtil;

    /// `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_FloatingPointImpUtil FloatingPointUtil;

    /// `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_IntegerImpUtil IntegerUtil;

    /// `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_StringImpUtil StringUtil;

    /// `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 BerUtil_TimeImpUtil TimeUtil;

    // CLASS METHODS

    /// 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`, `bsl::string_view`,
    /// `bslstl::StringRef` and BDE date/time types are supported.
    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 bsl::string_view&  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);
};

                             // ==================
                             // 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`.
struct BerUtil_Imp {

    // CLASS METHODS

    /// 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.
    static int putStringValue(bsl::streambuf *streamBuf,
                              const char     *string,
                              int             stringLength);
};

// ============================================================================
//                        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>(
                                  static_cast<unsigned long long>(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);
}

/// # Implementation Note
/// This implementation requires the platform use a 2's complement
/// representation for signed integer values.
inline
int BerUtil_IntegerImpUtil::put40BitIntegerValue(bsl::streambuf     *streamBuf,
                                                 bsls::Types::Int64  value)
{
    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()));
}

// 'bsl::string_view' Encoding

inline
int BerUtil_StringImpUtil::putStringViewValue(
                                            bsl::streambuf          *streamBuf,
                                            const bsl::string_view&  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
    }

    // DoS attack mitigation: Any reasonable ISO8601 string will be smaller
    // than this.  Even an ISO 8601 duration (not currently supported) with all
    // its elements (years, months, days, hours, minutes, and seconds) present
    // with 17 significant digits and negative signs would be 117 characters.
    if (length > 126) {
        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<unsigned>(signedTimezoneOffsetHi) << 8) |
            (static_cast<unsigned>(timezoneOffsetLo) << 0);

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

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

    BSLS_ASSERT_OPT(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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(0 == "Unreachable");
#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 bsl::string_view&  value,
                                      const BerEncoderOptions *)
{
    return StringUtil::putStringViewValue(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 ----------------------------------
