diff --git a/toolsrc/include/vcpkg/base/chrono.h b/toolsrc/include/vcpkg/base/chrono.h index aa764a59783702..6f6e2b31773f23 100644 --- a/toolsrc/include/vcpkg/base/chrono.h +++ b/toolsrc/include/vcpkg/base/chrono.h @@ -52,8 +52,10 @@ namespace vcpkg::Chrono static Optional get_current_date_time(); static Optional parse(CStringView str); - constexpr CTime() noexcept : m_tm{0} {} - explicit constexpr CTime(tm t) noexcept : m_tm{t} {} + constexpr CTime() noexcept : m_tm {0} {} + explicit constexpr CTime(tm t) noexcept : m_tm {t} {} + + CTime add_hours(const int hours) const; std::string to_string() const; @@ -62,4 +64,6 @@ namespace vcpkg::Chrono private: mutable tm m_tm; }; + + tm get_current_date_time_local(); } diff --git a/toolsrc/include/vcpkg/base/system.h b/toolsrc/include/vcpkg/base/system.h index 005e09a3f7d631..af56e45c1cfee6 100644 --- a/toolsrc/include/vcpkg/base/system.h +++ b/toolsrc/include/vcpkg/base/system.h @@ -8,8 +8,6 @@ namespace vcpkg::System { - tm get_current_date_time(); - fs::path get_exe_path_of_current_process(); struct CMakeVariable diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index bcce8ceb2a800d..3589881a7c7b3b 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -33,6 +33,12 @@ using namespace vcpkg; +// 24 hours/day * 30 days/month * 6 months +static constexpr int SURVEY_INTERVAL_IN_HOURS = 24 * 30 * 6; + +// Initial survey appears after 10 days. Therefore, subtract 24 hours/day * 10 days +static constexpr int SURVEY_INITIAL_OFFSET_IN_HOURS = SURVEY_INTERVAL_IN_HOURS - 24 * 10; + void invalid_command(const std::string& cmd) { System::println(System::Color::error, "invalid command: %s", cmd); @@ -120,12 +126,12 @@ static void inner(const VcpkgCmdArguments& args) auto maybe_surveydate = Chrono::CTime::parse(surveydate); if (auto p_surveydate = maybe_surveydate.get()) { - auto delta = std::chrono::system_clock::now() - p_surveydate->to_time_point(); - // 24 hours/day * 30 days/month - if (std::chrono::duration_cast(delta).count() > 24 * 30) + const auto now = Chrono::CTime::get_current_date_time().value_or_exit(VCPKG_LINE_INFO); + const auto delta = now.to_time_point() - p_surveydate->to_time_point(); + if (std::chrono::duration_cast(delta).count() > SURVEY_INTERVAL_IN_HOURS) { std::default_random_engine generator( - static_cast(std::chrono::system_clock::now().time_since_epoch().count())); + static_cast(now.to_time_point().time_since_epoch().count())); std::uniform_int_distribution distribution(1, 4); if (distribution(generator) == 1) @@ -214,7 +220,9 @@ static void load_config() if (config.last_completed_survey.empty()) { - config.last_completed_survey = config.user_time; + const auto now = Chrono::CTime::parse(config.user_time).value_or_exit(VCPKG_LINE_INFO); + const Chrono::CTime offset = now.add_hours(-SURVEY_INITIAL_OFFSET_IN_HOURS); + config.last_completed_survey = offset.to_string(); } GlobalState::g_surveydate.lock()->assign(config.last_completed_survey); diff --git a/toolsrc/src/vcpkg/base/chrono.cpp b/toolsrc/src/vcpkg/base/chrono.cpp index 2a76f5df0ecc91..405e7660564db3 100644 --- a/toolsrc/src/vcpkg/base/chrono.cpp +++ b/toolsrc/src/vcpkg/base/chrono.cpp @@ -5,6 +5,61 @@ namespace vcpkg::Chrono { + static std::time_t get_current_time_as_time_since_epoch() + { + using std::chrono::system_clock; + return system_clock::to_time_t(system_clock::now()); + } + + static std::time_t utc_mktime(tm* time_ptr) + { +#if defined(_WIN32) + return _mkgmtime(time_ptr); +#else + return timegm(time_ptr); +#endif + } + + static tm to_local_time(const std::time_t& t) + { + tm parts {}; +#if defined(_WIN32) + localtime_s(&parts, &t); +#else + parts = *localtime(&t); +#endif + return parts; + } + + static Optional to_utc_time(const std::time_t& t) + { + tm parts {}; +#if defined(_WIN32) + const errno_t err = gmtime_s(&parts, &t); + if (err) + { + return nullopt; + } +#else + auto null_if_failed = gmtime_r(&t, &parts); + if (null_if_failed == nullptr) + { + return nullopt; + } +#endif + return parts; + } + + static tm date_plus_hours(tm* date, const int hours) + { + using namespace std::chrono_literals; + static constexpr std::chrono::seconds SECONDS_IN_ONE_HOUR = + std::chrono::duration_cast(1h); + + const std::time_t date_in_seconds = utc_mktime(date) + (hours * SECONDS_IN_ONE_HOUR.count()); + return to_utc_time(date_in_seconds).value_or_exit(VCPKG_LINE_INFO); + } + static std::string format_time_userfriendly(const std::chrono::nanoseconds& nanos) { using std::chrono::duration_cast; @@ -63,30 +118,14 @@ namespace vcpkg::Chrono Optional CTime::get_current_date_time() { - CTime ret; - -#if defined(_WIN32) - struct _timeb timebuffer; - - _ftime_s(&timebuffer); - - const errno_t err = gmtime_s(&ret.m_tm, &timebuffer.time); - - if (err) + const std::time_t ct = get_current_time_as_time_since_epoch(); + const Optional opt = to_utc_time(ct); + if (auto p_tm = opt.get()) { - return nullopt; + return CTime {*p_tm}; } -#else - time_t now = {0}; - time(&now); - auto null_if_failed = gmtime_r(&now, &ret.m_tm); - if (null_if_failed == nullptr) - { - return nullopt; - } -#endif - return ret; + return nullopt; } Optional CTime::parse(CStringView str) @@ -111,19 +150,28 @@ namespace vcpkg::Chrono ret.m_tm.tm_year -= 1900; if (ret.m_tm.tm_mon < 1) return nullopt; ret.m_tm.tm_mon -= 1; - mktime(&ret.m_tm); + utc_mktime(&ret.m_tm); + return ret; } + CTime CTime::add_hours(const int hours) const { return CTime {date_plus_hours(&this->m_tm, hours)}; } + std::string CTime::to_string() const { - std::array date{}; + std::array date {}; strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S.0Z", &m_tm); return &date[0]; } std::chrono::system_clock::time_point CTime::to_time_point() const { - const time_t t = mktime(&m_tm); + const time_t t = utc_mktime(&m_tm); return std::chrono::system_clock::from_time_t(t); } + + tm get_current_date_time_local() + { + const std::time_t now_time = get_current_time_as_time_since_epoch(); + return Chrono::to_local_time(now_time); + } } diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp index 78ba28324cdf6b..d95752bbaef46b 100644 --- a/toolsrc/src/vcpkg/base/system.cpp +++ b/toolsrc/src/vcpkg/base/system.cpp @@ -19,19 +19,6 @@ namespace vcpkg::System { - tm get_current_date_time() - { - using std::chrono::system_clock; - std::time_t now_time = system_clock::to_time_t(system_clock::now()); - tm parts{}; -#if defined(_WIN32) - localtime_s(&parts, &now_time); -#else - parts = *localtime(&now_time); -#endif - return parts; - } - fs::path get_exe_path_of_current_process() { #if defined(_WIN32) @@ -402,7 +389,7 @@ namespace vcpkg::System #if defined(_WIN32) const HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); - CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info{}; + CONSOLE_SCREEN_BUFFER_INFO console_screen_buffer_info {}; GetConsoleScreenBufferInfo(console_handle, &console_screen_buffer_info); const auto original_color = console_screen_buffer_info.wAttributes; diff --git a/toolsrc/src/vcpkg/commands.exportifw.cpp b/toolsrc/src/vcpkg/commands.exportifw.cpp index ae106196a00f62..62725a90acacf8 100644 --- a/toolsrc/src/vcpkg/commands.exportifw.cpp +++ b/toolsrc/src/vcpkg/commands.exportifw.cpp @@ -13,7 +13,7 @@ namespace vcpkg::Export::IFW static std::string create_release_date() { - const tm date_time = System::get_current_date_time(); + const tm date_time = Chrono::get_current_date_time_local(); // Format is: YYYY-mm-dd // 10 characters + 1 null terminating character will be written for a total of 11 chars diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp index 8161c0a62aa4b5..eec9a39f205428 100644 --- a/toolsrc/src/vcpkg/export.cpp +++ b/toolsrc/src/vcpkg/export.cpp @@ -108,7 +108,7 @@ namespace vcpkg::Export static std::string create_export_id() { - const tm date_time = System::get_current_date_time(); + const tm date_time = Chrono::get_current_date_time_local(); // Format is: YYYYmmdd-HHMMSS // 15 characters + 1 null terminating character will be written for a total of 16 chars @@ -227,10 +227,10 @@ namespace vcpkg::Export { const std::vector integration_files_relative_to_root = { {".vcpkg-root"}, - {fs::path{"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"}, - {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"}, - {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"}, - {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"}, + {fs::path {"scripts"} / "buildsystems" / "msbuild" / "applocal.ps1"}, + {fs::path {"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"}, + {fs::path {"scripts"} / "buildsystems" / "vcpkg.cmake"}, + {fs::path {"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"}, }; for (const fs::path& file : integration_files_relative_to_root)