Rework the tzAbbr() helper of dateTimeToString()

Break out the offset-specific code (partly in preparation for handling
its localization better, but also) to ensure we try more candidates
and, when using the system locale, only use QTimeZone::displayName()
if we must - as it may oblige us to instantiate the system time-zone
instance. Tweak ordering of #include headers while I'm about it.

Change-Id: I183ddd1bcb181120df5561bfdc4d89a218292ee0
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit 1b909695a66500b3cceb0fee668b12a663ed3b8b)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Edward Welbourne 2024-10-14 14:04:27 +02:00 committed by Qt Cherry-pick Bot
parent 04a9d7fe00
commit fcd52bf984

View File

@ -23,6 +23,7 @@ QT_WARNING_DISABLE_GCC("-Wfree-nonheap-object") // false positive tracking
#include "qplatformdefs.h" #include "qplatformdefs.h"
#include "qcalendar.h"
#include "qdatastream.h" #include "qdatastream.h"
#include "qdebug.h" #include "qdebug.h"
#include "qhashfunctions.h" #include "qhashfunctions.h"
@ -56,7 +57,6 @@ QT_WARNING_DISABLE_GCC("-Wfree-nonheap-object") // false positive tracking
#include "private/qcalendarbackend_p.h" #include "private/qcalendarbackend_p.h"
#include "private/qgregoriancalendar_p.h" #include "private/qgregoriancalendar_p.h"
#include "qcalendar.h"
#include <q20iterator.h> #include <q20iterator.h>
@ -3527,6 +3527,41 @@ QString QLocale::pmText() const
return d->m_data->postMeridiem().getData(pm_data); return d->m_data->postMeridiem().getData(pm_data);
} }
// For the benefit of QCalendar, below.
static QString offsetFromAbbreviation(QString &&text)
{
QStringView tail{text};
// May need to strip a prefix:
if (tail.startsWith("UTC"_L1) || tail.startsWith("GMT"_L1))
tail = tail.sliced(3);
// TODO: there may be a locale-specific alternative prefix.
// Hard to know without zone-name L10n details, though.
return (tail.isEmpty() // The Qt::UTC case omits the zero offset:
? u"+00:00"_s
// Whole-hour offsets may lack the zero minutes:
: (tail.size() <= 3
? tail + ":00"_L1
: std::move(text).right(tail.size())));
}
static QString zoneOffsetFormat([[maybe_unused]] const QLocale &locale,
const QDateTime &when,
int offsetSeconds)
{
QString text =
#if QT_CONFIG(timezone)
locale != QLocale::system()
? when.timeRepresentation().displayName(when, QTimeZone::OffsetName, locale)
:
#endif
when.toOffsetFromUtc(offsetSeconds).timeZoneAbbreviation();
if (!text.isEmpty())
text = offsetFromAbbreviation(std::move(text));
// else: no suitable representation of the zone.
return text;
}
// Another intrusion from QCalendar, using some of the tools above: // Another intrusion from QCalendar, using some of the tools above:
QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime &datetime, QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime &datetime,
@ -3700,26 +3735,32 @@ QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime &
case 't': { case 't': {
enum AbbrType { Long, Offset, Short }; enum AbbrType { Long, Offset, Short };
const auto tzAbbr = [locale](const QDateTime &when, AbbrType type) { const auto tzAbbr = [locale](const QDateTime &when, AbbrType type) {
QString text;
if (type == Offset) {
text = zoneOffsetFormat(locale, when, when.offsetFromUtc());
// When using timezone_locale data, this should always succeed:
if (!text.isEmpty())
return text;
}
#if QT_CONFIG(timezone) #if QT_CONFIG(timezone)
if (type != Short || locale != QLocale::system()) { if (type != Short || locale != QLocale::system()) {
QTimeZone::NameType mode = QTimeZone::NameType mode =
type == Short ? QTimeZone::ShortName type == Short ? QTimeZone::ShortName
: type == Long ? QTimeZone::LongName : QTimeZone::OffsetName; : type == Long ? QTimeZone::LongName : QTimeZone::OffsetName;
QString text = when.timeRepresentation().displayName(when, mode, locale); text = when.timeRepresentation().displayName(when, mode, locale);
if (!text.isEmpty()) if (!text.isEmpty())
return text; return text;
// else fall back to an unlocalized one if we can manage it: // else fall back to an unlocalized one if we can manage it:
} // else: prefer QDateTime's abbreviation, for backwards-compatibility. } // else: prefer QDateTime's abbreviation, for backwards-compatibility.
#endif // else, make do with non-localized abbreviation: #endif // else, make do with non-localized abbreviation:
if (type != Offset) { // Absent timezone_locale data, Offset might still reach here:
QString text = when.timeZoneAbbreviation(); if (type == Offset) // Our prior failure might not have tried this:
if (!text.isEmpty()) text = when.toOffsetFromUtc(when.offsetFromUtc()).timeZoneAbbreviation();
return text; if (text.isEmpty()) // Notably including type != Offset
// else fall back to Offset version: text = when.timeZoneAbbreviation();
} return type == Offset ? offsetFromAbbreviation(std::move(text)) : text;
// For Offset, we can coerce to a UTC-based zone's abbreviation:
return when.toOffsetFromUtc(when.offsetFromUtc()).timeZoneAbbreviation();
}; };
used = true; used = true;
repeat = qMin(repeat, 4); repeat = qMin(repeat, 4);
// If we don't have a date-time, use the current system time: // If we don't have a date-time, use the current system time:
@ -3732,21 +3773,8 @@ QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime &
case 3: // ±hh:mm case 3: // ±hh:mm
case 2: // ±hhmm (we'll remove the ':' at the end) case 2: // ±hhmm (we'll remove the ':' at the end)
text = tzAbbr(when, Offset); text = tzAbbr(when, Offset);
if (!text.isEmpty()) { if (repeat == 2)
QStringView tail{text}; text.remove(u':');
// May need to strip a prefix:
if (tail.startsWith("UTC"_L1) || tail.startsWith("GMT"_L1))
tail = tail.sliced(3);
// TODO: there may be a locale-specific alternative prefix.
text = (tail.isEmpty() // The Qt::UTC case omits the zero offset:
? u"+00:00"_s
// Whole-hour offsets may lack the zero minutes:
: (tail.size() <= 3
? tail + ":00"_L1
: std::move(text).right(tail.size())));
if (repeat == 2)
text.remove(u':');
} // else: no suitable representation of the zone.
break; break;
default: default:
text = tzAbbr(when, Short); text = tzAbbr(when, Short);