diff --git a/panels/notification/bubble/bubblemodel.cpp b/panels/notification/bubble/bubblemodel.cpp index ef8682033..455161497 100644 --- a/panels/notification/bubble/bubblemodel.cpp +++ b/panels/notification/bubble/bubblemodel.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -289,13 +288,10 @@ void BubbleModel::updateBubbleTimeTip() } for (auto item : m_bubbles) { - qint64 diff = QDateTime::currentMSecsSinceEpoch() - item->ctime(); - diff /= 1000; // secs - if (diff >= 60) { - QString timeTip; - timeTip = tr("%1 minutes ago").arg(diff / 60); + QString timeTip = NotifyEntity::formatRelativeTime(item->ctime()); + if (!timeTip.isEmpty()) { item->setTimeTip(timeTip); - }; + } } Q_EMIT dataChanged(index(0), index(m_bubbles.size() - 1), {BubbleModel::TimeTip}); diff --git a/panels/notification/center/notifyitem.cpp b/panels/notification/center/notifyitem.cpp index d427be535..44a0d2f77 100644 --- a/panels/notification/center/notifyitem.cpp +++ b/panels/notification/center/notifyitem.cpp @@ -1,16 +1,12 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "notifyitem.h" #include -#include #include -#include // For RelativeDateTimeFormatter -#include // For SimpleDateFormat - #include "notifyaccessor.h" namespace notification { @@ -66,81 +62,14 @@ QString AppNotifyItem::time() const return m_time; } -namespace -{ -QString toQString(const icu::UnicodeString &icuString) -{ - // Get a pointer to the internal UTF-16 buffer of the icu::UnicodeString. - // The buffer is not necessarily null-terminated, so we also need the length. - const UChar *ucharData = icuString.getBuffer(); - int32_t length = icuString.length(); - - // QString has a constructor that takes a const QChar* and a length. - // UChar is typically a 16-bit unsigned integer, which is compatible with QChar. - // Static_cast is used here for explicit type conversion, though often - // UChar and QChar are typedefs to the same underlying type (e.g., unsigned short). - return QString(reinterpret_cast(ucharData), length); -} - -[[maybe_unused]] icu::UnicodeString fromQString(const QString &qstr) -{ - return icu::UnicodeString(qstr.utf16(), qstr.length()); -} - -} // anonymous namespace - void AppNotifyItem::updateTime() { - QDateTime time = QDateTime::fromMSecsSinceEpoch(m_entity.cTime()); - if (!time.isValid()) - return; - - using namespace icu; - static std::unique_ptr formatter; - static UErrorCode cachedStatus = U_ZERO_ERROR; - if (!formatter) { - cachedStatus = U_ZERO_ERROR; - formatter = std::make_unique(icu::Locale::getDefault(), - nullptr, // Use default NumberFormat - UDAT_STYLE_LONG, - UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, - cachedStatus); - } - UErrorCode status = U_ZERO_ERROR; // For any per-call ICU operations - UnicodeString result; - - QString ret; - QDateTime currentTime = QDateTime::currentDateTime(); - auto elapsedDay = time.daysTo(currentTime); - if (elapsedDay == 0) { - qint64 msec = QDateTime::currentMSecsSinceEpoch() - m_entity.cTime(); - auto minute = msec / 1000 / 60; - if (minute <= 0) { - ret = tr("Just now"); - } else if (minute > 0 && minute < 60) { - formatter->format(minute, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, result, status); - ret = toQString(result); - } else { - const auto hour = minute / 60; - formatter->format(hour, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, result, status); - ret = toQString(result); - } - } else if (elapsedDay >= 1 && elapsedDay < 2) { - formatter->format(1, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, result, status); - UnicodeString combinedString; - UErrorCode timeStatus = U_ZERO_ERROR; - SimpleDateFormat timeFormatter("HH:mm", icu::Locale::getDefault(), timeStatus); - UnicodeString timeString; - UDate udate = static_cast(m_entity.cTime()); - timeFormatter.format(udate, timeString, timeStatus); - formatter->combineDateAndTime(result, timeString, combinedString, status); - ret = toQString(combinedString); - } else if (elapsedDay >= 2 && elapsedDay < 7) { - ret = QLocale::system().toString(time, "ddd hh:mm"); - } else { - ret = time.toString(QLocale::system().dateFormat(QLocale::ShortFormat)); + QString ret = NotifyEntity::formatRelativeTime(m_entity.cTime()); + if (ret.isEmpty()) { + if (!QDateTime::fromMSecsSinceEpoch(m_entity.cTime()).isValid()) + return; + ret = tr("Just now"); } - m_time = ret; } diff --git a/panels/notification/common/notifyentity.cpp b/panels/notification/common/notifyentity.cpp index ff9fdb834..d0b627797 100644 --- a/panels/notification/common/notifyentity.cpp +++ b/panels/notification/common/notifyentity.cpp @@ -1,13 +1,19 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "notifyentity.h" #include +#include #include #include +#include +#include + +#include + namespace notification { Q_LOGGING_CATEGORY(notifyLog, "org.deepin.dde.shell.notification") @@ -350,4 +356,69 @@ QVariantMap NotifyEntity::parseHint(const QString &hint) return map; } +namespace { + +QString toQString(const icu::UnicodeString &icuString) +{ + const UChar *ucharData = icuString.getBuffer(); + int32_t length = icuString.length(); + return QString(reinterpret_cast(ucharData), length); +} + +} // anonymous namespace + +QString NotifyEntity::formatRelativeTime(qint64 ctimeMs) +{ + QDateTime time = QDateTime::fromMSecsSinceEpoch(ctimeMs); + if (!time.isValid()) + return {}; + + using namespace icu; + static std::unique_ptr formatter; + static UErrorCode cachedStatus = U_ZERO_ERROR; + if (!formatter) { + cachedStatus = U_ZERO_ERROR; + formatter = std::make_unique( + icu::Locale::getDefault(), + nullptr, + UDAT_STYLE_LONG, + UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, + cachedStatus); + } + UErrorCode status = U_ZERO_ERROR; + UnicodeString result; + + QDateTime currentTime = QDateTime::currentDateTime(); + auto elapsedDay = time.daysTo(currentTime); + + if (elapsedDay == 0) { + qint64 msec = QDateTime::currentMSecsSinceEpoch() - ctimeMs; + auto minute = msec / 1000 / 60; + if (minute <= 0) { + return {}; + } else if (minute > 0 && minute < 60) { + formatter->format(minute, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, result, status); + return toQString(result); + } else { + const auto hour = minute / 60; + formatter->format(hour, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, result, status); + return toQString(result); + } + } else if (elapsedDay >= 1 && elapsedDay < 2) { + formatter->format(1, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, result, status); + UnicodeString combinedString; + UErrorCode timeStatus = U_ZERO_ERROR; + SimpleDateFormat timeFormatter("HH:mm", icu::Locale::getDefault(), timeStatus); + UnicodeString timeString; + UDate udate = static_cast(ctimeMs); + timeFormatter.format(udate, timeString, timeStatus); + formatter->combineDateAndTime(result, timeString, combinedString, status); + return toQString(combinedString); + } else if (elapsedDay >= 2 && elapsedDay < 7) { + return QLocale::system().toString(time, "ddd hh:mm"); + } else { + return time.toString(QLocale::system().dateFormat(QLocale::ShortFormat)); + } +} + } diff --git a/panels/notification/common/notifyentity.h b/panels/notification/common/notifyentity.h index 741c6003c..9b72ec97b 100644 --- a/panels/notification/common/notifyentity.h +++ b/panels/notification/common/notifyentity.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -93,6 +93,10 @@ class NotifyEntity QString bodyIcon() const; + // Formats a creation time (ms since epoch) as a locale-aware relative + // time string. Returns empty string if less than 1 minute or invalid. + static QString formatRelativeTime(qint64 ctimeMs); + private: static QString convertHintsToString(const QVariantMap &map); static QString convertActionsToString(const QStringList &actions);