From 56555e417722f29e03eef2d2c3e2be51dff513fa Mon Sep 17 00:00:00 2001 From: xionglinlin Date: Thu, 16 Apr 2026 12:35:19 +0800 Subject: [PATCH] fix: centralize relative time formatting logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved relative time formatting logic from BubbleModel and AppNotifyItem to a common static method in NotifyEntity to eliminate code duplication and fix incorrect time display issues. Previously, BubbleModel used a simple minutes calculation while AppNotifyItem used ICU library for locale-aware formatting, causing inconsistent time display between notification bubble and center. Now both components use the same unified formatting logic. The changes include: 1. Added static method NotifyEntity::formatRelativeTime() that handles all relative time formatting using ICU library 2. Removed QDateTime include from bubblemodel.cpp as it's no longer needed 3. Simplified updateBubbleTimeTip() in BubbleModel to use the new common method 4. Simplified updateTime() in AppNotifyItem to use the new common method with fallback to "Just now" 5. Updated copyright years to 2024-2026 in modified files Log: Fixed inconsistent time display between notification bubble and center Influence: 1. Test notification time display in both bubble and center views 2. Verify time formatting for different time intervals (just now, minutes, hours, yesterday, days, weeks) 3. Check locale-specific formatting works correctly 4. Verify time updates dynamically as notifications age 5. Test with notifications from different time periods fix: 集中相对时间格式化逻辑 将相对时间格式化逻辑从 BubbleModel 和 AppNotifyItem 移动到 NotifyEntity 的公共静态方法中,以消除代码重复并修复时间显示错误问题。之前, BubbleModel 使用简单的分钟计算,而 AppNotifyItem 使用 ICU 库进行本地化感 知的格式化,导致通知气泡和中心之间的时间显示不一致。现在两个组件都使用相 同的统一格式化逻辑。 变更包括: 1. 添加静态方法 NotifyEntity::formatRelativeTime(),使用 ICU 库处理所有 相对时间格式化 2. 从 bubblemodel.cpp 中移除不再需要的 QDateTime 包含 3. 简化 BubbleModel 中的 updateBubbleTimeTip() 以使用新的公共方法 4. 简化 AppNotifyItem 中的 updateTime() 以使用新的公共方法,并回退到"刚 刚" 5. 在修改的文件中更新版权年份为 2024-2026 Log: 修复通知气泡和中心之间时间显示不一致的问题 Influence: 1. 测试通知气泡和中心视图中的时间显示 2. 验证不同时间间隔(刚刚、分钟、小时、昨天、天数、周数)的时间格式化 3. 检查本地化特定的格式化是否正确工作 4. 验证时间随通知老化而动态更新 5. 测试来自不同时间段的通知 PMS: BUG-355185 Change-Id: I65ddbd0ea5ec943fd7b2c9aa55bc5ed29bd0522b --- panels/notification/bubble/bubblemodel.cpp | 10 +-- panels/notification/center/notifyitem.cpp | 83 ++------------------- panels/notification/common/notifyentity.cpp | 73 +++++++++++++++++- panels/notification/common/notifyentity.h | 6 +- 4 files changed, 86 insertions(+), 86 deletions(-) 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);