// bdlt_localtimeoffset.h                                             -*-C++-*-
#ifndef INCLUDED_BDLT_LOCALTIMEOFFSET
#define INCLUDED_BDLT_LOCALTIMEOFFSET

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

//@PURPOSE: Provide utilities to retrieve the local time offset.
//
//@CLASSES:
//   bdlt::LocalTimeOffset: namespace for local time offset functions
//
//@SEE_ALSO: bsls_timeinterval, bsls_systemtime, bdlt_currenttime
//
//@DESCRIPTION: This component provides a `struct`, `bdlt::LocalTimeOffset`, in
// which are defined a series of static methods for using a callback function
// to retrieve the local time offset (the difference between the currently
// executing task's local time and UTC time) at a specified UTC date and time.
// `LocalTimeoffset` provides a function `localTimeOffset` that delegates to
// the currently installed local time offset callback.  By default,
// `localTimeOffsetDefault` is installed as the local time offset callback.
// Clients can configure the default callback function by calling the
// `setLocalTimeOffsetCallback` function.
//
///Thread Safety
///-------------
// The functions provided by `bdlt::LocalTimeOffset` are *thread-safe* (meaning
// they may be called concurrently from multiple threads), including those that
// set and retrieve the callback function.  In addition, user-supplied callback
// functions must be *thread-safe*.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic `bdlt::LocalTimeOffset` Usage
/// - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates how to use `bdlt::LocalTimeOffset`.
//
// First, obtain the current UTC time - ignoring milliseconds - using
// `bsls::SystemTime` and `bdlt::EpochUtil` (note that clients may prefer
// `bdlt_currenttime`, which is not shown here for dependency reasons):
// ```
// bsls::TimeInterval now = bsls::SystemTime::nowRealtimeClock();
//
// bdlt::Datetime utc = bdlt::EpochUtil::epoch() +
//                             bdlt::DatetimeInterval(0, 0, 0, now.seconds());
// ```
// Then, obtain the local time offset:
// ```
// bsls::TimeInterval localOffset =
//                                bdlt::LocalTimeOffset::localTimeOffset(utc);
// ```
// Next, add the offset to the UTC time to obtain the local time:
// ```
// bdlt::Datetime local = utc;
// local.addSeconds(localOffset.seconds());
// ```
// Finally, stream the two time values to `stdout`:
// ```
// bsl::cout << "utc   = " << utc << bsl::endl;
// bsl::cout << "local = " << local << bsl::endl;
// ```
// The streaming operator produces output in the following format on `stdout`:
// ```
// utc   = ddMONyyyy_hh:mm::ss.000
// local = ddMONyyyy_hh:mm::ss.000
// ```
//
///Example 2: Using the Local Time Offset Callback
///- - - - - - - - - - - - - - - - - - - - - - - -
// Suppose one has to provide time stamp values that always reflect local time
// for a given location, even when local time transitions into and out of
// daylight saving time.  Further suppose that one must do this quite often
// (e.g., for every record in a high frequency log), so the performance of the
// default method for calculating local time offset is not adequate.  Creation
// and installation of a specialized user-defined callback for local time
// offset allows one to solve this problem.
//
// First, create a utility class that provides a method of type
// `bdlt::LocalTimeOffset::LocalTimeOffsetCallback` that is valid for the
// location of interest (New York) for the period of interest (the year 2013).
// ```
// struct MyLocalTimeOffsetUtilNewYork2013 {
//
//   private:
//     // DATA
//     static int            s_useCount;
//     static bdlt::Datetime s_startOfDaylightSavingTime;  // UTC Datetime
//     static bdlt::Datetime s_resumptionOfStandardTime;   // UTC Datetime
//
//   public:
//     // CLASS METHODS
//
//     /// Return a `bsls::TimeInterval` value representing the difference
//     /// between the local time for the "America/New_York" timezone and
//     /// UTC time at the specified `utcDatetime`.  The behavior is
//     /// undefined unless `2013 == utcDatetime.date().year()`.
//     static bsls::TimeInterval localTimeOffset(
//                                         const bdlt::Datetime& utcDatetime);
//
//     /// Return the number of invocations of the `localTimeOffset` since
//     /// the start of the process.
//     static int useCount();
// };
//
// // DATA
// int MyLocalTimeOffsetUtilNewYork2013::s_useCount = 0;
//
// bdlt::Datetime
// MyLocalTimeOffsetUtilNewYork2013::s_startOfDaylightSavingTime(2013,
//                                                                  3,
//                                                                 10,
//                                                                  7);
// bdlt::Datetime
// MyLocalTimeOffsetUtilNewYork2013::s_resumptionOfStandardTime(2013,
//                                                                11,
//                                                                 3,
//                                                                 6);
//
// // CLASS METHODS
// bsls::TimeInterval MyLocalTimeOffsetUtilNewYork2013::localTimeOffset(
//                                          const bdlt::Datetime& utcDatetime)
// {
//     assert(2013 == utcDatetime.date().year());
//
//     ++s_useCount;
//     int seconds = utcDatetime < s_startOfDaylightSavingTime ? -18000 :
//                   utcDatetime < s_resumptionOfStandardTime  ? -14400 :
//                                                               -18000;
//     return bsls::TimeInterval(seconds, 0);
// }
//
// int MyLocalTimeOffsetUtilNewYork2013::useCount()
// {
//     return s_useCount;
// }
// ```
// Note that the transition times into and out of daylight saving for New York
// are given in UTC.  Also notice that we do not attempt to make the
// `localTimeOffset` method `inline`, since we must take its address to install
// it as the callback.
//
// Then, we install this `localTimeOffset` as the local time offset callback.
// ```
// bdlt::LocalTimeOffset::LocalTimeOffsetCallback defaultCallback =
//                          bdlt::LocalTimeOffset::setLocalTimeOffsetCallback(
//                                         &MyLocalTimeOffsetUtilNewYork2013::
//                                                           localTimeOffset);
//
// assert(bdlt::LocalTimeOffset::localTimeOffsetDefault == defaultCallback);
// assert(&MyLocalTimeOffsetUtilNewYork2013::localTimeOffset
//                       == bdlt::LocalTimeOffset::localTimeOffsetCallback());
// ```
// Now, we can use the `bdlt::LocalTimeOffset::localTimeOffset` method to
// obtain the local time offsets in New York on several dates of interest.  The
// increasing values from our `useCount` method assures us that the callback we
// defined is indeed being used.
// ```
// assert(0 == MyLocalTimeOffsetUtilNewYork2013::useCount());
//
// bsls::Types::Int64 offset;
// bdlt::Datetime     newYearsDay(2013,  1,  1);
// bdlt::Datetime     independenceDay(2013,  7,  4);
// bdlt::Datetime     newYearsEve(2013, 12, 31);
//
// offset = bdlt::LocalTimeOffset::localTimeOffset(newYearsDay).seconds();
// assert(-5 * 3600 == offset);
// assert(        1 == MyLocalTimeOffsetUtilNewYork2013::useCount());
//
// offset = bdlt::LocalTimeOffset::localTimeOffset(independenceDay).seconds();
// assert(-4 * 3600 == offset);
// assert(        2 == MyLocalTimeOffsetUtilNewYork2013::useCount());
//
// offset = bdlt::LocalTimeOffset::localTimeOffset(newYearsEve).seconds();
// assert(-5 * 3600 == offset);
// assert(        3 == MyLocalTimeOffsetUtilNewYork2013::useCount());
// ```
// Finally, to be neat, we restore the local time offset callback to the
// default callback:
// ```
// bdlt::LocalTimeOffset::setLocalTimeOffsetCallback(defaultCallback);
// assert(&bdlt::LocalTimeOffset::localTimeOffsetDefault
//                       == bdlt::LocalTimeOffset::localTimeOffsetCallback());
// ```

#include <bdlscm_version.h>

#include <bdlt_datetime.h>

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

namespace BloombergLP {
namespace bdlt {

                            // =====================
                            // class LocalTimeOffset
                            // =====================

/// This `struct` provides a namespace for local-time-offset procedures
/// including a configurable global callback mechanism.  The use of these
/// procedures is thread-safe (see `Thread Safety`).
struct LocalTimeOffset {

    // TYPES

    /// `LocalTimeOffsetCallback` is an alias for the type of a function
    /// that returns a `bsls::TimeInterval` value representing the
    /// difference between local time and UTC time at the specified
    /// `utcDatetime`.  This function must be thread-safe in multi-threaded
    /// builds.  Note that the installed callback function must have
    /// geographic information specifying the local timezone.
    typedef bsls::TimeInterval (*LocalTimeOffsetCallback)(
                                                  const Datetime& utcDatetime);

  private:
    static bsls::AtomicOperations::AtomicTypes::Pointer
                                  s_localTimeOffsetCallback_p;
                                       // address of local-time-offset callback

  public:
    // CLASS METHODS

                        // ** computation method **

    /// Return a `bsls::TimeInterval` value representing the difference
    /// between local time and UTC time at the specified `utcDatetime`.
    /// This method uses the currently installed local-time-offset callback
    /// mechanism.
    static bsls::TimeInterval localTimeOffset(const Datetime& utcDatetime);

                        // ** default callback **

    /// Return a `bsls::TimeInterval` value representing the difference
    /// between local time and UTC time at the specified `utcDatetime`.
    /// Note that the local time zone is determined by the `TZ` environment
    /// variable in the same manner as the `localtime` POSIX function.
    static bsls::TimeInterval localTimeOffsetDefault(
                                                  const Datetime& utcDatetime);

                        // ** set callback **

    /// Set the specified `callback` as the function to be used to return a
    /// `bsls::TimeInterval` value representing the difference between
    /// local time and UTC time at a specified UTC date and time.  Return
    /// the previously installed `LocalTimeOffsetCallback` function.  The
    /// behavior is undefined unless `0 != callback`.
    static LocalTimeOffsetCallback setLocalTimeOffsetCallback(
                                             LocalTimeOffsetCallback callback);

                        // ** get current callback **

    /// Return the currently installed `LocalTimeOffsetCallback` function.
    static LocalTimeOffsetCallback localTimeOffsetCallback();
};

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

                        // ** computation method **

inline
bsls::TimeInterval LocalTimeOffset::
                                   localTimeOffset(const Datetime& utcDatetime)
{
    return localTimeOffsetCallback()(utcDatetime);
}

                        // ** set callback **

inline
LocalTimeOffset::LocalTimeOffsetCallback
  LocalTimeOffset::setLocalTimeOffsetCallback(LocalTimeOffsetCallback callback)
{
    BSLS_ASSERT(callback);

    LocalTimeOffsetCallback previousCallback = localTimeOffsetCallback();
    bsls::AtomicOperations::setPtrRelease(
        &s_localTimeOffsetCallback_p,
        reinterpret_cast<void *>(
            reinterpret_cast<bsls::Types::IntPtr>(callback)));

    return previousCallback;
}

                        // ** get current callback **

inline
LocalTimeOffset::LocalTimeOffsetCallback
                                     LocalTimeOffset::localTimeOffsetCallback()
{
    return reinterpret_cast<LocalTimeOffsetCallback>(
        reinterpret_cast<bsls::Types::IntPtr>(
            bsls::AtomicOperations::getPtrAcquire(
                &s_localTimeOffsetCallback_p)));
}

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

#endif

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