Back to Github

Introduction

🗂 Code structure

🛠 Build Instructions

🧩 Example

🦋 Types

🗝 Member Functions

🌐 Global Operators

C++ DataFrame C++ DataFrame C++ DataFrame C++ DataFrame C++ DataFrame

Introduction

Since DataFrame is a statistical library, it often deals with time-series data. So, it needs to keep track of time.
The most efficient way of indexing DataFrame by time is to use an index type of time_t for second precision or double or long long integer for more precision. DateTime class provides a more elaborate handling of time. Also, it is a general handy DateTime object. DateTime is a cool and handy object to manipulate date/time with nanosecond precision and multi timezone capability. It has a very simple and intuitive interface that allows you to break date/time to their components, reassemble date/time from their components, advance or pullback date/time with different granularities, and more.
DateTime is one of the types that DataFrame library can read/write from/to files and serialization.



Code structure 🗂

Both the header (DateTime.h) and source (DateTime.cc) files are part of the DataFrame project. They are in the usual include/Utils and src/Utils directories.

Build Instructions 🛠

Follow the DataFrame build instructions.


Example 🧩

This library can have up to Nano second precision depending on what systems calls are available. These are some example code:
    DateTime    now;
    DateTime    gmt_now (DT_TIME_ZONE::GMT);
    DateTime    hk_now (DT_TIME_ZONE::AS_HONG_KONG);

    cout << "Local Time is: " << now.string_format (DT_FORMAT::DT_TM2) << std::endl;
    cout << "GMT Time is: " << gmt_now.string_format (DT_FORMAT::DT_TM2) << std::endl;

    double  diff = now.diff_seconds (gmt_now);

    const DateTime  paris("02/11/2010 12:31:56 PAR", DT_DATE_STYLE::AME_STYLE);
    const DateTime  tehran("02/11/2010 12:31:56 THR", DT_DATE_STYLE::AME_STYLE);

    // Tehran must be 2.5 hours ahead of Paris
    //
    assert(((paris.time() - tehran.time()) == 9000));

    now = 19721202;
    gmt_now = 19721210;
    diff = now.diff_weekdays (gmt_now);

    now.add_days(3)
    now.add_weekdays(-2);
For more examples see file date_time_tester.cc


Types 🦋

These constants are used for formatting date/time into strings:
enum class DT_FORMAT : unsigned short int  {
    AMR_DT = 1,        // e.g. 09/16/99
    AMR_DT_CTY = 2,    // e.g. 09/16/1999
    EUR_DT = 3,        // e.g. 16/09/99
    EUR_DT_CTY = 4,    // e.g. 16/09/1999
    DT_TM = 5,         // e.g. 09/16/1999 13:51:04
    SCT_DT = 6,        // e.g. Sep 16, 1999
    DT_MMDDYYYY = 7,   // e.g. 09161999
    DT_YYYYMMDD = 8,   // e.g. 19990916
    DT_TM2 = 9,        // e.g. 09/16/1999 13:51:04.124
    DT_DATETIME = 10,  // e.g. 19990916 13:51:04.124
    DT_PRECISE = 11,   // e.g. 1516179600.874123908 = Epoch.Nanoseconds
    ISO_DT_TM = 12,    // e.g. 1999-09-16 13:51:04.000234
    ISO_DT = 13,       // e.g. 1999-09-16
    ISO_DT_NANO = 14,  // e.g. 1999-09-16 13:51:04.123456789
    AMR_DT_TM = 15,    // e.g. 09/16/1999 13:51:04.000234
    EUR_DT_TM = 16,    // e.g. 16/09/1999 13:51:04.000234
};

These are the available time zones, used in a few methods and constructors.
    enum class DT_TIME_ZONE : short int  {
        LOCAL = -2,
        GMT = 0,
        AM_BUENOS_AIRES = 1,
        AM_CHICAGO = 2,
        AM_LOS_ANGELES = 3,
        AM_MEXICO_CITY = 4,
        AM_NEW_YORK = 5,
        AS_DUBAI = 6,
        AS_HONG_KONG = 7,
        AS_SHANGHAI = 8,
        AS_SINGAPORE = 9,
        AS_TEHRAN = 10,
        AS_TEL_AVIV = 11,
        AS_TOKYO = 12,
        AU_MELBOURNE = 13,
        AU_SYDNEY = 14,
        BR_RIO_DE_JANEIRO = 15,
        EU_BERLIN = 16,
        EU_LONDON = 17,
        EU_MOSCOW = 18,
        EU_PARIS = 19,
        EU_ROME = 20,
        EU_VIENNA = 21,
        EU_ZURICH = 22,
        UTC = 23,
        AS_SEOUL = 24,
        AS_TAIPEI = 25,
        EU_STOCKHOLM = 26,
        NZ = 27,
        EU_OSLO = 28,
        EU_WARSAW = 29,
        EU_BUDAPEST = 30
    };

Week days: 1 - 7 (Sunday - Saturday), used by various methods:
    enum class DT_WEEKDAY : unsigned char  {
        BAD_DAY = 0,
        SUN = 1,
        MON = 2,
        TUE = 3,
        WED = 4,
        THU = 5,
        FRI = 6,
        SAT = 7
    };

Months: 1 - 12 (January - December), used by various methods:
    enum class DT_MONTH : unsigned char  {
        BAD_MONTH = 0,
        JAN = 1,
        FEB = 2,
        MAR = 3,
        APR = 4,
        MAY = 5,
        JUN = 6,
        JUL = 7,
        AUG = 8,
        SEP = 9,
        OCT = 10,
        NOV = 11,
        DEC = 12
    };

These constants are used for parsing data:
    enum class DT_DATE_STYLE : unsigned char  {
        YYYYMMDD = 1,
        AME_STYLE = 2,  // MM/DD/YYYY
        EUR_STYLE = 3,  // YYYY/MM/DD
        ISO_STYLE = 3.  // YYYY-MM-DD
    };

Types used in DateTime class:
    using DateType = unsigned int;            // YYYYMMDD
    using DatePartType = unsigned short int;  // year, month etc.
    using HourType = unsigned short int;      // 0 - 23
    using MinuteType = unsigned short int;    // 0 - 59
    using SecondType = unsigned short int;    // 0 - 59
    using MillisecondType = short int;        // 0 - 999
    using MicrosecondType = int;              // 0 - 999,999
    using NanosecondType =  int;              // 0 - 999,999,999
    using EpochType = time_t;                 // Signed epoch
    using LongTimeType = long long int;       // Nano seconds since epoch

This table lists the 3-letter abbreviations for allowable time zones that can appear
in strings parsed by DateTime constructor or assignment operator:

inline static const
std::unordered_map<std::string_view, DT_TIME_ZONE>  ZONE_STR_TO_TIME_ZONE  {
    { "LOC", DT_TIME_ZONE::LOCAL },
    { "GMT", DT_TIME_ZONE::GMT },
    { "BUE", DT_TIME_ZONE::AM_BUENOS_AIRES },
    { "CHI", DT_TIME_ZONE::AM_CHICAGO },
    { "LAX", DT_TIME_ZONE::AM_LOS_ANGELES },
    { "MEX", DT_TIME_ZONE::AM_MEXICO_CITY },
    { "NYC", DT_TIME_ZONE::AM_NEW_YORK },
    { "DXB", DT_TIME_ZONE::AS_DUBAI },
    { "HKG", DT_TIME_ZONE::AS_HONG_KONG },
    { "SHA", DT_TIME_ZONE::AS_SHANGHAI },
    { "SIN", DT_TIME_ZONE::AS_SINGAPORE },
    { "THR", DT_TIME_ZONE::AS_TEHRAN },
    { "TLV", DT_TIME_ZONE::AS_TEL_AVIV },
    { "TYO", DT_TIME_ZONE::AS_TOKYO },
    { "MEL", DT_TIME_ZONE::AU_MELBOURNE },
    { "SYD", DT_TIME_ZONE::AU_SYDNEY },
    { "RIO", DT_TIME_ZONE::BR_RIO_DE_JANEIRO },
    { "BER", DT_TIME_ZONE::EU_BERLIN },
    { "LON", DT_TIME_ZONE::EU_LONDON },
    { "MOW", DT_TIME_ZONE::EU_MOSCOW },
    { "PAR", DT_TIME_ZONE::EU_PARIS },
    { "ROM", DT_TIME_ZONE::EU_ROME },
    { "VIE", DT_TIME_ZONE::EU_VIENNA },
    { "ZRH", DT_TIME_ZONE::EU_ZURICH },
    { "UTC", DT_TIME_ZONE::UTC },
    { "SEL", DT_TIME_ZONE::AS_SEOUL },
    { "TAY", DT_TIME_ZONE::AS_TAIPEI },
    { "STO", DT_TIME_ZONE::EU_STOCKHOLM },
    { "AKL", DT_TIME_ZONE::NZ },
    { "OSL", DT_TIME_ZONE::EU_OSLO },
    { "WAW", DT_TIME_ZONE::EU_WARSAW },
    { "BUD", DT_TIME_ZONE::EU_BUDAPEST },
};


Member Functions 🗝

    // A constructor that creates a DateTime initialized to now.
    // tz: Desired time zone from DT_TIME_ZONE above.
    //
    explicit
    DateTime(DT_TIME_ZONE tz = DT_TIME_ZONE::LOCAL) noexcept;

    // The constructor that creates a DateTime based on parameters passed.
    //
    explicit
    DateTime(DateType d,  // Date e.g. 20180112
             HourType hr = 0,  // Hour e.g. 13
             MinuteType mn = 0,  // Minute e.g. 45
             SecondType sc = 0,  // Second e.g. 45
             NanosecondType ns = 0,  // Nano-second e.g. 123456789
             DT_TIME_ZONE tz = DT_TIME_ZONE::LOCAL) noexcept;  // Desired time zone from DT_TIME_ZONE above

    // The constructor that creates a DateTime by parsing a string and based on parameters passed.
    // Currently, the following formats are supported.
    // [TZN] denotes an optional 3-letter time zone abbreviation listed above. The default is local time zone.
    //  (1)  YYYYMMDD [TZN]
    // AME_STYLE:
    //  (2)  MM/DD/YYYY [TZN]
    //  (3)  MM/DD/YYYY HH [TZN]
    //  (4)  MM/DD/YYYY HH:MM [TZN]
    //  (5)  MM/DD/YYYY HH:MM:SS [TZN]
    //  (6)  MM/DD/YYYY HH:MM:SS.MMM [TZN]        // Milliseconds
    //  (7)  MM/DD/YYYY HH:MM:SS.IIIIII [TZN]     // Microseconds
    //  (8)  MM/DD/YYYY HH:MM:SS.NNNNNNNNN [TZN]  // Nanoseconds
    //
    // EUR_STYLE:
    //  (9)  YYYY/MM/DD [TZN]
    //  (10) YYYY/MM/DD HH [TZN]
    //  (11) YYYY/MM/DD HH:MM [TZN]
    //  (12) YYYY/MM/DD HH:MM:SS [TZN]
    //  (13) YYYY/MM/DD HH:MM:SS.MMM [TZN]        // Milliseconds
    //  (14) YYYY/MM/DD HH:MM:SS.IIIIII [TZN]     // Microseconds
    //  (15) YYYY/MM/DD HH:MM:SS.NNNNNNNNN [TZN]  // Nanoseconds
    //
    // ISO_STYLE:
    //  (16) YYYY-MM-DD [TZN]
    //  (17) YYYY-MM-DD HH [TZN]
    //  (18) YYYY-MM-DD HH:MM [TZN]
    //  (19) YYYY-MM-DD HH:MM:SS [TZN]
    //  (20) YYYY-MM-DD HH:MM:SS.MMM [TZN]        // Milliseconds
    //  (21) YYYY-MM-DD HH:MM:SS.IIIIII [TZN]     // Microseconds
    //  (22) YYYY-MM-DD HH:MM:SS.NNNNNNNNN [TZN]  // Nanoseconds
    //
    explicit DateTime(const char *s,
                      DT_DATE_STYLE ds = DT_DATE_STYLE::YYYYMMDD,
                      DT_TIME_ZONE tz = DT_TIME_ZONE::LOCAL);

    DateTime(const DateTime &that);
    DateTime(DateTime &&that);

    // A convenient method, if you already have a DateTime instance and want to change the date/time quickly.
    // the_time: Time as epoch
    //
    // nanosec: Nano seconds
    //
    void set_time(EpochType the_time, NanosecondType nanosec = 0) noexcept;

    // Changes the time zone to desired time zone.
    // NOTE: This method is not multithread-safe. This method modifies the TZ environment
    //       variable which changes the time zone for the entire program.
    //
    // tz: Desired time zone
    //
    void set_timezone(DT_TIME_ZONE tz);

    // Returns the current time zone.
    //
    DT_TIME_ZONE get_timezone() const;

    // Sets self to right-hand-side.
    //
    // rhs: A date e.g.  dt = 20181215
    //
    DateTime &operator = (DateType rhs);

    // Sets self to right-hand-side.
    // Currently, the following formats are supported.
    // [TZN] denotes an optional 3-letter time zone abbreviation listed above. The default is local time zone.
    // (1)  YYYYMMDD [TZN]
    // (2)  YYYYMMDD HH [TZN]
    // (3)  YYYYMMDD HH:MM [TZN]
    // (4)  YYYYMMDD HH:MM:SS [TZN]
    // (5)  YYYYMMDD HH:MM:SS.MMM [TZN]
    //
    // rhs: A date/time string e.g.  dt = “20181215 08:34 GMT”;
    //
    DateTime &operator = (const char *rhs);

    // Compares self with right-hand-side and returns an integer result accordingly.
    //
    // rhs: Another DateTime instance
    //
    int dt_compare(const DateTime &rhs) const;

    // These methods return the corresponding date/time parts.
    //
    DateType date() const noexcept;                // e.g. 20020303
    DatePartType year() const noexcept;            // e.g. 1990
    DT_MONTH month() const noexcept;               // JAN - DEC 
    DatePartType dmonth() const noexcept;          // 1 - 31
    DatePartType dyear() const noexcept;           // 1 - 366
    DT_WEEKDAY dweek() const noexcept;             // SUN - SAT
    HourType hour() const noexcept;                // 0 - 23
    MinuteType minute() const noexcept;            // 0 - 59
    SecondType sec() const noexcept;               // 0 - 59
    MillisecondType msec() const noexcept;         // 0 - 999
    MicrosecondType microsec() const noexcept;     // 0 - 999,999
    NanosecondType nanosec() const noexcept;       // 0 - 999,999,999
    EpochType time() const noexcept;               // Like time()
    EpochType days() const noexcept;               // Days since epoch
    LongTimeType long_time() const noexcept;       // Nano seconds since epoch
    DatePartType days_in_month() const noexcept;   // 28, 29, 30, 31

    // These return the diff including the fraction of the unit. This is why they return a double.
    // The diff could be +/- based on "this - that"
    //
    double diff_seconds(const DateTime &that) const;
    double diff_minutes(const DateTime &that) const noexcept;
    double diff_hours(const DateTime &that) const noexcept;
    double diff_days(const DateTime &that) const noexcept;
    double diff_weekdays(const DateTime &that) const noexcept;
    double diff_weeks(const DateTime &that) const noexcept;

    // These methods either advance or pullback the time accordingly. The parameter to these methods could be +/-.
    //
    void add_nanoseconds(long nanosecs) noexcept;
    void add_seconds(EpochType secs) noexcept;
    void add_days(long days) noexcept;
    void add_weekdays(long days) noexcept;
    void add_months(long months) noexcept;
    void add_years(long years) noexcept;

    // These methods return a Boolean indicating holidays or special days.
    //
    bool is_weekend() const noexcept;
    bool is_newyear() const noexcept;
    bool is_leap_year() const noexcept;
    bool is_xmas() const noexcept;
    bool is_us_business_day() const noexcept;
    bool is_us_bank_holiday() const noexcept;
    bool is_valid() const noexcept;

    // These methods format the date/time into a string based on the format parameter
    //
    // T: Type of string
    // result: a string instance to store the formatted date/time
    //
    template<typename T>
    void date_to_str(DT_FORMAT format, T &result) const;

    // format: String format parameter based on DT_FORMAT above
    //
    std::string string_format(DT_FORMAT format) const;


Global DateTime Operators 🌐

// DateTime output operator to a stream
//
template<typename S>
S &operator << (S &o, const DateTime &rhs);

// DateTime comparison operators
// 
bool operator == (const DateTime &lhs, const DateTime &rhs) noexcept;
bool operator != (const DateTime &lhs, const DateTime &rhs) noexcept;
bool operator < (const DateTime &lhs, const DateTime &rhs) noexcept;
bool operator <= (const DateTime &lhs, const DateTime &rhs) noexcept;
bool operator > (const DateTime &lhs, const DateTime &rhs) noexcept;
bool operator >= (const DateTime &lhs, const DateTime &rhs) noexcept;

// DateTime/DateTime arithmetic operators
//
double operator + (const DateTime &lhs, const DateTime &rhs) noexcept;
double operator - (const DateTime &lhs, const DateTime &rhs) noexcept;
double operator * (const DateTime &lhs, const DateTime &rhs) noexcept;
double operator / (const DateTime &lhs, const DateTime &rhs) noexcept;

// DateTime/double arithmetic operators
//
double operator + (const DateTime &lhs, double rhs) noexcept;
double operator - (const DateTime &lhs, double rhs) noexcept;
double operator * (const DateTime &lhs, double rhs) noexcept;
double operator / (const DateTime &lhs, double rhs) noexcept;
DateTime &operator += (DateTime &lhs, double rhs) noexcept;
DateTime &operator -= (DateTime &lhs, double rhs) noexcept;
DateTime &operator *= (DateTime &lhs, double rhs) noexcept;
DateTime &operator /= (DateTime &lhs, double rhs) noexcept;

// double/DateTime arithmetic operators
//
double operator + (double lhs, const DateTime &rhs) noexcept;
double operator - (double lhs, const DateTime &rhs) noexcept;
double operator * (double lhs, const DateTime &rhs) noexcept;
double operator / (double lhs, const DateTime &rhs) noexcept;
double &operator += (double &lhs, const DateTime &rhs) noexcept;
double &operator -= (double &lhs, const DateTime &rhs) noexcept;
double &operator *= (double &lhs, const DateTime &rhs) noexcept;
double &operator /= (double &lhs, const DateTime &rhs) noexcept;

// std::hash specialization for DateTime
//
namespace std  {
    template<>
    struct  hash<typename hmdf::DateTime>;
};