Skip to content

Commit 47fd70c

Browse files
committed
fix: centralize relative time formatting logic
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
1 parent c8251d0 commit 47fd70c

4 files changed

Lines changed: 86 additions & 86 deletions

File tree

panels/notification/bubble/bubblemodel.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
#include <QTimer>
1212
#include <QLoggingCategory>
13-
#include <QDateTime>
1413
#include <QImage>
1514
#include <QTemporaryFile>
1615
#include <QUrl>
@@ -289,13 +288,10 @@ void BubbleModel::updateBubbleTimeTip()
289288
}
290289

291290
for (auto item : m_bubbles) {
292-
qint64 diff = QDateTime::currentMSecsSinceEpoch() - item->ctime();
293-
diff /= 1000; // secs
294-
if (diff >= 60) {
295-
QString timeTip;
296-
timeTip = tr("%1 minutes ago").arg(diff / 60);
291+
QString timeTip = NotifyEntity::formatRelativeTime(item->ctime());
292+
if (!timeTip.isEmpty()) {
297293
item->setTimeTip(timeTip);
298-
};
294+
}
299295
}
300296

301297
Q_EMIT dataChanged(index(0), index(m_bubbles.size() - 1), {BubbleModel::TimeTip});

panels/notification/center/notifyitem.cpp

Lines changed: 6 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
#include "notifyitem.h"
66

77
#include <QDateTime>
8-
#include <QLocale>
98
#include <QLoggingCategory>
109

11-
#include <unicode/reldatefmt.h> // For RelativeDateTimeFormatter
12-
#include <unicode/smpdtfmt.h> // For SimpleDateFormat
13-
1410
#include "notifyaccessor.h"
1511

1612
namespace notification {
@@ -66,81 +62,14 @@ QString AppNotifyItem::time() const
6662
return m_time;
6763
}
6864

69-
namespace
70-
{
71-
QString toQString(const icu::UnicodeString &icuString)
72-
{
73-
// Get a pointer to the internal UTF-16 buffer of the icu::UnicodeString.
74-
// The buffer is not necessarily null-terminated, so we also need the length.
75-
const UChar *ucharData = icuString.getBuffer();
76-
int32_t length = icuString.length();
77-
78-
// QString has a constructor that takes a const QChar* and a length.
79-
// UChar is typically a 16-bit unsigned integer, which is compatible with QChar.
80-
// Static_cast is used here for explicit type conversion, though often
81-
// UChar and QChar are typedefs to the same underlying type (e.g., unsigned short).
82-
return QString(reinterpret_cast<const QChar *>(ucharData), length);
83-
}
84-
85-
[[maybe_unused]] icu::UnicodeString fromQString(const QString &qstr)
86-
{
87-
return icu::UnicodeString(qstr.utf16(), qstr.length());
88-
}
89-
90-
} // anonymous namespace
91-
9265
void AppNotifyItem::updateTime()
9366
{
94-
QDateTime time = QDateTime::fromMSecsSinceEpoch(m_entity.cTime());
95-
if (!time.isValid())
96-
return;
97-
98-
using namespace icu;
99-
static std::unique_ptr<RelativeDateTimeFormatter> formatter;
100-
static UErrorCode cachedStatus = U_ZERO_ERROR;
101-
if (!formatter) {
102-
cachedStatus = U_ZERO_ERROR;
103-
formatter = std::make_unique<RelativeDateTimeFormatter>(icu::Locale::getDefault(),
104-
nullptr, // Use default NumberFormat
105-
UDAT_STYLE_LONG,
106-
UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE,
107-
cachedStatus);
108-
}
109-
UErrorCode status = U_ZERO_ERROR; // For any per-call ICU operations
110-
UnicodeString result;
111-
112-
QString ret;
113-
QDateTime currentTime = QDateTime::currentDateTime();
114-
auto elapsedDay = time.daysTo(currentTime);
115-
if (elapsedDay == 0) {
116-
qint64 msec = QDateTime::currentMSecsSinceEpoch() - m_entity.cTime();
117-
auto minute = msec / 1000 / 60;
118-
if (minute <= 0) {
119-
ret = tr("Just now");
120-
} else if (minute > 0 && minute < 60) {
121-
formatter->format(minute, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, result, status);
122-
ret = toQString(result);
123-
} else {
124-
const auto hour = minute / 60;
125-
formatter->format(hour, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, result, status);
126-
ret = toQString(result);
127-
}
128-
} else if (elapsedDay >= 1 && elapsedDay < 2) {
129-
formatter->format(1, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, result, status);
130-
UnicodeString combinedString;
131-
UErrorCode timeStatus = U_ZERO_ERROR;
132-
SimpleDateFormat timeFormatter("HH:mm", icu::Locale::getDefault(), timeStatus);
133-
UnicodeString timeString;
134-
UDate udate = static_cast<UDate>(m_entity.cTime());
135-
timeFormatter.format(udate, timeString, timeStatus);
136-
formatter->combineDateAndTime(result, timeString, combinedString, status);
137-
ret = toQString(combinedString);
138-
} else if (elapsedDay >= 2 && elapsedDay < 7) {
139-
ret = QLocale::system().toString(time, "ddd hh:mm");
140-
} else {
141-
ret = time.toString(QLocale::system().dateFormat(QLocale::ShortFormat));
67+
QString ret = NotifyEntity::formatRelativeTime(m_entity.cTime());
68+
if (ret.isEmpty()) {
69+
if (!QDateTime::fromMSecsSinceEpoch(m_entity.cTime()).isValid())
70+
return;
71+
ret = tr("Just now");
14272
}
143-
14473
m_time = ret;
14574
}
14675

panels/notification/common/notifyentity.cpp

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
#include "notifyentity.h"
66

77
#include <QDateTime>
8+
#include <QLocale>
89
#include <QStringList>
910
#include <QLoggingCategory>
1011

12+
#include <unicode/reldatefmt.h>
13+
#include <unicode/smpdtfmt.h>
14+
15+
#include <memory>
16+
1117
namespace notification {
1218
Q_LOGGING_CATEGORY(notifyLog, "org.deepin.dde.shell.notification")
1319

@@ -350,4 +356,69 @@ QVariantMap NotifyEntity::parseHint(const QString &hint)
350356
return map;
351357
}
352358

359+
namespace {
360+
361+
QString toQString(const icu::UnicodeString &icuString)
362+
{
363+
const UChar *ucharData = icuString.getBuffer();
364+
int32_t length = icuString.length();
365+
return QString(reinterpret_cast<const QChar *>(ucharData), length);
366+
}
367+
368+
} // anonymous namespace
369+
370+
QString NotifyEntity::formatRelativeTime(qint64 ctimeMs)
371+
{
372+
QDateTime time = QDateTime::fromMSecsSinceEpoch(ctimeMs);
373+
if (!time.isValid())
374+
return {};
375+
376+
using namespace icu;
377+
static std::unique_ptr<RelativeDateTimeFormatter> formatter;
378+
static UErrorCode cachedStatus = U_ZERO_ERROR;
379+
if (!formatter) {
380+
cachedStatus = U_ZERO_ERROR;
381+
formatter = std::make_unique<RelativeDateTimeFormatter>(
382+
icu::Locale::getDefault(),
383+
nullptr,
384+
UDAT_STYLE_LONG,
385+
UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE,
386+
cachedStatus);
387+
}
388+
UErrorCode status = U_ZERO_ERROR;
389+
UnicodeString result;
390+
391+
QDateTime currentTime = QDateTime::currentDateTime();
392+
auto elapsedDay = time.daysTo(currentTime);
393+
394+
if (elapsedDay == 0) {
395+
qint64 msec = QDateTime::currentMSecsSinceEpoch() - ctimeMs;
396+
auto minute = msec / 1000 / 60;
397+
if (minute <= 0) {
398+
return {};
399+
} else if (minute > 0 && minute < 60) {
400+
formatter->format(minute, UDAT_DIRECTION_LAST, UDAT_RELATIVE_MINUTES, result, status);
401+
return toQString(result);
402+
} else {
403+
const auto hour = minute / 60;
404+
formatter->format(hour, UDAT_DIRECTION_LAST, UDAT_RELATIVE_HOURS, result, status);
405+
return toQString(result);
406+
}
407+
} else if (elapsedDay >= 1 && elapsedDay < 2) {
408+
formatter->format(1, UDAT_DIRECTION_LAST, UDAT_RELATIVE_DAYS, result, status);
409+
UnicodeString combinedString;
410+
UErrorCode timeStatus = U_ZERO_ERROR;
411+
SimpleDateFormat timeFormatter("HH:mm", icu::Locale::getDefault(), timeStatus);
412+
UnicodeString timeString;
413+
UDate udate = static_cast<UDate>(ctimeMs);
414+
timeFormatter.format(udate, timeString, timeStatus);
415+
formatter->combineDateAndTime(result, timeString, combinedString, status);
416+
return toQString(combinedString);
417+
} else if (elapsedDay >= 2 && elapsedDay < 7) {
418+
return QLocale::system().toString(time, "ddd hh:mm");
419+
} else {
420+
return time.toString(QLocale::system().dateFormat(QLocale::ShortFormat));
421+
}
422+
}
423+
353424
}

panels/notification/common/notifyentity.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -93,6 +93,10 @@ class NotifyEntity
9393

9494
QString bodyIcon() const;
9595

96+
// Formats a creation time (ms since epoch) as a locale-aware relative
97+
// time string. Returns empty string if less than 1 minute or invalid.
98+
static QString formatRelativeTime(qint64 ctimeMs);
99+
96100
private:
97101
static QString convertHintsToString(const QVariantMap &map);
98102
static QString convertActionsToString(const QStringList &actions);

0 commit comments

Comments
 (0)