Rework QTZP::displayName() to use locale and data() more
We should only use the isoOffsetFormat() version of offset name for QLocale::C - while other locales may coincide with it, we don't know so shouldn't just assume. Add a query for whether a locale is the one used by the backend for the abbreviation in its data() and, when it is, use that abbreviation as short name. The TZ backend was searching its transitions for help with the abbreviation; we can do that also in the base-class, so introduce a data(TimeType) that will search transition history (if available) for a suitable transition from which to take the abbreviation (when appropriate). This shall make displayName() return an empty string in various cases where it did not, before; this is more honest than returning a string that's inappropriate for the locale for which it was meant to be answering. Task-number: QTBUG-114914 Task-number: QTBUG-115158 Task-number: QTBUG-122448 Change-Id: I523229def63aafcd2009b8406b2c3d13f7c01d9c Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
f3bf304de7
commit
e813acbb02
|
|
@ -172,22 +172,29 @@ QString QTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
|
|||
QTimeZone::NameType nameType,
|
||||
const QLocale &locale) const
|
||||
{
|
||||
if (nameType == QTimeZone::OffsetName)
|
||||
return isoOffsetFormat(offsetFromUtc(atMSecsSinceEpoch));
|
||||
const Data tran = data(atMSecsSinceEpoch);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs()) {
|
||||
if (nameType == QTimeZone::OffsetName && locale.language() == QLocale::C)
|
||||
return isoOffsetFormat(tran.offsetFromUtc);
|
||||
if (nameType == QTimeZone::ShortName && isDataLocale(locale))
|
||||
return tran.abbreviation;
|
||||
|
||||
if (isDaylightTime(atMSecsSinceEpoch))
|
||||
return displayName(QTimeZone::DaylightTime, nameType, locale);
|
||||
else
|
||||
return displayName(QTimeZone::StandardTime, nameType, locale);
|
||||
QTimeZone::TimeType timeType
|
||||
= tran.daylightTimeOffset != 0 ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
|
||||
return displayName(timeType, nameType, locale);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString QTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
|
||||
QTimeZone::NameType nameType,
|
||||
const QLocale &locale) const
|
||||
{
|
||||
Q_UNUSED(timeType);
|
||||
Q_UNUSED(nameType);
|
||||
Q_UNUSED(locale);
|
||||
if (nameType == QTimeZone::OffsetName && isDataLocale(locale)) {
|
||||
const Data tran = data(timeType);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs())
|
||||
return isoOffsetFormat(tran.offsetFromUtc);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
|
@ -227,6 +234,56 @@ bool QTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
|
|||
return false;
|
||||
}
|
||||
|
||||
QTimeZonePrivate::Data QTimeZonePrivate::data(QTimeZone::TimeType timeType) const
|
||||
{
|
||||
// True if tran is valid and has the DST-ness to match timeType:
|
||||
const auto validMatch = [timeType](const QTimeZonePrivate::Data &tran) {
|
||||
return tran.atMSecsSinceEpoch != invalidMSecs()
|
||||
&& ((timeType == QTimeZone::DaylightTime) != (tran.daylightTimeOffset == 0));
|
||||
};
|
||||
|
||||
// Get current tran, use if suitable:
|
||||
const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch();
|
||||
QTimeZonePrivate::Data tran = data(currentMSecs);
|
||||
if (validMatch(tran))
|
||||
return tran;
|
||||
|
||||
if (hasTransitions()) {
|
||||
// Otherwise, next tran probably flips DST-ness:
|
||||
tran = nextTransition(currentMSecs);
|
||||
if (validMatch(tran))
|
||||
return tran;
|
||||
|
||||
// Failing that, prev (or present, if current MSecs is exactly a
|
||||
// transition moment) tran defines what data() got us and the one before
|
||||
// that probably flips DST-ness; failing that, keep marching backwards
|
||||
// in search of a DST interval:
|
||||
tran = previousTransition(currentMSecs + 1);
|
||||
while (tran.atMSecsSinceEpoch != invalidMSecs()) {
|
||||
tran = previousTransition(tran.atMSecsSinceEpoch);
|
||||
if (validMatch(tran))
|
||||
return tran;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
Returns true if the abbreviation given in data()'s returns is appropriate
|
||||
for use in the given \a locale.
|
||||
|
||||
Base implementation assumes data() corresponds to the system locale; derived
|
||||
classes should override if their data() is something else (such as
|
||||
C/English).
|
||||
*/
|
||||
bool QTimeZonePrivate::isDataLocale(const QLocale &locale) const
|
||||
{
|
||||
// Guess data is for the system locale unless backend overrides that.
|
||||
return locale == QLocale::system();
|
||||
}
|
||||
|
||||
QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
|
||||
{
|
||||
Q_UNUSED(forMSecsSinceEpoch);
|
||||
|
|
@ -906,6 +963,19 @@ QTimeZonePrivate::Data QUtcTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
|
|||
return d;
|
||||
}
|
||||
|
||||
// Override to shortcut past base's complications:
|
||||
QTimeZonePrivate::Data QUtcTimeZonePrivate::data(QTimeZone::TimeType timeType) const
|
||||
{
|
||||
Q_UNUSED(timeType);
|
||||
return data(QDateTime::currentMSecsSinceEpoch());
|
||||
}
|
||||
|
||||
bool QUtcTimeZonePrivate::isDataLocale(const QLocale &locale) const
|
||||
{
|
||||
// Officially only supports C locale names; these are surely also viable for English.
|
||||
return locale.language() == QLocale::C || locale.language() == QLocale::English;
|
||||
}
|
||||
|
||||
void QUtcTimeZonePrivate::init(const QByteArray &zoneId)
|
||||
{
|
||||
m_id = zoneId;
|
||||
|
|
@ -933,6 +1003,15 @@ QString QUtcTimeZonePrivate::comment() const
|
|||
return m_comment;
|
||||
}
|
||||
|
||||
// Override to bypass complications in base-class:
|
||||
QString QUtcTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
|
||||
QTimeZone::NameType nameType,
|
||||
const QLocale &locale) const
|
||||
{
|
||||
Q_UNUSED(atMSecsSinceEpoch);
|
||||
return displayName(QTimeZone::StandardTime, nameType, locale);
|
||||
}
|
||||
|
||||
QString QUtcTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
|
||||
QTimeZone::NameType nameType,
|
||||
const QLocale &locale) const
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ public:
|
|||
virtual bool isDaylightTime(qint64 atMSecsSinceEpoch) const;
|
||||
|
||||
virtual Data data(qint64 forMSecsSinceEpoch) const;
|
||||
virtual Data data(QTimeZone::TimeType timeType) const;
|
||||
virtual bool isDataLocale(const QLocale &locale) const;
|
||||
QDateTimePrivate::ZoneState stateAtZoneTime(qint64 forLocalMSecs,
|
||||
QDateTimePrivate::TransitionOptions resolve) const;
|
||||
|
||||
|
|
@ -179,11 +181,15 @@ public:
|
|||
QUtcTimeZonePrivate *clone() const override;
|
||||
|
||||
Data data(qint64 forMSecsSinceEpoch) const override;
|
||||
Data data(QTimeZone::TimeType timeType) const override;
|
||||
bool isDataLocale(const QLocale &locale) const override;
|
||||
|
||||
QLocale::Territory territory() const override;
|
||||
QString comment() const override;
|
||||
|
||||
using QTimeZonePrivate::displayName;
|
||||
QString displayName(qint64 atMSecsSinceEpoch,
|
||||
QTimeZone::NameType nameType,
|
||||
const QLocale &locale) const override;
|
||||
QString displayName(QTimeZone::TimeType timeType,
|
||||
QTimeZone::NameType nameType,
|
||||
const QLocale &locale) const override;
|
||||
|
|
@ -241,6 +247,7 @@ public:
|
|||
bool hasDaylightTime() const override;
|
||||
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
|
||||
|
||||
using QTimeZonePrivate::data;
|
||||
Data data(qint64 forMSecsSinceEpoch) const override;
|
||||
|
||||
bool hasTransitions() const override;
|
||||
|
|
@ -321,6 +328,8 @@ public:
|
|||
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
|
||||
|
||||
Data data(qint64 forMSecsSinceEpoch) const override;
|
||||
Data data(QTimeZone::TimeType timeType) const override;
|
||||
bool isDataLocale(const QLocale &locale) const override;
|
||||
|
||||
bool hasTransitions() const override;
|
||||
Data nextTransition(qint64 afterMSecsSinceEpoch) const override;
|
||||
|
|
@ -378,6 +387,7 @@ public:
|
|||
bool hasDaylightTime() const override;
|
||||
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
|
||||
|
||||
using QTimeZonePrivate::data;
|
||||
Data data(qint64 forMSecsSinceEpoch) const override;
|
||||
|
||||
bool hasTransitions() const override;
|
||||
|
|
@ -432,6 +442,7 @@ public:
|
|||
bool hasDaylightTime() const override;
|
||||
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
|
||||
|
||||
using QTimeZonePrivate::data;
|
||||
Data data(qint64 forMSecsSinceEpoch) const override;
|
||||
|
||||
bool hasTransitions() const override;
|
||||
|
|
@ -481,6 +492,7 @@ public:
|
|||
bool hasDaylightTime() const override;
|
||||
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
|
||||
|
||||
using QTimeZonePrivate::data;
|
||||
Data data(qint64 forMSecsSinceEpoch) const override;
|
||||
|
||||
QByteArray systemTimeZoneId() const override;
|
||||
|
|
|
|||
|
|
@ -1045,59 +1045,20 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
|
|||
if (m_icu->isValid())
|
||||
return m_icu->displayName(timeType, nameType, locale);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(timeType);
|
||||
Q_UNUSED(nameType);
|
||||
Q_UNUSED(locale);
|
||||
#endif
|
||||
// If ICU is unavailable, fall back to abbreviations.
|
||||
// Abbreviations don't have GenericTime
|
||||
if (timeType == QTimeZone::GenericTime)
|
||||
timeType = QTimeZone::StandardTime;
|
||||
|
||||
// Get current tran, if valid and is what we want, then use it
|
||||
const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch();
|
||||
QTimeZonePrivate::Data tran = data(currentMSecs);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs()
|
||||
&& ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0)
|
||||
|| (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) {
|
||||
return tran.abbreviation;
|
||||
// TZ only provides C-locale abbreviations and offset:
|
||||
if (nameType != QTimeZone::LongName && isDataLocale(locale)) {
|
||||
QTimeZonePrivate::Data tran = data(timeType);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs()) {
|
||||
if (nameType == QTimeZone::ShortName)
|
||||
return tran.abbreviation;
|
||||
// Save base class repeating the data(timeType) query:
|
||||
if (locale.language() == QLocale::C)
|
||||
return isoOffsetFormat(tran.offsetFromUtc);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise get next tran and if valid and is what we want, then use it
|
||||
tran = nextTransition(currentMSecs);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs()
|
||||
&& ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0)
|
||||
|| (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) {
|
||||
return tran.abbreviation;
|
||||
}
|
||||
|
||||
// Otherwise get prev tran and if valid and is what we want, then use it
|
||||
tran = previousTransition(currentMSecs);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs())
|
||||
tran = previousTransition(tran.atMSecsSinceEpoch);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs()
|
||||
&& ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0)
|
||||
|| (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) {
|
||||
return tran.abbreviation;
|
||||
}
|
||||
|
||||
// Otherwise is strange sequence, so work backwards through trans looking for first match, if any
|
||||
auto it = std::partition_point(tranCache().cbegin(), tranCache().cend(),
|
||||
[currentMSecs](const QTzTransitionTime &at) {
|
||||
return at.atMSecsSinceEpoch <= currentMSecs;
|
||||
});
|
||||
|
||||
while (it != tranCache().cbegin()) {
|
||||
--it;
|
||||
tran = dataForTzTransition(*it);
|
||||
int offset = tran.daylightTimeOffset;
|
||||
if ((timeType == QTimeZone::DaylightTime) != (offset == 0))
|
||||
return tran.abbreviation;
|
||||
}
|
||||
|
||||
// Otherwise if no match use current data
|
||||
return data(currentMSecs).abbreviation;
|
||||
// Otherwise, fall back to base class:
|
||||
return QTimeZonePrivate::displayName(timeType, nameType, locale);
|
||||
}
|
||||
|
||||
QString QTzTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
|
||||
|
|
@ -1184,6 +1145,64 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
|
|||
return dataFromRule(cached_data.m_tranRules.at(last->ruleIndex), forMSecsSinceEpoch);
|
||||
}
|
||||
|
||||
// Overridden because the final iteration over transitions only needs to look
|
||||
// forward and backwards one transition within the POSIX rule (when there is
|
||||
// one, as is common) to settle the whole period it covers, so we can then skip
|
||||
// all other transitions of the POSIX rule and iterate tranCache() backwards
|
||||
// from its most recent transition.
|
||||
QTimeZonePrivate::Data QTzTimeZonePrivate::data(QTimeZone::TimeType timeType) const
|
||||
{
|
||||
// True if tran is valid and has the DST-ness to match timeType:
|
||||
const auto validMatch = [timeType](const QTimeZonePrivate::Data &tran) {
|
||||
return tran.atMSecsSinceEpoch != invalidMSecs()
|
||||
&& ((timeType == QTimeZone::DaylightTime) != (tran.daylightTimeOffset == 0));
|
||||
};
|
||||
|
||||
// Get current tran, use if suitable:
|
||||
const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch();
|
||||
QTimeZonePrivate::Data tran = data(currentMSecs);
|
||||
if (validMatch(tran))
|
||||
return tran;
|
||||
|
||||
// Otherwise, next tran probably flips DST-ness:
|
||||
tran = nextTransition(currentMSecs);
|
||||
if (validMatch(tran))
|
||||
return tran;
|
||||
|
||||
// Failing that, prev (or present, if current MSecs is eactly a transition
|
||||
// moment) tran defines what data() got us and the one before that probably
|
||||
// flips DST-ness:
|
||||
tran = previousTransition(currentMSecs + 1);
|
||||
if (tran.atMSecsSinceEpoch != invalidMSecs())
|
||||
tran = previousTransition(tran.atMSecsSinceEpoch);
|
||||
if (validMatch(tran))
|
||||
return tran;
|
||||
|
||||
// Otherwise, we can look backwards through transitions for a match; if we
|
||||
// have a POSIX rule, it clearly doesn't do DST (or we'd have hit it by
|
||||
// now), so we only need to look in the tranCache() up to now.
|
||||
const auto untilNow = [currentMSecs](const QTzTransitionTime &at) {
|
||||
return at.atMSecsSinceEpoch <= currentMSecs;
|
||||
};
|
||||
auto it = std::partition_point(tranCache().cbegin(), tranCache().cend(), untilNow);
|
||||
// That's the end or first future transition; we don't want to look at it,
|
||||
// but at all those before it.
|
||||
while (it != tranCache().cbegin()) {
|
||||
--it;
|
||||
tran = dataForTzTransition(*it);
|
||||
if ((timeType == QTimeZone::DaylightTime) != (tran.daylightTimeOffset == 0))
|
||||
return tran;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool QTzTimeZonePrivate::isDataLocale(const QLocale &locale) const
|
||||
{
|
||||
// TZ data uses English / C locale names:
|
||||
return locale.language() == QLocale::C || locale.language() == QLocale::English;
|
||||
}
|
||||
|
||||
bool QTzTimeZonePrivate::hasTransitions() const
|
||||
{
|
||||
return true;
|
||||
|
|
|
|||
Loading…
Reference in New Issue