QLocale - Fix Mac date format code translation

Mac uses the CLDR format codes which need to be translated into their
Qt equivalent.  The existing code mistranslates the year code, is
outdated for a number of new codes introduced in recent versions of
CLDR, and by default accepted any codes it didn't recognize.

This change updates support to the latest version of CLDR, fixes the
treatment of years, and defaults to ignoring any new format codes
added in the future.

Note that this change cannot have auto tests written as the system
locale formats change between versions of OSX.  Testing must be
done manually by changing system locale and formats.

Task-number: QTBUG-25057

Change-Id: I69dda25b4a0b38d3971995644546306876922d57
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
bb10
John Layt 2013-09-18 15:53:18 +01:00 committed by The Qt Project
parent 279db88c39
commit 77dc33dcdb
2 changed files with 83 additions and 39 deletions

View File

@ -166,6 +166,11 @@ static QString macTimeToString(const QTime &time, bool short_format)
return QCFString(CFDateFormatterCreateStringWithDate(0, myFormatter, myDate));
}
// Mac uses the Unicode CLDR format codes
// http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
// See also qtbase/util/local_database/dateconverter.py
// Makes the assumption that input formats are always well formed and consecutive letters
// never exceed the maximum for the format code.
static QString macToQtFormat(const QString &sys_fmt)
{
QString result;
@ -185,55 +190,97 @@ static QString macToQtFormat(const QString &sys_fmt)
int repeat = qt_repeatCount(sys_fmt, i);
switch (c.unicode()) {
case 'G': // Qt doesn't support these :(
case 'Y':
case 'D':
case 'F':
case 'w':
case 'W':
case 'g':
// Qt does not support the following options
case 'G': // Era (1..5): 4 = long, 1..3 = short, 5 = narrow
case 'Y': // Year of Week (1..n): 1..n = padded number
case 'U': // Cyclic Yar Name (1..5): 4 = long, 1..3 = short, 5 = narrow
case 'Q': // Quarter (1..4): 4 = long, 3 = short, 1..2 = padded number
case 'q': // Standalone Quarter (1..4): 4 = long, 3 = short, 1..2 = padded number
case 'w': // Week of Year (1..2): 1..2 = padded number
case 'W': // Week of Month (1): 1 = number
case 'D': // Day of Year (1..3): 1..3 = padded number
case 'F': // Day of Week in Month (1): 1 = number
case 'g': // Modified Julian Day (1..n): 1..n = padded number
case 'A': // Milliseconds in Day (1..n): 1..n = padded number
break;
case 'u': // extended year - use 'y'
if (repeat < 4)
case 'y': // Year (1..n): 2 = short year, 1 & 3..n = padded number
case 'u': // Extended Year (1..n): 2 = short year, 1 & 3..n = padded number
// Qt only supports long (4) or short (2) year, use long for all others
if (repeat == 2)
result += QLatin1String("yy");
else
result += QLatin1String("yyyy");
break;
case 'S': // fractional second
case 'M': // Month (1..5): 4 = long, 3 = short, 1..2 = number, 5 = narrow
case 'L': // Standalone Month (1..5): 4 = long, 3 = short, 1..2 = number, 5 = narrow
// Qt only supports long, short and number, use short for narrow
if (repeat == 5)
result += QLatin1String("MMM");
else
result += QString(repeat, QLatin1Char('M'));
break;
case 'd': // Day of Month (1..2): 1..2 padded number
result += QString(repeat, c);
break;
case 'E': // Day of Week (1..6): 4 = long, 1..3 = short, 5..6 = narrow
// Qt only supports long, short and padded number, use short for narrow
if (repeat == 4)
result += QLatin1String("dddd");
else
result += QLatin1String("ddd");
break;
case 'e': // Local Day of Week (1..6): 4 = long, 3 = short, 5..6 = narrow, 1..2 padded number
case 'c': // Standalone Local Day of Week (1..6): 4 = long, 3 = short, 5..6 = narrow, 1..2 padded number
// Qt only supports long, short and padded number, use short for narrow
if (repeat >= 5)
result += QLatin1String("ddd");
else
result += QString(repeat, QLatin1Char('d'));
break;
case 'a': // AM/PM (1): 1 = short
// Translate to Qt uppercase AM/PM
result += QLatin1String("AP");
break;
case 'h': // Hour [1..12] (1..2): 1..2 = padded number
case 'K': // Hour [0..11] (1..2): 1..2 = padded number
case 'j': // Local Hour [12 or 24] (1..2): 1..2 = padded number
// Qt h is local hour
result += QString(repeat, QLatin1Char('h'));
break;
case 'H': // Hour [0..23] (1..2): 1..2 = padded number
case 'k': // Hour [1..24] (1..2): 1..2 = padded number
// Qt H is 0..23 hour
result += QString(repeat, QLatin1Char('H'));
break;
case 'm': // Minutes (1..2): 1..2 = padded number
case 's': // Seconds (1..2): 1..2 = padded number
result += QString(repeat, c);
break;
case 'S': // Fractional second (1..n): 1..n = tuncates to decimal places
// Qt uses msecs either unpadded or padded to 3 places
if (repeat < 3)
result += QLatin1Char('z');
else
result += QLatin1String("zzz");
break;
case 'E':
if (repeat <= 3)
result += QLatin1String("ddd");
else
result += QLatin1String("dddd");
break;
case 'e':
if (repeat >= 2)
result += QLatin1String("dd");
else
result += QLatin1Char('d');
break;
case 'a':
result += QLatin1String("AP");
break;
case 'k':
result += QString(repeat, QLatin1Char('H'));
break;
case 'K':
result += QString(repeat, QLatin1Char('h'));
break;
case 'z':
case 'Z':
case 'v':
case 'z': // Time Zone (1..4)
case 'Z': // Time Zone (1..5)
case 'O': // Time Zone (1, 4)
case 'v': // Time Zone (1, 4)
case 'V': // Time Zone (1..4)
case 'X': // Time Zone (1..5)
case 'x': // Time Zone (1..5)
result += QLatin1Char('t');
break;
default:
result += QString(repeat, c);
// a..z and A..Z are reserved for format codes, so any occurrence of these not
// already processed are not known and so unsupported formats to be ignored.
// All other chars are allowed as literals.
if (c < QLatin1Char('A') || c > QLatin1Char('z') ||
(c > QLatin1Char('Z') && c < QLatin1Char('a'))) {
result += QString(repeat, c);
}
break;
}

View File

@ -1407,10 +1407,7 @@ void tst_QLocale::macDefaultLocale()
QCOMPARE(locale.decimalPoint(), QChar('.'));
QCOMPARE(locale.groupSeparator(), QChar(','));
QCOMPARE(locale.dateFormat(QLocale::ShortFormat), QString("M/d/yy"));
if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_6)
QCOMPARE(locale.dateFormat(QLocale::LongFormat), QString("MMMM d, y"));
else
QCOMPARE(locale.dateFormat(QLocale::LongFormat), QString("MMMM d, yyyy"));
QCOMPARE(locale.dateFormat(QLocale::LongFormat), QString("MMMM d, yyyy"));
QCOMPARE(locale.timeFormat(QLocale::ShortFormat), QString("h:mm AP"));
QCOMPARE(locale.timeFormat(QLocale::LongFormat), QString("h:mm:ss AP t"));