diff --git a/runners/datetime/datetimerunner.h b/runners/datetime/datetimerunner.h --- a/runners/datetime/datetimerunner.h +++ b/runners/datetime/datetimerunner.h @@ -20,6 +20,7 @@ #define DATETIMERUNNER_H #include +#include #include #include @@ -42,7 +43,7 @@ void match(Plasma::RunnerContext &context) override; private: - QHash datetime(const QStringRef &tz); + QMultiHash timeZoneMatches(QString tz); void addMatch(const QString &text, const QString &clipboardText, Plasma::RunnerContext &context, const QString& iconName); }; diff --git a/runners/datetime/datetimerunner.cpp b/runners/datetime/datetimerunner.cpp --- a/runners/datetime/datetimerunner.cpp +++ b/runners/datetime/datetimerunner.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2006 Aaron Seigo * Copyright (C) 2010 Marco Martin * Copyright (C) 2015 Vishesh Handa + * Copyright (C) 2018 James D. Smith * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as @@ -37,70 +38,136 @@ addSyntax(Plasma::RunnerSyntax(dateWord, i18n("Displays the current date"))); addSyntax(Plasma::RunnerSyntax(dateWord + QLatin1String( " :q:" ), i18n("Displays the current date in a given timezone"))); addSyntax(Plasma::RunnerSyntax(timeWord, i18n("Displays the current time"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " 24" ), i18n("Displays the current time, in 24 hour time"))); addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ), i18n("Displays the current time in a given timezone"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ) + QLatin1String( " 24" ), i18n("Displays the local time from the given time of the form hh:mm [am/pm] or HH:mm, in 24 hour time"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ) + QLatin1String( " 24" ), i18n("Displays the current time in a given timezone, in 24 hour time"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ), i18n("Displays the local time from the given time of the form hh:mm [am/pm] or HH:mm"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ) + QLatin1String( " :q:" ), i18n("Displays the time in the given timezone converted from the given local time of the form hh:mm [am/pm] or HH:mm"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ) + QLatin1String( " :q:" ), i18n("Displays the given time of the form hh:mm [am/pm] or HH:mm converted from the given timezone to the local time"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ) + QLatin1String( " :q:" ) + QLatin1String( " 24" ), i18n("Displays the time in the given timezone converted from the given local time of the form hh:mm [am/pm] or HH:mm, in 24 hour time"))); + addSyntax(Plasma::RunnerSyntax(timeWord + QLatin1String( " :q:" ) + QLatin1String( " :q:" ) + QLatin1String( " 24" ), i18n("Displays the given time of the form hh:mm [am/pm] or HH:mm converted from the given timezone to the local time, in 24 hour time"))); } DateTimeRunner::~DateTimeRunner() { } void DateTimeRunner::match(Plasma::RunnerContext &context) { - const QString term = context.query(); - if (term.compare(dateWord, Qt::CaseInsensitive) == 0) { - const QString date = QLocale().toString(QDate::currentDate()); - addMatch(i18n("Today's date is %1", date), date, context, QStringLiteral("view-calendar-day")); - } else if (term.startsWith(dateWord + QLatin1Char( ' ' ), Qt::CaseInsensitive)) { - const auto tz = term.rightRef(term.length() - dateWord.length() - 1); - const auto dates = datetime(tz); - for(auto it = dates.constBegin(), itEnd = dates.constEnd(); it != itEnd; ++it) { - const QString date = QLocale().toString(*it); - addMatch(QStringLiteral("%1 - %2").arg(it.key(), date), date, context, QStringLiteral("view-calendar-day")); + const QStringList terms = context.query().split(QChar::Space, QString::SkipEmptyParts); + bool is24Hr = terms.contains(QStringLiteral("24")); + bool toLocalTime = QChar(terms.value(1).at(0)).isNumber() && (terms.value(1) != QStringLiteral("24")); + + QMultiHash zoneMatches; + QMultiHash dTMatches; + QString time; + QTime t; + + for (const QString &term : terms) { + if (term.contains(QStringLiteral(":"))) { + time = term; + } else if (term.compare(QStringLiteral("am"), Qt::CaseInsensitive) == 0) { + time += QChar::Space + term; + } else if (term.compare(QStringLiteral("pm"), Qt::CaseInsensitive) == 0) { + time += QChar::Space + term; + } else if (!QChar(term.at(0)).isNumber()) { + zoneMatches = timeZoneMatches(term); } - } else if (term.compare(timeWord, Qt::CaseInsensitive) == 0) { - const QString time = QLocale().toString(QTime::currentTime()); - addMatch(i18n("Current time is %1", time), time, context, QStringLiteral("clock")); - } else if (term.startsWith(timeWord + QLatin1Char( ' ' ), Qt::CaseInsensitive)) { - const auto tz = term.rightRef(term.length() - timeWord.length() - 1); - const auto times = datetime(tz); - for(auto it = times.constBegin(), itEnd = times.constEnd(); it != itEnd; ++it) { - const QString time = QLocale().toString(*it, QLocale::ShortFormat); - addMatch(QStringLiteral("%1 - %2").arg(it.key(), time), time, context, QStringLiteral("clock")); + } + + // Possible time formats. + QTime hmmapTime = QTime::fromString(time, QStringLiteral("h:mm ap")); + QTime hhmmapTime = QTime::fromString(time, QStringLiteral("hh:mm ap")); + QTime hmmTime = QTime::fromString(time, QStringLiteral("h:mm")); + QTime hhmmTime = QTime::fromString(time, QStringLiteral("hh:mm")); + + if (hmmapTime.isValid()) { + t = hmmapTime; + } else if (hhmmapTime.isValid()) { + t = hhmmapTime; + } else if (hmmTime.isValid()) { + t = hmmTime; + } else if (hhmmTime.isValid()) { + t = hhmmTime; + } + + // If there are no zone matches the time is localtime. + if (!zoneMatches.isEmpty()) { + for (auto it = zoneMatches.begin(); it != zoneMatches.end(); ++it) { + if (toLocalTime && t.isValid()) { + dTMatches.insert(it.key(), QDateTime(QDateTime::currentDateTime().date(), t, it.value()).toLocalTime()); + } else if (!toLocalTime && t.isValid()) { + dTMatches.insert(it.key(), QDateTime(QDateTime::currentDateTime().date(), t).toTimeZone(it.value())); + } else if (!toLocalTime && !t.isValid()) { + dTMatches.insert(it.key(), QDateTime::currentDateTime().toTimeZone(it.value())); + } + } + } else if ((terms.size() == 1) || ((terms.size() == 2) && is24Hr)) { + dTMatches.insert(QTimeZone::systemTimeZone().displayName(QTimeZone::GenericTime, QTimeZone::ShortName), QDateTime::currentDateTime()); + } else { + return; + } + + if (terms.value(0).compare(dateWord, Qt::CaseInsensitive) == 0) { + for (auto it = dTMatches.begin(); it != dTMatches.end(); ++it) { + const QDateTime &dt = it.value(); + const QString &tzName = it.key(); + QString date = QLocale().toString(dt.date()); + + addMatch(QStringLiteral("%1 - %2").arg(tzName, date), date, context, QStringLiteral("view-calendar-day")); + } + } else if (terms.value(0).compare(timeWord, Qt::CaseInsensitive) == 0) { + for (auto it = dTMatches.begin(); it != dTMatches.end(); ++it) { + const QDateTime &dt = it.value(); + const QString &tzName = it.key(); + QString dateTime; + + if (is24Hr) { + QString timeFormat24hr = QLocale().timeFormat(QLocale::ShortFormat); + timeFormat24hr.remove(QStringLiteral("a"), Qt::CaseInsensitive); + timeFormat24hr.remove(QStringLiteral("p"), Qt::CaseInsensitive); + + dateTime = QLocale().toString(dt.time(), timeFormat24hr.simplified()); + } else { + dateTime = QLocale().toString(dt.time(), QLocale::ShortFormat); + } + + // When converting time append a date string. + if (!zoneMatches.isEmpty()) { + dateTime += QStringLiteral(" - ") + QLocale().toString(dt.date(), QLocale::ShortFormat); + } + + addMatch(QStringLiteral("%1 - %2").arg(tzName, dateTime), dateTime, context, QStringLiteral("clock")); } } } -QHash DateTimeRunner::datetime(const QStringRef& tz) +QMultiHash DateTimeRunner::timeZoneMatches(QString tz) { - QHash ret; - // - // KTimeZone gives us the actual timezone names such as "Asia/Kolkatta" and does - // not give us country info. QTimeZone does not give us the actual timezone name - // This is why we are using both for now. - // - const QList timeZoneIds = QTimeZone::availableTimeZoneIds(); + QMultiHash ret; + + QList timeZoneIds = QTimeZone::availableTimeZoneIds(); for (const QByteArray& zoneId : timeZoneIds) { QTimeZone timeZone(zoneId); + const QString zone = timeZone.displayName(QTimeZone::GenericTime); const QString zoneName = QString::fromUtf8(zoneId); - if (zoneName.startsWith(tz, Qt::CaseInsensitive)) { - ret[zoneName] = QDateTime::currentDateTimeUtc().toTimeZone(timeZone); - continue; + if (zoneName.startsWith(tz, Qt::CaseInsensitive) && !ret.contains(zone)) { + ret.insert(zone, timeZone); } const QString country = QLocale::countryToString(timeZone.country()); - if (country.startsWith(tz, Qt::CaseInsensitive)) { - ret[country] = QDateTime::currentDateTimeUtc().toTimeZone(timeZone); - continue; + if (country.startsWith(tz, Qt::CaseInsensitive) && !ret.contains(zone)) { + ret.insert(zone, timeZone); } // FIXME: This only includes the current abbreviation and not old abbreviation or // other possible names. // Eg - depending on the current date, only CET or CEST will work const QString abbr = timeZone.abbreviation(QDateTime::currentDateTime()); - if (abbr.startsWith(tz, Qt::CaseInsensitive)) { - ret[abbr] = QDateTime::currentDateTimeUtc().toTimeZone(timeZone); - continue; + if (abbr.startsWith(tz, Qt::CaseInsensitive) && !ret.contains(zone)) { + ret.insert(zone, timeZone); } }