From 0a36a7c1db173089c25ea09029505a589a1c59e5 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 15 Jul 2022 15:59:40 +0200 Subject: [PATCH] Make 'zz' format an alias for 'z' in time format strings Also document the (seldom helpful) handling of over-long repeats of a format. Add test to QDateTime and amend QLocale test. [ChangeLog][QtCore][QDateTime] Doubling the 'z' format in a date-time or time format string now produces the same output as a single 'z'. Previously, this would have produced two copies of the milliseconds field (eliding any trailing zeros in each). Contrast with 'zzz', which produces the full milliseconds field, including any trailing zeros. Change-Id: I4c60462b062fee4079370096d745c191c1939506 Reviewed-by: Thiago Macieira --- src/corelib/text/qlocale.cpp | 4 +- src/corelib/time/qdatetime.cpp | 55 +++++++++++++++++-- .../auto/corelib/text/qlocale/tst_qlocale.cpp | 2 +- .../corelib/time/qdatetime/tst_qdatetime.cpp | 3 +- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index b19e4498d3..d3670c119a 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -3484,13 +3484,13 @@ QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime & case 'z': used = true; - repeat = (repeat >= 3) ? 3 : 1; + repeat = qMin(repeat, 3); // note: the millisecond component is treated like the decimal part of the seconds // so ms == 2 is always printed as "002", but ms == 200 can be either "2" or "200" result.append(locale.d->m_data->longLongToString(time.msec(), -1, 10, 3, QLocaleData::ZeroPadded)); - if (repeat == 1) { + if (repeat != 3) { if (result.endsWith(locale.zeroDigit())) result.chop(1); if (result.endsWith(locale.zeroDigit())) diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp index 2bb925c60b..c3b72073ec 100644 --- a/src/corelib/time/qdatetime.cpp +++ b/src/corelib/time/qdatetime.cpp @@ -1147,6 +1147,13 @@ QString QDate::toString(Qt::DateFormat format) const \note Day and month names are given in English (C locale). To get localized month and day names, use QLocale::system().toString(). + \note If a format character is repeated more times than the longest + expression in the table above using it, this part of the format will be read + as several expressions with no separator between them; the longest above, + possibly repeated as many times as there are copies of it, ending with a + residue that may be a shorter expression. Thus \c{'MMMMMMMMMM'} for a date + in May will contribute \c{"MayMay05"} to the output. + \sa fromString(), QDateTime::toString(), QTime::toString(), QLocale::toString() */ @@ -1611,6 +1618,14 @@ QDate QDate::fromString(QStringView string, Qt::DateFormat format) \snippet code/src_corelib_time_qdatetime.cpp 3 + \note If a format character is repeated more times than the longest + expression in the table above using it, this part of the format will be read + as several expressions with no separator between them; the longest above, + possibly repeated as many times as there are copies of it, ending with a + residue that may be a shorter expression. Thus \c{'MMMMMMMMMM'} would match + \c{"MayMay05"} and set the month to May. Likewise, \c{'MMMMMM'} would match + \c{"May08"} and find it inconsistent, leading to an invalid date. + \sa toString(), QDateTime::fromString(), QTime::fromString(), QLocale::toDate() */ @@ -1912,12 +1927,14 @@ QString QTime::toString(Qt::DateFormat format) const \row \li mm \li The minute with a leading zero (00 to 59) \row \li s \li The whole second, without any leading zero (0 to 59) \row \li ss \li The whole second, with a leading zero where applicable (00 to 59) - \row \li z \li The fractional part of the second, to go after a decimal - point, without trailing zeroes (0 to 999). Thus "\c{s.z}" - reports the seconds to full available (millisecond) precision - without trailing zeroes. - \row \li zzz \li The fractional part of the second, to millisecond - precision, including trailing zeroes where applicable (000 to 999). + \row \li z or zz + \li The fractional part of the second, to go after a decimal point, + without trailing zeroes. Thus "\c{s.z}" reports the seconds to + full available (millisecond) precision without trailing zeroes (0 + to 999). + \row \li zzz + \li The fractional part of the second, to millisecond precision, + including trailing zeroes where applicable (000 to 999). \row \li AP or A \li Use AM/PM display. \c A/AP will be replaced by 'AM' or 'PM'. In localized forms (only relevant to \l{QLocale::toString()}), the @@ -1960,8 +1977,16 @@ QString QTime::toString(Qt::DateFormat format) const \note To get localized forms of AM or PM (the AP, ap, A, a, aP or Ap formats), use QLocale::system().toString(). + \note If a format character is repeated more times than the longest + expression in the table above using it, this part of the format will be read + as several expressions with no separator between them; the longest above, + possibly repeated as many times as there are copies of it, ending with a + residue that may be a shorter expression. Thus \c{'HHHHH'} for the time + 08:00 will contribute \c{"08088"} to the output. + \sa fromString(), QDate::toString(), QDateTime::toString(), QLocale::toString() */ +// ### Qt 7 The 't' format specifiers should be specific to QDateTime (compare fromString). QString QTime::toString(QStringView format) const { return QLocale::c().toString(*this, format); @@ -2342,6 +2367,15 @@ QTime QTime::fromString(QStringView string, Qt::DateFormat format) \note If localized forms of am or pm (the AP, ap, Ap, aP, A or a formats) are to be recognized, use QLocale::system().toTime(). + \note If a format character is repeated more times than the longest + expression in the table above using it, this part of the format will be read + as several expressions with no separator between them; the longest above, + possibly repeated as many times as there are copies of it, ending with a + residue that may be a shorter expression. Thus \c{'HHHHH'} would match + \c{"08088"} or \c{"080808"} and set the hour to 8; if the time string + contained "070809" it would "match" but produce an inconsistent result, + leading to an invalid time. + \sa toString(), QDateTime::fromString(), QDate::fromString(), QLocale::toTime(), QLocale::toDateTime() */ @@ -5061,6 +5095,15 @@ QDateTime QDateTime::fromString(QStringView string, Qt::DateFormat format) English (C locale). If localized month and day names or localized forms of AM/PM are to be recognized, use QLocale::system().toDateTime(). + \note If a format character is repeated more times than the longest + expression in the table above using it, this part of the format will be read + as several expressions with no separator between them; the longest above, + possibly repeated as many times as there are copies of it, ending with a + residue that may be a shorter expression. Thus \c{'tttttt'} would match + \c{"Europe/BerlinEurope/Berlin"} and set the zone to Berlin time; if the + date-time string contained "Europe/BerlinZ" it would "match" but produce an + inconsistent result, leading to an invalid date-time. + \sa toString(), QDate::fromString(), QTime::fromString(), QLocale::toDateTime() */ diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index 4dd2cf6aff..df2721cfc3 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -1664,7 +1664,7 @@ void tst_QLocale::formatTime_data() QTest::newRow("C-quote-dquote-H") << QTime(1, 2, 3) << "C" << "'\"H\"'" << "\"H\""; QTest::newRow("C-H:m:s.z") << QTime(1, 2, 3, 456) << "C" << "H:m:s.z" << "1:2:3.456"; - QTest::newRow("C-H:m:s.zz") << QTime(1, 2, 3, 456) << "C" << "H:m:s.zz" << "1:2:3.456456"; + QTest::newRow("C-H:m:s.zz") << QTime(1, 2, 3, 456) << "C" << "H:m:s.zz" << "1:2:3.456"; QTest::newRow("C-H:m:s.zzz") << QTime(1, 2, 3, 456) << "C" << "H:m:s.zzz" << "1:2:3.456"; QTest::newRow("C-H:m:s.z=400") << QTime(1, 2, 3, 400) << "C" << "H:m:s.z" << "1:2:3.4"; QTest::newRow("C-H:m:s.zzz=400") << QTime(1, 2, 3, 400) << "C" << "H:m:s.zzz" << "1:2:3.400"; diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index c12ae8b632..090a19d4cc 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -1098,10 +1098,11 @@ void tst_QDateTime::toString_strformat() { // Most tests are in QLocale, just test that the api works. QDate testDate(2013, 1, 1); - QTime testTime(1, 2, 3); + QTime testTime(1, 2, 3, 456); QDateTime testDateTime(testDate, testTime, Qt::UTC); QCOMPARE(testDate.toString("yyyy-MM-dd"), QString("2013-01-01")); QCOMPARE(testTime.toString("hh:mm:ss"), QString("01:02:03")); + QCOMPARE(testTime.toString("hh:mm:ss.zz"), QString("01:02:03.456")); QCOMPARE(testDateTime.toString("yyyy-MM-dd hh:mm:ss t"), QString("2013-01-01 01:02:03 UTC")); // TODO QTBUG-95966: find better ways to use repeated 't' QCOMPARE(testDateTime.toString("yyyy-MM-dd hh:mm:ss tt"), QString("2013-01-01 01:02:03 UTCUTC"));