Fix macOS system locale's formatting of negative years
It leaves off the minus sign. Change-Id: Iad72349368d8849330524144033453cbd79e9e7c Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
8926eb86c6
commit
f75d36fd18
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
@ -43,6 +43,8 @@
|
||||
#include "qvariant.h"
|
||||
#include "qdatetime.h"
|
||||
|
||||
#include "private/qstringiterator_p.h"
|
||||
#include "private/qgregoriancalendar_p.h"
|
||||
#ifdef Q_OS_DARWIN
|
||||
#include "private/qcore_mac_p.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
@ -148,19 +150,123 @@ static QVariant macDayName(int day, QSystemLocale::QueryType type)
|
||||
return {};
|
||||
}
|
||||
|
||||
static QVariant macDateToString(QDate date, bool short_format)
|
||||
static QString macZeroDigit()
|
||||
{
|
||||
QCFType<CFDateRef> myDate = QDateTime(date, QTime()).toCFDate();
|
||||
QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
|
||||
QCFType<CFNumberFormatterRef> numberFormatter =
|
||||
CFNumberFormatterCreate(nullptr, locale, kCFNumberFormatterNoStyle);
|
||||
const int zeroDigit = 0;
|
||||
QCFType<CFStringRef> value
|
||||
= CFNumberFormatterCreateStringWithValue(nullptr, numberFormatter,
|
||||
kCFNumberIntType, &zeroDigit);
|
||||
return QString::fromCFString(value);
|
||||
}
|
||||
|
||||
static QString zeroPad(QString &&number, int minDigits, const QString &zero)
|
||||
{
|
||||
// Need to pad with zeros, possibly after a sign.
|
||||
int insert = -1, digits = 0;
|
||||
auto it = QStringIterator(number);
|
||||
while (it.hasNext()) {
|
||||
int here = it.index();
|
||||
if (QChar::isDigit(it.next())) {
|
||||
if (insert < 0)
|
||||
insert = here;
|
||||
++digits;
|
||||
} // else: assume we're stepping over a sign (or maybe grouping separator)
|
||||
}
|
||||
Q_ASSERT(digits > 0);
|
||||
Q_ASSERT(insert >= 0);
|
||||
while (digits++ < minDigits)
|
||||
number.insert(insert, zero);
|
||||
|
||||
return std::move(number);
|
||||
}
|
||||
|
||||
static QString trimTwoDigits(QString &&number)
|
||||
{
|
||||
// Retain any sign, but remove all but the last two digits.
|
||||
// We know number has at least four digits - it came from fourDigitYear().
|
||||
// Note that each digit might be a surrogate pair.
|
||||
int first = -1, prev = -1, last = -1;
|
||||
auto it = QStringIterator(number);
|
||||
while (it.hasNext()) {
|
||||
int here = it.index();
|
||||
if (QChar::isDigit(it.next())) {
|
||||
if (first == -1)
|
||||
last = first = here;
|
||||
else if (last != -1)
|
||||
prev = std::exchange(last, here);
|
||||
}
|
||||
}
|
||||
Q_ASSERT(first >= 0);
|
||||
Q_ASSERT(prev > first);
|
||||
Q_ASSERT(last > prev);
|
||||
number.remove(first, prev - first);
|
||||
return std::move(number);
|
||||
}
|
||||
|
||||
static QString fourDigitYear(int year, const QString &zero)
|
||||
{
|
||||
// Return year formatted as an (at least) four digit number:
|
||||
QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
|
||||
QCFType<CFNumberFormatterRef> numberFormatter =
|
||||
CFNumberFormatterCreate(nullptr, locale, kCFNumberFormatterNoStyle);
|
||||
QCFType<CFStringRef> value = CFNumberFormatterCreateStringWithValue(nullptr, numberFormatter,
|
||||
kCFNumberIntType, &year);
|
||||
auto text = QString::fromCFString(value);
|
||||
if (year > -1000 && year < 1000)
|
||||
text = zeroPad(std::move(text), 4, zero);
|
||||
return text;
|
||||
}
|
||||
|
||||
static QString macDateToStringImpl(QDate date, CFDateFormatterStyle style)
|
||||
{
|
||||
QCFType<CFDateRef> myDate = date.startOfDay().toCFDate();
|
||||
QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent();
|
||||
CFDateFormatterStyle style = short_format ? kCFDateFormatterShortStyle : kCFDateFormatterLongStyle;
|
||||
QCFType<CFDateFormatterRef> myFormatter
|
||||
= CFDateFormatterCreate(kCFAllocatorDefault,
|
||||
mylocale, style,
|
||||
= CFDateFormatterCreate(kCFAllocatorDefault, mylocale, style,
|
||||
kCFDateFormatterNoStyle);
|
||||
QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(0, myFormatter, myDate);
|
||||
return QString::fromCFString(text);
|
||||
}
|
||||
|
||||
static QVariant macDateToString(QDate date, bool short_format)
|
||||
{
|
||||
const int year = date.year();
|
||||
QString fakeYear, trueYear;
|
||||
if (year < 0) {
|
||||
// System API (in macOS 11.0, at least) discards sign :-(
|
||||
// Simply negating the year won't do as the resulting year typically has
|
||||
// a different pattern of week-days.
|
||||
int matcher = QGregorianCalendar::yearSharingWeekDays(date);
|
||||
Q_ASSERT(matcher > 0);
|
||||
Q_ASSERT(matcher % 100 != date.month());
|
||||
Q_ASSERT(matcher % 100 != date.day());
|
||||
// i.e. there can't be any confusion between the two-digit year and
|
||||
// month or day-of-month in the formatted date.
|
||||
QString zero = macZeroDigit();
|
||||
fakeYear = fourDigitYear(matcher, zero);
|
||||
trueYear = fourDigitYear(year, zero);
|
||||
date = QDate(matcher, date.month(), date.day());
|
||||
}
|
||||
QString text = macDateToStringImpl(date, short_format
|
||||
? kCFDateFormatterShortStyle
|
||||
: kCFDateFormatterLongStyle);
|
||||
if (year < 0) {
|
||||
if (text.contains(fakeYear))
|
||||
return std::move(text).replace(fakeYear, trueYear);
|
||||
// Cope with two-digit year:
|
||||
fakeYear = trimTwoDigits(std::move(fakeYear));
|
||||
trueYear = trimTwoDigits(std::move(trueYear));
|
||||
if (text.contains(fakeYear))
|
||||
return std::move(text).replace(fakeYear, trueYear);
|
||||
// That should have worked.
|
||||
qWarning("Failed to fix up year when formatting a date in year %d", year);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
static QVariant macTimeToString(QTime time, bool short_format)
|
||||
{
|
||||
QCFType<CFDateRef> myDate = QDateTime(QDate::currentDate(), time).toCFDate();
|
||||
@ -363,17 +469,6 @@ static QVariant macCurrencySymbol(QLocale::CurrencySymbolFormat format)
|
||||
return {};
|
||||
}
|
||||
|
||||
static QVariant macZeroDigit()
|
||||
{
|
||||
QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
|
||||
QCFType<CFNumberFormatterRef> numberFormatter =
|
||||
CFNumberFormatterCreate(nullptr, locale, kCFNumberFormatterNoStyle);
|
||||
static const int zeroDigit = 0;
|
||||
QCFType<CFStringRef> value = CFNumberFormatterCreateStringWithValue(nullptr, numberFormatter,
|
||||
kCFNumberIntType, &zeroDigit);
|
||||
return QString::fromCFString(value);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SYSTEMLOCALE
|
||||
static QVariant macFormatCurrency(const QSystemLocale::CurrencyToStringArgument &arg)
|
||||
{
|
||||
|
@ -2650,9 +2650,6 @@ void tst_QLocale::dateFormat()
|
||||
// And, indeed, one for a negative year:
|
||||
old = sys.toString(QDate(-1173, 5, 1), QLocale::LongFormat);
|
||||
QVERIFY(!old.isEmpty());
|
||||
#ifdef Q_OS_DARWIN // bug in qlocale_mac.mm, fix coming shortly
|
||||
QEXPECT_FAIL("", "Darwin converts year to positive", Continue);
|
||||
#endif
|
||||
QVERIFY2(old.contains(u"-1173"), qPrintable(old + QLatin1String(" for locale ") + sys.name()));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user