diff options
Diffstat (limited to 'src/corelib/text/qlocale_mac.mm')
-rw-r--r-- | src/corelib/text/qlocale_mac.mm | 208 |
1 files changed, 112 insertions, 96 deletions
diff --git a/src/corelib/text/qlocale_mac.mm b/src/corelib/text/qlocale_mac.mm index 9a25bee0de..89339be2eb 100644 --- a/src/corelib/text/qlocale_mac.mm +++ b/src/corelib/text/qlocale_mac.mm @@ -32,9 +32,11 @@ static void printLocalizationInformation() if (!lcLocale().isDebugEnabled()) return; +#if defined(Q_OS_MACOS) // Trigger initialization of standard user defaults, so that Foundation picks // up -AppleLanguages and -AppleLocale passed on the command line. Q_UNUSED(NSUserDefaults.standardUserDefaults); +#endif auto singleLineDescription = [](NSArray *array) { NSString *str = [array description]; @@ -236,12 +238,16 @@ static QString fourDigitYear(int year, const QString &zero) static QString macDateToStringImpl(QDate date, CFDateFormatterStyle style) { - QCFType<CFDateRef> myDate = date.startOfDay().toCFDate(); + // Use noon on the given date, to avoid complications that can arise for + // dates before 1900 (see QTBUG-54955) using different UTC offset than + // QDateTime extrapolates backwards from time_t functions that only work + // back to 1900. (Alaska and Phillipines may still be borked, though.) + QCFType<CFDateRef> myDate = QDateTime(date, QTime(12, 0)).toCFDate(); QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent(); QCFType<CFDateFormatterRef> myFormatter = CFDateFormatterCreate(kCFAllocatorDefault, mylocale, style, kCFDateFormatterNoStyle); - QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(0, myFormatter, myDate); + QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(nullptr, myFormatter, myDate); return QString::fromCFString(text); } @@ -249,12 +255,14 @@ static QVariant macDateToString(QDate date, bool short_format) { const int year = date.year(); QString fakeYear, trueYear; - if (year < 0) { + if (year < 1583) { // 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. + // Furthermore (see QTBUG-54955), Darwin uses the Julian calendar for + // dates before 1582-10-15, leading to discrepancies. int matcher = QGregorianCalendar::yearSharingWeekDays(date); - Q_ASSERT(matcher > 0); + Q_ASSERT(matcher >= 1583); 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 @@ -267,7 +275,7 @@ static QVariant macDateToString(QDate date, bool short_format) QString text = macDateToStringImpl(date, short_format ? kCFDateFormatterShortStyle : kCFDateFormatterLongStyle); - if (year < 0) { + if (year < 1583) { if (text.contains(fakeYear)) return std::move(text).replace(fakeYear, trueYear); // Cope with two-digit year: @@ -315,99 +323,107 @@ static QVariant macToQtFormat(QStringView sys_fmt) } QChar c = sys_fmt.at(i); - qsizetype repeat = qt_repeatCount(sys_fmt.mid(i)); + qsizetype repeat = qt_repeatCount(sys_fmt.sliced(i)); switch (c.unicode()) { // 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 Year 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 '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 += "yy"_L1; - else - result += "yyyy"_L1; - break; - 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 += "MMM"_L1; - else - result += QString(repeat, u'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 += "dddd"_L1; - else - result += "ddd"_L1; - 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 += "ddd"_L1; - else - result += QString(repeat, 'd'_L1); - break; - case 'a': // AM/PM (1): 1 = short - // Translate to Qt uppercase AM/PM - result += "AP"_L1; - 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, 'h'_L1); - 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, 'H'_L1); - break; - case 'm': // Minutes (1..2): 1..2 = padded number - case 's': // Seconds (1..2): 1..2 = padded number + case 'A': // Milliseconds in Day (1..n): 1..n = padded number + case 'C': // Input skeleton symbol. + 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 'G': // Era (1..5): 4 = long, 1..3 = short, 5 = narrow + case 'j': // Input skeleton symbol. + case 'J': // Input skeleton symbol. + case 'l': // Deprecated Chinese leap month indicator. + case 'q': // Standalone Quarter (1..4): 4 = long, 3 = short, 1,2 = padded number + case 'Q': // Quarter (1..4): 4 = long, 3 = short, 1,2 = padded number + case 'U': // Cyclic Year Name (1..5): 4 = long, 1..3 = short, 5 = narrow + case 'w': // Week of Year (1,2): 1,2 = padded number + case 'W': // Week of Month (1): 1 = number + case 'Y': // Year for Week-of-year calendars (1..n): 1..n = padded number + break; + + case 'u': // Extended Year (1..n), padded number. + // Explicitly has no special case for 'uu' as only the last two digits. + result += "yyyy"_L1; + break; + case 'y': // 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 += "yy"_L1; + else + result += "yyyy"_L1; + break; + case 'L': // Standalone Month (1..5): 4 = long, 3 = short, 1,2 = number, 5 = narrow + case 'M': // 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 += "MMM"_L1; + else + result += QString(repeat, u'M'); + break; + case 'd': // Day of Month (1,2): 1,2 padded number + result += QString(repeat, c); + break; + case 'c': // Standalone version of 'e' + case 'e': // Local Day of Week (1..6): 4 = long, 3 = short, 5,6 = narrow, 1,2 padded number + // "Local" only affects numeric form: depends on locale's start-day of the week. + case 'E': // Day of Week (1..6): 4 = long, 1..3 = short, 5,6 = narrow + // Qt only supports long, short: use short for narrow and padded number. + if (repeat == 4) + result += "dddd"_L1; + else + result += "ddd"_L1; + break; + case 'a': // AM/PM (1..n): Qt supports no distinctions + case 'b': // Like a, but also distinguishing noon, midnight (ignore difference). + case 'B': // Flexible day period (at night, &c.) + // Translate to Qt AM/PM, using locale-appropriate case: + result += "Ap"_L1; + break; + case 'h': // Hour [1..12] (1,2): 1,2 = padded number + case 'K': // Hour [0..11] (1,2): 1,2 = padded number + result += QString(repeat, 'h'_L1); + 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, 'H'_L1); + 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 = truncates to decimal places + // Qt uses msecs either unpadded or padded to 3 places + if (repeat < 3) + result += u'z'; + else + result += "zzz"_L1; + break; + case 'O': // Time Zone (1, 4) + result += u't'; + break; + case 'v': // Time Zone (1, 4) + case 'V': // Time Zone (1..4) + result += "tttt"_L1; + break; + case 'x': // Time Zone (1..5) + case 'X': // Time Zone (1..5) + result += (repeat > 1 && (repeat & 1)) ? "ttt"_L1 : "tt"_L1; + break; + case 'z': // Time Zone (1..4) + case 'Z': // Time Zone (1..5) + result += repeat < 4 ? "tt"_L1 : repeat > 4 ? "ttt"_L1 : "t"_L1; + break; + default: + // 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 < u'A' || c > u'z' || (c > u'Z' && c < u'a')) result += QString(repeat, c); - break; - case 'S': // Fractional second (1..n): 1..n = truncates to decimal places - // Qt uses msecs either unpadded or padded to 3 places - if (repeat < 3) - result += u'z'; - else - result += "zzz"_L1; - break; - 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 += u't'; - break; - default: - // 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 < u'A' || c > u'z' || (c > u'Z' && c < u'a')) - result += QString(repeat, c); - break; + break; } i += repeat; @@ -565,7 +581,7 @@ static QLocale::Language codeToLanguage(QStringView s) return QLocalePrivate::codeToLanguage(s); } -QVariant QSystemLocale::query(QueryType type, QVariant in) const +QVariant QSystemLocale::query(QueryType type, QVariant &&in) const { QMacAutoReleasePool pool; @@ -633,7 +649,7 @@ QVariant QSystemLocale::query(QueryType type, QVariant in) const case CurrencySymbol: return macCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt())); case CurrencyToString: - return macFormatCurrency(in.value<QSystemLocale::CurrencyToStringArgument>()); + return macFormatCurrency(in.value<CurrencyToStringArgument>()); case UILanguages: { QStringList result; QCFType<CFArrayRef> languages = CFLocaleCopyPreferredLanguages(); |