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

namespace BloombergLP {
namespace bdlt {

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

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.

  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
    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 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 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 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 convertFromHHMMSSmmm(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 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 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 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 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 convertToHHMMSSmmm(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 bool isValidHHMM(int timeValue);
        // 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 isValidHHMMSS(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 isValidHHMMSSmmm(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'.
};

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

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