/* * Calendar Functions * (C) 1999-2010,2017 Jack Lloyd * (C) 2015 Simon Warta (Kullo GmbH) * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include namespace Botan { namespace { std::tm do_gmtime(std::time_t time_val) { std::tm tm; #if defined(BOTAN_TARGET_OS_HAS_GMTIME_S) ::gmtime_s(&tm, &time_val); // Windows #elif defined(BOTAN_TARGET_OS_HAS_GMTIME_R) ::gmtime_r(&time_val, &tm); // Unix/SUSv2 #else std::tm* tm_p = std::gmtime(&time_val); if (tm_p == nullptr) throw Encoding_Error("time_t_to_tm could not convert"); tm = *tm_p; #endif return tm; } /* Portable replacement for timegm, _mkgmtime, etc Algorithm due to Howard Hinnant See https://howardhinnant.github.io/date_algorithms.html#days_from_civil for details and explaination. The code is slightly simplified by our assumption that the date is at least 1970, which is sufficient for our purposes. */ size_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day) { if(month <= 2) year -= 1; const uint32_t era = year / 400; const uint32_t yoe = year - era * 400; // [0, 399] const uint32_t doy = (153*(month + (month > 2 ? -3 : 9)) + 2)/5 + day-1; // [0, 365] const uint32_t doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] return era * 146097 + doe - 719468; } } std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const { if(get_year() < 1970) throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1970"); // 32 bit time_t ends at January 19, 2038 // https://msdn.microsoft.com/en-us/library/2093ets1.aspx // Throw after 2037 if 32 bit time_t is used if(get_year() > 2037 && sizeof(std::time_t) == 4) { throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037 on this system"); } else if(get_year() >= 2400) { // This upper bound is somewhat arbitrary throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2400"); } const uint64_t seconds_64 = (days_since_epoch(get_year(), get_month(), get_day()) * 86400) + (get_hour() * 60 * 60) + (get_minutes() * 60) + get_seconds(); const time_t seconds_time_t = static_cast(seconds_64); if(seconds_64 - seconds_time_t != 0) { throw Invalid_Argument("calendar_point::to_std_timepoint time_t overflow"); } return std::chrono::system_clock::from_time_t(seconds_time_t); } std::string calendar_point::to_string() const { // desired format: --
T:: std::stringstream output; output << std::setfill('0') << std::setw(4) << get_year() << "-" << std::setw(2) << get_month() << "-" << std::setw(2) << get_day() << "T" << std::setw(2) << get_hour() << ":" << std::setw(2) << get_minutes() << ":" << std::setw(2) << get_seconds(); return output.str(); } calendar_point calendar_value( const std::chrono::system_clock::time_point& time_point) { std::tm tm = do_gmtime(std::chrono::system_clock::to_time_t(time_point)); return calendar_point(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } }