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