Skip to content

datetime封装常用功能

Published: at 10:46 AM | 10 min read

datetime支持windows,linux,异常安全,封装了常见的转换函数,精确到毫秒。

支持常见日期的计算、修改、比较。

#pragma once

#include <string>
#include <ctime>

class DateTime {
public:
    static DateTime now();
    static int64_t nowMSTimestamp();
    static int64_t nowTimestamp();
    static DateTime fromDateTimeString(const std::string &datetime);
    static DateTime fromYmdHMS(const std::string &datetime);
    static DateTime fromDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond);
    static DateTime fromDate(int year, int month, int day);
    static bool isLeapYear(int year);
    static int daysOfMonth(int year, int month);
    static int datePeriod(const DateTime &from, const DateTime &to);
    static std::pair<int, int> yearDayPeriod(DateTime date1, DateTime date2);

    DateTime();
    explicit DateTime(tm& tmstruct);
    DateTime(tm &tmstruct, int millisecond);
    DateTime(const DateTime& dateTime);
    DateTime& operator=(const DateTime& dateTime);

    void setTM(tm &tmstruct, int millisecond);
    DateTime& fromTimestamp(int64_t second);
    DateTime& fromMsTimestamp(int64_t millisecond);
    void swap(DateTime& dateTime);
    tm makeTM() const;
    tm* tmstruct(tm &tmstruct) const;
    std::string format(const char *format) const;
    int64_t sub(const DateTime &from);

    // eg :20201222
    std::string formatYmd() const;
    // eg :20201222135030
    std::string formatYmdHMS() const;
    // eg: 2020-12-22
    std::string formatDate() const;
    // eg: 2020-12-22 17:40:07
    std::string formatDateTime() const;
    // eg: 2020-12-22 17:40:07.123
    std::string formatDateTimeMS() const;
    // eg: 17:40
    std::string formatHourMinute() const;
    // eg: 17:40:07
    std::string formatHourMinuteSecond() const;

    int daysInMonth() const;
    int daysInYear() const;
    int daysInWeek() const;
    bool parseFull(int &year, int &month, int &day, int &hour, int &minute, int &second, int &millisecond) const;
    bool parseYearMonthDay(int &year, int &month, int &day) const;
    DateTime& addYear(int years, bool *success = NULL);
    DateTime& addMonth(int months, bool *success = NULL);
    DateTime& addDay(int days, bool *success = NULL);
    DateTime& addHour(int hours, bool *success = NULL);
    DateTime& addMinute(int minutes, bool *success = NULL);
    DateTime& addSecond(int seconds, bool *success = NULL);

    int year() const;
    int month() const;
    int day() const;
    int hour() const;
    int minute() const;
    int second() const;

    inline int millisec() const { return msTimestamp_ % 1000; }
    inline time_t timestamp() const { return msTimestamp_ / 1000; }
    inline time_t msTimestamp() const { return msTimestamp_; }
    inline bool operator == (const DateTime& dateTime) const { return msTimestamp_ == dateTime.msTimestamp_; }
    inline bool operator != (const DateTime& dateTime) const { return msTimestamp_ != dateTime.msTimestamp_; }
    inline bool operator <  (const DateTime& dateTime) const { return msTimestamp_ < dateTime.msTimestamp_; }
    inline bool operator <= (const DateTime& dateTime) const { return msTimestamp_ <= dateTime.msTimestamp_; }
    inline bool operator >  (const DateTime& dateTime) const { return msTimestamp_ > dateTime.msTimestamp_; }
    inline bool operator >= (const DateTime& dateTime) const { return msTimestamp_ >= dateTime.msTimestamp_; }
    inline void swap(DateTime& d1, DateTime& d2) { d1.swap(d2); }

private:
    int64_t msTimestamp_;
};

#include "datetime.h"
#include <chrono>
#include <iostream>
#include <boost/date_time/gregorian/gregorian.hpp>

boost::gregorian::date gregorianDate(const DateTime &dt)
{
    boost::gregorian::date ret;
    std::string date = dt.formatDate();
    if (date.empty()) {
        return ret;
    }

    try {
        ret = boost::gregorian::from_string(date);
    } catch (std::exception &e) {
		LOGGER_WARN("gregorianDate ERROR, date:" << date);
    } catch (...) {
		LOGGER_WARN("gregorianDate ERROR, date:" << date);
    }
    return ret;
}

DateTime DateTime::now()
{
    DateTime dt;
    return dt.fromMsTimestamp(DateTime::nowMSTimestamp());
}

int64_t DateTime::nowMSTimestamp()
{
    using namespace std::chrono;
    return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}

int64_t DateTime::nowTimestamp()
{
    using namespace std::chrono;
    return duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
}

DateTime DateTime::fromYmdHMS(const std::string &datetime)
{
    int year, month, day, hour, minute, second;
    year = month = day = hour = minute = second = 0;
    int count = sscanf(datetime.c_str(), "%4d%2d%2d%2d%2d%2d",
        &year, &month, &day, &hour, &minute, &second);
    if (count >= 3) {
        return DateTime::fromDateTime(year, month, day, hour, minute, second, 0);
    }
    return DateTime();
}

DateTime DateTime::fromDateTimeString(const std::string &datetime)
{
    int year, month, day, hour, minute, second, millisec;
    year = month = day = hour = minute = second = millisec = 0;
    int count = sscanf(datetime.c_str(), "%4d-%2d-%2d %2d:%2d:%2d.%3d",
        &year, &month, &day, &hour, &minute, &second, &millisec);
    if (count >= 3) {
        return DateTime::fromDateTime(year, month, day, hour, minute, second, millisec);
    }
    return DateTime();
}

DateTime DateTime::fromDateTime(int year, int month, int day, int hour, int minute, int second, int millisec)
{
    struct tm local;
    local.tm_year = year - 1900;
    local.tm_mon = month - 1;
    local.tm_mday = day;
    local.tm_hour = hour;
    local.tm_min = minute;
    local.tm_sec = second;
    return DateTime(local, millisec);
}

DateTime DateTime::fromDate(int year, int month, int day)
{
    return DateTime::fromDateTime(year, month, day, 0, 0, 0, 0);
}

bool DateTime::isLeapYear(int year)
{
    return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
}

int DateTime::daysOfMonth(int year, int month)
{
    static int daysOfMonthTable[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    if (month >= 1 && month <= 12) {
        if (month == 2 && isLeapYear(year)) {
            return 29;
        } else {
            return daysOfMonthTable[month];
        }
    }
    return 0;
}

int DateTime::datePeriod(const DateTime &from, const DateTime &to)
{
    std::string str = "[" + from.formatDate() + "/" + to.formatDate() + "]";
    try {
        return boost::gregorian::date_period_from_string(str).length().days();
    } catch (std::exception &e) {
		LOGGER_WARN("DateTime datePeriod exception, error:" << e.what() << ", str:" << str);
    } catch (...) {
		LOGGER_WARN("DateTime datePeriod exception, unkown error, str:" << str);
    }
    return 0;
}

std::pair<int, int> DateTime::yearDayPeriod(DateTime date1, DateTime date2)
{
    if (date1 > date2) {
        date1.swap(date2);
    }
    int yearStart = date1.year();
    int yearEnd = date2.year();
    int y = yearEnd - yearStart;
    date1.addYear(y);
    if (date1 > date2) {
        date1.addYear(-1);
        y -= 1;
    }
    int d = DateTime::datePeriod(date1, date2);
    return std::make_pair(y, d);
}

DateTime::DateTime() : msTimestamp_(0) {}
DateTime::DateTime(tm& tmstruct) { setTM(tmstruct, 0); }
DateTime::DateTime(tm &tmstruct, int millisec) { setTM(tmstruct, millisec); }
DateTime::DateTime(const DateTime& dateTime) : msTimestamp_(dateTime.msTimestamp_) { }
DateTime& DateTime::operator=(const DateTime& dateTime)
{
    if (&dateTime != this) {
        msTimestamp_ = dateTime.msTimestamp_;
    }
    return *this;
}

void DateTime::setTM(tm &tmstruct, int millisec)
{
    time_t tt = mktime(&tmstruct);
    if (tt != -1) {
        if (millisec > 0 && millisec < 1000) {
            msTimestamp_ = tt * 1000 + millisec;
        } else {
            msTimestamp_ = tt * 1000;
        }
    } else {
        msTimestamp_ = 0;
    }
}

DateTime& DateTime::fromTimestamp(int64_t second)
{
    msTimestamp_ = second * 1000;
    return *this;
}

DateTime& DateTime::fromMsTimestamp(int64_t millisecond)
{
    msTimestamp_ = millisecond;
    return *this;
}

void DateTime::swap(DateTime& dateTime)
{
    std::swap(msTimestamp_, dateTime.msTimestamp_);
}

tm DateTime::makeTM() const
{
    tm local;
    tmstruct(local);
    return local;
}

tm* DateTime::tmstruct(tm &tmstruct) const
{
    time_t tt = msTimestamp_ / 1000;
#ifdef WIN32
    errno_t err = localtime_s(&tmstruct, &tt);
    if (err != 0) {
		LOGGER_WARN("localtime_s error:" << err);
        return NULL;
    }
    return &tmstruct;
#else
    tm *t = localtime_r(&tt, &tmstruct);
    if (!t) {
		LOGGER_WARN("localtime_r error, value:" << tt);
    }
    return t;
#endif
}

std::string DateTime::format(const char *format) const
{
    tm local;
    if (tmstruct(local)) {
        char buffer[100];
        strftime(buffer, sizeof(buffer), format, &local);
        return buffer;
    }
    return "";
}

int64_t DateTime::sub(const DateTime &from)
{
    return msTimestamp_ - from.msTimestamp();
}

// eg :20201222
std::string DateTime::formatYmd() const
{
    return format("%Y%m%d");
}

// eg :20201222135030
std::string DateTime::formatYmdHMS() const
{
    return format("%Y%m%d%H%M%S");
}

// eg: 2020-12-22
std::string DateTime::formatDate() const
{
    return format("%Y-%m-%d");
}

// eg: 2020-12-22 17:40:07
std::string DateTime::formatDateTime() const
{
    return format("%Y-%m-%d %H:%M:%S");
}

// eg: 2020-12-22 17:40:07.123
std::string DateTime::formatDateTimeMS() const
{
    std::string dt = formatDateTime();
    if (dt.empty()) {
        return dt;
    }
    char buf[32] = { 0 };
    sprintf(buf, ".%03d", millisec());
    return dt + std::string(buf);
}

// eg: 17:40
std::string DateTime::formatHourMinute() const
{
    return format("%H:%M");
}

// eg: 17:40:07
std::string DateTime::formatHourMinuteSecond() const
{
    return format("%H:%M:%S");
}

int DateTime::daysInMonth() const
{
    return makeTM().tm_mday;
}

int DateTime::daysInYear() const
{
    return makeTM().tm_yday;
}

int DateTime::daysInWeek() const
{
    return makeTM().tm_wday;
}

bool DateTime::parseFull(int &year, int &month, int &day, int &hour, int &minute, int &second, int &millisec) const
{
    tm local;
    if (tmstruct(local)) {
        year = local.tm_year + 1900;
        month = local.tm_mon + 1;
        day = local.tm_mday;
        hour = local.tm_hour;
        minute = local.tm_min;
        second = local.tm_sec;
        millisec = msTimestamp_ % 1000;
        return true;
    }
    return false;
}

bool DateTime::parseYearMonthDay(int &year, int &month, int &day) const
{
    int hour, minute, second, millisec;
    return parseFull(year, month, day, hour, minute, second, millisec);
}

DateTime& DateTime::addYear(int years, bool *success)
{
    int year, month, day, hour, minute, second, millisec;
    year = month = day = hour = minute = second = millisec = 0;
    if (!parseFull(year, month, day, hour, minute, second, millisec)) {
		LOGGER_WARN("parseFull ERROR, msTimestamp:" << msTimestamp_);
        return *this;
    }

    try {
        boost::gregorian::date d(year, month, day);
        d += boost::gregorian::years(years);
        year = d.year();
        month = d.month();
        day = d.day();
        if (success) {
            *success = true;
        }
    } catch (std::exception &e) {
		LOGGER_WARN("addYear exception, err:" << e.what() << ", msTimestamp:" << msTimestamp_ << ", value:" << years);
    } catch (...) {
		LOGGER_WARN("addYear exception, unkown, msTimestamp:" << msTimestamp_ << ", value:" << years);
    }

    msTimestamp_ = DateTime::fromDateTime(year, month, day, hour, minute, second, millisec).msTimestamp();
    return *this;
}

DateTime& DateTime::addMonth(int months, bool *success)
{
    int year, month, day, hour, minute, second, millisec;
    year = month = day = hour = minute = second = millisec = 0;
    if (!parseFull(year, month, day, hour, minute, second, millisec)) {
		LOGGER_WARN("parseFull ERROR, msTimestamp:" << msTimestamp_);
        return *this;
    }

    try {
        boost::gregorian::date d(year, month, day);
        d += boost::gregorian::months(months);
        year = d.year();
        month = d.month();
        day = d.day();
        if (success) {
            *success = true;
        }
    } catch (std::exception &e) {
		LOGGER_WARN("addMonth exception, err:" << e.what() << ", msTimestamp:" << msTimestamp_ << ", value:" << months);
    } catch (...) {
		LOGGER_WARN("addMonth exception, unkown, msTimestamp:" << msTimestamp_ << ", value:" << months);
    }

    msTimestamp_ = DateTime::fromDateTime(year, month, day, hour, minute, second, millisec).msTimestamp();
    return *this;
}

DateTime& DateTime::addDay(int days, bool *success)
{
    int year, month, day, hour, minute, second, millisec;
    year = month = day = hour = minute = second = millisec = 0;
    if (!parseFull(year, month, day, hour, minute, second, millisec)) {
		LOGGER_WARN("parseFull ERROR, msTimestamp:" << msTimestamp_);
        return *this;
    }

    try {
        boost::gregorian::date d(year, month, day);
        d += boost::gregorian::days(days);
        year = d.year();
        month = d.month();
        day = d.day();
        if (success) {
            *success = true;
        }
    } catch (std::exception &e) {
		LOGGER_WARN("addDay exception, err:" << e.what() << ", msTimestamp:" << msTimestamp_ << ", value:" << days);
    } catch (...) {
		LOGGER_WARN("addDay exception, unkown, msTimestamp:" << msTimestamp_ << ", value:" << days);
    }

    msTimestamp_ = DateTime::fromDateTime(year, month, day, hour, minute, second, millisec).msTimestamp();
    return *this;
}

DateTime& DateTime::addHour(int hours, bool *success)
{
    try {
        msTimestamp_ = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::milliseconds(msTimestamp_) + std::chrono::hours(hours)).count();
        if (success) {
            *success = true;
        }
    } catch (std::exception &e) {
		LOGGER_WARN("addHour exception, err:" << e.what() << ", msTimestamp:" << msTimestamp_ << ", value:" << hours);
    } catch (...) {
		LOGGER_WARN("addHour exception, unkown, msTimestamp:" << msTimestamp_ << ", value:" << hours);
    }
    return *this;
}

DateTime& DateTime::addMinute(int minutes, bool *success)
{
    try {
        msTimestamp_ = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::milliseconds(msTimestamp_) + std::chrono::minutes(minutes)).count();
        if (success) {
            *success = true;
        }
    } catch (std::exception &e) {
		LOGGER_WARN("addMinute exception, err:" << e.what() << ", msTimestamp:" << msTimestamp_ << ", value:" << minutes);
    } catch (...) {
		LOGGER_WARN("addMinute exception, unkown, msTimestamp:" << msTimestamp_ << ", value:" << minutes);
    }

    return *this;
}

DateTime& DateTime::addSecond(int seconds, bool *success)
{
    try {
        msTimestamp_ = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::milliseconds(msTimestamp_) + std::chrono::seconds(seconds)).count();
        if (success) {
            *success = true;
        }
    } catch (std::exception &e) {
		LOGGER_WARN("addSecond exception, err:" << e.what() << ", msTimestamp:" << msTimestamp_ << ", value:" << seconds);
    } catch (...) {
		LOGGER_WARN("addSecond exception, unkown, msTimestamp:" << msTimestamp_ << ", value:" << seconds);
    }
    return *this;
}

int DateTime::year() const { return makeTM().tm_year + 1900; }
int DateTime::month() const { return makeTM().tm_mon + 1; }
int DateTime::day() const { return makeTM().tm_mday; }
int DateTime::hour() const { return makeTM().tm_hour; }
int DateTime::minute() const { return makeTM().tm_min; }
int DateTime::second() const { return makeTM().tm_sec; }