From cb7ec8de707cfcdd2aba7cb41afe1bd2417ef307 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 3 Aug 2023 14:30:36 +0200 Subject: [PATCH] Fix issue where the time is wrong when starting a profile paused (closes #2826) --- include/openspace/util/time.h | 21 +++++---- src/util/time.cpp | 69 +++++++++++++++++++++++++--- src/util/time_lua.inl | 85 +++++++---------------------------- src/util/timemanager.cpp | 15 ++++--- 4 files changed, 98 insertions(+), 92 deletions(-) diff --git a/include/openspace/util/time.h b/include/openspace/util/time.h index 40dc00b6bb..48d70ef3b7 100644 --- a/include/openspace/util/time.h +++ b/include/openspace/util/time.h @@ -70,6 +70,12 @@ public: /// \overload static double convertTime(const std::string& time) static double convertTime(const char* time); + /** + * Returns the current wall time as an ISO 8601 date string (YYYY-MM-DDTHH-MN-SS) in + * the UTC timezone. + */ + static std::string currentWallTime(); + explicit Time(double secondsJ2000 = -1); explicit Time(const std::string& time); Time(const Time& other) = default; @@ -157,16 +163,13 @@ public: double advanceTime(double tickTime); /** - * Sets a relative time from profile. + * Modifies the passed time (first argument) by the delta time (second argument). The + * first argument must be an ISO 8601 date string. The second argument should be a + * string of the form [-]XX(s,m,h,d,M,y] with (s)econds, (m)inutes, (h)ours, (d)ays, + * (M)onths, and (y)ears as units and an optional - sign to move backwards in time. + * The return value is in the form of an ISO 8601 date string. */ - static void setTimeRelativeFromProfile(const std::string& setTime); - - /** - * Sets an absolute time from profile. - * \param setTime a string containing time to set, which must be a valid - * ISO 8601-like date string of the format YYYY-MM-DDTHH:MN:SS - */ - static void setTimeAbsoluteFromProfile(const std::string& setTime); + static std::string advancedTime(std::string base, std::string change); /** * Returns the Lua library that contains all Lua functions available to change the diff --git a/src/util/time.cpp b/src/util/time.cpp index 883b7848bb..d83b6378da 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -60,6 +60,18 @@ double Time::convertTime(const char* time) { return SpiceManager::ref().ephemerisTimeFromDate(time); } +std::string Time::currentWallTime() { + std::time_t t = std::time(nullptr); + std::tm* utcTime = std::gmtime(&t); + + std::string time = fmt::format( + "{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}", + utcTime->tm_year + 1900, utcTime->tm_mon + 1, utcTime->tm_mday, + utcTime->tm_hour, utcTime->tm_min, utcTime->tm_sec + ); + return time; +} + Time::Time(double secondsJ2000) : _time(secondsJ2000) {} Time::Time(const std::string& time) : @@ -132,14 +144,57 @@ void Time::ISO8601(char* buffer) const { SpiceManager::ref().dateFromEphemerisTime(_time, buffer, S, Format); } -void Time::setTimeRelativeFromProfile(const std::string& setTime) { - std::string t = currentWallTime(); - std::variant t2 = advancedTime(t, setTime); - ::setTime(std::get(t2)); -} +std::string Time::advancedTime(std::string base, std::string change) { + double j2000Seconds = Time::convertTime(base); -void Time::setTimeAbsoluteFromProfile(const std::string& setTime) { - ::setTime(setTime); + double dt = 0.0; + if (change.empty()) { + throw ghoul::RuntimeError("Modifier string must not be empty"); + } + ghoul::trimWhitespace(change); + bool isNegative = false; + if (change[0] == '-') { + isNegative = true; + change = change.substr(1); + } + + auto it = std::find_if( + change.begin(), change.end(), + [](unsigned char c) { + const bool digit = std::isdigit(c) != 0; + const bool isDot = c == '.'; + return !digit && !isDot; + } + ); + + try { + double value = std::stod(std::string(change.begin(), it)); + std::string uName = std::string(it, change.end()); + + TimeUnit unit = TimeUnit::Second; + if (uName == "s") { unit = TimeUnit::Second; } + else if (uName == "m") { unit = TimeUnit::Minute; } + else if (uName == "h") { unit = TimeUnit::Hour; } + else if (uName == "d") { unit = TimeUnit::Day; } + else if (uName == "M") { unit = TimeUnit::Month; } + else if (uName == "y") { unit = TimeUnit::Year; } + else { + throw ghoul::RuntimeError(fmt::format("Unknown unit '{}'", uName)); + } + + dt = openspace::convertTime(value, unit, TimeUnit::Second); + if (isNegative) { + dt *= -1.0; + } + } + catch (...) { + throw ghoul::RuntimeError(fmt::format( + "Error parsing relative time offset '{}'", change + )); + } + + std::string_view ret = Time(j2000Seconds + dt).ISO8601(); + return std::string(ret); } scripting::LuaLibrary Time::luaLibrary() { diff --git a/src/util/time_lua.inl b/src/util/time_lua.inl index 171773f2b5..e32ff13bdf 100644 --- a/src/util/time_lua.inl +++ b/src/util/time_lua.inl @@ -291,15 +291,7 @@ namespace { * UTC timezone. */ [[codegen::luawrap]] std::string currentWallTime() { - std::time_t t = std::time(nullptr); - std::tm* utcTime = std::gmtime(&t); - - std::string time = fmt::format( - "{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}", - utcTime->tm_year + 1900, utcTime->tm_mon + 1, utcTime->tm_mday, - utcTime->tm_hour, utcTime->tm_min, utcTime->tm_sec - ); - return time; + return openspace::Time::currentWallTime(); } /** @@ -323,77 +315,30 @@ namespace { std::variant base, std::variant change) { - using namespace openspace; - - double j2000Seconds = -1.0; - bool usesISO = false; + std::string b; if (std::holds_alternative(base)) { - j2000Seconds = Time::convertTime(std::get(base)); - usesISO = true; + b = std::get(base); } else { - j2000Seconds = std::get(base); - usesISO = false; + b = openspace::Time(std::get(base)).ISO8601(); } - - double dt = 0.0; - if (std::holds_alternative(change)) { - dt = std::get(change); + + std::string c; + if (std::holds_alternative(change)) { + c = std::get(change); } else { - std::string modifier = std::get(change); - if (modifier.empty()) { - throw ghoul::lua::LuaError("Modifier string must not be empty"); - } - ghoul::trimWhitespace(modifier); - bool isNegative = false; - if (modifier[0] == '-') { - isNegative = true; - modifier = modifier.substr(1); - } - - auto it = std::find_if( - modifier.begin(), modifier.end(), - [](unsigned char c) { - const bool digit = std::isdigit(c) != 0; - const bool isDot = c == '.'; - return !digit && !isDot; - } - ); - - try { - double value = std::stod(std::string(modifier.begin(), it)); - std::string uName = std::string(it, modifier.end()); - - TimeUnit unit = TimeUnit::Second; - if (uName == "s") { unit = TimeUnit::Second; } - else if (uName == "m") { unit = TimeUnit::Minute; } - else if (uName == "h") { unit = TimeUnit::Hour; } - else if (uName == "d") { unit = TimeUnit::Day; } - else if (uName == "M") { unit = TimeUnit::Month; } - else if (uName == "y") { unit = TimeUnit::Year; } - else { - throw ghoul::lua::LuaError(fmt::format("Unknown unit '{}'", uName)); - } - - dt = convertTime(value, unit, TimeUnit::Second); - if (isNegative) { - dt *= -1.0; - } - } - catch (...) { - throw ghoul::lua::LuaError(fmt::format( - "Error parsing relative time offset '{}'", modifier - )); - } + double v = std::get(change); + c = fmt::format("{}s", v); } - if (usesISO) { - std::string_view ret = Time(j2000Seconds + dt).ISO8601(); - return std::string(ret); + std::string res = openspace::Time::advancedTime(std::move(b), std::move(c)); + + if (std::holds_alternative(base)) { + return res; } else { - return j2000Seconds + dt; + return openspace::Time::convertTime(res); } } diff --git a/src/util/timemanager.cpp b/src/util/timemanager.cpp index e400f3dec0..94ed7f7700 100644 --- a/src/util/timemanager.cpp +++ b/src/util/timemanager.cpp @@ -941,13 +941,17 @@ void TimeManager::setTimeFromProfile(const Profile& p) { if (p.time.has_value()) { switch (p.time->type) { case Profile::Time::Type::Relative: - Time::setTimeRelativeFromProfile(p.time->value); + { + std::string t = Time::currentWallTime(); + std::variant t2 = + Time::advancedTime(t, p.time->value); + ghoul_assert(std::holds_alternative(t2), "Wrong type"); + _currentTime.data() = Time(std::get(t2)); break; - + } case Profile::Time::Type::Absolute: - Time::setTimeAbsoluteFromProfile(p.time->value); + _currentTime.data() = Time(p.time->value); break; - default: throw ghoul::MissingCaseException(); } @@ -956,8 +960,7 @@ void TimeManager::setTimeFromProfile(const Profile& p) { } else { // No value was specified so we are using 'now' instead - std::string now = std::string(Time::now().UTC()); - Time::setTimeAbsoluteFromProfile(now); + _currentTime.data() = Time::now(); } }