diff --git a/src/librssguard/3rd-party/qdatetimeparser/qdatetimeparser.cpp b/src/librssguard/3rd-party/qdatetimeparser/qdatetimeparser.cpp index aaaa83f5a..f9a966e31 100644 --- a/src/librssguard/3rd-party/qdatetimeparser/qdatetimeparser.cpp +++ b/src/librssguard/3rd-party/qdatetimeparser/qdatetimeparser.cpp @@ -3,8 +3,8 @@ #include "3rd-party/qdatetimeparser/qdatetimeparser.h" -#include "private/qstringiterator_p.h" -#include "private/qtenvironmentvariables_p.h" +// #include "private/qstringiterator_p.h" +// #include "private/qtenvironmentvariables_p.h" #include #include @@ -354,6 +354,184 @@ int QDateTimeParser::sectionPos(const SectionNode& sn) const { Helper function for parseSection. */ +class QStringIterator { + QString::const_iterator i, pos, e; + static_assert((std::is_same::value)); + static bool less(const QChar* lhs, const QChar* rhs) noexcept { + return std::less{}(lhs, rhs); + } + + public: + explicit QStringIterator(QStringView string, qsizetype idx = 0) + : i(string.begin()), pos(i + idx), e(string.end()) {} + + inline explicit QStringIterator(const QChar* begin, const QChar* end) : i(begin), pos(begin), e(end) {} + + explicit QStringIterator(const QChar* begin, qsizetype idx, const QChar* end) + : i(begin), pos(begin + idx), e(end) {} + + inline QString::const_iterator position() const { + return pos; + } + + qsizetype index() const { + return pos - i; + } + + inline void setPosition(QString::const_iterator position) { + Q_ASSERT_X(!less(position, i) && !less(e, position), Q_FUNC_INFO, "position out of bounds"); + pos = position; + } + + // forward iteration + + inline bool hasNext() const { + return less(pos, e); + } + + inline void advance() { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + + if (Q_UNLIKELY((pos++)->isHighSurrogate())) { + if (Q_LIKELY(pos != e && pos->isLowSurrogate())) + ++pos; + } + } + + inline void advanceUnchecked() { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + + if (Q_UNLIKELY((pos++)->isHighSurrogate())) { + Q_ASSERT(hasNext() && pos->isLowSurrogate()); + ++pos; + } + } + + inline char32_t peekNextUnchecked() const { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + + if (Q_UNLIKELY(pos->isHighSurrogate())) { + Q_ASSERT(less(pos + 1, e) && pos[1].isLowSurrogate()); + return QChar::surrogateToUcs4(pos[0], pos[1]); + } + + return pos->unicode(); + } + + inline char32_t peekNext(char32_t invalidAs = QChar::ReplacementCharacter) const { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + + if (Q_UNLIKELY(pos->isSurrogate())) { + if (Q_LIKELY(pos->isHighSurrogate())) { + const QChar* low = pos + 1; + if (Q_LIKELY(low != e && low->isLowSurrogate())) + return QChar::surrogateToUcs4(*pos, *low); + } + return invalidAs; + } + + return pos->unicode(); + } + + inline char32_t nextUnchecked() { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + + const QChar cur = *pos++; + if (Q_UNLIKELY(cur.isHighSurrogate())) { + Q_ASSERT(hasNext() && pos->isLowSurrogate()); + return QChar::surrogateToUcs4(cur, *pos++); + } + return cur.unicode(); + } + + inline char32_t next(char32_t invalidAs = QChar::ReplacementCharacter) { + Q_ASSERT_X(hasNext(), Q_FUNC_INFO, "iterator hasn't a next item"); + + const QChar uc = *pos++; + if (Q_UNLIKELY(uc.isSurrogate())) { + if (Q_LIKELY(uc.isHighSurrogate() && hasNext() && pos->isLowSurrogate())) + return QChar::surrogateToUcs4(uc, *pos++); + return invalidAs; + } + + return uc.unicode(); + } + + // backwards iteration + + inline bool hasPrevious() const { + return less(i, pos); + } + + inline void recede() { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + + if (Q_UNLIKELY((--pos)->isLowSurrogate())) { + const QChar* high = pos - 1; + if (Q_LIKELY(high != i - 1 && high->isHighSurrogate())) + --pos; + } + } + + inline void recedeUnchecked() { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + + if (Q_UNLIKELY((--pos)->isLowSurrogate())) { + Q_ASSERT(hasPrevious() && pos[-1].isHighSurrogate()); + --pos; + } + } + + inline char32_t peekPreviousUnchecked() const { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + + if (Q_UNLIKELY(pos[-1].isLowSurrogate())) { + Q_ASSERT(less(i + 1, pos) && pos[-2].isHighSurrogate()); + return QChar::surrogateToUcs4(pos[-2], pos[-1]); + } + return pos[-1].unicode(); + } + + inline char32_t peekPrevious(char32_t invalidAs = QChar::ReplacementCharacter) const { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + + if (Q_UNLIKELY(pos[-1].isSurrogate())) { + if (Q_LIKELY(pos[-1].isLowSurrogate())) { + const QChar* high = pos - 2; + if (Q_LIKELY(high != i - 1 && high->isHighSurrogate())) + return QChar::surrogateToUcs4(*high, pos[-1]); + } + return invalidAs; + } + + return pos[-1].unicode(); + } + + inline char32_t previousUnchecked() { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + + const QChar cur = *--pos; + if (Q_UNLIKELY(cur.isLowSurrogate())) { + Q_ASSERT(hasPrevious() && pos[-1].isHighSurrogate()); + return QChar::surrogateToUcs4(*--pos, cur); + } + return cur.unicode(); + } + + inline char32_t previous(char32_t invalidAs = QChar::ReplacementCharacter) { + Q_ASSERT_X(hasPrevious(), Q_FUNC_INFO, "iterator hasn't a previous item"); + + const QChar uc = *--pos; + if (Q_UNLIKELY(uc.isSurrogate())) { + if (Q_LIKELY(uc.isLowSurrogate() && hasPrevious() && pos[-1].isHighSurrogate())) + return QChar::surrogateToUcs4(*--pos, uc); + return invalidAs; + } + + return uc.unicode(); + } +}; + static qsizetype digitCount(QStringView str) { qsizetype digits = 0; for (QStringIterator it(str); it.hasNext();) { @@ -1187,12 +1365,14 @@ static QTime actualTime(QDateTimeParser::Sections known, \internal */ static int startsWithLocalTimeZone(QStringView name, const QDateTime& when) { + /* // On MS-Win, at least when system zone is UTC, the tzname[]s may be empty. for (int i = 0; i < 2; ++i) { const QString zone(qTzName(i)); if (!zone.isEmpty() && name.startsWith(zone)) return zone.size(); } + */ // Mimic what QLocale::toString() would have used, to ensure round-trips work: const QString local = QDateTime(when.date(), when.time()).timeZoneAbbreviation(); if (name.startsWith(local)) diff --git a/src/librssguard/miscellaneous/textfactory.cpp b/src/librssguard/miscellaneous/textfactory.cpp index 4e88a8f8e..9b3e623cf 100644 --- a/src/librssguard/miscellaneous/textfactory.cpp +++ b/src/librssguard/miscellaneous/textfactory.cpp @@ -90,7 +90,7 @@ QDateTime TextFactory::parseDateTime(const QString& date_time, QString* used_dt_ QDateTime dt; QTime time_zone_offset; - const QLocale locale(QLocale::Language::C); + // const QLocale locale(QLocale::Language::C); bool positive_time_zone_offset = false; static QStringList date_patterns = dateTimePatterns(); QStringList timezone_offset_patterns; @@ -115,6 +115,10 @@ QDateTime TextFactory::parseDateTime(const QString& date_time, QString* used_dt_ dt = locale.toDateTime(input_date_chopped, pattern); #endif */ + // NOTE: We use QDateTimeParser class extracted/modified from Qt sources to allow + // matching only prefixes of input date strings. + // + // Built-in parser only matches if the WHOLE input strings matches. QDateTimeParser par(QMetaType::QDateTime, QDateTimeParser::FromString, QCalendar()); par.setDefaultLocale(QLocale::c()); par.parseFormat(pattern);