// 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 !defined(_MSC_VER) # include #endif namespace scwx { namespace 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}; 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 (defined(_MSC_VER) || defined(__clang__)) # define FORMAT_STRING_24_HOUR "{:%Y-%m-%d %H:%M:%S %Z}" # define FORMAT_STRING_12_HOUR "{:%Y-%m-%d %I:%M:%S %p %Z}" namespace date = std::chrono; namespace df = std; #else # define FORMAT_STRING_24_HOUR "%Y-%m-%d %H:%M:%S %Z" # define FORMAT_STRING_12_HOUR "%Y-%m-%d %I:%M:%S %p %Z" using namespace date; namespace df = date; #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(FORMAT_STRING_24_HOUR, zt); } else { os << df::format(FORMAT_STRING_12_HOUR, 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(FORMAT_STRING_24_HOUR, timeInSeconds); } else { os << df::format(FORMAT_STRING_12_HOUR, timeInSeconds); } } } return os.str(); } template std::optional> TryParseDateTime(const std::string& dateTimeFormat, const std::string& str) { using namespace std::chrono; #if !(defined(_MSC_VER) || defined(__clang__)) 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 util } // namespace scwx