NexusForce 1.0.0
A Modern C++ Library with extended functionality, web components, and utility libraries
载入中...
搜索中...
未找到
datetime.hpp
浏览该文件的文档.
1#ifndef NEFORCE_CORE_TIME_DATETIME_HPP__
2#define NEFORCE_CORE_TIME_DATETIME_HPP__
3
16
18NEFORCE_BEGIN_NAMESPACE__
19
83
91class NEFORCE_API date : public iobject<date>, public icommon<date> {
92public:
94
95private:
96 date_type year_ = 1970;
97 date_type month_ = 1;
98 date_type day_ = 1;
99
100public:
104 static constexpr int32_t month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
105
106public:
110 constexpr date() noexcept = default;
111
112 NEFORCE_CONSTEXPR20 ~date() = default;
113
122 constexpr explicit date(date_type year, const date_type month, const date_type day) noexcept {
123 if (is_valid(year, month, day)) {
124 year_ = year;
125 month_ = month;
126 day_ = day;
127 }
128 }
129
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;
134
139 NEFORCE_NODISCARD constexpr date_type year() const noexcept { return year_; }
140
145 NEFORCE_NODISCARD constexpr date_type month() const noexcept { return month_; }
146
151 NEFORCE_NODISCARD constexpr date_type day() const noexcept { return day_; }
152
160 static constexpr bool is_valid(date_type year, date_type month, date_type day) noexcept {
161 if (year < 1900 || year > 9999) {
162 return false;
163 }
164 if (month < 1 || month > 12) {
165 return false;
166 }
167 return day > 0 && day <= days_of_month(year, month);
168 }
169
174 NEFORCE_NODISCARD constexpr bool is_valid() const noexcept { return is_valid(year_, month_, day_); }
175
180 static constexpr date epoch() noexcept { return date{}; }
181
187 static constexpr bool is_leap_year(const date_type year) noexcept {
188 return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
189 }
190
195 NEFORCE_NODISCARD constexpr date_type days_of_week() const noexcept {
196 date_type y = year_;
197 date_type m = month_;
198 const date_type d = day_;
199 if (m < 3) {
200 y--;
201 m += 12;
202 }
203 return (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7;
204 }
205
212 static constexpr date_type days_of_month(const date_type year, const date_type month) noexcept {
214 if (month == 2 && is_leap_year(year)) {
215 day += 1;
216 }
217 return day;
218 }
219
224 NEFORCE_NODISCARD constexpr date_type days_of_year() const noexcept {
225 date_type days = 0;
226 for (date_type i = 1; i < month_; ++i) {
227 days += days_of_month(year_, i);
228 }
229 return days + day_;
230 }
231
236 NEFORCE_NODISCARD constexpr int64_t to_julian_day() const noexcept {
237 const date_type a = (14 - month_) / 12;
238 const date_type year = year_ + 4800 - a;
239 const date_type month = month_ + 12 * a - 3;
240 return day_ + (153 * month + 2) / 5 + 365 * year + year / 4 - year / 100 + year / 400 - 32045;
241 }
242
248 static constexpr date from_julian_day(const int64_t julian_day) noexcept {
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;
255
256 const auto day = static_cast<date_type>(e - (153 * m + 2) / 5 + 1);
257 const auto month = static_cast<date_type>(m + 3 - 12 * (m / 10));
258 const auto year = static_cast<date_type>(100 * b + d - 4800 + (m / 10));
259
260 return date(year, month, day);
261 }
262
266 constexpr void clear() noexcept {
267 year_ = 1970;
268 month_ = 1;
269 day_ = 1;
270 }
271
275 NEFORCE_NODISCARD constexpr bool equal_to(const date& rhs) const noexcept {
276 return year_ == rhs.year_ && month_ == rhs.month_ && day_ == rhs.day_;
277 }
278
282 NEFORCE_NODISCARD constexpr bool less_than(const date& rhs) const noexcept {
283 if (year_ < rhs.year_) {
284 return true;
285 }
286 if (year_ == rhs.year_ && month_ < rhs.month_) {
287 return true;
288 }
289 if (year_ == rhs.year_ && month_ == rhs.month_ && day_ < rhs.day_) {
290 return true;
291 }
292 return false;
293 }
294
300 constexpr date& operator+=(const date_type day) noexcept {
301 if (day == 0) {
302 return *this;
303 }
304 if (day < 0) {
305 return *this -= -day;
306 }
307
308 if (day > 365) {
309 const int64_t jd = to_julian_day() + day;
310 *this = from_julian_day(jd);
311 return *this;
312 }
313
314 date_type remaining = day;
315 while (remaining > 0) {
316 const date_type days_in_month = days_of_month(year_, month_);
317 const date_type available = days_in_month - day_ + 1;
318
319 if (remaining < available) {
320 day_ += remaining;
321 break;
322 }
323
324 remaining -= available;
325 day_ = 1;
326 if (++month_ > 12) {
327 month_ = 1;
328 ++year_;
329 }
330 }
331 return *this;
332 }
333
339 constexpr date& operator-=(const date_type day) noexcept {
340 if (day < 0) {
341 return *this += -day;
342 }
343
344 day_ -= day;
345 while (day_ <= 0) {
346 if (--month_ == 0) {
347 month_ = 12;
348 year_--;
349 }
350 day_ += days_of_month(year_, month_);
351 }
352 return *this;
353 }
354
360 constexpr date operator+(const date_type day) const noexcept {
361 date ret(*this);
362 ret += day;
363 return ret;
364 }
365
371 constexpr date operator-(const date_type day) const noexcept {
372 date ret(*this);
373 ret -= day;
374 return ret;
375 }
376
380 constexpr date& operator++() {
381 *this += 1;
382 return *this;
383 }
384
388 constexpr date operator++(int) {
389 const date ret(*this);
390 *this += 1;
391 return ret;
392 }
393
399 constexpr date_type operator-(const date& other) const noexcept {
400 return static_cast<date_type>(to_julian_day() - other.to_julian_day());
401 }
402
407 NEFORCE_NODISCARD constexpr size_t to_hash() const noexcept {
408 constexpr hash<date_type> hasher;
409 return hasher(day()) ^ hasher(month()) ^ hasher(year());
410 }
411
416 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const {
417 return _NEFORCE format("{:04d}-{:02d}-{:02d}", year(), month(), day());
418 }
419
426 NEFORCE_NODISCARD constexpr static date parse(const string_view view) {
427 if (view.size() != 10 || view[4] != '-' || view[7] != '-') {
428 NEFORCE_THROW_EXCEPTION(value_exception("Wrong string formation."));
429 }
430 const date_type year = integer32::parse(view.substr(0, 4)).value();
431 const date_type month = integer32::parse(view.substr(5, 2)).value();
432 const date_type day = integer32::parse(view.substr(8, 2)).value();
433 return date(year, month, day);
434 }
435
440 constexpr void swap(date& other) noexcept {
441 _NEFORCE swap(year_, other.year_);
442 _NEFORCE swap(month_, other.month_);
443 _NEFORCE swap(day_, other.day_);
444 }
445};
446
447
454class NEFORCE_API time : public iobject<time>, public icommon<time> {
455public:
457
458private:
459 time_type hours_ = 0;
460 time_type minutes_ = 0;
461 time_type seconds_ = 0;
462
463public:
467 constexpr time() noexcept = default;
468
472 NEFORCE_CONSTEXPR20 ~time() = default;
473
480 constexpr explicit time(const time_type hour, const time_type minute, const time_type second) noexcept {
481 if (is_valid(hour, minute, second)) {
482 hours_ = hour;
483 minutes_ = minute;
484 seconds_ = second;
485 }
486 }
487
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;
492
497 NEFORCE_NODISCARD constexpr time_type hours() const noexcept { return hours_; }
498
503 NEFORCE_NODISCARD constexpr time_type minutes() const noexcept { return minutes_; }
504
509 NEFORCE_NODISCARD constexpr time_type seconds() const noexcept { return seconds_; }
510
518 static constexpr bool is_valid(time_type hour, time_type minute, time_type second) noexcept {
519 return hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60;
520 }
521
526 NEFORCE_NODISCARD constexpr bool is_valid() const noexcept { return is_valid(hours_, minutes_, seconds_); }
527
531 constexpr void clear() noexcept {
532 hours_ = 0;
533 minutes_ = 0;
534 seconds_ = 0;
535 }
536
541 NEFORCE_NODISCARD constexpr time_type to_seconds() const noexcept {
542 return hours_ * 3600 + minutes_ * 60 + seconds_;
543 }
544
548 NEFORCE_NODISCARD constexpr bool equal_to(const time& other) const noexcept {
549 return hours_ == other.hours_ && minutes_ == other.minutes_ && seconds_ == other.seconds_;
550 }
551
555 NEFORCE_NODISCARD constexpr bool less_than(const time& other) const noexcept {
556 if (hours_ < other.hours_) {
557 return true;
558 }
559 if (hours_ == other.hours_) {
560 if (minutes_ < other.minutes_) {
561 return true;
562 }
563 if (minutes_ == other.minutes_ && seconds_ < other.seconds_) {
564 return true;
565 }
566 }
567 return false;
568 }
569
575 constexpr time& operator+=(const time_type seconds) {
576 if (seconds < 0) {
577 return *this -= -seconds;
578 }
579
580 seconds_ += seconds;
581 const time_type extra_min = seconds_ / 60;
582 seconds_ %= 60;
583
584 minutes_ += extra_min;
585 const time_type extra_hour = minutes_ / 60;
586 minutes_ %= 60;
587
588 hours_ += extra_hour;
589 hours_ %= 24;
590
591 return *this;
592 }
593
599 constexpr time& operator-=(const time_type seconds) noexcept {
600 if (seconds < 0) {
601 return *this += -seconds;
602 }
603
604 int64_t total_sec = to_seconds() - seconds;
605 total_sec %= 86400;
606 if (total_sec < 0) {
607 total_sec += 86400;
608 }
609
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);
613 return *this;
614 }
615
621 constexpr time operator+(const time_type seconds) const noexcept {
622 time ret(*this);
623 ret += seconds;
624 return ret;
625 }
626
632 constexpr time operator-(const time_type seconds) const noexcept {
633 time ret(*this);
634 ret -= seconds;
635 return ret;
636 }
637
641 constexpr time& operator++() { return *this += 1; }
642
646 constexpr time operator++(int) {
647 const time ret(*this);
648 *this += 1;
649 return ret;
650 }
651
655 constexpr time& operator--() { return *this -= 1; }
656
660 constexpr time operator--(int) {
661 const time ret(*this);
662 *this -= 1;
663 return ret;
664 }
665
671 constexpr time_type operator-(const time& other) const noexcept {
672 time_type sec_diff = (hours_ - other.hours_) * 3600;
673 sec_diff += (minutes_ - other.minutes_) * 60;
674 sec_diff += (seconds_ - other.seconds_);
675 return sec_diff;
676 }
677
682 NEFORCE_NODISCARD constexpr size_t to_hash() const noexcept {
683 constexpr hash<time_type> hasher;
684 return hasher(hours()) ^ hasher(minutes()) ^ hasher(seconds());
685 }
686
691 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const {
692 return _NEFORCE format("{:02d}:{:02d}:{:02d}", hours(), minutes(), seconds());
693 }
694
701 NEFORCE_NODISCARD static constexpr time parse(const string_view view) {
702 if (view.size() != 8 || view[2] != ':' || view[5] != ':') {
703 NEFORCE_THROW_EXCEPTION(value_exception("Wrong string formation."));
704 }
705 const time_type hour = integer32::parse(view.substr(0, 2)).value();
706 const time_type minute = integer32::parse(view.substr(3, 2)).value();
707 const time_type second = integer32::parse(view.substr(6, 2)).value();
708 return time(hour, minute, second);
709 }
710
715 constexpr void swap(time& other) noexcept {
716 _NEFORCE swap(hours_, other.hours_);
717 _NEFORCE swap(minutes_, other.minutes_);
718 _NEFORCE swap(seconds_, other.seconds_);
719 }
720};
721
722
734class NEFORCE_API datetime : public iobject<datetime>, public icommon<datetime> {
735public:
736 using date_type = _NEFORCE date::date_type;
737 using time_type = _NEFORCE time::time_type;
738
739private:
740 _NEFORCE date date_;
741 _NEFORCE time time_;
742 int64_t offset_seconds_ = 0;
743 bool has_timezone_ = false;
744
745private:
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]) {
756 return i + 1;
757 }
758 }
759 return 0;
760 }
761
762public:
763 constexpr datetime() noexcept = default;
764
765 NEFORCE_CONSTEXPR20 ~datetime() = default;
766
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;
771
781 constexpr explicit datetime(const date_type year, const date_type month, const date_type day, const time_type hour,
782 const time_type minute, const time_type second) noexcept :
783 date_(year, month, day),
784 time_(hour, minute, second) {}
785
796 constexpr explicit datetime(const date_type year, const date_type month, const date_type day, const time_type hour,
797 const time_type minute, const time_type second, const int64_t offset) noexcept :
798 date_(year, month, day),
799 time_(hour, minute, second),
800 offset_seconds_(offset),
801 has_timezone_(true) {}
802
808 constexpr explicit datetime(const _NEFORCE date& date, const _NEFORCE time& time) noexcept :
809 date_(date),
810 time_(time) {}
811
818 constexpr explicit datetime(const _NEFORCE date& date, const _NEFORCE time& time, const int64_t offset) noexcept :
819 date_(date),
820 time_(time),
821 offset_seconds_(offset),
822 has_timezone_(true) {}
823
828 constexpr explicit datetime(const _NEFORCE date& date) noexcept :
829 date_(date) {}
830
835 constexpr explicit datetime(const _NEFORCE time& time) noexcept :
836 time_(time) {}
837
842 constexpr explicit datetime(_NEFORCE date&& date) noexcept :
843 date_(date) {
844 date.clear();
845 }
846
851 constexpr explicit datetime(_NEFORCE time&& time) noexcept :
852 time_(time) {
853 time.clear();
854 }
855
861 constexpr explicit datetime(_NEFORCE date&& date, _NEFORCE time&& time) noexcept :
862 date_(date),
863 time_(time) {
864 date.clear();
865 time.clear();
866 }
867
874 constexpr explicit datetime(_NEFORCE date&& date, _NEFORCE time&& time, const int64_t offset) noexcept :
875 date_(date),
876 time_(time),
877 offset_seconds_(offset),
878 has_timezone_(true) {
879 date.clear();
880 time.clear();
881 }
882
887 NEFORCE_NODISCARD constexpr bool is_valid() const noexcept { return date_.is_valid() && time_.is_valid(); }
888
894 constexpr datetime& operator=(const _NEFORCE date& date) noexcept {
895 date_ = date;
896 return *this;
897 }
898
904 constexpr datetime& operator=(_NEFORCE date&& date) noexcept {
905 date_ = date;
906 date.clear();
907 return *this;
908 }
909
915 constexpr datetime& operator=(const _NEFORCE time& time) noexcept {
916 time_ = time;
917 return *this;
918 }
919
925 constexpr datetime& operator=(_NEFORCE time&& time) noexcept {
926 time_ = time;
927 time.clear();
928 return *this;
929 }
930
935 NEFORCE_NODISCARD constexpr const _NEFORCE date& date() const noexcept { return date_; }
936
941 NEFORCE_NODISCARD constexpr const _NEFORCE time& time() const noexcept { return time_; }
942
947 NEFORCE_NODISCARD constexpr time_type hours() const noexcept { return time_.hours(); }
948
953 NEFORCE_NODISCARD constexpr time_type minutes() const noexcept { return time_.minutes(); }
954
959 NEFORCE_NODISCARD constexpr time_type seconds() const noexcept { return time_.seconds(); }
960
965 NEFORCE_NODISCARD constexpr date_type year() const noexcept { return date_.year(); }
966
971 NEFORCE_NODISCARD constexpr date_type month() const noexcept { return date_.month(); }
972
977 NEFORCE_NODISCARD constexpr date_type day() const noexcept { return date_.day(); }
978
983 NEFORCE_NODISCARD constexpr bool has_timezone() const noexcept { return has_timezone_; }
984
989 NEFORCE_NODISCARD constexpr int64_t offset_seconds() const noexcept { return offset_seconds_; }
990
995 NEFORCE_NODISCARD static constexpr datetime epoch() noexcept { return datetime{}; }
996
1001 NEFORCE_NODISCARD static datetime now() noexcept;
1002
1006 constexpr void clear() noexcept {
1007 date_.clear();
1008 time_.clear();
1009 offset_seconds_ = 0;
1010 has_timezone_ = false;
1011 }
1012
1016 NEFORCE_NODISCARD constexpr bool equal_to(const datetime& other) const noexcept {
1017 return date_ == other.date_ && time_ == other.time_ && has_timezone_ == other.has_timezone_ &&
1018 offset_seconds_ == other.offset_seconds_;
1019 }
1020
1024 NEFORCE_NODISCARD constexpr bool less_than(const datetime& other) const noexcept {
1025 if (date_ < other.date_) {
1026 return true;
1027 } else if (date_ == other.date_) {
1028 if (time_ < other.time_) {
1029 return true;
1030 } else if (time_ == other.time_) {
1031 if (has_timezone_ && other.has_timezone_ && offset_seconds_ < other.offset_seconds_) {
1032 return true;
1033 }
1034 }
1035 }
1036 return false;
1037 }
1038
1044 constexpr datetime& operator+=(const int64_t seconds) {
1045 if (seconds < 0) {
1046 return *this -= -seconds;
1047 }
1048
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());
1052
1053 int64_t new_total_sec = current_total_sec + seconds;
1054 int64_t days_to_add = new_total_sec / 86400;
1055 new_total_sec %= 86400;
1056
1057 if (new_total_sec < 0) {
1058 new_total_sec += 86400;
1059 days_to_add--;
1060 }
1061
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));
1066 return *this;
1067 }
1068
1074 constexpr datetime& operator-=(const int64_t seconds) noexcept {
1075 if (seconds < 0) {
1076 return *this += -seconds;
1077 }
1078
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());
1082
1083 int64_t new_total_sec = current_total_sec - seconds;
1084 int64_t days_to_subtract = 0;
1085
1086 if (new_total_sec < 0) {
1087 days_to_subtract = (-new_total_sec + 86399) / 86400;
1088 new_total_sec += days_to_subtract * 86400;
1089 }
1090
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));
1095 return *this;
1096 }
1097
1103 constexpr datetime operator+(const int64_t seconds) const noexcept {
1104 datetime ret(*this);
1105 ret += seconds;
1106 return ret;
1107 }
1108
1114 constexpr datetime operator-(const int64_t seconds) const noexcept {
1115 datetime ret(*this);
1116 ret -= seconds;
1117 return ret;
1118 }
1119
1123 constexpr datetime& operator++() { return *this += 1; }
1124
1128 constexpr datetime operator++(int) {
1129 const datetime ret(*this);
1130 *this += 1;
1131 return ret;
1132 }
1133
1137 constexpr datetime& operator--() { return *this -= 1; }
1138
1142 constexpr datetime operator--(int) {
1143 const datetime ret(*this);
1144 *this -= 1;
1145 return ret;
1146 }
1147
1153 constexpr time_type operator-(const datetime& other) const noexcept {
1154 const datetime lhs_utc = this->to_UTC();
1155 const datetime rhs_utc = other.to_UTC();
1156
1157 const time_type day_diff = lhs_utc.date_ - rhs_utc.date_;
1158 time_type sec_diff = day_diff * 86400;
1159 sec_diff += (lhs_utc.time_ - rhs_utc.time_);
1160 return sec_diff;
1161 }
1162
1167 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_offset_string() const {
1168 if (!has_timezone_) {
1169 return {};
1170 }
1171 if (offset_seconds_ == 0) {
1172 return "Z";
1173 }
1174 int64_t total_sec = offset_seconds_;
1175 const char sign = total_sec >= 0 ? '+' : '-';
1176 total_sec = total_sec >= 0 ? total_sec : -total_sec;
1177 const int64_t hours = total_sec / 3600;
1178 const int64_t minutes = (total_sec % 3600) / 60;
1179 return sign + _NEFORCE format("{:02d}:{:02d}", hours, minutes);
1180 }
1181
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) {
1191 utc_time = utc.to_UTC();
1192 }
1193 datetime local = utc_time + offset;
1194 local.offset_seconds_ = offset;
1195 local.has_timezone_ = true;
1196 return local;
1197 }
1198
1203 NEFORCE_NODISCARD constexpr datetime to_UTC() const noexcept {
1204 if (!has_timezone_) {
1205 return *this;
1206 }
1207 datetime utc = *this;
1208 utc -= offset_seconds_;
1209 utc.offset_seconds_ = 0;
1210 utc.has_timezone_ = true;
1211 return utc;
1212 }
1213
1219 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_RFC3339() const {
1220 if (has_timezone_) {
1221 return date_.to_string() + "T" + time_.to_string() + to_offset_string();
1222 } else {
1223 return date_.to_string() + "T" + time_.to_string() + "Z";
1224 }
1225 }
1226
1233 NEFORCE_NODISCARD static constexpr datetime parse_RFC3339(const string_view view) {
1234 if (view.size() < 20 || view[10] != 'T') {
1235 NEFORCE_THROW_EXCEPTION(value_exception("Invalid RFC3339 datetime format."));
1236 }
1237
1238 const _NEFORCE date d = date::parse(view.substr(0, 10));
1239 const _NEFORCE time t = time::parse(view.substr(11, 8));
1240
1241 if (view.size() == 19) {
1242 NEFORCE_THROW_EXCEPTION(value_exception("RFC3339 requires timezone offset (Z or ±HH:MM)."));
1243 }
1244
1245 if (view.size() > 19 && view[19] == 'Z') {
1246 if (view.size() != 20) {
1247 NEFORCE_THROW_EXCEPTION(value_exception("Invalid RFC3339 format: trailing characters after 'Z'."));
1248 }
1249 return datetime(d, t, 0);
1250 } else if (view.size() > 19 && (view[19] == '+' || view[19] == '-')) {
1251 if (view.size() != 25) {
1252 NEFORCE_THROW_EXCEPTION(value_exception("Invalid RFC3339 timezone offset format."));
1253 }
1254 if (view[22] != ':') {
1255 NEFORCE_THROW_EXCEPTION(value_exception("Invalid RFC3339 timezone offset format, expected ':'."));
1256 }
1257
1258 const char sign = view[19];
1259 const int hours = integer32::parse(view.substr(20, 2)).value();
1260 const int minutes = integer32::parse(view.substr(23, 2)).value();
1261
1263 NEFORCE_THROW_EXCEPTION(value_exception("Invalid RFC3339 timezone offset values."));
1264 }
1265
1266 int32_t total_offset = hours * 3600 + minutes * 60;
1267 if (sign == '-') {
1268 total_offset = -total_offset;
1269 }
1270 return datetime(d, t, total_offset);
1271 }
1272
1273 NEFORCE_THROW_EXCEPTION(value_exception("Invalid RFC3339 datetime format."));
1274 }
1275
1281 NEFORCE_CONSTEXPR20 bool try_parse_RFC3339(const string_view view) noexcept {
1282 try {
1283 datetime tmp = datetime::parse_RFC3339(view);
1284 this->swap(tmp);
1285 } catch (...) {
1286 return false;
1287 }
1288 return true;
1289 }
1290
1295 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_RFC1123() const {
1296 constexpr string_view months_string[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1297 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1298
1299 constexpr string_view weekdays_string[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
1300
1301 const _NEFORCE date utc_date = date();
1302 const _NEFORCE time utc_time = time();
1303
1304 int wday = utc_date.days_of_week();
1305 if (wday < 0 || wday >= 7) {
1306 wday = 0;
1307 }
1308
1309 int mon_idx = utc_date.month() - 1;
1310 if (mon_idx < 0 || mon_idx >= 12) {
1311 mon_idx = 0;
1312 }
1313
1314 return _NEFORCE format("{}, {:02d} {} {} {} GMT", weekdays_string[wday], utc_date.day(), months_string[mon_idx],
1315 utc_date.year(), utc_time.to_string());
1316 }
1317
1324 NEFORCE_NODISCARD static constexpr datetime parse_RFC1123(string_view view) {
1325 if (view.size() < 29) {
1326 NEFORCE_THROW_EXCEPTION(value_exception("Invalid date length."));
1327 }
1328 if (view.substr(3, 2) != ", ") {
1329 NEFORCE_THROW_EXCEPTION(value_exception("Invalid date format"));
1330 }
1331
1332 view.remove_prefix(5);
1333 const int day = integer32::parse(view.head(2)).value();
1334 view.remove_prefix(3);
1335 const int mon = months_to_int(view.head(3));
1336 if (mon == 0) {
1337 NEFORCE_THROW_EXCEPTION(value_exception("Invalid month in date"));
1338 }
1339 view.remove_prefix(4);
1340 const int year = integer32::parse(view.head(4)).value();
1341 view.remove_prefix(5);
1342 const int hour = integer32::parse(view.head(2)).value();
1343 view.remove_prefix(3);
1344 const int minute = integer32::parse(view.head(2)).value();
1345 view.remove_prefix(3);
1346 const int second = integer32::parse(view.head(2)).value();
1347 view.remove_prefix(3);
1348
1349 if (view != "GMT") {
1350 NEFORCE_THROW_EXCEPTION(value_exception("Invalid timezone in date"));
1351 }
1352 return datetime(year, mon, day, hour, minute, second);
1353 }
1354
1360 NEFORCE_CONSTEXPR20 bool try_parse_RFC1123(const string_view view) noexcept {
1361 try {
1362 datetime tmp = datetime::parse_RFC1123(view);
1363 this->swap(tmp);
1364 } catch (...) {
1365 return false;
1366 }
1367 return true;
1368 }
1369
1374 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_ISO8601() const {
1375 return date_.to_string() + "T" + time_.to_string();
1376 }
1377
1384 NEFORCE_NODISCARD static constexpr datetime parse_ISO8601(const string_view view) {
1385 if (view.size() < 19 || view[10] != 'T') {
1386 NEFORCE_THROW_EXCEPTION(value_exception("Invalid ISO 8601 datetime format."));
1387 }
1388 const _NEFORCE date d = _NEFORCE date::parse(view.substr(0, 10));
1389 size_t time_len = 8;
1390 if (view.size() >= 19) {
1391 time_len = 8;
1392 }
1393 const _NEFORCE time t = _NEFORCE time::parse(view.substr(11, time_len));
1394 return datetime(d, t);
1395 }
1396
1402 NEFORCE_CONSTEXPR20 bool try_parse_ISO8601(const string_view view) noexcept {
1403 try {
1404 datetime tmp = datetime::parse_ISO8601(view);
1405 this->swap(tmp);
1406 } catch (...) {
1407 return false;
1408 }
1409 return true;
1410 }
1411
1416 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const {
1417 return date_.to_string() + " " + time_.to_string();
1418 }
1419
1426 NEFORCE_NODISCARD static constexpr datetime parse(const string_view view) {
1427 if (view.size() != 19 || view[10] != ' ') {
1428 NEFORCE_THROW_EXCEPTION(value_exception("Wrong string formation."));
1429 }
1430 const _NEFORCE date d = date::parse(view.substr(0, 10));
1431 const _NEFORCE time t = time::parse(view.substr(11, 8));
1432 return datetime(d, t);
1433 }
1434
1439 NEFORCE_NODISCARD constexpr size_t to_hash() const noexcept {
1440 return date_.to_hash() ^ time_.to_hash() ^ hash<bool>()(has_timezone_) ^ hash<int64_t>()(offset_seconds_);
1441 }
1442
1447 constexpr void swap(datetime& other) noexcept {
1448 _NEFORCE swap(date_, other.date_);
1449 _NEFORCE swap(time_, other.time_);
1450 _NEFORCE swap(offset_seconds_, other.offset_seconds_);
1451 _NEFORCE swap(has_timezone_, other.has_timezone_);
1452 }
1453};
1454
1455
1463class NEFORCE_API timestamp : public iobject<timestamp>, public ipackage<timestamp, int64_t> {
1464public:
1466
1467 constexpr timestamp() noexcept = default;
1468
1469 NEFORCE_CONSTEXPR20 ~timestamp() = default;
1470
1471 constexpr timestamp(const timestamp& other) noexcept :
1472 ipackage(other.value_) {}
1473
1474 constexpr timestamp& operator=(const timestamp& other) noexcept {
1475 value_ = other.value_;
1476 return *this;
1477 }
1478
1479 constexpr timestamp(timestamp&& other) noexcept :
1480 ipackage(other.value_) {
1481 other.clear();
1482 }
1483
1484 constexpr timestamp& operator=(timestamp&& other) noexcept {
1485 value_ = other.value_;
1486 other.clear();
1487 return *this;
1488 }
1489
1494 constexpr explicit timestamp(const value_type value) noexcept :
1495 ipackage(value) {}
1496
1501 constexpr explicit timestamp(const datetime& dt) noexcept {
1502 const datetime utc = dt.to_UTC();
1503 value_ = utc - datetime::epoch().to_UTC();
1504 }
1505
1510 NEFORCE_NODISCARD static timestamp now() noexcept { return timestamp(datetime::now()); }
1511
1516 NEFORCE_NODISCARD constexpr datetime to_datetime() const noexcept { return datetime::epoch() + value_; }
1517
1522 NEFORCE_NODISCARD NEFORCE_CONSTEXPR20 string to_string() const { return integer64(value_).to_string(); }
1523
1529 NEFORCE_NODISCARD static constexpr timestamp parse(const string_view view) {
1530 return timestamp{integer64::parse(view).value()};
1531 }
1532
1536 constexpr void clear() noexcept { value_ = 0; }
1537};
1538 // DateTime
1540
1541NEFORCE_END_NAMESPACE__
1542#endif // NEFORCE_CORE_TIME_DATETIME_HPP__
constexpr size_type size() const noexcept
获取字符串长度
constexpr basic_string_view substr(const size_type off=0, const size_type count=npos) const
获取子视图
constexpr void remove_prefix(const size_type n) noexcept
移除前缀
constexpr basic_string_view head(const size_type count=npos) const
获取头部子串
日期类
static constexpr date parse(const string_view view)
从字符串解析
constexpr date_type days_of_week() const noexcept
获取星期几
int32_t date_type
日期分量类型
constexpr date & operator++()
前置递增(加1天)
constexpr bool equal_to(const date &rhs) const noexcept
相等比较
constexpr bool less_than(const date &rhs) const noexcept
小于比较
constexpr date() noexcept=default
默认构造函数,创建1970-01-01
constexpr size_t to_hash() const noexcept
计算哈希值
constexpr bool is_valid() const noexcept
检查日期是否有效
constexpr date operator++(int)
后置递增(加1天)
constexpr date_type year() const noexcept
获取年份
static constexpr date from_julian_day(const int64_t julian_day) 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
日期加天数
constexpr string to_string() const
转换为字符串
constexpr date_type days_of_year() const noexcept
获取一年中的第几天
constexpr date_type operator-(const date &other) const noexcept
日期差(天数)
constexpr date_type month() 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)
constexpr date & operator-=(const date_type day) noexcept
日期减天数
constexpr date_type day() const noexcept
获取日期
日期时间类
constexpr datetime & operator++()
前置递增(加1秒)
constexpr time_type operator-(const datetime &other) const noexcept
时间差(秒数)
constexpr datetime(const _NEFORCE date &date) noexcept
从日期构造(时间部分为00:00:00)
constexpr const _NEFORCE time & time() 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
检查时间是否有效
constexpr datetime & operator=(_NEFORCE time &&time) noexcept
从时间移动赋值
static constexpr datetime from_UTC(const datetime &utc, const int32_t offset=0) noexcept
从UTC时间转换为本地时间
static constexpr datetime parse(const string_view view)
解析简单格式
constexpr date_type year() const noexcept
获取年份
constexpr datetime(_NEFORCE date &&date, _NEFORCE time &&time) noexcept
从日期和时间对象移动构造
constexpr date_type month() const noexcept
获取月份
constexpr string to_string() const
转换为简单格式
static constexpr datetime parse_RFC1123(string_view view)
解析 RFC 1123
constexpr string to_offset_string() const
转换为时区偏移字符串
constexpr datetime(_NEFORCE date &&date) noexcept
从日期构造(时间部分为00:00:00)
constexpr int64_t offset_seconds() const noexcept
获取时区偏移
constexpr datetime & operator=(const _NEFORCE time &time) noexcept
从时间赋值
constexpr time_type minutes() const noexcept
获取分钟
constexpr const _NEFORCE date & date() const noexcept
获取日期部分
constexpr string to_RFC3339() const
转换为 RFC 3339
constexpr datetime(_NEFORCE time &&time) noexcept
从时间构造(日期部分为1970-01-01)
constexpr string to_RFC1123() const
转换为 RFC 1123
constexpr size_t to_hash() const noexcept
计算哈希值
constexpr date_type day() const noexcept
获取日期
_NEFORCE date::date_type date_type
日期分量类型
constexpr datetime operator+(const int64_t seconds) const noexcept
加秒数
constexpr bool has_timezone() const noexcept
检查是否有时区信息
constexpr datetime(_NEFORCE date &&date, _NEFORCE time &&time, const int64_t offset) noexcept
从日期、时间对象和时区移动构造
constexpr bool try_parse_ISO8601(const string_view view) noexcept
尝试解析 ISO 8601
constexpr datetime & operator=(const _NEFORCE date &date) noexcept
从日期赋值
constexpr datetime(const _NEFORCE date &date, const _NEFORCE time &time) noexcept
从日期和时间对象构造
constexpr time_type seconds() const noexcept
获取秒
static datetime now() noexcept
获取当前本地时间
_NEFORCE time::time_type time_type
时间分量类型
constexpr datetime & operator+=(const int64_t seconds)
加秒数
static constexpr datetime parse_ISO8601(const string_view view)
解析 ISO 8601
static constexpr datetime epoch() noexcept
获取纪元起始时间(1970-01-01 00:00:00 UTC)
constexpr datetime operator++(int)
后置递增(加1秒)
constexpr datetime operator--(int)
后置递减(减1秒)
constexpr datetime & operator=(_NEFORCE date &&date) noexcept
从日期移动赋值
constexpr datetime(const _NEFORCE time &time) noexcept
从时间构造(日期部分为1970-01-01)
constexpr bool try_parse_RFC1123(const string_view view) noexcept
尝试解析 RFC 1123
constexpr datetime operator-(const int64_t seconds) const noexcept
减秒数
constexpr string to_ISO8601() const
转换为 ISO 8601
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
从日期、时间和时区构造
constexpr datetime to_UTC() const noexcept
转换为UTC时间
constexpr time_type hours() const noexcept
获取小时
static constexpr datetime parse_RFC3339(const string_view view)
解析 RFC 3339
constexpr bool equal_to(const datetime &other) const noexcept
相等比较
constexpr bool try_parse_RFC3339(const string_view view) noexcept
尝试解析 RFC 3339
constexpr void swap(datetime &other) noexcept
交换两个日期时间
constexpr datetime & operator--()
前置递减(减1秒)
constexpr bool less_than(const datetime &other) const noexcept
小于比较
时间类
constexpr time_type operator-(const time &other) const noexcept
时间差(秒数)
constexpr string to_string() const
转换为字符串
constexpr time operator--(int)
后置递减(减1秒)
constexpr time_type to_seconds() const noexcept
转换为总秒数
constexpr void clear() noexcept
重置为00:00:00
constexpr size_t to_hash() const noexcept
计算哈希值
constexpr time() noexcept=default
默认构造函数,创建00:00:00
int32_t time_type
时间分量类型
constexpr bool is_valid() const noexcept
检查时间是否有效
constexpr time operator-(const time_type seconds) const noexcept
时间减秒数
constexpr time_type minutes() const noexcept
获取分钟
constexpr time_type seconds() const noexcept
获取秒
static constexpr time parse(const string_view view)
从字符串解析
constexpr time & operator--()
前置递减(减1秒)
constexpr time_type hours() const noexcept
获取小时
constexpr time & operator-=(const time_type seconds) noexcept
时间减秒数
constexpr time & operator+=(const time_type seconds)
时间加秒数
constexpr bool equal_to(const time &other) const noexcept
相等比较
constexpr bool less_than(const time &other) const noexcept
小于比较
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 time operator+(const time_type seconds) const noexcept
时间加秒数
时间戳类
static timestamp now() noexcept
获取当前时间戳
constexpr datetime to_datetime() const noexcept
转换为日期时间
constexpr string to_string() const
转换为字符串
int64_t value_type
值类型
constexpr timestamp(const value_type value) noexcept
从秒数构造
constexpr timestamp(const datetime &dt) noexcept
从日期时间构造
static constexpr timestamp parse(const string_view view)
从字符串解析
constexpr void clear() noexcept
重置为0
long long int64_t
64位有符号整数类型
int int32_t
32位有符号整数类型
duration< int64_t, ratio< 86400 > > days
天持续时间
duration< int64_t, ratio< 3600 > > hours
小时持续时间
duration< int64_t, ratio< 60 > > minutes
分钟持续时间
duration< int64_t > seconds
秒持续时间
constexpr string format(const string_view fmt, Args &&... args)
格式化字符串
constexpr int sign(const T &value) noexcept
获取数值的符号
basic_string_view< char > string_view
字符字符串视图
void swap()=delete
删除无参数的swap重载
数值类型包装类
哈希函数的主模板
通用接口,同时具备可比较和可哈希功能
64位整数包装类
可解析对象接口
constexpr package_type value() const noexcept
变量处理异常