From c265c7f2c698ce51c039d3fe009fe09981ce1267 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Sun, 22 Sep 2019 11:15:58 -0700 Subject: [PATCH] [nightcolor] Use local time in Automatic and Location mode Summary: Currently Night Color doesn't handle time zones very well. For example, if the user's time zone is UTC-8, then computed timings of sunrise and sunset (in UTC) will be a bit gibberish as sunset occurs before sunrise rather than vice versa. This change switches relevant parts of Night Color to local time in order to fix expectations about the order of morning and evening in updateSunTimings() method as well to simplify the code a bit (dealing with UTC and local time can be painful sometimes). CCBUG: 412211 Reviewers: #kwin, romangg Reviewed By: #kwin, romangg Subscribers: romangg, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D24153 --- colorcorrection/manager.cpp | 58 ++++++++++++++++--------------------- colorcorrection/manager.h | 2 +- colorcorrection/suncalc.cpp | 41 ++++++++++++++++---------- colorcorrection/suncalc.h | 2 +- 4 files changed, 53 insertions(+), 50 deletions(-) diff --git a/colorcorrection/manager.cpp b/colorcorrection/manager.cpp index cb91faa11f..723bcce2a2 100644 --- a/colorcorrection/manager.cpp +++ b/colorcorrection/manager.cpp @@ -371,13 +371,7 @@ void Manager::resetSlowUpdateStartTimer() connect(m_slowUpdateStartTimer, &QTimer::timeout, this, &Manager::resetSlowUpdateStartTimer); updateSunTimings(false); - int diff; - if (m_mode == NightColorMode::Timings) { - // Timings mode is in local time - diff = QDateTime::currentDateTime().msecsTo(m_next.first); - } else { - diff = QDateTime::currentDateTimeUtc().msecsTo(m_next.first); - } + const int diff = QDateTime::currentDateTime().msecsTo(m_next.first); if (diff <= 0) { qCCritical(KWIN_COLORCORRECTION) << "Error in time calculation. Deactivating Night Color."; return; @@ -393,7 +387,7 @@ void Manager::resetSlowUpdateTimer() delete m_slowUpdateTimer; m_slowUpdateTimer = nullptr; - QDateTime now = QDateTime::currentDateTimeUtc(); + QDateTime now = QDateTime::currentDateTime(); bool isDay = daylight(); int targetTemp = isDay ? m_dayTargetTemp : m_nightTargetTemp; @@ -443,21 +437,18 @@ void Manager::slowUpdate(int targetTemp) void Manager::updateSunTimings(bool force) { - QDateTime todayNow = QDateTime::currentDateTimeUtc(); + QDateTime todayNow = QDateTime::currentDateTime(); if (m_mode == NightColorMode::Timings) { - - QDateTime todayNowLocal = QDateTime::currentDateTime(); - - QDateTime morB = QDateTime(todayNowLocal.date(), m_morning); + QDateTime morB = QDateTime(todayNow.date(), m_morning); QDateTime morE = morB.addSecs(m_trTime * 60); - QDateTime eveB = QDateTime(todayNowLocal.date(), m_evening); + QDateTime eveB = QDateTime(todayNow.date(), m_evening); QDateTime eveE = eveB.addSecs(m_trTime * 60); - if (morB <= todayNowLocal && todayNowLocal < eveB) { + if (morB <= todayNow && todayNow < eveB) { m_next = DateTimes(eveB, eveE); m_prev = DateTimes(morB, morE); - } else if (todayNowLocal < morB) { + } else if (todayNow < morB) { m_next = DateTimes(morB, morE); m_prev = DateTimes(eveB.addDays(-1), eveE.addDays(-1)); } else { @@ -481,62 +472,63 @@ void Manager::updateSunTimings(bool force) if (daylight()) { // next is morning m_prev = m_next; - m_next = getSunTimings(todayNow.date().addDays(1), lat, lng, true); + m_next = getSunTimings(todayNow.addDays(1), lat, lng, true); } else { // next is evening m_prev = m_next; - m_next = getSunTimings(todayNow.date(), lat, lng, false); + m_next = getSunTimings(todayNow, lat, lng, false); } } if (force || !checkAutomaticSunTimings()) { // in case this fails, reset them - DateTimes morning = getSunTimings(todayNow.date(), lat, lng, true); + DateTimes morning = getSunTimings(todayNow, lat, lng, true); if (todayNow < morning.first) { - m_prev = getSunTimings(todayNow.date().addDays(-1), lat, lng, false); + m_prev = getSunTimings(todayNow.addDays(-1), lat, lng, false); m_next = morning; } else { - DateTimes evening = getSunTimings(todayNow.date(), lat, lng, false); + DateTimes evening = getSunTimings(todayNow, lat, lng, false); if (todayNow < evening.first) { m_prev = morning; m_next = evening; } else { m_prev = evening; - m_next = getSunTimings(todayNow.date().addDays(1), lat, lng, true); + m_next = getSunTimings(todayNow.addDays(1), lat, lng, true); } } } } -DateTimes Manager::getSunTimings(QDate date, double latitude, double longitude, bool morning) const +DateTimes Manager::getSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning) const { - Times times = calculateSunTimings(date, latitude, longitude, morning); + DateTimes dateTimes = calculateSunTimings(dateTime, latitude, longitude, morning); // At locations near the poles it is possible, that we can't // calculate some or all sun timings (midnight sun). // In this case try to fallback to sensible default values. - bool beginDefined = !times.first.isNull(); - bool endDefined = !times.second.isNull(); + bool beginDefined = !dateTimes.first.isNull(); + bool endDefined = !dateTimes.second.isNull(); if (!beginDefined || !endDefined) { if (beginDefined) { - times.second = times.first.addMSecs( FALLBACK_SLOW_UPDATE_TIME ); + dateTimes.second = dateTimes.first.addMSecs( FALLBACK_SLOW_UPDATE_TIME ); } else if (endDefined) { - times.first = times.second.addMSecs( - FALLBACK_SLOW_UPDATE_TIME); + dateTimes.first = dateTimes.second.addMSecs( - FALLBACK_SLOW_UPDATE_TIME ); } else { // Just use default values for morning and evening, but the user // will probably deactivate Night Color anyway if he is living // in a region without clear sun rise and set. - times.first = morning ? QTime(6,0,0) : QTime(18,0,0); - times.second = times.first.addMSecs( FALLBACK_SLOW_UPDATE_TIME ); + const QTime referenceTime = morning ? QTime(6, 0) : QTime(18, 0); + dateTimes.first = QDateTime(dateTime.date(), referenceTime); + dateTimes.second = dateTimes.first.addMSecs( FALLBACK_SLOW_UPDATE_TIME ); } } - return DateTimes(QDateTime(date, times.first, Qt::UTC), QDateTime(date, times.second, Qt::UTC)); + return dateTimes; } bool Manager::checkAutomaticSunTimings() const { if (m_prev.first.isValid() && m_prev.second.isValid() && m_next.first.isValid() && m_next.second.isValid()) { - QDateTime todayNow = QDateTime::currentDateTimeUtc(); + QDateTime todayNow = QDateTime::currentDateTime(); return m_prev.first <= todayNow && todayNow < m_next.first && m_prev.first.msecsTo(m_next.first) < MSC_DAY * 23./24; } @@ -558,7 +550,7 @@ int Manager::currentTargetTemp() const return m_nightTargetTemp; } - QDateTime todayNow = QDateTime::currentDateTimeUtc(); + QDateTime todayNow = QDateTime::currentDateTime(); auto f = [this, todayNow](int target1, int target2) { if (todayNow <= m_prev.second) { diff --git a/colorcorrection/manager.h b/colorcorrection/manager.h index 853d3dbc15..0f4e20248e 100644 --- a/colorcorrection/manager.h +++ b/colorcorrection/manager.h @@ -154,7 +154,7 @@ private: void resetSlowUpdateTimer(); void updateSunTimings(bool force); - DateTimes getSunTimings(QDate date, double latitude, double longitude, bool morning) const; + DateTimes getSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning) const; bool checkAutomaticSunTimings() const; bool daylight() const; diff --git a/colorcorrection/suncalc.cpp b/colorcorrection/suncalc.cpp index 217d788b6a..d7fb78f03f 100644 --- a/colorcorrection/suncalc.cpp +++ b/colorcorrection/suncalc.cpp @@ -21,6 +21,7 @@ along with this program. If not, see . #include "constants.h" #include +#include #include namespace KWin { @@ -31,7 +32,14 @@ namespace ColorCorrect { #define SUN_RISE_SET -0.833 #define SUN_HIGH 2.0 -QPair calculateSunTimings(QDate prompt, double latitude, double longitude, bool morning) +static QTime convertToLocalTime(const QDateTime &when, const QTime &utcTime) +{ + const QTimeZone timeZone = QTimeZone::systemTimeZone(); + const int utcOffset = timeZone.offsetFromUtc(when); + return utcTime.addSecs(utcOffset); +} + +QPair calculateSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning) { // calculations based on https://aa.quae.nl/en/reken/zonpositie.html // accuracy: +/- 5min @@ -44,7 +52,8 @@ QPair calculateSunTimings(QDate prompt, double latitude, double lo const double lng = -longitude; // lw // times - const double juPrompt = prompt.toJulianDay(); // J + const QDateTime utcDateTime = dateTime.toUTC(); + const double juPrompt = utcDateTime.date().toJulianDay(); // J const double ju2000 = 2451545.; // J2000 // geometry @@ -141,22 +150,24 @@ QPair calculateSunTimings(QDate prompt, double latitude, double lo begin += 0.5; end += 0.5; - QTime timeBegin, timeEnd; + QDateTime dateTimeBegin; + QDateTime dateTimeEnd; - if (std::isnan(begin)) { - timeBegin = QTime(); - } else { - double timePart = begin - (int)begin; - timeBegin = QTime::fromMSecsSinceStartOfDay((int)( timePart * MSC_DAY )); - } - if (std::isnan(end)) { - timeEnd = QTime(); - } else { - double timePart = end - (int)end; - timeEnd = QTime::fromMSecsSinceStartOfDay((int)( timePart * MSC_DAY )); + if (!std::isnan(begin)) { + const double dayFraction = begin - int(begin); + const QTime utcTime = QTime::fromMSecsSinceStartOfDay(dayFraction * MSC_DAY); + const QTime localTime = convertToLocalTime(dateTime, utcTime); + dateTimeBegin = QDateTime(dateTime.date(), localTime); } - return QPair (timeBegin, timeEnd); + if (!std::isnan(end)) { + const double dayFraction = end - int(end); + const QTime utcTime = QTime::fromMSecsSinceStartOfDay(dayFraction * MSC_DAY); + const QTime localTime = convertToLocalTime(dateTime, utcTime); + dateTimeEnd = QDateTime(dateTime.date(), localTime); + } + + return { dateTimeBegin, dateTimeEnd }; } } diff --git a/colorcorrection/suncalc.h b/colorcorrection/suncalc.h index 79943fe821..ab1c75f3b2 100644 --- a/colorcorrection/suncalc.h +++ b/colorcorrection/suncalc.h @@ -38,7 +38,7 @@ namespace ColorCorrect * @since 5.12 */ -QPair calculateSunTimings(QDate prompt, double latitude, double longitude, bool morning); +QPair calculateSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning); }