HPTime.h

// HPTime.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2004

#pragma once
#ifndef Ceda_cxUtils_HPTime_H
#define Ceda_cxUtils_HPTime_H

#include "MsWindows.h"
#include "cxUtils.h"
#include "xostream.h"
#include "xstring.h"
#include "CedaAssert.h"
#include "StringStream.h"
#include <ctime>
#include <atomic>

#if !defined(_WIN32)
    cxUtils_API void Sleep(int millisec);
#endif

#ifdef _WIN32
    #define CEDA_USE_WIN32_HPTIME 1
#else
    #define CEDA_USE_WIN32_HPTIME 0
#endif

namespace ceda
{
typedef uint64 HPTIME_TICKS;

///////////////////////////////////////////////////////////////////////////////////////////////////
// HPTime

// Represents either a time stamp or a time span with high precision (to 100 nanosecs) and will not
// overflow for 58000 years.  Time is a scalar quantity and therefore can be added, differenced etc.
// Stored as UTC (so independent of the local time)

class cxUtils_API HPTime
{
public:
    // Get the current time.  This uses the high precision timer so its short term accuracy is very good
    static HPTime GetCurrentTime();

    static HPTime FromTicks(HPTIME_TICKS ticks) { HPTime ht; ht.m_ticks = ticks; return ht; }
    #ifdef _WIN32
        static HPTime FromFileTime(FILETIME ft) { HPTime ht; ht.m_ticks = reinterpret_cast<HPTIME_TICKS&>(ft); return ht; }
    #endif

    HPTime() : m_ticks(0) {}

    // Conversiom from double (in seconds) is useful when this HPTime represents a time span
    //HPTime(double secs) : m_ticks((HPTIME_TICKS) (secs * 1.0E7)) {}
    
    bool IsZero() const { return m_ticks == 0; }
    void SetZero() { m_ticks = 0; }

    // Get the time in the format hh:mm:ss.sss...   where the seconds uses the given
    // number of decimal places 
    xstring TimeStampToTimeString(int numDecPlaces) const;
    xstring TimeSpanToTimeString(int numDecPlaces) const;

    std::time_t to_time_t() const;
    void from_time_t(std::time_t t);

    /*
    The localtime() function converts the calendar time timep to broken-down time 
    representation, expressed relative to the user's specified timezone.

    Broken-down time is stored in the structure tm which is defined in <time.h> as follows:

        struct tm 
        {
            int tm_sec;     The number of seconds after the minute, normally in the range 0 to 59, 
                            but can be up to 60 to allow for leap seconds.

            int tm_min;     The number of minutes after the hour, in the range 0 to 59.

            int tm_hour;    The number of hours past midnight, in the range 0 to 23.

            int tm_mday;    The day of the month, in the range 1 to 31.

            int tm_mon;     The number of months since January, in the range 0 to 11.

            int tm_year;    The number of years since 1900.

            int tm_wday;    The number of days since Sunday, in the range 0 to 6.

            int tm_yday;    The number of days since January 1, in the range 0 to 365.

            int tm_isdst;   A flag that indicates whether daylight saving time is in effect at the 
                            time described. The value is positive if daylight saving time is in effect, 
                            zero if it is not, and negative if the information is not available.
        };
    */
    bool localtime(std::tm* result) const;

    /*
    Converts to broken-down time representation, expressed in Coordinated Universal Time (UTC). It 
    may return false when the year does not fit into an integer.
    */
    bool gmtime(std::tm* result) const;

    /*
    Sets this HPTime from a broken-down time structure, expressed as local time, to calendar time 
    representation. The function ignores the values supplied by the caller in the tm_wday and 
    tm_yday fields. The value specified in the tm_isdst field informs mktime() whether or not 
    daylight saving time (DST) is in effect for the time supplied in the tm structure: a positive 
    value means DST is in effect; zero means that DST is not in effect; and a negative value means 
    that mktime() should (use timezone information and system databases to) attempt to determine 
    whether DST is in effect at the specified time.

    The mktime() function modifies the fields of the tm structure as follows: tm_wday and tm_yday 
    are set to values determined from the contents of the other fields; if structure members are 
    outside their valid interval, they will be normalized (so that, for example, 40 October is 
    changed into 9 November); tm_isdst is set (regardless of its initial value) to a positive 
    value or to 0, respectively, to indicate whether DST is or is not in effect at the specified 
    time. Calling mktime() also sets the external variable tzname with information about the 
    current timezone.

    If the specified broken-down time cannot be represented as calendar time (seconds since the 
    Epoch), mktime() returns false and does not alter the members of the broken-down time 
    structure.
    */
    bool mktime(std::tm* tm);
    
    /*
    (see standard C++ documentation for strftime)

     Specifier          Replaced by                                                     Example
      --------------------------------------------------------------------------------------------------
        %a      Abbreviated weekday name *                                              Thu
        %A      Full weekday name *                                                     Thursday
        %b      Abbreviated month name *                                                Aug
        %B      Full month name *                                                       August
        %c      Date and time representation *                                          Thu Aug 23 14:55:02 2001
        %C      Year divided by 100 and truncated to integer (00-99)                    20
        %d      Day of the month, zero-padded (01-31)                                   23
        %D      Short MM/DD/YY date, equivalent to %m/%d/%y                             08/23/01
        %e      Day of the month, space-padded ( 1-31)                                  23
        %F      Short YYYY-MM-DD date, equivalent to %Y-%m-%d                           2001-08-23
        %g      Week-based year, last two digits (00-99)                                01
        %G      Week-based year                                                         2001
        %h      Abbreviated month name * (same as %b)                                   Aug
        %H      Hour in 24h format (00-23)                                              14
        %I      Hour in 12h format (01-12)                                              02
        %j      Day of the year (001-366)                                               235
        %m      Month as a decimal number (01-12)                                       08
        %M      Minute (00-59)                                                          55
        %n      New-line character ('\n')
        %p      AM or PM designation                                                    PM
        %r      12-hour clock time *                                                    02:55:02 pm
        %R      24-hour HH:MM time, equivalent to %H:%M                                 14:55
        %S      Second (00-61)                                                          02
        %t      Horizontal-tab character ('\t')
        %T      ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S                 14:55:02
        %u      ISO 8601 weekday as number with Monday as 1 (1-7)                       4
        %U      Week number with the first Sunday as the first day of week one (00-53)  33
        %V      ISO 8601 week number (00-53)                                            34
        %w      Weekday as a decimal number with Sunday as 0 (0-6)                      4
        %W      Week number with the first Monday as the first day of week one (00-53)  34
        %x      Date representation *                                                   08/23/01
        %X      Time representation *                                                   14:55:02
        %y      Year, last two digits (00-99)                                           01
        %Y      Year                                                                    2001
        %z      ISO 8601 offset from UTC in timezone (1 minute=1, 1 hour=100)
                If timezone cannot be termined, no characters                           +100
        %Z      Timezone name or abbreviation *
                If timezone cannot be termined, no characters                           CDT
        %%      A % sign                                                                %
    */
    xstring FormatTimeStamp2(ConstStringZ format) const;

    /*
    DEPRECATED

    s is a pointer to a format picture string to use to form the time string. If s is nullptr, the 
    function uses the time format of the user default locale. 

    Use the following elements to construct a format picture string. If you use spaces to separate 
    the elements in the format string, these spaces will appear in the same location in the output 
    string. The letters must be in uppercase or lowercase as shown (for example, "ss", not "SS"). 
    Characters in the format string that are enclosed in single quotation marks will appear in the 
    same location and unchanged in the output string. 

    Picture     Meaning 
    ---------------------------------------------------------------------------------
    h           Hours with no leading zero for single-digit hours; 12-hour clock 
    hh          Hours with leading zero for single-digit hours; 12-hour clock 
    H           Hours with no leading zero for single-digit hours; 24-hour clock 
    HH          Hours with leading zero for single-digit hours; 24-hour clock 
    m           Minutes with no leading zero for single-digit minutes 
    mm          Minutes with leading zero for single-digit minutes 
    s           Seconds with no leading zero for single-digit seconds 
    ss          Seconds with leading zero for single-digit seconds 
    t           One character time marker string, such as A or P 
    tt          Multicharacter time marker string, such as AM or PM 

    For example, to get the time string 

    "11:29:40 PM"

    use the following picture string: 

    "hh':'mm':'ss tt"
    */
    xstring FormatTimeStamp(ConstStringZ s) const; 
    xstring FormatTimeSpan(ConstStringZ s) const; 

    /*
    DEPRECATED

    s is a pointer to a format picture string to use to form the date string. If s is nullptr, the 
    function uses the date format of the user default locale. 

    Use the following elements to construct a format picture string. If you use spaces to separate 
    the elements in the format string, these spaces will appear in the same location in the output 
    string. The letters must be in uppercase or lowercase as shown in the table (for example, "MM" 
    not "mm"). Characters in the format string that are enclosed in single quotation marks will 
    appear in the same location and unchanged in the output string. 
    
    Picture                     Meaning 
    ---------------------------------------------------------------------------------
    d           Day of month as digits with no leading zero for single-digit days. 
    dd          Day of month as digits with leading zero for single-digit days. 
    ddd         Day of week as a three-letter abbreviation. The function uses the 
                LOCALE_SABBREVDAYNAME value associated with the specified locale. 
    dddd        Day of week as its full name. The function uses the LOCALE_SDAYNAME value 
                associated with the specified locale. 
    M           Month as digits with no leading zero for single-digit months. 
    MM          Month as digits with leading zero for single-digit months. 
    MMM         Month as a three-letter abbreviation. The function uses the LOCALE_SABBREVMONTHNAME 
                value associated with the specified locale. 
    MMMM        Month as its full name. The function uses the LOCALE_SMONTHNAME value associated 
                with the specified locale. 
    y           Year as last two digits, but with no leading zero for years less than 10. 
    yy          Year as last two digits, but with leading zero for years less than 10. 
    yyyy        Year represented by full four digits. 
    gg          Period/era string. The function uses the CAL_SERASTRING value associated with the 
                specified locale. This element is ignored if the date to be formatted does not have 
                an associated era or period string. 

    For example, to get the date string 

    "Wed, Aug 31 94"

    use the following picture string: 

    "ddd',' MMM dd yy"
    */
    xstring FormatDate(ConstStringZ s) const;

    // in UTC
    bool Set(int year, int month, int day, int hour, int minute, int second, int milliseconds);

    /*
    // When this HPTime represents a time stamp
    int GetYear() const;
    int GetMonth() const;
    int GetDay() const;
    int GetHour() const;
    int GetMinute() const;
    int GetSecond() const;
    int GetDayOfWeek() const;
    */

    // When this HPTime represents a time span
    //int GetTimeSpanYears() const { cxTodo(); return 0; }
    //int GetTimeSpanMonths() const { cxTodo(); return 0; }
    int GetTimeSpanDays() const { return (int)(m_ticks / ((HPTIME_TICKS)10000000*60*60*24)); }
    int GetTimeSpanHours() const { return (int)(m_ticks / ((HPTIME_TICKS)10000000*60*60)); }
    int GetTimeSpanMinutes() const { return (int)(m_ticks / ((HPTIME_TICKS)10000000*60)); }
    int GetTimeSpanSeconds() const { return (int)(m_ticks / ((HPTIME_TICKS)10000000)); }
    double GetTotalTimeSpanInSeconds() const { return (int64) m_ticks * 1.0E-7; }
    double GetTotalTimeSpanInMilliSeconds() const { return (int64) m_ticks * 1.0E-4; }
    double GetTotalTimeSpanInMicroSeconds() const { return (int64) m_ticks * 1.0E-1; }

    HPTime& operator+=(const HPTime& other) { m_ticks += other.m_ticks; return *this; }
    HPTime& operator-=(const HPTime& other) { m_ticks -= other.m_ticks; return *this; }
    HPTime& operator*=(double f) { m_ticks = (HPTIME_TICKS) ((int64) m_ticks * f); return *this; }
    HPTime& operator/=(double f) { m_ticks = (HPTIME_TICKS) ((int64) m_ticks / f); return *this; }
    HPTime operator-() { return FromTicks(- (int64) m_ticks); }
    HPTime operator+() { return *this; } 

    // When the HPTime is a "time stamp", this represents the number of 100-nanosecond intervals
    // since January 1, 1601, expressed in Coordinated Universal Time (UTC) format.
    // When the HPTime is a "time span", it represents the difference in two time stamps in 
    // 100-nanosecond units.
    HPTIME_TICKS m_ticks;
};

inline bool operator==(HPTime t1, HPTime t2) { return t1.m_ticks == t2.m_ticks; }
inline bool operator!=(HPTime t1, HPTime t2) { return t1.m_ticks != t2.m_ticks; }
inline bool operator<(HPTime t1, HPTime t2) { return t1.m_ticks < t2.m_ticks; }
inline bool operator<=(HPTime t1, HPTime t2) { return t1.m_ticks <= t2.m_ticks; }
inline bool operator>(HPTime t1, HPTime t2) { return t1.m_ticks > t2.m_ticks; }
inline bool operator>=(HPTime t1, HPTime t2) { return t1.m_ticks >= t2.m_ticks; }

inline const HPTime operator-(HPTime t1, HPTime t2) { return HPTime::FromTicks(t1.m_ticks - t2.m_ticks); }
inline const HPTime operator+(HPTime t1, HPTime t2) { return HPTime::FromTicks(t1.m_ticks + t2.m_ticks); }
inline const HPTime operator*(HPTime t, double f) { return HPTime::FromTicks((HPTIME_TICKS) (t.m_ticks * f)); }
inline const HPTime operator*(double f,HPTime t) { return HPTime::FromTicks((HPTIME_TICKS) (t.m_ticks * f)); }
inline const HPTime operator/(HPTime t, double f) { cxAssert(f != 0); return HPTime::FromTicks((HPTIME_TICKS) (t.m_ticks / f)); }

///////////////////////////////////////////////////////////////////////////////////////////////////
// HPTimeStamp<numDecPlaces>

template <int numDecPlaces = 1>
struct HPTimeStamp : public HPTime
{
    HPTimeStamp() {}
    HPTimeStamp(HPTime ht) : HPTime(ht) {}

    //HPTimeStamp& operator=(HPTime& other) { (HPTime&)*this = other; return *this; }
    //operator HPTime() const { return *this; }
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// HPTimeSpan<numDecPlaces>

template <int numDecPlaces = 1>
struct HPTimeSpan : public HPTime
{
    HPTimeSpan() {}
    HPTimeSpan(HPTime ht) : HPTime(ht) {}
    
    //HPTimeSpan& operator=(HPTime& other) { (HPTime&)*this = other; return *this; }
    //operator HPTime() const { return *this; }
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// HPTimer

/*
Used to measure and trace the elapsed time taken by a section of code.

The following example measures the time taken by calls f() and g().  By default the timer automatically 
resets with each call to Done()

    void foo()
    {
        HPTimer timer;
        
        f();
        timer.Done() << "Time taken by f()";

        g();
        timer.Done() << "Time taken by g()";
    }
*/

class cxUtils_API HPTimer : public StringStream
{
public:
    // Ctor calls Reset() to initialise the start time to which the elapsed time will be measured.
    HPTimer(bool autoDone = false);

    // Calls TraceLastElapsedTime() to trace the last elapsed time (if present) 
    ~HPTimer();

    // First calls TraceLastElapsedTime() to trace the last elapsed time (if present) 
    // Then calculates the next elapsed time, and returns an ostream that can be used to write
    // a string associated with this elapsed time.
    // If reset=true then calls Reset() to allow the next measurement of an elapsed time to start
    // from time zero.
    HPTimer& Done(bool reset = true);

    // Traces the last measured elapsed time if present, and clears the elapsed time (so it won't be
    // traced again)
    // Can be used to force the trace of the elapsed time measured by the last call to Done().
    // Typically clients don't need to call this function directly, because it is called from Done()
    // and also when the HPTimer destructs.
    void TraceLastElapsedTime();

    double GetElapsedTimeInSeconds() const;

    // Reset the timer - i.e. initialise the start time to which the elapsed time will be measured
    void Reset();

    void BeginSkip() 
    { 
        m_beginSkipTime = HPTime::GetCurrentTime();
        std::atomic_thread_fence(std::memory_order_seq_cst);
    }
    void EndSkip() 
    { 
        std::atomic_thread_fence(std::memory_order_seq_cst);
        m_startTime += HPTime::GetCurrentTime() - m_beginSkipTime; 
    }
    
private:
    bool m_autoDone;
    HPTime m_beginSkipTime;
    HPTimeStamp<1> m_startTime;
    bool m_haveElapsedTime;
    HPTimeSpan<1> m_elapsedTime;   // -1.0 indicates that no elapsed time has been calculated yet
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// Must be inside ceda namespace !!!

inline xostream& operator<<(xostream& os, const HPTime& p)
{
    os << p.TimeStampToTimeString(4);
    return os;
}

template <int numDecPlaces>
xostream& operator<<(xostream& os, HPTimeStamp<numDecPlaces> ts)
{
    os << ts.TimeStampToTimeString(numDecPlaces);
    return os;
}

template <int numDecPlaces>
xostream& operator<<(xostream& os, HPTimeSpan<numDecPlaces> ts)
{
    os << ts.TimeSpanToTimeString(numDecPlaces);
    return os;
}

} // namespace ceda

#endif // include guard