From 2ce8238dee5fa9313581311980c4c72e694db627 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Thu, 13 Jul 2023 19:26:30 +0800 Subject: [PATCH] add get gmt time str, get local time str --- include/ylt/thirdparty/cinatra/define.h | 1 + include/ylt/thirdparty/cinatra/time_util.hpp | 413 +++++++++++++++++++ include/ylt/thirdparty/cinatra/utils.hpp | 263 ------------ include/ylt/util/time_util.h | 28 +- src/util/tests/test_time_util.cpp | 16 +- 5 files changed, 452 insertions(+), 269 deletions(-) create mode 100644 include/ylt/thirdparty/cinatra/time_util.hpp diff --git a/include/ylt/thirdparty/cinatra/define.h b/include/ylt/thirdparty/cinatra/define.h index 77651a3cb..245419b40 100644 --- a/include/ylt/thirdparty/cinatra/define.h +++ b/include/ylt/thirdparty/cinatra/define.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include diff --git a/include/ylt/thirdparty/cinatra/time_util.hpp b/include/ylt/thirdparty/cinatra/time_util.hpp new file mode 100644 index 000000000..a2d687206 --- /dev/null +++ b/include/ylt/thirdparty/cinatra/time_util.hpp @@ -0,0 +1,413 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "define.h" + +namespace cinatra { +namespace time_util { +inline constexpr bool is_leap(int year) { + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); +} + +inline constexpr int get_day_index(std::string_view str) { + return week_table[((str[0] & ~0x20) ^ (str[2] & ~0x20)) % week_table.size()]; +} + +inline constexpr int get_month_index(std::string_view str) { + return month_table[((str[1] & ~0x20) + (str[2] & ~0x20)) % + month_table.size()]; +} + +inline constexpr int days_in(int m, int year) { + constexpr int index = get_month_index("Feb"); + if (m == index && is_leap(year)) { + return 29; + } + return int(days_before[m + 1] - days_before[m]); +} + +inline constexpr int get_digit(std::string_view sv, int width) { + int num = 0; + for (int i = 0; i < width; i++) { + if ('0' <= sv[i] && sv[i] <= '9') { + num = num * 10 + (sv[i] - '0'); + } + else { + return -1; + } + } + return num; +} + +inline constexpr std::uint64_t days_since_epoch(int year) { + auto y = std::uint64_t(std::int64_t(year) - absolute_zero_year); + + auto n = y / 400; + y -= 400 * n; + auto d = days_per_400_years * n; + n = y / 100; + y -= 100 * n; + d += days_per_100_years * n; + n = y / 4; + y -= 4 * n; + d += days_per_4_years * n; + n = y; + d += 365 * n; + return d; +} + +inline std::pair faster_mktime(int year, int month, int day, + int hour, int min, int sec, + int day_of_week) { + auto d = days_since_epoch(year); + d += std::uint64_t(days_before[month]); + constexpr int index = get_month_index("Mar"); + if (is_leap(year) && month >= index) { + d++; // February 29 + } + d += std::uint64_t(day - 1); + auto abs = d * seconds_per_day; + abs += + std::uint64_t(hour * seconds_per_hour + min * seconds_per_minute + sec); + constexpr int day_index = get_day_index("Mon"); + if (day_of_week != -1) { + std::int64_t wday = ((abs + std::uint64_t(day_index) * seconds_per_day) % + seconds_per_week) / + seconds_per_day; + if (wday != day_of_week) { + return {false, 0}; + } + } + return {true, std::int64_t(abs) + (absolute_to_internal + internal_to_unix)}; +} + +template +inline constexpr std::array get_format() { + if constexpr (Format == time_format::http_format) { + return http_time_format; + } + else if constexpr (Format == time_format::utc_format) { + return utc_time_format; + } + else { + return utc_time_without_punctuation_format; + } +} +} // namespace time_util + +template +inline std::pair get_timestamp( + const std::string &gmt_time_str) { + using namespace time_util; + std::string_view sv(gmt_time_str); + int year, month, day, hour, min, sec, day_of_week; + int len_of_gmt_time_str = (int)gmt_time_str.length(); + int len_of_processed_part = 0; + int len_of_ignored_part = 0; // second_decimal_part is ignored + char c; + constexpr std::array real_format = + time_util::get_format(); + if constexpr (Format == time_format::utc_format) { + day_of_week = -1; + } + else if (Format == time_format::utc_without_punctuation_format) { + day_of_week = -1; + } + + for (auto &comp : real_format) { + switch (comp) { + case component_of_time_format::ending: + goto travel_done; + break; + case component_of_time_format::colon: + case component_of_time_format::comma: + case component_of_time_format::SP: + case component_of_time_format::hyphen: + case component_of_time_format::dot: + case component_of_time_format::T: + case component_of_time_format::Z: + if (len_of_gmt_time_str - len_of_processed_part < 1) { + return {false, 0}; + } + c = sv[len_of_processed_part]; + if ((comp == component_of_time_format::Z && c != 'Z') || + (comp == component_of_time_format::T && c != 'T') || + (comp == component_of_time_format::dot && c != '.') || + (comp == component_of_time_format::hyphen && c != '-') || + (comp == component_of_time_format::SP && c != ' ') || + (comp == component_of_time_format::colon && c != ':') || + (comp == component_of_time_format::comma && c != ',')) { + return {false, 0}; + } + len_of_processed_part += 1; + break; + case component_of_time_format::year: + if (len_of_gmt_time_str - len_of_processed_part < 4) { + return {false, 0}; + } + if ((year = get_digit(sv.substr(len_of_processed_part, 4), 4)) == -1) { + return {false, 0}; + } + len_of_processed_part += 4; + break; + case component_of_time_format::month_name: + if (len_of_gmt_time_str - len_of_processed_part < 3) { + return {false, 0}; + } + if ((month = get_month_index(sv.substr(len_of_processed_part, 3))) == + -1) { + return {false, 0}; + } + len_of_processed_part += 3; + break; + case component_of_time_format::hour: + case component_of_time_format::minute: + case component_of_time_format::second: + case component_of_time_format::month: + case component_of_time_format::day: + if (len_of_gmt_time_str - len_of_processed_part < 2) { + return {false, 0}; + } + int digit; + if ((digit = get_digit(sv.substr(len_of_processed_part, 2), 2)) == -1) { + return {false, 0}; + } + if (comp == component_of_time_format::hour) { + hour = digit; + if (hour < 0 || hour >= 24) { + return {false, 0}; + } + } + else if (comp == component_of_time_format::minute) { + min = digit; + if (min < 0 || min >= 60) { + return {false, 0}; + } + } + else if (comp == component_of_time_format::month) { + month = digit; + if (month < 1 || month > 12) { + return {false, 0}; + } + month--; + } + else if (comp == component_of_time_format::second) { + sec = digit; + if (sec < 0 || sec >= 60) { + return {false, 0}; + } + } + else { + day = digit; + } + len_of_processed_part += 2; + break; + case component_of_time_format::day_name: + if (len_of_gmt_time_str - len_of_processed_part < 3) { + return {false, 0}; + } + if ((day_of_week = get_day_index(sv.substr(len_of_processed_part, 3))) < + 0) { + return {false, 0}; + } + len_of_processed_part += 3; + break; + case component_of_time_format::GMT: + if (len_of_gmt_time_str - len_of_processed_part < 3) { + return {false, 0}; + } + if (sv.substr(len_of_processed_part, 3) != "GMT") { + return {false, 0}; + } + len_of_processed_part += 3; + break; + case component_of_time_format::second_decimal_part: + int cur = len_of_processed_part; + while (cur < len_of_gmt_time_str && + (sv[cur] >= '0' && sv[cur] <= '9')) { + len_of_ignored_part++; + cur++; + } + if (cur == len_of_processed_part) { + return {false, 0}; + } + len_of_processed_part = cur; + break; + } + } +travel_done: + if ((len_of_processed_part != len_of_gmt_time_str) || + (len_of_processed_part != len_of_http_time_format && + (len_of_processed_part - len_of_ignored_part) != + len_of_utc_time_format) && + (len_of_processed_part - len_of_ignored_part) != + len_of_utc_time_without_punctuation_format) { + return {false, 0}; + } + if (day < 1 || day > days_in(month, year)) { + return {false, 0}; + } + return faster_mktime(year, month, day, hour, min, sec, day_of_week); +} + +constexpr char digits[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; +constexpr std::string_view WDAY[7] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; +constexpr std::string_view YMON[12] = {"Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec"}; + +template +inline void to_int(int num, char c, char *p) { + for (int i = 0; i < N; i++) { + p[N - 1 - i] = digits[num % 10]; + num = num / 10; + } + + p[N] = c; +} + +inline void to_year(char *buf, int year, char c) { to_int<4>(year, c, buf); } +inline void to_month(char *buf, int month, char c) { to_int<2>(month, c, buf); } +inline void to_day(char *buf, int day, char c) { to_int<2>(day, c, buf); } +inline void to_hour(char *buf, int day, char c) { to_int<2>(day, c, buf); } +inline void to_min(char *buf, int day, char c) { to_int<2>(day, c, buf); } +inline void to_sec(char *buf, int day, char c) { to_int<2>(day, c, buf); } + +template +inline std::string_view get_local_time_str(char (&buf)[N], std::time_t t, + std::string_view format) { + static_assert(N >= 20, "wrong buf"); + struct tm *loc_time = gmtime(&t); + + char *p = buf; + + for (int i = 0; i < format.size(); ++i) { + if (format[i] == '%') { + char c = i + 2 < format.size() ? format[i + 2] : '0'; + i++; + if (format[i] == 'Y') { + to_year(p, loc_time->tm_year + 1900, c); + p += 5; + } + else if (format[i] == 'm') { + to_month(p, loc_time->tm_mon + 1, c); + p += 3; + } + else if (format[i] == 'd') { + to_day(p, loc_time->tm_mday, c); + p += 3; + } + else if (format[i] == 'H') { + to_hour(p, loc_time->tm_hour + Hour, c); + p += 3; + } + else if (format[i] == 'M') { + to_min(p, loc_time->tm_min, c); + p += 3; + } + else if (format[i] == 'S') { + to_sec(p, loc_time->tm_sec, c); + p += 3; + } + else if (format[i] == 'a') { + memcpy(p, WDAY[loc_time->tm_wday].data(), 3); + p += 3; + *p++ = c; + *p++ = ' '; + } + else if (format[i] == 'b') { + memcpy(p, YMON[loc_time->tm_mon].data(), 3); + p += 3; + *p = c; + p += 1; + } + } + } + + size_t n = p - buf - 1; + + return {buf, n}; +} + +// template +// inline std::string_view get_local_time_str(char (&buf)[N], std::time_t t) { +// struct tm *loc_time = localtime(&t); +// size_t n = strftime(buf, N, "%Y-%m-%d %H:%M:%S", loc_time); +// return {buf, n}; +// } + +inline std::string_view get_local_time_str( + std::chrono::system_clock::time_point t) { + static thread_local char buf[32]; + static thread_local std::chrono::seconds last_sec{}; + static thread_local size_t last_size{}; + + std::chrono::system_clock::duration d = t.time_since_epoch(); + std::chrono::seconds s = std::chrono::duration_cast(d); + if (last_sec == s) { + return {buf, last_size}; + } + + auto tm = std::chrono::system_clock::to_time_t(t); + + auto str = get_local_time_str(buf, tm, "%Y-%m-%d %H:%M:%S"); + last_size = str.size(); + last_sec = s; + + return str; +} + +inline std::string_view get_local_time_str() { + return get_local_time_str(std::chrono::system_clock::now()); +} + +// template +// inline std::string_view get_gmt_time_str2(char (&buf)[N], std::time_t t) { +// static_assert(N >= 29, "wrong buf"); +// struct tm *gmt_time = gmtime(&t); +// size_t n = strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", +// gmt_time); return {buf, n}; +// } + +template +inline std::string_view get_gmt_time_str(char (&buf)[N], std::time_t t) { + static_assert(N >= 29, "wrong buf"); + auto s = get_local_time_str<0>(buf, t, "%a, %d %b %Y %H:%M:%S"); + size_t size = s.size(); + memcpy(buf + size, " GMT", 4); + + return {s.data(), size + 4}; +} + +inline std::string_view get_gmt_time_str( + std::chrono::system_clock::time_point t) { + static thread_local char buf[32]; + static thread_local std::chrono::seconds last_sec{}; + static thread_local size_t last_size{}; + + std::chrono::system_clock::duration d = t.time_since_epoch(); + std::chrono::seconds s = std::chrono::duration_cast(d); + if (last_sec == s) { + return {buf, last_size}; + } + + auto tm = std::chrono::system_clock::to_time_t(t); + + auto str = get_gmt_time_str(buf, tm); + last_size = str.size(); + last_sec = s; + + return str; +} + +inline std::string_view get_gmt_time_str() { + return get_gmt_time_str(std::chrono::system_clock::now()); +} + +} // namespace cinatra diff --git a/include/ylt/thirdparty/cinatra/utils.hpp b/include/ylt/thirdparty/cinatra/utils.hpp index aedb5bcd9..032924cb2 100644 --- a/include/ylt/thirdparty/cinatra/utils.hpp +++ b/include/ylt/thirdparty/cinatra/utils.hpp @@ -837,269 +837,6 @@ constexpr auto get_method_arr() { return arr; } -inline std::string get_time_str(std::time_t t) { - std::stringstream ss; - ss << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S"); - return ss.str(); -} - -inline std::string get_gmt_time_str(std::time_t t) { - struct tm *GMTime = gmtime(&t); - char buff[512] = {0}; - strftime(buff, sizeof(buff), "%a, %d %b %Y %H:%M:%S GMT", GMTime); - return buff; -} - -namespace time_util { -inline constexpr bool is_leap(int year) { - return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); -} - -inline constexpr int get_day_index(std::string_view str) { - return week_table[((str[0] & ~0x20) ^ (str[2] & ~0x20)) % week_table.size()]; -} - -inline constexpr int get_month_index(std::string_view str) { - return month_table[((str[1] & ~0x20) + (str[2] & ~0x20)) % - month_table.size()]; -} - -inline constexpr int days_in(int m, int year) { - constexpr int index = get_month_index("Feb"); - if (m == index && is_leap(year)) { - return 29; - } - return int(days_before[m + 1] - days_before[m]); -} - -inline constexpr int get_digit(std::string_view sv, int width) { - int num = 0; - for (int i = 0; i < width; i++) { - if ('0' <= sv[i] && sv[i] <= '9') { - num = num * 10 + (sv[i] - '0'); - } - else { - return -1; - } - } - return num; -} - -inline constexpr std::uint64_t days_since_epoch(int year) { - auto y = std::uint64_t(std::int64_t(year) - absolute_zero_year); - - auto n = y / 400; - y -= 400 * n; - auto d = days_per_400_years * n; - n = y / 100; - y -= 100 * n; - d += days_per_100_years * n; - n = y / 4; - y -= 4 * n; - d += days_per_4_years * n; - n = y; - d += 365 * n; - return d; -} - -inline std::pair faster_mktime(int year, int month, int day, - int hour, int min, int sec, - int day_of_week) { - auto d = days_since_epoch(year); - d += std::uint64_t(days_before[month]); - constexpr int index = get_month_index("Mar"); - if (is_leap(year) && month >= index) { - d++; // February 29 - } - d += std::uint64_t(day - 1); - auto abs = d * seconds_per_day; - abs += - std::uint64_t(hour * seconds_per_hour + min * seconds_per_minute + sec); - constexpr int day_index = get_day_index("Mon"); - if (day_of_week != -1) { - std::int64_t wday = ((abs + std::uint64_t(day_index) * seconds_per_day) % - seconds_per_week) / - seconds_per_day; - if (wday != day_of_week) { - return {false, 0}; - } - } - return {true, std::int64_t(abs) + (absolute_to_internal + internal_to_unix)}; -} - -template -inline constexpr std::array get_format() { - if constexpr (Format == time_format::http_format) { - return http_time_format; - } - else if constexpr (Format == time_format::utc_format) { - return utc_time_format; - } - else { - return utc_time_without_punctuation_format; - } -} -} // namespace time_util - -template -inline std::pair get_timestamp( - const std::string &gmt_time_str) { - using namespace time_util; - std::string_view sv(gmt_time_str); - int year, month, day, hour, min, sec, day_of_week; - int len_of_gmt_time_str = (int)gmt_time_str.length(); - int len_of_processed_part = 0; - int len_of_ignored_part = 0; // second_decimal_part is ignored - char c; - constexpr std::array real_format = - time_util::get_format(); - if constexpr (Format == time_format::utc_format) { - day_of_week = -1; - } - else if (Format == time_format::utc_without_punctuation_format) { - day_of_week = -1; - } - - for (auto &comp : real_format) { - switch (comp) { - case component_of_time_format::ending: - goto travel_done; - break; - case component_of_time_format::colon: - case component_of_time_format::comma: - case component_of_time_format::SP: - case component_of_time_format::hyphen: - case component_of_time_format::dot: - case component_of_time_format::T: - case component_of_time_format::Z: - if (len_of_gmt_time_str - len_of_processed_part < 1) { - return {false, 0}; - } - c = sv[len_of_processed_part]; - if ((comp == component_of_time_format::Z && c != 'Z') || - (comp == component_of_time_format::T && c != 'T') || - (comp == component_of_time_format::dot && c != '.') || - (comp == component_of_time_format::hyphen && c != '-') || - (comp == component_of_time_format::SP && c != ' ') || - (comp == component_of_time_format::colon && c != ':') || - (comp == component_of_time_format::comma && c != ',')) { - return {false, 0}; - } - len_of_processed_part += 1; - break; - case component_of_time_format::year: - if (len_of_gmt_time_str - len_of_processed_part < 4) { - return {false, 0}; - } - if ((year = get_digit(sv.substr(len_of_processed_part, 4), 4)) == -1) { - return {false, 0}; - } - len_of_processed_part += 4; - break; - case component_of_time_format::month_name: - if (len_of_gmt_time_str - len_of_processed_part < 3) { - return {false, 0}; - } - if ((month = get_month_index(sv.substr(len_of_processed_part, 3))) == - -1) { - return {false, 0}; - } - len_of_processed_part += 3; - break; - case component_of_time_format::hour: - case component_of_time_format::minute: - case component_of_time_format::second: - case component_of_time_format::month: - case component_of_time_format::day: - if (len_of_gmt_time_str - len_of_processed_part < 2) { - return {false, 0}; - } - int digit; - if ((digit = get_digit(sv.substr(len_of_processed_part, 2), 2)) == -1) { - return {false, 0}; - } - if (comp == component_of_time_format::hour) { - hour = digit; - if (hour < 0 || hour >= 24) { - return {false, 0}; - } - } - else if (comp == component_of_time_format::minute) { - min = digit; - if (min < 0 || min >= 60) { - return {false, 0}; - } - } - else if (comp == component_of_time_format::month) { - month = digit; - if (month < 1 || month > 12) { - return {false, 0}; - } - month--; - } - else if (comp == component_of_time_format::second) { - sec = digit; - if (sec < 0 || sec >= 60) { - return {false, 0}; - } - } - else { - day = digit; - } - len_of_processed_part += 2; - break; - case component_of_time_format::day_name: - if (len_of_gmt_time_str - len_of_processed_part < 3) { - return {false, 0}; - } - if ((day_of_week = get_day_index(sv.substr(len_of_processed_part, 3))) < - 0) { - return {false, 0}; - } - len_of_processed_part += 3; - break; - case component_of_time_format::GMT: - if (len_of_gmt_time_str - len_of_processed_part < 3) { - return {false, 0}; - } - if (sv.substr(len_of_processed_part, 3) != "GMT") { - return {false, 0}; - } - len_of_processed_part += 3; - break; - case component_of_time_format::second_decimal_part: - int cur = len_of_processed_part; - while (cur < len_of_gmt_time_str && - (sv[cur] >= '0' && sv[cur] <= '9')) { - len_of_ignored_part++; - cur++; - } - if (cur == len_of_processed_part) { - return {false, 0}; - } - len_of_processed_part = cur; - break; - } - } -travel_done: - if ((len_of_processed_part != len_of_gmt_time_str) || - (len_of_processed_part != len_of_http_time_format && - (len_of_processed_part - len_of_ignored_part) != - len_of_utc_time_format) && - (len_of_processed_part - len_of_ignored_part) != - len_of_utc_time_without_punctuation_format) { - return {false, 0}; - } - if (day < 1 || day > days_in(month, year)) { - return {false, 0}; - } - return faster_mktime(year, month, day, hour, min, sec, day_of_week); -} - -inline std::string get_cur_time_str() { - return get_time_str(std::time(nullptr)); -} - inline const std::map get_cookies_map( std::string_view cookies_str) { std::map cookies; diff --git a/include/ylt/util/time_util.h b/include/ylt/util/time_util.h index 9074d226f..2b2abc2a4 100644 --- a/include/ylt/util/time_util.h +++ b/include/ylt/util/time_util.h @@ -15,7 +15,7 @@ */ #pragma once -#include +#include namespace ylt { using time_format = cinatra::time_format; @@ -26,7 +26,29 @@ inline std::pair get_timestamp( return cinatra::get_timestamp(gmt_time_str); } -inline std::string get_gmt_time_str(std::time_t t) { - return cinatra::get_gmt_time_str(t); +inline std::string_view get_gmt_time_str(std::time_t t) { + return cinatra::get_gmt_time_str(std::chrono::system_clock::from_time_t(t)); +} + +template +inline std::string_view get_gmt_time_str(char (&buf)[N], std::time_t t) { + return cinatra::get_gmt_time_str(buf, t); +} + +inline std::string_view get_gmt_time_str() { + return cinatra::get_gmt_time_str(); +} + +inline std::string_view get_local_time_str(std::time_t t) { + return cinatra::get_local_time_str(std::chrono::system_clock::from_time_t(t)); +} + +template +inline std::string_view get_local_time_str(char (&buf)[N], std::time_t t) { + return cinatra::get_local_time_str(buf, t); +} + +inline std::string_view get_local_time_str() { + return cinatra::get_local_time_str(); } } // namespace ylt \ No newline at end of file diff --git a/src/util/tests/test_time_util.cpp b/src/util/tests/test_time_util.cpp index fafe7481b..d576b75ec 100644 --- a/src/util/tests/test_time_util.cpp +++ b/src/util/tests/test_time_util.cpp @@ -1,18 +1,26 @@ #include #include +#include int main() { + std::cout << ylt::get_gmt_time_str() << "\n"; + std::cout << ylt::get_local_time_str() << "\n"; + std::string tm_str = "Mon, 02 Jan 2006 15:04:05 GMT"; if (auto [ok, t] = ylt::get_timestamp(tm_str); ok) { - std::cout << cinatra::get_gmt_time_str(t) << "\n"; + std::cout << cinatra::get_gmt_time_str( + std::chrono::system_clock::from_time_t(t)) + << "\n"; assert(ylt::get_gmt_time_str(t) == tm_str); } std::string utc_str = "2006-01-02T15:04:05.000Z"; if (auto [ok, t] = ylt::get_timestamp(utc_str); ok) { - std::cout << cinatra::get_gmt_time_str(t) << "\n"; + std::cout << cinatra::get_gmt_time_str( + std::chrono::system_clock::from_time_t(t)) + << "\n"; assert(ylt::get_gmt_time_str(t) == tm_str); } @@ -21,7 +29,9 @@ int main() { ylt::get_timestamp( utc_str1); ok) { - std::cout << cinatra::get_gmt_time_str(t) << "\n"; + std::cout << cinatra::get_gmt_time_str( + std::chrono::system_clock::from_time_t(t)) + << "\n"; assert(ylt::get_gmt_time_str(t) == tm_str); } } \ No newline at end of file