File indexing completed on 2024-05-19 05:37:54

0001 /*
0002     SPDX-FileCopyrightText: 2009 Aaron Seigo <aseigo@kde.org>
0003 
0004     Moon Phase:
0005     SPDX-FileCopyrightText: 1998, 2000 Stephan Kulow <coolo@kde.org>
0006     SPDX-FileCopyrightText: 2009 Davide Bettio <davide.bettio@kdemail.net>
0007 
0008     Solar position:
0009     SPDX-FileCopyrightText: 2009 Petri Damsten <damu@iki.fi>
0010 
0011     SPDX-License-Identifier: LGPL-2.0-or-later
0012 */
0013 
0014 #include "timesource.h"
0015 
0016 #include <QDateTime>
0017 
0018 #include <KLazyLocalizedString>
0019 #include <KLocalizedString>
0020 
0021 #include "solarsystem.h"
0022 
0023 // timezone is defined in msvc
0024 #ifdef timezone
0025 #undef timezone
0026 #endif
0027 
0028 TimeSource::TimeSource(const QString &name, QObject *parent)
0029     : Plasma5Support::DataContainer(parent)
0030     , m_offset(0)
0031     , m_latitude(0)
0032     , m_longitude(0)
0033     , m_sun(nullptr)
0034     , m_moon(nullptr)
0035     , m_moonPosition(false)
0036     , m_solarPosition(false)
0037     , m_local(false)
0038 {
0039     setObjectName(name);
0040     setTimeZone(parseName(name));
0041 }
0042 
0043 void TimeSource::setTimeZone(const QString &tz)
0044 {
0045     m_tzName = tz;
0046     m_local = m_tzName == kli18n("Local").untranslatedText();
0047     if (m_local) {
0048         m_tzName = QString::fromUtf8(QTimeZone::systemTimeZoneId());
0049     }
0050 
0051     if (m_local) {
0052         m_tz = QTimeZone(QTimeZone::systemTimeZoneId());
0053     } else {
0054         m_tz = QTimeZone(m_tzName.toUtf8());
0055         if (!m_tz.isValid()) {
0056             m_tz = QTimeZone(QTimeZone::systemTimeZoneId());
0057         }
0058     }
0059 
0060     const QString trTimezone = i18n(m_tzName.toUtf8());
0061     setData(kli18n("Timezone").untranslatedText(), trTimezone);
0062 
0063     const QStringList tzParts = trTimezone.split('/', Qt::SkipEmptyParts);
0064     if (tzParts.count() == 1) {
0065         // no '/' so just set it as the city
0066         setData(kli18n("Timezone City").untranslatedText(), trTimezone);
0067     } else if (tzParts.count() == 2) {
0068         setData(kli18n("Timezone Continent").untranslatedText(), tzParts.value(0));
0069         setData(kli18n("Timezone City").untranslatedText(), tzParts.value(1));
0070     } else { // for zones like America/Argentina/Buenos_Aires
0071         setData(kli18n("Timezone Continent").untranslatedText(), tzParts.value(0));
0072         setData(kli18n("Timezone Country").untranslatedText(), tzParts.value(1));
0073         setData(kli18n("Timezone City").untranslatedText(), tzParts.value(2));
0074     }
0075 
0076     updateTime();
0077 }
0078 
0079 TimeSource::~TimeSource()
0080 {
0081     // First delete the moon, that does not delete the Sun, and then the Sun
0082     // If the Sun is deleted before the moon, the moon has a invalid pointer
0083     // to where the Sun was pointing.
0084     delete m_moon;
0085     delete m_sun;
0086 }
0087 
0088 void TimeSource::updateTime()
0089 {
0090     QDateTime timeZoneDateTime = QDateTime::currentDateTime().toTimeZone(m_tz);
0091 
0092     int offset = m_tz.offsetFromUtc(timeZoneDateTime);
0093     if (m_offset != offset) {
0094         m_offset = offset;
0095     }
0096 
0097     setData(kli18n("Offset").untranslatedText(), m_offset);
0098 
0099     QString abbreviation = m_tz.abbreviation(timeZoneDateTime);
0100     setData(kli18n("Timezone Abbreviation").untranslatedText(), abbreviation);
0101 
0102     QDateTime dt;
0103     if (m_userDateTime) {
0104         dt = data()[QStringLiteral("DateTime")].toDateTime();
0105     } else {
0106         dt = timeZoneDateTime;
0107     }
0108 
0109     if (m_solarPosition || m_moonPosition) {
0110         const QDate prev = data()[QStringLiteral("DateTime")].toDate();
0111         const bool updateDailies = prev != dt.date();
0112 
0113         if (m_solarPosition) {
0114             if (updateDailies) {
0115                 addDailySolarPositionData(dt);
0116             }
0117 
0118             addSolarPositionData(dt);
0119         }
0120 
0121         if (m_moonPosition) {
0122             if (updateDailies) {
0123                 addDailyMoonPositionData(dt);
0124             }
0125 
0126             addMoonPositionData(dt);
0127         }
0128     }
0129 
0130     if (!m_userDateTime) {
0131         setData(kli18n("DateTime").untranslatedText(), dt);
0132 
0133         forceImmediateUpdate();
0134     }
0135 }
0136 
0137 QString TimeSource::parseName(const QString &name)
0138 {
0139     m_userDateTime = false;
0140     if (!name.contains('|')) {
0141         // the simple case where it's just a timezone request
0142         return name;
0143     }
0144 
0145     // the various keys we recognize
0146     constexpr const auto latitude = kli18n("Latitude");
0147     constexpr const auto longitude = kli18n("Longitude");
0148     constexpr const auto solar = kli18n("Solar");
0149     constexpr const auto moon = kli18n("Moon");
0150     constexpr const auto datetime = kli18n("DateTime");
0151 
0152     // now parse out what we got handed in
0153     const QStringList list = name.split('|', Qt::SkipEmptyParts);
0154 
0155     const int listSize = list.size();
0156     for (int i = 1; i < listSize; ++i) {
0157         const QString arg = list[i];
0158         const int n = arg.indexOf('=');
0159 
0160         if (n != -1) {
0161             const QString key = arg.mid(0, n);
0162             const QString value = arg.mid(n + 1);
0163 
0164             if (key == latitude.untranslatedText()) {
0165                 m_latitude = value.toDouble();
0166             } else if (key == longitude.untranslatedText()) {
0167                 m_longitude = value.toDouble();
0168             } else if (key == datetime.untranslatedText()) {
0169                 QDateTime dt = QDateTime::fromString(value, Qt::ISODate);
0170                 if (dt.isValid()) {
0171                     setData(kli18n("DateTime").untranslatedText(), dt);
0172                     m_userDateTime = true;
0173                 }
0174             }
0175         } else if (arg == solar.untranslatedText()) {
0176             m_solarPosition = true;
0177         } else if (arg == moon.untranslatedText()) {
0178             m_moonPosition = true;
0179         }
0180     }
0181 
0182     // timezone is first item ...
0183     return list.at(0);
0184 }
0185 
0186 Sun *TimeSource::sun()
0187 {
0188     if (!m_sun) {
0189         m_sun = new Sun();
0190     }
0191     m_sun->setPosition(m_latitude, m_longitude);
0192     return m_sun;
0193 }
0194 
0195 Moon *TimeSource::moon()
0196 {
0197     if (!m_moon) {
0198         m_moon = new Moon(sun());
0199     }
0200     m_moon->setPosition(m_latitude, m_longitude);
0201     return m_moon;
0202 }
0203 
0204 void TimeSource::addMoonPositionData(const QDateTime &dt)
0205 {
0206     Moon *m = moon();
0207     m->calcForDateTime(dt, m_offset);
0208     setData(QStringLiteral("Moon Azimuth"), m->azimuth());
0209     setData(QStringLiteral("Moon Zenith"), 90 - m->altitude());
0210     setData(QStringLiteral("Moon Corrected Elevation"), m->calcElevation());
0211     setData(QStringLiteral("MoonPhaseAngle"), m->phase());
0212 }
0213 
0214 void TimeSource::addDailyMoonPositionData(const QDateTime &dt)
0215 {
0216     Moon *m = moon();
0217     QList<QPair<QDateTime, QDateTime>> times = m->timesForAngles(QList<double>() << -0.833, dt, m_offset);
0218     setData(QStringLiteral("Moonrise"), times[0].first);
0219     setData(QStringLiteral("Moonset"), times[0].second);
0220     m->calcForDateTime(QDateTime(dt.date(), QTime(12, 0)), m_offset);
0221     setData(QStringLiteral("MoonPhase"), int(m->phase() / 360.0 * 29.0));
0222 }
0223 
0224 void TimeSource::addSolarPositionData(const QDateTime &dt)
0225 {
0226     Sun *s = sun();
0227     s->calcForDateTime(dt, m_offset);
0228     setData(QStringLiteral("Azimuth"), s->azimuth());
0229     setData(QStringLiteral("Zenith"), 90.0 - s->altitude());
0230     setData(QStringLiteral("Corrected Elevation"), s->calcElevation());
0231 }
0232 
0233 void TimeSource::addDailySolarPositionData(const QDateTime &dt)
0234 {
0235     Sun *s = sun();
0236     QList<QPair<QDateTime, QDateTime>> times = s->timesForAngles(QList<double>() << -0.833 << -6.0 << -12.0 << -18.0, dt, m_offset);
0237 
0238     setData(QStringLiteral("Sunrise"), times[0].first);
0239     setData(QStringLiteral("Sunset"), times[0].second);
0240     setData(QStringLiteral("Civil Dawn"), times[1].first);
0241     setData(QStringLiteral("Civil Dusk"), times[1].second);
0242     setData(QStringLiteral("Nautical Dawn"), times[2].first);
0243     setData(QStringLiteral("Nautical Dusk"), times[2].second);
0244     setData(QStringLiteral("Astronomical Dawn"), times[3].first);
0245     setData(QStringLiteral("Astronomical Dusk"), times[3].second);
0246 }