[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
icc-effect-5.17.5
Vlad Zahorodnii 2019-09-22 11:15:58 -07:00
parent 3e7d9cd1ab
commit c265c7f2c6
4 changed files with 53 additions and 50 deletions

View File

@ -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) {

View File

@ -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;

View File

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "constants.h"
#include <QDateTime>
#include <QTimeZone>
#include <QtMath>
namespace KWin {
@ -31,7 +32,14 @@ namespace ColorCorrect {
#define SUN_RISE_SET -0.833
#define SUN_HIGH 2.0
QPair<QTime, QTime> 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<QDateTime, QDateTime> 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<QTime, QTime> 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<QTime, QTime> 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<QTime,QTime> (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 };
}
}

View File

@ -38,7 +38,7 @@ namespace ColorCorrect
* @since 5.12
*/
QPair<QTime, QTime> calculateSunTimings(QDate prompt, double latitude, double longitude, bool morning);
QPair<QDateTime, QDateTime> calculateSunTimings(const QDateTime &dateTime, double latitude, double longitude, bool morning);
}