// bdlt_timeutil.h                                                    -*-C++-*-
#ifndef INCLUDED_BDLT_TIMEUTIL
#define INCLUDED_BDLT_TIMEUTIL

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

//@PURPOSE: Provide common non-primitive operations on `bdlt::Time`.
//
//@CLASSES:
//  bdlt::TimeUtil: namespace for static functions operating on `bdlt::Time`
//
//@SEE_ALSO: bdlt_time
//
//@DESCRIPTION: This component provides non-primitive operations on
// `bdlt::Time` objects.  In particular, the `bdlt::TimeUtil` namespace defined
// in this component provides conversions among `bdlt::Time` values and their
// corresponding non-negative integral values (e.g., `convertFromHHMM`,
// `convertToHHMMSSmmm`), and methods to validate such integral values (e.g.,
// `isValidHHMMSS`) before passing them to the corresponding "convertFrom"
// method.
//
///Converting from Seconds-from-Midnight to `bdlt::Datetime`
///---------------------------------------------------------
// Seconds-from-midnight is a date-time representation used in some financial
// applications.  Seconds-from-midnight is a lossy representation (using
// heuristics to determine the date), and conversions for that representation
// are provided in a higher-level package that is not part of the
// BDE open-source libraries (see `bsitzo_secondsfrommidnightutil`).
//
///Usage
///------
// Following are examples illustrating basic use of this component.
//
///Example 1
///- - - - -
// First, we demonstrate how to use `bdlt::TimeUtil` to
// convert from an integer representation of time in "HHMMSSmmm" format to a
// `bdlt::Time`.  Our first time will be around 3:45 pm.
// ```
// //      format: HHMMSSmmm
// int timeValue = 154502789;
//
// bdlt::Time result = bdlt::TimeUtil::convertFromHHMMSSmmm(timeValue);
//
// bsl::cout << result << bsl::endl;
// ```
// The code above produces the following on `stdout`:
// ```
// 15:45:02.789
// ```
// Then, we demonstrate a different time, 3:32:24.832 am.  Note that we do not
// lead the integer value with `0`:
// ```
// //  format: HHMMSSmmm
// timeValue =  33224832;      // Do not start with leading '0' as that would
//                             // make the value octal and incorrect.
//
// result = bdlt::TimeUtil::convertFromHHMMSSmmm(timeValue);
//
// bsl::cout << result << bsl::endl;
// ```
// The code above produces the following on `stdout`:
// ```
// 03:32:24.832
// ```
// Now, we demonstrate how `bdlt::TimeUtil` provides methods that can be used
// to validate integral time values before passing them to the various
// "convert" methods.  For example:
// ```
// assert( bdlt::TimeUtil::isValidHHMMSSmmm(timeValue));
// ```
// Finally, we demonstrate catching an invalid time value, 12:61:02.789 pm:
// ```
// //         format: HHMMSSmmm
// int badTimeValue = 126102789;
//
// assert(!bdlt::TimeUtil::isValidHHMMSSmmm(badTimeValue));
// ```
//
///Example 2
///- - - - -
// The following snippet of code demonstrates how to use `bdlt::TimeUtil` to
// convert from a `bdlt::Time` to an integer representation of time in "HHMM",
// "HHMMSS", and "HHMMSSmmm" formats:
// ```
// bdlt::Time time(12, 45, 2, 789);
// int        timeHHMM      = bdlt::TimeUtil::convertToHHMM(time);
// int        timeHHMMSS    = bdlt::TimeUtil::convertToHHMMSS(time);
// int        timeHHMMSSmmm = bdlt::TimeUtil::convertToHHMMSSmmm(time);
//
// bsl::cout << "Time in HHMM:      " << timeHHMM      << bsl::endl;
// bsl::cout << "Time in HHMMSS:    " << timeHHMMSS    << bsl::endl;
// bsl::cout << "Time in HHMMSSmmm: " << timeHHMMSSmmm << bsl::endl;
// ```
// The code above produces the following on `stdout`:
// ```
// Time in HHMM:      1245
// Time in HHMMSS:    124502
// Time in HHMMSSmmm: 124502789
// ```
// Note that the millisecond and/or second fields of `bdlt::Time` are ignored
// depending on the conversion method that is called.

#include <bdlscm_version.h>

#include <bdlt_time.h>

#include <bsls_assert.h>

#include <bsl_optional.h>

namespace BloombergLP {
namespace bdlt {

                                // ===============
                                // struct TimeUtil
                                // ===============

/// This `struct` provides a namespace for common non-primitive procedures
/// that operate on `Time` objects.  These methods are alias-safe and
/// exception-neutral.
struct TimeUtil {

  private:
    // PRIVATE TYPES
    enum {
        k_HHMMSSMMM_HH_FACTOR = 10000000,
        k_HHMMSSMMM_MM_FACTOR =   100000,
        k_HHMMSSMMM_SS_FACTOR =     1000,

        k_HHMMSS_HH_FACTOR    =    10000,
        k_HHMMSS_MM_FACTOR    =      100,

        k_HHMM_HH_FACTOR      =      100
    };

  public:
    // CLASS METHODS

    /// Return the `bdlt::Time` value corresponding to the specified
    /// `timeValue`, where `timeValue` is a non-negative integer that, when
    /// expressed in decimal notation, contains exactly four digits
    /// (counting leading zeros, if any): two digits for the hour and two
    /// digits for the minute.  For example, 309 is converted to
    /// `Time(3, 9)` (03:09:00.000).  More formally, `timeValue` is
    /// interpreted as:
    /// ```
    /// hour * 100 + minute
    /// ```
    /// The behavior is undefined unless `timeValue` represents a valid time
    /// in the allowable range for `bdlt::Time`
    /// (00:00:00.000 - 23:59:00.000, and 24:00:00.000).
    static Time convertFromHHMM(int timeValue);

    /// Return the `bdlt::Time` value corresponding to the specified
    /// `timeValue`, where `timeValue` is a non-negative integer that, when
    /// expressed in decimal notation, contains exactly six digits (counting
    /// leading zeros, if any): two digits for the hour, two digits for the
    /// minute, and two digits for the second.  For example, 30907 is
    /// converted to `Time(3, 9, 7)` (03:09:07.000).  More formally,
    /// `timeValue` is interpreted as:
    /// ```
    /// hour * 10000 + minute * 100 + second
    /// ```
    /// The behavior is undefined unless `timeValue` represents a valid time
    /// in the allowable range for `bdlt::Time`
    /// (00:00:00.000 - 23:59:59.000, and 24:00:00.000).
    static Time convertFromHHMMSS(int timeValue);

    /// Return the `bdlt::Time` value corresponding to the specified
    /// `timeValue`, where `timeValue` is a non-negative integer that, when
    /// expressed in decimal notation, contains exactly nine digits
    /// (counting leading zeros, if any): two digits for the hour, two
    /// digits for the minute, two digits for the second, and three digits
    /// for the millisecond.  For example, 30907056 is converted to
    /// `Time(3, 9, 7, 56)` (03:09:07.056).  More formally, `timeValue` is
    /// interpreted as:
    /// ```
    /// hour * 10000000 + minute * 100000 + second * 1000 + millisecond
    /// ```
    /// The behavior is undefined unless `timeValue` represents a valid time
    /// in the allowable range for `bdlt::Time`
    /// (00:00:00.000 - 23:59:59.999, and 24:00:00.000).
    static Time convertFromHHMMSSmmm(int timeValue);

    /// Return the non-negative integer representing the same time as the
    /// specified `value` that, when expressed in decimal notation, contains
    /// exactly four digits (counting leading zeros, if any): two digits for
    /// the hour and two digits for the minute.  For example,
    /// `Time(3, 9, sec, ms)`, where `0 <= sec < 60` and `0 <= ms < 1000`,
    /// is converted to 309.  More formally, this method returns:
    /// ```
    /// value.hour() * 100 + value.minute()
    /// ```
    static int convertToHHMM(const Time& value);

    /// Return the non-negative integer representing the same time as the
    /// specified `value` that, when expressed in decimal notation, contains
    /// exactly six digits (counting leading zeros, if any): two digits for
    /// the hour, two digits for the minute, and two digits for the second.
    /// For example, `Time(3, 9, 7, ms)`, where `0 <= ms < 1000`, is
    /// converted to 30907.  More formally, this method returns:
    /// ```
    /// value.hour() * 10000 + value.minute() * 100 + value.second()
    /// ```
    static int convertToHHMMSS(const Time& value);

    /// Return the non-negative integer representing the same time as the
    /// specified `value` that, when expressed in decimal notation, contains
    /// exactly nine digits (counting leading zeros, if any): two digits for
    /// the hour, two digits for the minute, two digits for the second, and
    /// three digits for the millisecond.  For example, `Time(3, 9, 7, 56)`
    /// is converted to 30907056.  More formally, this method returns:
    /// ```
    /// value.hour() * 10000000 + value.minute() * 100000
    ///                         + value.second() * 1000
    ///                         + value.millisecond()
    /// ```
    static int convertToHHMMSSmmm(const Time& value);

    /// Return an `optional` having a `Time` with the specified `hour`,
    /// `minute`, `second`, and the optionally specified `millisecond` and
    /// `microsecond`, if those form a valid `Time` (see `Time::isValid`);
    /// otherwise return an `optional` without a value.
    static bsl::optional<Time> fromHms(int hour,
                                       int minute,
                                       int second,
                                       int millisecond = 0,
                                       int microsecond = 0);

    /// Return `true` if the specified `timeValue` is a non-negative integer
    /// that represents a valid four-digit time value suitable for passing
    /// to `convertFromHHMM`, and `false` otherwise.  `timeValue` is a valid
    /// four-digit time value if, when expressed in decimal notation, it
    /// contains exactly four digits (counting leading zeros, if any): two
    /// digits for the hour and two digits for the minute, where either
    /// `0 <= hour < 24` and `0 <= minute < 60`, or `2400 == timeValue`.
    static bool isValidHHMM(int timeValue);

    /// Return `true` if the specified `timeValue` is a non-negative integer
    /// that represents a valid six-digit time value suitable for passing to
    /// `convertFromHHMMSS`, and `false` otherwise.  `timeValue` is a valid
    /// six-digit time value if, when expressed in decimal notation, it
    /// contains exactly six digits (counting leading zeros, if any): two
    /// digits for the hour, two digits for the minute, and two digits for
    /// the second, where either `0 <= hour < 24`, `0 <= minute < 60`, and
    /// `0 <= second < 60`, or `240000 == timeValue`.
    static bool isValidHHMMSS(int timeValue);

    /// Return `true` if the specified `timeValue` is a non-negative integer
    /// that represents a valid nine-digit time value suitable for passing
    /// to `convertFromHHMMSSmmm`, and `false` otherwise.  `timeValue` is a
    /// valid nine-digit time value if, when expressed in decimal notation,
    /// it contains exactly nine digits (counting leading zeros, if any):
    /// two digits for the hour, two digits for the minute, two digits for
    /// the second, and three digits for the millisecond, where either
    /// `0 <= hour < 24`, `0 <= minute < 60`, `0 <= second < 60`, and
    /// `0 <= millisecond < 1000`, or `240000000 == timeValue`.
    static bool isValidHHMMSSmmm(int timeValue);
};

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

                               // ---------------
                               // struct TimeUtil
                               // ---------------

                              // -----------------
                              // Level-0 Functions
                              // -----------------

// CLASS METHODS
inline
bool TimeUtil::isValidHHMM(int timeValue)
{
    return Time::isValid(timeValue / k_HHMM_HH_FACTOR,
                         timeValue % k_HHMM_HH_FACTOR);
}

inline
bool TimeUtil::isValidHHMMSS(int timeValue)
{
    return Time::isValid(timeValue / k_HHMMSS_HH_FACTOR,
                        (timeValue % k_HHMMSS_HH_FACTOR) / k_HHMMSS_MM_FACTOR,
                         timeValue % k_HHMMSS_MM_FACTOR);
}

inline
bool TimeUtil::isValidHHMMSSmmm(int timeValue)
{
    return Time::isValid(
                    timeValue / k_HHMMSSMMM_HH_FACTOR,
                   (timeValue % k_HHMMSSMMM_HH_FACTOR) / k_HHMMSSMMM_MM_FACTOR,
                   (timeValue % k_HHMMSSMMM_MM_FACTOR) / k_HHMMSSMMM_SS_FACTOR,
                    timeValue % k_HHMMSSMMM_SS_FACTOR);
}

                            // -------------------
                            // All Other Functions
                            // -------------------

inline
Time TimeUtil::convertFromHHMM(int timeValue)
{
    BSLS_ASSERT_SAFE(TimeUtil::isValidHHMM(timeValue));

    return Time(timeValue / k_HHMM_HH_FACTOR,
                timeValue % k_HHMM_HH_FACTOR);
}

inline
Time TimeUtil::convertFromHHMMSS(int timeValue)
{
    BSLS_ASSERT_SAFE(TimeUtil::isValidHHMMSS(timeValue));

    return Time(timeValue / k_HHMMSS_HH_FACTOR,
               (timeValue % k_HHMMSS_HH_FACTOR) / k_HHMMSS_MM_FACTOR,
                timeValue % k_HHMMSS_MM_FACTOR);
}

inline
Time TimeUtil::convertFromHHMMSSmmm(int timeValue)
{
    BSLS_ASSERT_SAFE(TimeUtil::isValidHHMMSSmmm(timeValue));

    return Time(timeValue / k_HHMMSSMMM_HH_FACTOR,
               (timeValue % k_HHMMSSMMM_HH_FACTOR) / k_HHMMSSMMM_MM_FACTOR,
               (timeValue % k_HHMMSSMMM_MM_FACTOR) / k_HHMMSSMMM_SS_FACTOR,
                timeValue % k_HHMMSSMMM_SS_FACTOR);
}

inline
int TimeUtil::convertToHHMM(const Time& value)
{
    return value.hour() * k_HHMM_HH_FACTOR + value.minute();
}

inline
int TimeUtil::convertToHHMMSS(const Time& value)
{
    return value.hour()   * k_HHMMSS_HH_FACTOR
         + value.minute() * k_HHMMSS_MM_FACTOR
         + value.second();
}

inline
int TimeUtil::convertToHHMMSSmmm(const Time& value)
{
    return value.hour()   * k_HHMMSSMMM_HH_FACTOR
         + value.minute() * k_HHMMSSMMM_MM_FACTOR
         + value.second() * k_HHMMSSMMM_SS_FACTOR
         + value.millisecond();
}

inline
bsl::optional<Time> TimeUtil::fromHms(int hour,
                                      int minute,
                                      int second,
                                      int millisecond,
                                      int microsecond)
{
    return Time::isValid(hour, minute, second, millisecond, microsecond)
        ? bsl::optional<Time>(bsl::in_place,
                              hour,
                              minute,
                              second,
                              millisecond,
                              microsecond)
        : bsl::nullopt;
}

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