1#ifndef NEFORCE_CORE_TIME_DATETIME_HPP__
2#define NEFORCE_CORE_TIME_DATETIME_HPP__
18NEFORCE_BEGIN_NAMESPACE__
104 static constexpr int32_t month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
110 constexpr date() noexcept = default;
112 NEFORCE_CONSTEXPR20 ~
date() = default;
130 constexpr date(
const date&)
noexcept =
default;
131 constexpr date& operator=(
const date&)
noexcept =
default;
132 constexpr date(
date&&) noexcept = default;
133 constexpr
date& operator=(
date&&) noexcept = default;
139 NEFORCE_NODISCARD constexpr
date_type year() const noexcept {
return year_; }
151 NEFORCE_NODISCARD
constexpr date_type day() const noexcept {
return day_; }
161 if (year < 1900 || year > 9999) {
164 if (month < 1 || month > 12) {
188 return (
year % 4 == 0 &&
year % 100 != 0) || (
year % 400 == 0);
203 return (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7;
249 const int64_t a = julian_day + 32044;
250 const int64_t b = (4 * a + 3) / 146097;
251 const int64_t c = a - (146097 * b) / 4;
252 const int64_t d = (4 * c + 3) / 1461;
253 const int64_t e = c - (1461 * d) / 4;
254 const int64_t m = (5 * e + 2) / 153;
276 return year_ == rhs.year_ && month_ == rhs.month_ && day_ == rhs.day_;
283 if (year_ < rhs.year_) {
286 if (year_ == rhs.year_ && month_ < rhs.month_) {
289 if (year_ == rhs.year_ && month_ == rhs.month_ && day_ < rhs.day_) {
305 return *
this -= -
day;
315 while (remaining > 0) {
317 const date_type available = days_in_month - day_ + 1;
319 if (remaining < available) {
324 remaining -= available;
341 return *
this += -
day;
389 const date ret(*
this);
407 NEFORCE_NODISCARD
constexpr size_t to_hash() const noexcept {
409 return hasher(
day()) ^ hasher(
month()) ^ hasher(
year());
416 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20
string to_string()
const {
427 if (view.
size() != 10 || view[4] !=
'-' || view[7] !=
'-') {
441 _NEFORCE
swap(year_, other.year_);
442 _NEFORCE
swap(month_, other.month_);
443 _NEFORCE
swap(day_, other.day_);
467 constexpr time() noexcept = default;
472 NEFORCE_CONSTEXPR20 ~
time() = default;
481 if (
is_valid(hour, minute, second)) {
488 constexpr time(
const time&)
noexcept =
default;
489 constexpr time& operator=(
const time&)
noexcept =
default;
490 constexpr time(
time&&) noexcept = default;
491 constexpr
time& operator=(
time&&) noexcept = default;
519 return hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60;
542 return hours_ * 3600 + minutes_ * 60 + seconds_;
549 return hours_ == other.hours_ && minutes_ == other.minutes_ && seconds_ == other.seconds_;
556 if (hours_ < other.hours_) {
559 if (hours_ == other.hours_) {
560 if (minutes_ < other.minutes_) {
563 if (minutes_ == other.minutes_ && seconds_ < other.seconds_) {
581 const time_type extra_min = seconds_ / 60;
584 minutes_ += extra_min;
585 const time_type extra_hour = minutes_ / 60;
588 hours_ += extra_hour;
610 hours_ =
static_cast<time_type>(total_sec / 3600);
611 minutes_ =
static_cast<time_type>((total_sec % 3600) / 60);
612 seconds_ =
static_cast<time_type>(total_sec % 60);
647 const time ret(*
this);
661 const time ret(*
this);
672 time_type sec_diff = (hours_ - other.hours_) * 3600;
673 sec_diff += (minutes_ - other.minutes_) * 60;
674 sec_diff += (seconds_ - other.seconds_);
682 NEFORCE_NODISCARD
constexpr size_t to_hash() const noexcept {
691 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20
string to_string()
const {
702 if (view.
size() != 8 || view[2] !=
':' || view[5] !=
':') {
708 return time(hour, minute, second);
716 _NEFORCE
swap(hours_, other.hours_);
717 _NEFORCE
swap(minutes_, other.minutes_);
718 _NEFORCE
swap(seconds_, other.seconds_);
740 _NEFORCE
date date_{};
741 _NEFORCE
time time_{};
743 bool has_timezone_ =
false;
751 static constexpr int months_to_int(
const string_view view) {
752 constexpr string_view months_string[] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
753 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
754 for (
int i = 0; i < 12; ++i) {
755 if (view == months_string[i]) {
763 constexpr datetime() noexcept = default;
765 NEFORCE_CONSTEXPR20 ~datetime() = default;
767 constexpr datetime(const datetime&) noexcept = default;
768 constexpr datetime& operator=(const datetime&) noexcept = default;
769 constexpr datetime(datetime&&) noexcept = default;
770 constexpr datetime& operator=(datetime&&) noexcept = default;
784 time_(hour, minute, second) {}
799 time_(hour, minute, second),
800 offset_seconds_(offset),
801 has_timezone_(
true) {}
821 offset_seconds_(offset),
822 has_timezone_(
true) {}
877 offset_seconds_(offset),
878 has_timezone_(
true) {
887 constexpr bool is_valid() const noexcept {
return date_.is_valid() && time_.is_valid(); }
935 NEFORCE_NODISCARD
constexpr const _NEFORCE
date&
date() const noexcept {
return date_; }
941 NEFORCE_NODISCARD
constexpr const _NEFORCE
time&
time() const noexcept {
return time_; }
947 NEFORCE_NODISCARD
constexpr time_type hours() const noexcept {
return time_.hours(); }
965 NEFORCE_NODISCARD
constexpr date_type year() const noexcept {
return date_.year(); }
971 NEFORCE_NODISCARD
constexpr date_type month() const noexcept {
return date_.month(); }
977 NEFORCE_NODISCARD
constexpr date_type day() const noexcept {
return date_.day(); }
983 NEFORCE_NODISCARD
constexpr bool has_timezone() const noexcept {
return has_timezone_; }
995 NEFORCE_NODISCARD
static constexpr datetime
epoch() noexcept {
return datetime{}; }
1001 NEFORCE_NODISCARD
static datetime
now() noexcept;
1009 offset_seconds_ = 0;
1010 has_timezone_ =
false;
1016 constexpr bool operator==(
const datetime& other)
const noexcept {
1017 return date_ == other.date_ && time_ == other.time_ && has_timezone_ == other.has_timezone_ &&
1018 offset_seconds_ == other.offset_seconds_;
1024 constexpr bool operator<(
const datetime& other)
const noexcept {
1025 if (date_ < other.date_) {
1027 }
else if (date_ == other.date_) {
1028 if (time_ < other.time_) {
1030 }
else if (time_ == other.time_) {
1031 if (has_timezone_ && other.has_timezone_ && offset_seconds_ < other.offset_seconds_) {
1049 const int64_t current_total_sec =
static_cast<int64_t>(time_.hours()) * 3600 +
1050 static_cast<int64_t>(time_.minutes()) * 60 +
1051 static_cast<int64_t>(time_.seconds());
1054 int64_t days_to_add = new_total_sec / 86400;
1055 new_total_sec %= 86400;
1057 if (new_total_sec < 0) {
1058 new_total_sec += 86400;
1062 date_ +=
static_cast<date_type>(days_to_add);
1063 time_ = _NEFORCE
time(
static_cast<time_type>(new_total_sec / 3600),
1064 static_cast<time_type>((new_total_sec % 3600) / 60),
1065 static_cast<time_type>(new_total_sec % 60));
1079 const int64_t current_total_sec =
static_cast<int64_t>(time_.hours()) * 3600 +
1080 static_cast<int64_t>(time_.minutes()) * 60 +
1081 static_cast<int64_t>(time_.seconds());
1086 if (new_total_sec < 0) {
1087 days_to_subtract = (-new_total_sec + 86399) / 86400;
1088 new_total_sec += days_to_subtract * 86400;
1091 date_ -=
static_cast<date_type>(days_to_subtract);
1092 time_ = _NEFORCE
time(
static_cast<time_type>(new_total_sec / 3600),
1093 static_cast<time_type>((new_total_sec % 3600) / 60),
1094 static_cast<time_type>(new_total_sec % 60));
1104 datetime ret(*
this);
1115 datetime ret(*
this);
1129 const datetime ret(*
this);
1143 const datetime ret(*
this);
1154 const datetime lhs_utc = this->
to_UTC();
1155 const datetime rhs_utc = other.to_UTC();
1157 const time_type day_diff = lhs_utc.date_ - rhs_utc.date_;
1159 sec_diff += (lhs_utc.time_ - rhs_utc.time_);
1168 if (!has_timezone_) {
1171 if (offset_seconds_ == 0) {
1174 int64_t total_sec = offset_seconds_;
1175 const char sign = total_sec >= 0 ?
'+' :
'-';
1176 total_sec = total_sec >= 0 ? total_sec : -total_sec;
1188 static constexpr datetime
from_UTC(
const datetime& utc,
const int32_t offset = 0) noexcept {
1189 datetime utc_time = utc;
1190 if (utc.has_timezone_ && utc.offset_seconds_ != 0) {
1193 datetime local = utc_time + offset;
1194 local.offset_seconds_ = offset;
1195 local.has_timezone_ =
true;
1203 NEFORCE_NODISCARD
constexpr datetime
to_UTC() const noexcept {
1204 if (!has_timezone_) {
1207 datetime utc = *
this;
1208 utc -= offset_seconds_;
1209 utc.offset_seconds_ = 0;
1210 utc.has_timezone_ =
true;
1219 if (has_timezone_) {
1222 return date_.to_string() +
"T" + time_.to_string();
1233 if (view.
size() < 20 || view[10] !=
'T') {
1234 NEFORCE_THROW_EXCEPTION(
value_exception(
"Invalid ISO UTC datetime format."));
1240 if (view.
size() > 19 && view[19] ==
'Z') {
1241 return datetime(d, t, 0);
1242 }
else if (view.
size() > 19 && (view[19] ==
'+' || view[19] ==
'-')) {
1243 const char sign = view[19];
1246 if (view.
size() >= pos + 2) {
1247 hours = integer32::parse(view.
substr(pos, 2)).value();
1249 if (view.
size() >= pos + 3 && view[pos] ==
':') {
1251 if (view.
size() >= pos + 2) {
1259 total_offset = -total_offset;
1261 return datetime(d, t, total_offset);
1263 return datetime(d, t);
1286 constexpr string_view months_string[] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
1287 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
1289 constexpr string_view weekdays_string[] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"};
1291 const _NEFORCE
date utc_date =
date();
1292 const _NEFORCE
time utc_time =
time();
1294 int wday = utc_date.days_of_week();
1295 if (wday < 0 || wday >= 7) {
1299 int mon_idx = utc_date.month() - 1;
1300 if (mon_idx < 0 || mon_idx >= 12) {
1304 return _NEFORCE
format(
"{}, {:02d} {} {} {} GMT", weekdays_string[wday], utc_date.day(), months_string[mon_idx],
1305 utc_date.year(), utc_time.to_string());
1315 if (view.
size() < 29) {
1318 if (view.
substr(3, 2) !=
", ") {
1323 const int day = integer32::parse(view.
substr(0, 2)).value();
1325 const int mon = months_to_int(view.
substr(0, 3));
1330 const int year = integer32::parse(view.
substr(0, 4)).value();
1332 const int hour = integer32::parse(view.
substr(0, 2)).value();
1334 const int minute = integer32::parse(view.
substr(0, 2)).value();
1336 const int second = integer32::parse(view.
substr(0, 2)).value();
1339 if (view !=
"GMT") {
1342 return datetime(
year, mon,
day, hour, minute, second);
1365 return date_.to_string() +
"T" + time_.to_string();
1375 if (view.
size() < 19 || view[10] !=
'T') {
1376 NEFORCE_THROW_EXCEPTION(
value_exception(
"Invalid ISO datetime format."));
1379 size_t time_len = 8;
1380 if (view.
size() >= 19) {
1384 return datetime(d, t);
1406 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20
string to_string()
const {
1407 return date_.to_string() +
" " + time_.to_string();
1417 if (view.
size() != 19 || view[10] !=
' ') {
1422 return datetime(d, t);
1429 NEFORCE_NODISCARD
constexpr size_t to_hash() const noexcept {
1437 constexpr void swap(datetime& other)
noexcept {
1438 _NEFORCE
swap(date_, other.date_);
1439 _NEFORCE
swap(time_, other.time_);
1440 _NEFORCE
swap(offset_seconds_, other.offset_seconds_);
1441 _NEFORCE
swap(has_timezone_, other.has_timezone_);
1453class NEFORCE_API timestamp :
public iobject<timestamp>,
public ipackage<timestamp, int64_t> {
1457 constexpr timestamp() noexcept = default;
1459 NEFORCE_CONSTEXPR20 ~timestamp() = default;
1461 constexpr timestamp(const timestamp& other) noexcept :
1462 ipackage(other.
value_) {}
1465 value_ = other.value_;
1469 constexpr timestamp(timestamp&& other) noexcept :
1470 ipackage(other.value_) {
1474 constexpr timestamp& operator=(timestamp&& other)
noexcept {
1475 value_ = other.value_;
1520 return timestamp{integer64::parse(view).value()};
1531NEFORCE_END_NAMESPACE__
NEFORCE_NODISCARD constexpr size_type size() const noexcept
获取字符串长度
NEFORCE_NODISCARD constexpr basic_string_view substr(const size_type off=0, size_type count=npos) const
获取子视图
constexpr void remove_prefix(const size_type n) noexcept
移除前缀
constexpr bool operator==(const date &rhs) const noexcept
相等比较
NEFORCE_NODISCARD constexpr date_type day() const noexcept
获取日期
constexpr date & operator++()
前置递增(加1天)
NEFORCE_NODISCARD constexpr size_t to_hash() const noexcept
计算哈希值
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const
转换为字符串
constexpr date() noexcept=default
默认构造函数,创建1970-01-01
constexpr bool operator<(const date &rhs) const noexcept
小于比较
constexpr bool is_valid() const noexcept
检查日期是否有效
constexpr date operator++(int)
后置递增(加1天)
NEFORCE_NODISCARD constexpr date_type days_of_year() const noexcept
获取一年中的第几天
static constexpr date from_julian_day(const int64_t julian_day) noexcept
从儒略日转换
NEFORCE_NODISCARD constexpr date_type year() const noexcept
获取年份
constexpr date operator-(const date_type day) const noexcept
日期减天数
static constexpr int32_t month_days[12]
每月天数表(非闰年)
constexpr void swap(date &other) noexcept
交换两个日期
static constexpr date_type days_of_month(const date_type year, const date_type month) noexcept
获取指定年月的天数
static constexpr bool is_valid(date_type year, date_type month, date_type day) noexcept
检查日期是否有效
static constexpr bool is_leap_year(const date_type year) noexcept
检查是否为闰年
constexpr date & operator+=(const date_type day) noexcept
日期加天数
NEFORCE_NODISCARD constexpr date_type days_of_week() const noexcept
获取星期几
constexpr date_type operator-(const date &other) const noexcept
日期差(天数)
constexpr void clear() noexcept
重置为纪元起始日期
constexpr date operator+(const date_type day) const noexcept
日期加天数
constexpr int64_t to_julian_day() const noexcept
转换为儒略日
static constexpr date epoch() noexcept
获取纪元起始日期(1970-01-01)
NEFORCE_NODISCARD constexpr date_type month() const noexcept
获取月份
NEFORCE_NODISCARD static constexpr date parse(const string_view view)
从字符串解析
constexpr date & operator-=(const date_type day) noexcept
日期减天数
constexpr datetime & operator++()
前置递增(加1秒)
NEFORCE_NODISCARD constexpr size_t to_hash() const noexcept
计算哈希值
constexpr time_type operator-(const datetime &other) const noexcept
时间差(秒数)
static NEFORCE_NODISCARD constexpr datetime parse_ISO(const string_view view)
解析ISO格式(无时区)
NEFORCE_NODISCARD constexpr date_type day() const noexcept
获取日期
NEFORCE_NODISCARD constexpr date_type year() const noexcept
获取年份
static NEFORCE_NODISCARD constexpr datetime parse_GMT(string_view view)
解析GMT格式
constexpr datetime(const _NEFORCE date &date) noexcept
从日期构造(时间部分为00:00:00)
NEFORCE_NODISCARD constexpr date_type month() const noexcept
获取月份
NEFORCE_NODISCARD constexpr const _NEFORCE date & date() const noexcept
获取日期部分
NEFORCE_NODISCARD constexpr int64_t offset_seconds() const noexcept
获取时区偏移
constexpr datetime(const _NEFORCE date &date, const _NEFORCE time &time, const int64_t offset) noexcept
从日期、时间对象和时区构造
constexpr void clear() noexcept
重置为纪元起始时间
constexpr bool is_valid() const noexcept
检查时间是否有效
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string_ISO() const
转换为ISO格式(无时区)
NEFORCE_NODISCARD constexpr bool has_timezone() const noexcept
检查是否有时区信息
constexpr datetime & operator=(_NEFORCE time &&time) noexcept
从时间移动赋值
static constexpr datetime from_UTC(const datetime &utc, const int32_t offset=0) noexcept
从UTC时间转换为本地时间
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string_ISO_UTC() const
转换为ISO格式带时区
NEFORCE_CONSTEXPR20 bool try_parse_GMT(const string_view view) noexcept
尝试解析GMT格式
constexpr datetime(_NEFORCE date &&date, _NEFORCE time &&time) noexcept
从日期和时间对象移动构造
constexpr bool operator==(const datetime &other) const noexcept
相等比较
static NEFORCE_NODISCARD constexpr datetime parse(const string_view view)
解析简单格式
constexpr datetime(_NEFORCE date &&date) noexcept
从日期构造(时间部分为00:00:00)
NEFORCE_NODISCARD constexpr time_type minutes() const noexcept
获取分钟
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const
转换为简单格式
constexpr datetime & operator=(const _NEFORCE time &time) noexcept
从时间赋值
constexpr datetime(_NEFORCE time &&time) noexcept
从时间构造(日期部分为1970-01-01)
_NEFORCE date::date_type date_type
日期分量类型
NEFORCE_NODISCARD constexpr const _NEFORCE time & time() const noexcept
获取时间部分
NEFORCE_NODISCARD constexpr datetime to_UTC() const noexcept
转换为UTC时间
constexpr datetime operator+(const int64_t seconds) const noexcept
加秒数
constexpr datetime(_NEFORCE date &&date, _NEFORCE time &&time, const int64_t offset) noexcept
从日期、时间对象和时区移动构造
constexpr datetime & operator=(const _NEFORCE date &date) noexcept
从日期赋值
constexpr datetime(const _NEFORCE date &date, const _NEFORCE time &time) noexcept
从日期和时间对象构造
NEFORCE_NODISCARD constexpr time_type hours() const noexcept
获取小时
_NEFORCE time::time_type time_type
时间分量类型
constexpr datetime & operator+=(const int64_t seconds)
加秒数
NEFORCE_NODISCARD constexpr time_type seconds() const noexcept
获取秒
static NEFORCE_NODISCARD constexpr datetime epoch() noexcept
获取纪元起始时间(1970-01-01 00:00:00 UTC)
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_offset_string() const
转换为时区偏移字符串
NEFORCE_CONSTEXPR20 bool try_parse_ISO_UTC(const string_view view) noexcept
尝试解析ISO格式带时区
constexpr datetime operator++(int)
后置递增(加1秒)
static NEFORCE_NODISCARD constexpr datetime parse_ISO_UTC(const string_view view)
解析ISO格式带时区
constexpr datetime operator--(int)
后置递减(减1秒)
constexpr datetime & operator=(_NEFORCE date &&date) noexcept
从日期移动赋值
constexpr datetime(const _NEFORCE time &time) noexcept
从时间构造(日期部分为1970-01-01)
constexpr datetime operator-(const int64_t seconds) const noexcept
减秒数
static NEFORCE_NODISCARD datetime now() noexcept
获取当前本地时间
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string_GMT() const noexcept
转换为GMT格式(RFC 1123)
constexpr datetime & operator-=(const int64_t seconds) noexcept
减秒数
constexpr datetime(const date_type year, const date_type month, const date_type day, const time_type hour, const time_type minute, const time_type second, const int64_t offset) noexcept
从日期、时间和时区构造
NEFORCE_CONSTEXPR20 bool try_parse_ISO(const string_view view) noexcept
尝试解析ISO格式(无时区)
constexpr void swap(datetime &other) noexcept
交换两个日期时间
constexpr datetime & operator--()
前置递减(减1秒)
constexpr bool operator<(const datetime &other) const noexcept
小于比较
constexpr bool operator==(const time &other) const noexcept
相等比较
constexpr time_type operator-(const time &other) const noexcept
时间差(秒数)
NEFORCE_NODISCARD constexpr time_type minutes() const noexcept
获取分钟
constexpr time operator--(int)
后置递减(减1秒)
static NEFORCE_NODISCARD constexpr time parse(const string_view view)
从字符串解析
NEFORCE_NODISCARD constexpr time_type hours() const noexcept
获取小时
constexpr void clear() noexcept
重置为00:00:00
constexpr time() noexcept=default
默认构造函数,创建00:00:00
constexpr bool is_valid() const noexcept
检查时间是否有效
constexpr time operator-(const time_type seconds) const noexcept
时间减秒数
NEFORCE_NODISCARD constexpr time_type to_seconds() const noexcept
转换为总秒数
NEFORCE_NODISCARD constexpr time_type seconds() const noexcept
获取秒
constexpr time & operator--()
前置递减(减1秒)
NEFORCE_NODISCARD constexpr size_t to_hash() const noexcept
计算哈希值
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const
转换为字符串
constexpr time & operator-=(const time_type seconds) noexcept
时间减秒数
constexpr time & operator+=(const time_type seconds)
时间加秒数
constexpr time & operator++()
前置递增(加1秒)
constexpr time operator++(int)
后置递增(加1秒)
static constexpr bool is_valid(time_type hour, time_type minute, time_type second) noexcept
检查时间是否有效
constexpr void swap(time &other) noexcept
交换两个时间
constexpr bool operator<(const time &other) const noexcept
小于比较
constexpr time operator+(const time_type seconds) const noexcept
时间加秒数
static NEFORCE_NODISCARD timestamp now() noexcept
获取当前时间戳
NEFORCE_NODISCARD constexpr datetime to_datetime() const noexcept
转换为日期时间
static NEFORCE_NODISCARD constexpr timestamp parse(const string_view view)
从字符串解析
constexpr timestamp(const value_type value) noexcept
从秒数构造
constexpr timestamp(const datetime &dt) noexcept
从日期时间构造
constexpr void clear() noexcept
重置为0
NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const
转换为字符串
long long int64_t
64位有符号整数类型
duration< int64_t, ratio< 86400 > > days
天持续时间
duration< int64_t, ratio< 3600 > > hours
小时持续时间
duration< int64_t, ratio< 60 > > minutes
分钟持续时间
duration< int64_t > seconds
秒持续时间
NEFORCE_CONSTEXPR14 int sign(const T &value) noexcept
获取数值的符号
basic_string_view< char > string_view
字符字符串视图
void swap()=delete
删除无参数的swap重载
NEFORCE_NODISCARD constexpr package_type value() const noexcept