From 8cf71bb617c34f52357ce0597c7d07601337828b Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 14 Aug 2026 12:45:58 +0200 Subject: refactor(mime): expose the Date: header parse as MimeParser::parseDate The date search needs it and the logic already existed inside a file-local function, including the fix for Qt::RFC2822Date rejecting a string that carries a trailing timezone comment. Extracted rather than rewritten, so the second caller cannot end up without that fix. --- src/mimeparser.cpp | 15 ++++++++++----- src/mimeparser.h | 11 +++++++++++ tests/test_mimeparser.cpp | 23 +++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/mimeparser.cpp b/src/mimeparser.cpp index ecf56f1..2782a4a 100644 --- a/src/mimeparser.cpp +++ b/src/mimeparser.cpp @@ -272,20 +272,25 @@ QString recipientSummary(const QString &rawTo, int maxNames) return summary; } -QString attachmentFolderName(const QString &rfc822Date, const QString &subject) +QDateTime MimeParser::parseDate(const QString &rfc822Date) { - // The date prefix sorts chronologically in a file manager. A Date: header - // that does not parse is simply dropped rather than guessed at. // A trailing timezone comment, "... +0200 (CEST)", is legal per RFC 5322 // and common in the wild, but Qt::RFC2822Date rejects the whole string // when one is present (verified on Qt 6.11). Strip comments before - // parsing, or every such message silently loses its date prefix. + // parsing, or every such message silently loses its date. QString cleaned = rfc822Date; cleaned.remove(QRegularExpression(QStringLiteral("\\s*\\([^)]*\\)"))); cleaned = cleaned.trimmed(); + return QDateTime::fromString(cleaned, Qt::RFC2822Date); +} + +QString attachmentFolderName(const QString &rfc822Date, const QString &subject) +{ + // The date prefix sorts chronologically in a file manager. A Date: header + // that does not parse is simply dropped rather than guessed at. QString prefix; - const QDateTime parsed = QDateTime::fromString(cleaned, Qt::RFC2822Date); + const QDateTime parsed = MimeParser::parseDate(rfc822Date); if (parsed.isValid()) prefix = parsed.toString(QStringLiteral("yyyy-MM-dd")); diff --git a/src/mimeparser.h b/src/mimeparser.h index 9fceb2f..da54434 100644 --- a/src/mimeparser.h +++ b/src/mimeparser.h @@ -19,6 +19,7 @@ #pragma once #include +#include #include #include #include @@ -143,4 +144,14 @@ public: MimeParser(); ParsedMessage parse(const QString &filePath) const; + + /// Parses an RFC 2822 `Date:` header, returning an invalid QDateTime when + /// nothing usable is there. + /// + /// **Strips comments before parsing**, because `Qt::RFC2822Date` rejects + /// the entire string when a trailing timezone comment is present, and + /// `... +0200 (CEST)` is legal per RFC 5322 and common in the wild + /// (verified on Qt 6.11). A parser without this silently loses the date on + /// a large share of real mail. + static QDateTime parseDate(const QString &rfc822Date); }; diff --git a/tests/test_mimeparser.cpp b/tests/test_mimeparser.cpp index bac71f6..af6307e 100644 --- a/tests/test_mimeparser.cpp +++ b/tests/test_mimeparser.cpp @@ -47,6 +47,7 @@ private slots: void recipientSummarySurvivesUnusableInput(); void folderNameSurvivesATimezoneComment(); void savingABatchNeverOverwrites(); + void parsesADateWithATimezoneComment(); private: QString fixture(const QString &name) const @@ -485,5 +486,27 @@ void TestMimeParser::recipientSummarySurvivesUnusableInput() recipientSummary(QStringLiteral("\"unterminated ")); } +void TestMimeParser::parsesADateWithATimezoneComment() +{ + // Qt::RFC2822Date rejects the WHOLE string when a trailing comment is + // present (verified on Qt 6.11), and "+0200 (CEST)" is both legal and + // common. Without the comment stripped, every such message loses its date + // silently: the attachment folder loses its prefix, and a date search + // offers nothing with no indication why. + const QDateTime withComment = MimeParser::parseDate( + QStringLiteral("Fri, 14 Aug 2026 09:30:00 +0200 (CEST)")); + QVERIFY(withComment.isValid()); + QCOMPARE(withComment.date(), QDate(2026, 8, 14)); + + const QDateTime plain = MimeParser::parseDate( + QStringLiteral("Fri, 14 Aug 2026 09:30:00 +0200")); + QVERIFY(plain.isValid()); + QCOMPARE(plain.date(), QDate(2026, 8, 14)); + + // Nothing usable is an invalid QDateTime, never a guess. + QVERIFY(!MimeParser::parseDate(QStringLiteral("last Tuesday")).isValid()); + QVERIFY(!MimeParser::parseDate(QString()).isValid()); +} + QTEST_MAIN(TestMimeParser) #include "test_mimeparser.moc" -- cgit v1.2.3