// Prevent redefinition of __cpp_lib_format #if defined(_MSC_VER) # include #endif // Enable chrono formatters #ifndef __cpp_lib_format # define __cpp_lib_format 202110L #endif #include #include #include #include #include #include #if (__cpp_lib_chrono < 201907L) # include #endif namespace scwx::util { static const std::string logPrefix_ = "scwx::util::time"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); static const std::unordered_map clockFormatName_ { {ClockFormat::_12Hour, "12-hour"}, {ClockFormat::_24Hour, "24-hour"}, {ClockFormat::Unknown, "?"}}; SCWX_GET_ENUM(ClockFormat, GetClockFormat, clockFormatName_) const std::string& GetClockFormatName(ClockFormat clockFormat) { return clockFormatName_.at(clockFormat); } std::chrono::system_clock::time_point TimePoint(uint32_t modifiedJulianDate, uint32_t milliseconds) { using namespace std::chrono; using sys_days = time_point; constexpr auto epoch = sys_days {1969y / December / 31d}; // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers): literals are used return epoch + (modifiedJulianDate * 24h) + std::chrono::milliseconds {milliseconds}; } std::string TimeString(std::chrono::system_clock::time_point time, ClockFormat clockFormat, const time_zone* timeZone, bool epochValid) { using namespace std::chrono; #if (__cpp_lib_chrono >= 201907L) namespace date = std::chrono; namespace df = std; static constexpr std::string_view kFormatString24Hour = "{:%Y-%m-%d %H:%M:%S %Z}"; static constexpr std::string_view kFormatString12Hour = "{:%Y-%m-%d %I:%M:%S %p %Z}"; #else using namespace date; namespace df = date; # define kFormatString24Hour "%Y-%m-%d %H:%M:%S %Z" # define kFormatString12Hour "%Y-%m-%d %I:%M:%S %p %Z" #endif auto timeInSeconds = time_point_cast(time); std::ostringstream os; if (epochValid || time.time_since_epoch().count() != 0) { if (timeZone != nullptr) { try { date::zoned_time zt = {timeZone, timeInSeconds}; if (clockFormat == ClockFormat::_24Hour) { os << df::format(kFormatString24Hour, zt); } else { os << df::format(kFormatString12Hour, zt); } } catch (const std::exception& ex) { static bool firstException = true; if (firstException) { logger_->warn("Zoned time error: {}", ex.what()); firstException = false; } // In the case where there is a time zone error (e.g., timezone // database, unicode issue, etc.), fall back to UTC timeZone = nullptr; } } if (timeZone == nullptr) { if (clockFormat == ClockFormat::_24Hour) { os << df::format(kFormatString24Hour, timeInSeconds); } else { os << df::format(kFormatString12Hour, timeInSeconds); } } } return os.str(); } template std::optional> TryParseDateTime(const std::string& dateTimeFormat, const std::string& str) { using namespace std::chrono; #if (__cpp_lib_chrono < 201907L) using namespace date; #endif std::optional> value = std::nullopt; std::chrono::sys_time dateTime; std::istringstream ssDateTime {str}; ssDateTime >> parse(dateTimeFormat, dateTime); if (!ssDateTime.fail()) { value = dateTime; } return value; } template std::optional> TryParseDateTime(const std::string& dateTimeFormat, const std::string& str); } // namespace scwx::util