// baltzo_timezoneutil.h                                              -*-C++-*-
#ifndef INCLUDED_BALTZO_TIMEZONEUTIL
#define INCLUDED_BALTZO_TIMEZONEUTIL

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

//@PURPOSE: Provide utilities for converting times among different time zones.
//
//@CLASSES:
//  baltzo::TimeZoneUtil: utilities for converting local time values
//
//@SEE_ALSO: baltzo_localdatetime, baltzo_zoneinfo, baltzo_defaultzoneinfocache
//
//@DESCRIPTION: This component provides a namespace, `baltzo::TimeZoneUtil`,
// containing utility functions for converting time values to and from their
// corresponding local time representations in (possibly) different time zones.
// The primary methods provided include:
// * `convertLocalToLocalTime` and `convertUtcToLocalTime`, for converting a
//   time to the corresponding local-time value in some time zone;
// * `convertLocalToUtc`, for converting a local-time value into the
//   corresponding UTC time value;
// * `initLocalTime`, for initializing a local-time value.
// Additionally, the `loadLocalTimePeriod` and `loadLocalTimePeriodForUtc`
// methods enable clients to obtain information about a time value, such as
// whether the provided time is a daylight-saving time value.  Finally note
// that, all of the functions in this utility component make use of a
// process-wide cache of time-zone information (see
// {`baltzo_defaultzoneinfocache`}).
//
///Valid, Ambiguous, and Invalid Local-Time Values
///-----------------------------------------------
// There are intervals around each daylight-saving time transition where a
// `bdlt::Datetime` object holding a local time may not describe a valid or
// unique clock time in the local time zone (see {`baltzo_localtimevalidity`}).
// When interpreting a local-time value represented using a `bdlt::Datetime`
// object with respect to a given time zone, there are three possible
// scenarios:
//
// 1. The local time is *valid* and *unique*: The local time representation is
//    valid, and unique within the time zone (the most likely scenario).  For
//    example, in New York at "Aug 31, 2010 12:00AM", DST was in effect and the
//    local-time offset from UTC was -4 hours.
// 2. The local time is *valid*, but *ambiguous*: The local time representation
//    is valid, but could correctly be interpreted as either of two possible
//    times, as may happen around a daylight-saving time transition where the
//    local-time offset from UTC increases (e.g., in the United States local
//    time "falls back" by an hour in the fall).  Thus, a local time within
//    such a transition period occurs twice, and is ambiguous without
//    additional information.  For example, in New York, daylight-saving time
//    was in effect until "Nov 7, 2010 2:00AM" when clocks were set back by an
//    hour; therefore, the time "Nov 7, 2010 1:30AM" occurred twice, and that
//    description (as represented in a `bdlt::Datetime` object) could refer to
//    either of those two times.
// 3. The local time is **invalid**: The local-time representation doesn't
//    correspond to a valid time within the given time zone, as may happen
//    around a daylight-saving time transition where the offset from UTC
//    decreases (e.g., in the United States local time "springs forward" by an
//    hour in the spring).  Thus, local times that are skipped during such a
//    transition are invalid.  For example, in New York, DST was in effect
//    starting "Mar 14, 2010 2:00AM" when clocks are set forward an hour;
//    therefore, the local time "Mar 14, 2010 2:30AM" never occurs.
//
// Note that the functions provided in this component guarantee a graceful
// handling of all three scenarios.  Ambiguity and invalidity, when they arise,
// are resolved according to a user-supplied daylight-saving time policy that
// describes how to interpret the input time values (see {`baltzo_dstpolicy`}).
//
///Daylight-Saving Time (DST) Policies and Disambiguation
/// - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The `baltzo::TimeZoneUtil` methods that take, as input, a `bdlt::Datetime`
// object representing a local time (i.e., a local-time value without a UTC
// offset) also accept an optional `baltzo::DstPolicy::Enum`.  This (optional)
// argument policy allows clients to specify how they would like the operation
// to interpret the input value (as such a value may be ambiguous or invalid
// within the indicated time zone -- see above).  Clients are, however,
// encouraged to use the default policy, `e_UNSPECIFIED`, unless there is some
// specific reason they require a different option.
//
// Three enumerated `baltzo::DstPolicy` values are supported:
//
// 1. `e_UNSPECIFIED` (default)
//   - The client does not explicitly indicate whether the associated input
//     time represents a daylight-saving time value, and the operation will
//     determine how to interpret the time solely based on the input itself.
//     If the input value is *valid* and *unique*, the operation will use its
//     (unique) corresponding UTC value.  If the input is either *ambiguous*
//     or *invalid*, the operation will use the later of two potential
//     interpretations of the input (determined, e.g., by applying the
//     standard and daylight-saving time UTC offsets to the input).  For
//     *invalid* times, this choice reflects the assumption that the user most
//     likely forgot to adjust their clock.  For *ambiguous* times, this
//     choice is arbitrary (but is consistent with common implementations of
//     the C standard library).
// 2. `e_STANDARD`
//   - Indicates that the operation should treat the associated input time
//     value as a standard time, using the UTC value computed by applying the
//     standard-time UTC offset.  Note that the standard-time UTC offset is
//     used even when the input time value is (unambiguously) *not* a standard
//     time, which would result in a UTC time that does not correspond to a
//     standard time within the time zone.
// 3. `e_DST`
//   - Indicates that the operation should treat the associated input time
//     value as a daylight-saving time, using the UTC value computed by
//     applying the daylight-saving time UTC offset.  Note that the
//     daylight-saving-time UTC offset is used even when the input time value
//     is (unambiguously) *not* a daylight-saving time, which would result in
//     a UTC time that does not correspond to a daylight-saving time within
//     the time zone.
//
// Note that these policies are intended to reflect the behavior of the C
// standard library function `mktime` and its interpretation of the `tm_isdst`
// value of the supplied `tm` structure.  The behavior for the "unspecified"
// policy, however, is not strictly defined by either the ISO or POSIX
// standards, and varies among implementations.
//
///Result of `convertLocalToUtc` for Various `baltzo::DstPolicy` Values
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// The following table summarizes the effect that the different
// `baltzo::DstPolicy` values have on a call to `convertLocalToUtc` for several
// possible time values in New York.  Note that standard local time in New York
// is UTC-5:00, and daylight-saving local time there is UTC-4:00.
// ```
//    Result format: UTC 'bdlt::Time' & corresponding local 'bdlt::TimeTz'.
// ,--------------------------------------------------------------------------.
// | Input in New York  |              'baltzo::DstPolicy::Enum'              |
// |     Local Time     |-----------------------------------------------------|
// |  (bdlt::Datetime)  |  *_UNSPECIFIED  |   *_STANDARD    |   *_DST         |
// |==========================================================================|
// | Jan  1, 2010 01:30 |  06:30:00 UTC   |  06:30:00 UTC   |  05:30:00 UTC   |
// |   (standard time)  | (01:30:00-5:00) | (01:30:00-5:00) | (00:30:00-5:00) |
// |                    |                 |       [1]       |      [2]        |
// |--------------------|-----------------------------------------------------|
// | Mar 14, 2010 02:30 |  07:30:00 UTC   |  07:30:00 UTC   |  06:30:00 UTC   |
// |     (invalid)      | (03:30:00-4:00) | (03:30:00-4:00) | (01:30:00-5:00) |
// |                    |      [3]        |       [4]       |      [5]        |
// |--------------------|-----------------------------------------------------|
// | Apr  1, 2010 01:30 |  05:30:00 UTC   |  06:30:00 UTC   |  05:30:00 UTC   |
// | (daylight-saving)  | (01:30:00-4:00) | (02:30:00-4:00) | (01:30:00-4:00) |
// |                    |                 |       [6]       |      [7]        |
// |--------------------|-----------------------------------------------------|
// | Nov  7, 2010 01:30 |  06:30:00 UTC   |  06:30:00 UTC   |  05:30:00  UTC  |
// |    (ambiguous)     | (01:30:00-5:00) | (01:30:00-5:00) | (01:30:00-4:00) |
// |                    |      [8]        |                 |                 |
// `--------------------------------------------------------------------------'
// ```
//
// 1. "Jan 1, 2010 01:30" is unambiguously a standard time value.  The result
//    is simply the corresponding UTC time "Jan 1, 2010 06:30 UTC".
// 2. "Jan 1, 2010 01:30" is unambiguously a standard time value, so the
//    supplied policy, `e_DST`, contradicts the actual occurrence of
//    daylight-saving time in New York.  The input time is adjusted by the UTC
//    offset for daylight-saving time in New York (-4:00) resulting in a UTC
//    time 05:30.  Note that the result, "Jan 1, 2010 05:30 UTC", corresponds
//    to the New York time "Jan 1, 2010 00:30:00-5:00" (a standard time).
// 3. "Mar 14, 2010 02:30" is not a valid local time in New York (a correctly
//    administered clock would have been set ahead an hour at 2:00AM).  The
//    operation will use the later of two potential values, determined by
//    applying the standard and daylight-saving time UTC offsets to the input
//    (07:30 UTC and 06:30 UTC, respectively).  Note that the selection of the
//    later time reflects an assumption that the user forgot to adjust the
//    clock.
// 4. The input time is adjusted by the UTC offset for standard time in New
//    York (-5:00) resulting in the UTC time 07:30.  Note that "Mar 14, 2010
//:   07:30 UTC" corresponds to the New York time "Mar 14, 2010 03:30-4:00" (a
//:   daylight-saving time).
//:
// 5. The input time is adjusted by the UTC offset for daylight-saving time in
//    New York (-4:00) resulting in the UTC time 06:30.  Note that "Mar 14,
//   2010. 06:30 UTC" corresponds to the New York time "Mar 14, 2010
//:   01:30-5:00" (a standard time).
//:
// 6. "Apr 1, 2010 01:30" is unambiguously a daylight-saving time value, so the
//    supplied policy `e_STANDARD` contradicts the actual occurrence of
//    daylight-saving time in New York.  The input time is adjusted by the UTC
//    offset for standard time in New York (-5:00) resulting in a UTC time
//:   06:30.  Note that "Apr 1, 2010 06:30 UTC" corresponds to the New York
//:   time "Apr 1, 2010 02:30:00-4:00" (a daylight-saving time).
//:
// 7. "Apr 1, 2010 01:30" is unambiguously a daylight-saving time value.  The
//    result is simply the corresponding UTC time "Apr 1, 2010 06:30 UTC".
// 8. "Nov 7, 2010 01:30" is a valid, but ambiguous, local time in New York
//    (clocks are set back by an hour at 2:00AM, so 1:30AM occurs twice).  The
//    operation will use the later of two potential values determined by
//    applying the standard and daylight-saving time UTC offsets to the input
//    (06:30 UTC and 05:30 UTC, respectively).  Note that the selection of the
//    later time is arbitrary, but is consistent with common implementations of
//    the C standard library.
//
///Thread Safety
///-------------
// The functions provided by `baltzo::TimeZoneUtil` are *thread-safe*, meaning
// they can be safely executed concurrently.
//
///Usage
///-----
// The following usage examples demonstrate how to use various functions
// provided by `baltzo::TimeZoneUtil` to perform conversions on various time
// representations.
//
///Example 1: Converting a UTC time to a Local Time
/// - - - - - - - - - - - - - - - - - - - - - - - -
// In this usage example, we illustrate how to convert a UTC time to its
// corresponding local time in a given time zone.  We start by creating a
// `bdlt::Datetime` object holding the UTC time "July 31, 2010 15:00:00":
// ```
// bdlt::Datetime utcTime(2010, 7, 31, 15, 0, 0);
// ```
// Then, we create a `baltzo::LocalDatetime` object to hold the result of the
// conversion operation:
// ```
// baltzo::LocalDatetime newYorkTime;
// ```
// Now, we call the `convertUtcToLocalTime` function in `baltzo::TimeZoneUtil`:
// ```
// int status = baltzo::TimeZoneUtil::convertUtcToLocalTime(&newYorkTime,
//                                                         "America/New_York",
//                                                         utcTime);
// if (0 != status) {
//     // A non-zero 'status' indicates there was an error in the conversion
//     // (e.g., the time zone id was not valid or the environment has not
//     // been correctly configured).
//
//     return 1;                                                     // RETURN
// }
// ```
// Finally, we observe that the result in `newYorkTime` is "July 31, 2010
// 11:00:00" and that the offset from UTC applied was -4 hours:
// ```
// const bdlt::Datetime test = newYorkTime.datetimeTz().localDatetime();
// assert(2010 == test.year());  assert(11 == test.hour());
// assert(   7 == test.month()); assert( 0 == test.minute());
// assert(  31 == test.day());   assert( 0 == test.second());
//
// assert( -4 * 60 == newYorkTime.datetimeTz().offset());
// ```
//
///Example 2: Converting a Local Time in One Time Zone to Another Time Zone
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example we illustrate how to convert a local time in a given time
// zone directly to its corresponding local time in another time zone.  In
// particular, we want to convert the time "July 31, 2010 15:00:00" in New York
// to its corresponding time in Rome, Italy.
//
// First, we create a `bdlt::Datetime` object representing the time "July 31,
// 2010 15:00:00" in New York:
// ```
// bdlt::Datetime newYorkTime(2010, 7, 31, 15, 0, 0);
// ```
// Now, let's apply the conversion operation to obtain a
// `baltzo::LocalDatetime` object representing the corresponding local time in
// Italy:
// ```
// baltzo::LocalDatetime romeTime;
// int status = baltzo::TimeZoneUtil::convertLocalToLocalTime(
//                                                       &romeTime,
//                                                       "Europe/Rome",
//                                                       newYorkTime,
//                                                       "America/New_York");
// if (0 != status) {
//     // A non-zero 'status' indicates there was an error in the conversion
//     // (e.g., the time zone id was not valid or the environment has not
//     // been correctly configured).
//
//     return 1;                                                     // RETURN
// }
// ```
// Notice that we did not specify the optional `dstPolicy` argument to
// `convertLocalToLocalTime`.  The default value should be appropriate for most
// users.
//
// Finally, we verify that the value of `romeTime` is "July 31, 2010 21:00:00",
// which is the time in Italy corresponding to "July 31, 2010 15:00:00" in New
// York:
// ```
// const bdlt::Datetime test = romeTime.datetimeTz().localDatetime();
// assert(2010 == test.year());  assert(21 == test.hour());
// assert(   7 == test.month()); assert( 0 == test.minute());
// assert(  31 == test.day());   assert( 0 == test.second());
//
// assert( 2 * 60 == romeTime.datetimeTz().offset());
// ```
//
///Example 3: Initializing a Local Time
/// - - - - - - - - - - - - - - - - - -
// In this example we illustrate how to create a `baltzo::LocalDatetime` from a
// `bdlt::Datetime`, which may not represent a unique (or valid) clock time.
//
// First, we create a `bdlt::Datetime` object for the New York local time "Jul
// 31, 2010 15:00:00".  Note that this local date-time occurs during a DST
// transition and is an invalid date-time.
// ```
// bdlt::Datetime uniqueTime(2010, 7, 31, 15, 0, 0);
// ```
// Then, we call `initLocalTime`, which returns a `baltzo::LocalDatetime`
// object.  `initLocalTime` also optionally returns
// `baltzo::LocalTimeValidity::Enum`, indicating whether the provided input was
// a valid and unique clock time.  Note that invalid or ambiguous times are
// resolved using the optionally provided `baltzo::DstPolicy::Enum` (see the
// section {Daylight-Saving Time (DST) Policies and Disambiguation}):
// ```
// baltzo::LocalDatetime             localTime;
// baltzo::LocalTimeValidity::Enum validity;
// int status = baltzo::TimeZoneUtil::initLocalTime(&localTime,
//                                                 &validity,
//                                                 uniqueTime,
//                                                 "America/New_York");
// if (0 != status) {
//     return 1;
// }
// ```
// Now, we verify the value of `localTime` is "Jul 31, 2010 15:00:00" with an
// offset of -4:00 from UTC, in the time zone "America/New_York".
// ```
// const bdlt::Datetime invalidTest = localTime.datetimeTz().localDatetime();
// assert(2010 == invalidTest.year());  assert(15 == invalidTest.hour());
// assert(   7 == invalidTest.month()); assert( 0 == invalidTest.minute());
// assert(  31 == invalidTest.day());   assert( 0 == invalidTest.second());
//
// assert( -4 * 60 == localTime.datetimeTz().offset());
// assert("America/New_York" == localTime.timeZoneId());
// ```
// In addition, the time provided represents a unique and valid clock time in
// New York (because it does not fall near a daylight-saving time transition):
// ```
// assert(baltzo::LocalTimeValidity::e_VALID_UNIQUE == validity);
// ```
// By contrast, if we call `initLocalTime` for a time value that falls during a
// during a daylight-saving time transition, the returned
// `baltzo::LocalTimeValidity::Enum` will indicate if the supplied time either
// does not represent a valid clock time in the time zone (as may occur when
// clocks are set forward), or does not represent a unique clock time (as may
// occur when clocks are set back).
//
// For example, suppose we call `initLocalTime` for "Mar 14, 2010 02:30"; this
// clock time does not occurs in New York, as clocks are set forward by an hour
// at 2am local time:
// ```
// bdlt::Datetime invalidTime(2010, 3, 14, 2, 30, 0);
// status = baltzo::TimeZoneUtil::initLocalTime(&localTime,
//                                             &validity,
//                                             invalidTime,
//                                            "America/New_York");
// if (0 != status) {
//     return 1;
// }
// ```
// Now, we verify the value of `localTime` represents a valid and unique time
// of "Mar 14, 2010 03:30:00-04:00" in the "America/New_York" time zone.
// ```
// const bdlt::Datetime test = localTime.datetimeTz().localDatetime();
// assert(2010 == test.year());  assert( 3 == test.hour());
// assert(   3 == test.month()); assert(30 == test.minute());
// assert(  14 == test.day());   assert( 0 == test.second());
//
// assert("America/New_York" == localTime.timeZoneId());
// assert( -4 * 60 == localTime.datetimeTz().offset());
// ```
// Finally, we verify that the validity status returned for `invalidTime` is
// `e_INVALID`:
// ```
// assert(baltzo::LocalTimeValidity::e_INVALID == validity);
// ```
//
///Example 4: Obtaining Information About a Time Value
///- - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example we illustrate how to obtain additional information about a
// local time in a given time zone using the `loadLocalTimePeriod` method.
// Using `loadLocalTimePeriod` a client can determine, for a point in time, the
// attributes that characterize local time in a given time zone (e.g., the
// offset from UTC, whether it is daylight-saving time) as well as the interval
// over which those attributes apply (see {`baltzo_localtimeperiod`}).
//
// First, we create a `baltzo::LocalDatetime` object for the New York local
// time "Jul 31, 2010 15:00:00-04:00".  Note that this `baltzo::LocalDatetime`
// may also be created as in example 3.
// ```
// bdlt::DatetimeTz localTimeTz(bdlt::Datetime(2010, 7, 31, 15, 0, 0),
//                              -4 * 60);
// baltzo::LocalDatetime localTime(localTimeTz, "America/New_York");
// ```
// Then, we call `loadLocalTimePeriod`, which returns a
// `baltzo::LocalTimePeriod` object that is loaded with attributes
// characterizing local time in New York on "Mar 14, 2010 03:30:00", and the
// interval over which those attributes are in effect.
// ```
// baltzo::LocalTimePeriod period;
// int status = baltzo::TimeZoneUtil::loadLocalTimePeriod(&period, localTime);
// if (0 != status) {
//     // A non-zero 'status' indicates there was an error in the conversion
//     // (e.g., the time zone id was not valid or the environment has not
//     // been correctly configured).
//
//     return 1;                                                     // RETURN
// }
// ```
// Now we examine the returned properties.  "Mar 14, 2010 03:30:00" is during
// daylight-saving time, which is -4:00 UTC, and the type of local time is
// sometimes abbreviated "EDT" for "Eastern Daylight Time".  "Eastern Daylight
// Time" is in effect from "Mar 14, 2010 7am UTC" to "Nov 7, 2010 6am UTC".
// Note that the abbreviation provided ("EDT") is not canonical or localized.
// In general the provided abbreviations should not be displayed to users (they
// are intended for development and debugging only):
// ```
// assert(true         == period.descriptor().dstInEffectFlag());
// assert(-4 * 60 * 60 == period.descriptor().utcOffsetInSeconds());
// assert("EDT"        == period.descriptor().description());
// assert(bdlt::Datetime(2010,  3, 14, 7, 0, 0) == period.utcStartTime());
// assert(bdlt::Datetime(2010, 11,  7, 6, 0, 0) == period.utcEndTime());
// ```

#include <balscm_version.h>

#include <baltzo_defaultzoneinfocache.h>
#include <baltzo_dstpolicy.h>
#include <baltzo_localtimevalidity.h>
#include <baltzo_timezoneutilimp.h>
#include <baltzo_localdatetime.h>

#include <bdlt_currenttime.h>
#include <bdlt_datetime.h>
#include <bdlt_datetimetz.h>

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

#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace baltzo {

class LocalTimePeriod;
class ZoneinfoCache;

                            // ===================
                            // struct TimeZoneUtil
                            // ===================

/// This `struct` provides a namespace for utility functions that convert
/// time values to, from, and between, their corresponding local time
/// representations in (possibly) different time zones.
///
/// These utility functions are:
/// * *alias-safe*
/// * *exception-neutral* (agnostic)
/// * *thread-safe*
/// For terminology see {`bsldoc_glossary`}.
struct TimeZoneUtil {

    // CLASS METHODS

    /// Load, into the specified `result`, the local time value that is the
    /// specified `interval` in the future of the specified `originalTime`
    /// (in the time zone `originalTime.timeZoneId()`).  Return 0 on
    /// success, and a non-zero value with no effect otherwise.  A return
    /// value of `ErrorCode::k_UNSUPPORTED_ID` indicates that
    /// `targetTimeZoneId` was not recognized, and a return value of
    /// `ErrorCode::k_OUT_OF_RANGE` indicates that the result of the
    /// operation would have been outside the range of values representable
    /// by the `result` type.  The resulting local-time is equivalent to
    /// adding `interval` to `originalTime.datetimeTz().utcDatetime()` and
    /// converting the result into the local time of
    /// `originalTime.timeZoneId()`.
    static int addInterval(LocalDatetime             *result,
                           const LocalDatetime&       originalTime,
                           const bsls::TimeInterval&  interval);

    /// Load, into the specified `result`, the local date-time value (in the
    /// time zone indicated by the specified `targetTimeZoneId`)
    /// corresponding to the specified `utcTime`.  The offset from UTC of
    /// the time zone is rounded down to minute precision.  Return 0 on
    /// success, and a non-zero value with no effect otherwise.  A return
    /// value of `ErrorCode::k_UNSUPPORTED_ID` indicates that
    /// `targetTimeZoneId` was not recognized, and a return value of
    /// `ErrorCode::k_OUT_OF_RANGE` indicates that the result of the
    /// operation would have been outside the range of values representable
    /// by the `result` type.
    static int convertUtcToLocalTime(LocalDatetime         *result,
                                     const char            *targetTimeZoneId,
                                     const bdlt::Datetime&  utcTime);
    static int convertUtcToLocalTime(bdlt::DatetimeTz      *result,
                                     const char            *targetTimeZoneId,
                                     const bdlt::Datetime&  utcTime);

    /// Load, into the specified `result`, the local date-time value (in the
    /// time zone indicated by the specified `targetTimeZoneId`)
    /// corresponding to the local time indicated by the specified
    /// `srcTime`.  The offset from UTC of both time zones is rounded down
    /// to minute precision.  Return 0 on success, and a non-zero value with
    /// no effect otherwise.  A return value of
    /// `ErrorCode::k_UNSUPPORTED_ID` indicates that `targetTimeZoneId` was
    /// not recognized, and a return value of `ErrorCode::k_OUT_OF_RANGE`
    /// indicates that the result of the operation would have been outside
    /// the range of values representable by the `result` type.
    static int convertLocalToLocalTime(LocalDatetime         *result,
                                       const char            *targetTimeZoneId,
                                       const LocalDatetime&   srcTime);
    static int convertLocalToLocalTime(
                                     LocalDatetime           *result,
                                     const char              *targetTimeZoneId,
                                     const bdlt::DatetimeTz&  srcTime);
    static int convertLocalToLocalTime(bdlt::DatetimeTz      *result,
                                       const char            *targetTimeZoneId,
                                       const LocalDatetime&   srcTime);
    static int convertLocalToLocalTime(
                                     bdlt::DatetimeTz        *result,
                                     const char              *targetTimeZoneId,
                                     const bdlt::DatetimeTz&  srcTime);

    /// Load, into the specified `result`, the local date-time value (in the
    /// time zone indicated by the specified `targetTimeZoneId`)
    /// corresponding to the local time indicated by the specified `srcTime`
    /// (in the time zone indicated by the specified `srcTimeZoneId`).
    /// Optionally specify a `dstPolicy` indicating whether or not `srcTime`
    /// represents a daylight-saving time value.  If `dstPolicy` is
    /// unspecified and `srcTime` is a unique and valid time in the source
    /// time zone, then perform the conversion using that uniquely described
    /// time; if `dstPolicy` is unspecified and `srcTime` is either
    /// ambiguous or invalid, then use the later of the two possible
    /// interpretations of `srcTime`.  The offset from UTC of both time
    /// zones is rounded down to minute precision.  Return 0 on success, and
    /// a non-zero value with no effect otherwise.  A return value of
    /// `ErrorCode::k_UNSUPPORTED_ID` indicates that either
    /// `targetTimeZoneId` or `srcTimeZoneId` was not recognized.
    static int convertLocalToLocalTime(LocalDatetime         *result,
                                       const char            *targetTimeZoneId,
                                       const bdlt::Datetime&  srcTime,
                                       const char            *srcTimeZoneId,
                                       DstPolicy::Enum        dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);
    static int convertLocalToLocalTime(bdlt::DatetimeTz      *result,
                                       const char            *targetTimeZoneId,
                                       const bdlt::Datetime&  srcTime,
                                       const char            *srcTimeZoneId,
                                       DstPolicy::Enum        dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);

    /// Load, into the specified `result`, the local date-time value --
    /// including the local date, time, and resolved UTC offset -- indicated
    /// by the specified `localTime` in the time zone indicated by the
    /// specified `timeZoneId`.  Optionally specify `resultValidity` in
    /// which to load the validity of `localTime` as being unique, ambiguous
    /// but valid, or invalid.  Optionally specify a `dstPolicy` indicating
    /// whether or not `localTime` represents a daylight-saving time value.
    /// If `dstPolicy` is unspecified and `localTime` is a unique and valid
    /// time in the source time zone, then perform the conversion using that
    /// uniquely described time; if `dstPolicy` is unspecified and
    /// `localTime` is either ambiguous or invalid, then use the later of
    /// the two possible interpretations of `localTime`.  The offset from
    /// UTC of the time zone is rounded down to minute precision.  Return 0
    /// on success, and a non-zero value with no effect otherwise.  A return
    /// value of `ErrorCode::k_UNSUPPORTED_ID` indicates that `timeZoneId`
    /// was not recognized.  The behavior is undefined unless the result of
    /// the initialization falls within the supported epoch.
    static int initLocalTime(bdlt::DatetimeTz      *result,
                             const bdlt::Datetime&  localTime,
                             const char            *timeZoneId,
                             DstPolicy::Enum        dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);
    static int initLocalTime(LocalDatetime         *result,
                             const bdlt::Datetime&  localTime,
                             const char            *timeZoneId,
                             DstPolicy::Enum        dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);
    static int initLocalTime(bdlt::DatetimeTz        *result,
                             LocalTimeValidity::Enum *resultValidity,
                             const bdlt::Datetime&    localTime,
                             const char              *timeZoneId,
                             DstPolicy::Enum          dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);
    static int initLocalTime(LocalDatetime           *result,
                             LocalTimeValidity::Enum *resultValidity,
                             const bdlt::Datetime&    localTime,
                             const char              *timeZoneId,
                             DstPolicy::Enum          dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);

    /// Load, into the specified `result`, the UTC time value that
    /// corresponds to the specified `localTime` in the time zone indicated
    /// by the specified `timeZoneId`.  Optionally specify a `dstPolicy`
    /// indicating whether or not `localTime` represents a daylight-saving
    /// time value.  If `dstPolicy` is unspecified and `localTime` is a
    /// unique and valid time in the source time zone, then perform the
    /// conversion using that uniquely described time; if `dstPolicy` is
    /// unspecified and `localTime` is either ambiguous or invalid, then use
    /// the later of the two possible interpretations of `localTime`.  The
    /// offset from UTC of the time zone is rounded down to minute
    /// precision.  Return 0 on success, and a non-zero value with no effect
    /// otherwise.  A return value of `ErrorCode::k_UNSUPPORTED_ID`
    /// indicates that `timeZoneId` was not recognized.  The behavior is
    /// undefined unless the result of the conversion falls within the
    /// supported epoch.
    static int convertLocalToUtc(bdlt::Datetime        *result,
                                 const bdlt::Datetime&  localTime,
                                 const char            *timeZoneId,
                                 DstPolicy::Enum        dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);
    static int convertLocalToUtc(LocalDatetime         *result,
                                 const bdlt::Datetime&  localTime,
                                 const char            *timeZoneId,
                                 DstPolicy::Enum        dstPolicy =
                                                     DstPolicy::e_UNSPECIFIED);

    /// Load, into the specified `result`, attributes characterizing the
    /// specified `localTime` (i.e., the offset from UTC, whether
    /// daylight-saving time is in effect and the description of the time
    /// zone), as well as the time interval over which those attributes
    /// apply.  Return 0 on success, and a non-zero value with no effect
    /// otherwise.  A return value of `ErrorCode::k_UNSUPPORTED_ID`
    /// indicates that `localTime.timeZoneId()` was not recognized.
    static int loadLocalTimePeriod(LocalTimePeriod      *result,
                                   const LocalDatetime&  localTime);

    /// Load, into the specified `result`, attributes characterizing the
    /// specified `localTime` in the time zone indicated by the specified
    /// `timeZoneId` (i.e., the offset from UTC, whether daylight-saving
    /// time is in effect and the description of the time zone), as well as
    /// the time interval over which those attributes apply.  Return 0 on
    /// success, and a non-zero value with no effect otherwise.  A return
    /// value of `ErrorCode::k_UNSUPPORTED_ID` indicates that `timeZoneId`
    /// was not recognized.
    static int loadLocalTimePeriod(LocalTimePeriod         *result,
                                   const bdlt::DatetimeTz&  localTime,
                                   const char              *timeZoneId);

    /// Load, into the specified `result`, attributes characterizing local
    /// time at the specified `utcTime` in the time zone indicated by the
    /// specified `timeZoneId` (i.e., the offset from UTC, whether
    /// daylight-saving time is in effect and the description of the time
    /// zone), as well as the time interval over which those attributes
    /// apply.  Return 0 on success, and a non-zero value with no effect
    /// otherwise.  A return value of `ErrorCode::k_UNSUPPORTED_ID`
    /// indicates that `timeZoneId` was not recognized.
    static int loadLocalTimePeriodForUtc(LocalTimePeriod       *result,
                                         const char            *timeZoneId,
                                         const bdlt::Datetime&  utcTime);

    /// Load, into the specified `result`, the current local time value
    /// in the time zone indicated by the specified `timeZoneId`.  Return 0
    /// on success, and a non-zero value with no effect otherwise.  A
    /// return value of `ErrorCode::k_UNSUPPORTED_ID` indicates
    /// that `timeZoneid` is not recognized.
    static int now(bdlt::DatetimeTz *result, const char *timeZoneId);
    static int now(LocalDatetime *result, const char  *timeZoneId);

    /// Load, into the specified `result`, `true` if the offset from UTC of
    /// the specified `localTime` (i.e., `localTime.offset()`) is consistent
    /// with the actual local time offset, as indicated by time zone data,
    /// at the UTC time `localTime.utcDatetime()` in the time zone indicated
    /// by the specified `timeZoneId`, and `false` otherwise.  Return 0 on
    /// success, and a non-zero value with `false` loaded into `result`
    /// otherwise.  A return value of `ErrorCode::k_UNSUPPORTED_ID`
    /// indicates that `timeZoneId` is not recognized.  Note that this
    /// operation verifies that the properties of the provided local time
    /// are consistent with the time zone data.
    static int validateLocalTime(bool                    *result,
                                 const bdlt::DatetimeTz&  localTime,
                                 const char              *timeZoneId);

    /// Load, into the specified `result`, `true` if the time zone
    /// identifier of the specified `localTime` (i.e.,
    /// `localTime.timeZoneId()`) is a valid identifier, and the offset from
    /// UTC of `localTime` (i.e., `localTime.datetimeTz().offset()`) is
    /// consistent with the actual local time offset, as indicated by time
    /// zone data, at the UTC time `localTime.datetimeTz().utcDatetime()` in
    /// the time zone indicated by `localTime.timeZoneId()`, and `false`
    /// otherwise.  Return 0 on success, and a non-zero value with `false`
    /// loaded into `result` otherwise.  A return value of
    /// `ErrorCode::k_UNSUPPORTED_ID` indicates that `timeZoneId` is not
    /// recognized.  Note that this operation verifies that the properties
    /// of the provided local time are consistent with the time zone data.
    static int validateLocalTime(bool *result, const LocalDatetime& localTime);
};

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

                            // -------------------
                            // struct TimeZoneUtil
                            // -------------------

// CLASS METHODS
inline
int TimeZoneUtil::convertUtcToLocalTime(
                                       bdlt::DatetimeTz      *result,
                                       const char            *targetTimeZoneId,
                                       const bdlt::Datetime&  utcTime)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(targetTimeZoneId);

    return TimeZoneUtilImp::convertUtcToLocalTime(
                                         result,
                                         targetTimeZoneId,
                                         utcTime,
                                         DefaultZoneinfoCache::defaultCache());
}

inline
int TimeZoneUtil::convertLocalToLocalTime(
                                        LocalDatetime        *result,
                                        const char           *targetTimeZoneId,
                                        const LocalDatetime&  srcTime)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(targetTimeZoneId);

    return convertUtcToLocalTime(result,
                                 targetTimeZoneId,
                                 srcTime.datetimeTz().utcDatetime());
}

inline
int TimeZoneUtil::convertLocalToLocalTime(
                                     LocalDatetime           *result,
                                     const char              *targetTimeZoneId,
                                     const bdlt::DatetimeTz&  srcTime)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(targetTimeZoneId);

    return convertUtcToLocalTime(result,
                                 targetTimeZoneId,
                                 srcTime.utcDatetime());
}

inline
int TimeZoneUtil::convertLocalToLocalTime(
                                        bdlt::DatetimeTz     *result,
                                        const char           *targetTimeZoneId,
                                        const LocalDatetime&  srcTime)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(targetTimeZoneId);

    return convertUtcToLocalTime(result,
                                 targetTimeZoneId,
                                 srcTime.datetimeTz().utcDatetime());
}

inline
int TimeZoneUtil::convertLocalToLocalTime(
                                     bdlt::DatetimeTz        *result,
                                     const char              *targetTimeZoneId,
                                     const bdlt::DatetimeTz&  srcTime)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(targetTimeZoneId);

    return convertUtcToLocalTime(result,
                                 targetTimeZoneId,
                                 srcTime.utcDatetime());
}

inline
int TimeZoneUtil::initLocalTime(bdlt::DatetimeTz        *result,
                                LocalTimeValidity::Enum *resultValidity,
                                const bdlt::Datetime&    localTime,
                                const char              *timeZoneId,
                                DstPolicy::Enum          dstPolicy)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(resultValidity);
    BSLS_ASSERT(timeZoneId);

    return TimeZoneUtilImp::initLocalTime(
                                         result,
                                         resultValidity,
                                         localTime,
                                         timeZoneId,
                                         dstPolicy,
                                         DefaultZoneinfoCache::defaultCache());
}

inline
int TimeZoneUtil::initLocalTime(bdlt::DatetimeTz     *result,
                               const bdlt::Datetime&  localTime,
                               const char            *timeZoneId,
                               DstPolicy::Enum        dstPolicy)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(timeZoneId);

    LocalTimeValidity::Enum validityStatus;
    return initLocalTime(result,
                         &validityStatus,
                         localTime,
                         timeZoneId,
                         dstPolicy);
}

inline
int TimeZoneUtil::loadLocalTimePeriod(LocalTimePeriod      *result,
                                      const LocalDatetime&  localTime)
{
    BSLS_ASSERT(result);

    return loadLocalTimePeriod(result,
                               localTime.datetimeTz(),
                               localTime.timeZoneId().c_str());
}

inline
int TimeZoneUtil::loadLocalTimePeriod(LocalTimePeriod         *result,
                                      const bdlt::DatetimeTz&  localTime,
                                      const char              *timeZoneId)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(timeZoneId);

    return loadLocalTimePeriodForUtc(result,
                                     timeZoneId,
                                     localTime.utcDatetime());
}

inline
int TimeZoneUtil::now(bdlt::DatetimeTz *result, const char *timeZoneId)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(timeZoneId);

    bdlt::Datetime utcNow = bdlt::CurrentTime::utc();
    return convertUtcToLocalTime(result, timeZoneId, utcNow);
}

inline
int TimeZoneUtil::now(LocalDatetime *result, const char *timeZoneId)
{
    BSLS_ASSERT(result);
    BSLS_ASSERT(timeZoneId);

    bdlt::Datetime utcNow = bdlt::CurrentTime::utc();
    return convertUtcToLocalTime(result, timeZoneId, utcNow);
}

inline
int TimeZoneUtil::validateLocalTime(bool                 *result,
                                    const LocalDatetime&  localTime)
{
    BSLS_ASSERT(result);

    return validateLocalTime(result,
                             localTime.datetimeTz(),
                             localTime.timeZoneId().c_str());
}

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

#endif

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