Qt 5 ICU

From Qt Wiki
(Redirected from Qt-5-ICU)
Jump to: navigation, search


THIS PLAN HAS NOW BEEN ABANDONED.

Please see http://wiki.qt.io/Qt-5-QLocale for the replacement planning. This info will slowly be migrated to the new page.

The decision to no longer use ICU was made as a result of discussions at QtCS and on the development list:

Summary

It has been decided to adopt ICU as the localization backend for Qt. This will bring many advantages:

  • Advanced features like collation for minimal effort
  • Lessened maintenance burden
  • Smaller library size as locale data no longer encoded in static arrays
  • Embedded platforms able to ship minimal set of locale files
  • Consistency with QtWebKit and JavaScript localization
  • Standard locale library across many platforms minimises porting requirements

ICU ships standard on all major platforms except Windows, and QtWebKit already requires ICU.

While QLocale has many similarities to ICU, the API for items like date/time formatting is different, and is not easily adapted for features like calendar systems. As such new API is required with backwards compatible support for the Qt4 style API. The intent is to change both the API and backend code to match the structure of ICU.

This change will include the addition of proper Time Zone support, see Qt-5-QTimeZone for more details.

To do: Investigate QML!

These notes are work in progress on how to use libicu as the localization backend in Qt 5.1. Please contact John Layt (jlayt@kde.org) for more information.

Proposed Solution

Following discussion at QtCS 2012, the following design is proposed. It is a balance between the desire of the Chief Maintainer to keep a single simple QLocale class, the practicalities of matching up with the ICU object model and complexities, and minimizing API clashes with the existing API.

Design Principles

These are the main design principles, for more details on the reasoning see the specific sections below.

  • ICU will by default be used for all platforms and will always be used when creating custom locales, i.e. not the system default locale.
  • If ICU support is not compiled then custom locales are not available.
  • A minimal non-ICU C locale formatter will be provided based on the existing utility routines for when QT_NO_QLOCALE or QT_NO_ICU are defined.
  • ICU does not use any user default locale override settings, so platform default locale support must be provided for consistency, overriding the ICU functions where appropriate.
  • No API to be provided in the default/system locale class to set locale settings to make default locale thread-safe and to prevent apps trying to override the system locale settings.
  • QLocale api to only provide basic default formatting api. Advanced formatting can either use ICU directly, or later the specialised Q classes.
  • Specialised Q formatter classes will be created to reflect the ICU and macOS object models and 'pure' api. This is primarily to keep the code easy to manage, allow for simpler implementation steps and targeted unit tests. Initially these classes will be private for internal use only, but will later be made public for advanced formatting options. QLocalePrivate will use these classes internally with the existing QLocale api mapping to them.
  • TBD: All new classes to be created via factory methods to ensure correct system backend is created? Default constructors will either create null objects or default locale objects?

Note that Lars has expressed a preference for a single flat QLocale class embedding all the functionality. Separate classes allows for cleaner code and test cycles during development, with the option later to collapse the code for performance reasons.

High Level Class Model

  • QNumberFormatter – A class that just does number formatting, equal to the ICU NumberFormat class and the Apple NSNumberFormatter class. Has a Private class that implements the various system backends, with a factory method to create the appropriate one. One backend uses Qt's existing number format code but just for C locale, which could be used on platforms that don’t want QLocale or ICU but still want QString to serialise numbers. Another backend will implement the ICU support, i.e. a thin wrapper around the NumberFormat class. The Mac, Windows and Linux platform backends would derive from the ICU backend, overriding the ICU calls to use the platform services where available, or falling back to ICU where the platform doesn’t provide functionality. QString and QByteArray which currently create a C QLocale could directly create one of these instead.
  • QTimeZone - Public container/calculator class solely to apply time calculations in terms of a time zone. Does not do formatting or parsing. Normally embedded in a QCalendarSystem and pointing to the ICU TimeZone object in use by that QCalendarSystem, but may be instantiated standalone via a factory method. Platform backend required.
  • QCalendarSystem - Public calculator class solely to apply date/time calculations in terms of a calendar system and time zone. Does not do formatting or parsing. Normally embedded in a QDateTimeFormatter and pointing to the ICU Calendar object in use by that QDateTimeFormatter, but may be instantiated standalone via a factory method. Platform backend required.
  • QDateTimeFormatter – A class that just does date/time formatting, same design as QNumberFormatter with C, ICU and platform backends required. Uses a QNumberFormatter and QCalendarSystem internally.
  • QLocale - Existing class to embed all localization functionality. All parsing and formatting methods are accessed via QLocale. In ICU terms it will wrap the Locale, NumberFormat and DateFormat objects in a single convenient class with a simple API for the most common use cases. Internally it will embed the relevant QNumberFormatter, QDateTimeFormatter, QCalendarSystem and QTimeZone classes. The existing QLocale api will convert calls to the new formatter classes API. TBD: May have custom number and date/time formatting defaults set, but this will cause the instance to detach from the original, i.e. if set on a copy of the system QLocale then will become a detached custom locale?
  • QString / QByteArray - Currently directly access the private member of a QLocale C instance to perform string number formatting. Convert to use a QNumberFormatter directly for efficency, will usually be ICU backend, but may be the internal C backend if not using ICU or QLocale.
  • QDateTime - Major rewrite to use time zone and calendar functionality with an msec counter instead of embedded QDate/QTime containers.

Implementation Plan

Initial implementation will be in parallel to existing locale code with just sufficient features to replace existing functionality using ICU only. QLocale and other classes will then be switched to use the new backends. The old backend will then be removed. Specific platform support will then be added. Advanced ICU functionality can then be added in future releases.

  • Implement QNumberFormatter for ICU, write full unit tests.
  • Implement basic QDateTimeFormatter for ICU excluding calendar and time zone support, write full unit tests.
  • Implement QCalendarSystem for ICU without time zone support, write full unit tests
  • Implement QTimeZone for ICU, write full unit tests
  • Integrate QTimeZone into QCalendarSystem, write full unit tests
  • Integrate QCalendarSystem into QDateTimeFormatter, write full unit tests
  • Add new ICU based classes to QLocalePrivate, created in parallel to existing QLocaleData backend
  • Convert existing QLocale number api to ICU classes, confirm existing QLocale unit tests pass
  • Convert existing QLocale date/time api to ICU classes, confirm existing QLocale unit tests pass
  • Add new QLocale date/time api, write new QLocale unit tests.
  • Implement non-ICU QNumberFormatter for C locale
  • Convert QString / QByteArray to use QNumberFormatter
  • Convert QDateTime
  • Remove old QLocaleData code
  • Implement platform backends

QNumberFormatter Design

  • Initially an internal class, make the API public after it has stabilised. Public API will allow advanced formatting options.
  • NumberFormat is defined at creation and cannot be changed in API calls, i.e. instance is either a Decimal, Currency or Scientific.
  • Can only be created via factory method to ensure correct derived class used?
  • Do we provide a public default constructor, and if so does it return the the default locale or C locale (either ICU or internal methods?)
  • QNumberFormatter API points directly to QNumberFormatterPrivate backend API, no other code in the main class.
  • QNumberFormatterPrivate implements backends: possibly base class internal routines, derived ICU class, derived Win, Mac classes (Linux always uses ICU so no backend needed except to check envar override?).
  • Internal Qt classes could directly use the Private class public methods?
  • ICU backend class to support full api, but only expose advanced options and setting of options in QCustomNumberFormatter class.
  • Any calls that override the defaults require a temp copy of the ICU class to set the overrides on, so will be slower as a result.
// Add new enums in Qlocale as may be used in QLocale api for both number and date/time. Alternative put in Qt namespace.
// Initially may be internal in QNumberFormatter if not required in public QLocale api.

class QNumberFormatterPrivate;

// Internal for now, make public later
// Implement full ICU support, parts may not be made public later

class Q_CORE_EXPORT QNumberFormatter
{
public:
 // TODO Move these enums to QLocale?

// Pre-defined number formats
 // ICU UNumberFormatStyle
 enum NumberFormat {
 DecimalFormat = 1, // 10.00
 ScientificFormat = 2, // 1E2
#ifdef QT_USE_ICU
 PercentageFormat = 3, // 10%
 OrdinalFormat = 4, // 10th
 SpelloutFormat = 5, // ten
 DurationFormat = 6, // 00:00:10
 CurrencyFormat = 7, // $10.00
 DecimalPatternFormat = 8, // User defined pattern and symbols
 RuleBasedFormat = 9, // User defined rules
 // ICU 4.8
 CurrencyCodeFormat = 10, // USD10.00
 CurrencyNameFormat = 11, // 10 US dollars
 LastNumberFormat = CurrencyNameFormat
#else
 LastNumberFormat = ScientificFormat
#endif // QT_USE_ICU
 };

// String Symbols and attributes affecting how a number is formatted
 // Selected ICU UNumberFormatSymbol and UNumberTextAttribute
 enum NumberSymbol {
 DecimalSeparatorSymbol = 1,
 GroupingSeparatorSymbol,
 PercentSymbol,
 MinusSignSymbol,
 PlusSignSymbol,
 ExponentialSymbol,
 PerMillSymbol,
 InfinitySymbol,
 NaNSymbol,
 CurrencySymbol,
 CurrencyInternationalSymbol,
 MonetarySeparatorSymbol,
 MonetaryGroupingSeparatorSymbol,
 PatternSeparatorSymbol,
 PatternDigitSymbol,
 PatternSignificantDigitSymbol,
 PatternPadEscapeSymbol,
 PatternFormatSymbolCount,
 // ICU 4.6
 ZeroDigitSymbol,
 OneDigitSymbol,
 TwoDigitSymbol,
 ThreeDigitSymbol,
 FourDigitSymbol,
 FiveDigitSymbol,
 SixDigitSymbol,
 SevenDigitSymbol,
 EightDigitSymbol,
 NineDigitSymbol
 };

// Integer Attibutes affecting how a number is formatted
 // String, bool, enum, and double attributes are handled separately
 // ICU UNumberAttribute
 enum NumberAttribute {
 MaximumIntegerDigits = 1,
 MinimumIntegerDigits,
 MaximumFractionDigits,
 MinimumFractionDigits,
 MaximumSignificantDigits,
 MinimumSignificantDigits,
 Multiplier,
 PrimaryGroupingSize,
 SecondaryGroupingSize
 };

enum RoundingMode {
 DefaultRoundingMode = 0,
 RoundCeiling,
 RoundFloor,
 RoundDown,
 RoundUp,
 RoundHalfEven,
 RoundHalfDown,
 RoundHalfUp,
 RoundUnnecessary
 };

// ICU
 enum PadPosition {
 DefaultPadPosition = 0,
 PadBeforePrefix,
 PadAfterPrefix,
 PadBeforeSuffix,
 PadAfterSuffix
 };

enum ParsingMode {
 DefaultParsingMode = 0,
 LenientParsing,
 StrictParsing
 };

// Create named locale
 QNumberFormatter(const QString &icuLocale, NumberFormat format = DecimalFormat);
 ~QNumberFormatter();

QNumberFormatter(const QNumberFormatter &other);
 QNumberFormatter &operator=(const QNumberFormatter &other);

bool isValid() const;
 bool isInvalid() const;

QString localeCode(QLocale::LocaleCodeType type = QLocale::RequestedLocaleCode) const;
 NumberFormat numberFormat() const;
 QString currencyCode() const;

QString numberSymbol(NumberSymbol symbol) const;
 qint32 numberAttribute(NumberAttribute attribute) const;
 bool usesSignificantDigits() const;

// Number Rounding settings
 RoundingMode roundingMode() const;
 double roundingIncrement() const;

// Number Padding settings
 qint32 paddingWidth() const;
 QString padding() const;
 PadPosition paddingPosition() const;

// Uses locale default settings
 QString toString(qint32 from) const;
 QString toString(qint64 from) const;
 QString toString(double from) const;

// Override padding only
 QString toString(qint32 from, int width, const QString &padding = QString(),
 PadPosition position = DefaultPadPosition) const;
 QString toString(qint64 from, int width, const QString &padding = QString(),
 PadPosition position = DefaultPadPosition) const;
 QString toString(double from, int width, const QString &padding = QString(),
 PadPosition position = DefaultPadPosition) const;

// Override Int/Frac digits and optionally rounding mode
 QString toString(double from,
 int maxIntDigits, int minIntDigits, int maxFracDigits, int MinFracDigits,
 RoundingMode mode = DefaultRoundingMode,
 double roundIncrement = 1.0) const;

// Override significant digits and optionally rounding mode
 QString toString(double from,
 int maxSigDigits, int minSigDigits,
 RoundingMode mode = DefaultRoundingMode,
 double roundIncrement = 1.0) const;

// Override all Int/Frac options
 QString toString(double from,
 int maxIntDigits, int minIntDigits, int maxFracDigits, int MinFracDigits,
 int width, const QString &padding = QString(),
 PadPosition position = DefaultPadPosition,
 RoundingMode mode = DefaultRoundingMode, double roundIncrement = 1.0) const;

// Override all significant digits options
 QString toString(double from,
 int maxSigDigits, int minSigDigits,
 int width, const QString &padding = QString(),
 PadPosition position = DefaultPadPosition,
 RoundingMode mode = DefaultRoundingMode,
 double roundIncrement = 1.0) const;

// Uses locale default settings
 qint32 toInt32(const QString &from, bool *ok = 0, ParsingMode mode = DefaultParsingMode) const;
 qint64 toInt64(const QString &from, bool *ok = 0, ParsingMode mode = DefaultParsingMode) const;
 double toDouble(const QString &from, bool *ok = 0, ParsingMode mode = DefaultParsingMode) const;

// Format/Parse any currency using this locale settings, only if NumberFormat is Currency type
 // Format will use the correct number of decimal places for currency code given
 QString toAnyCurrency(double from, const QString &currencyCode,
 int width = 1, const QString &padding = QString(),
 PadPosition position = DefaultPadPosition,
 RoundingMode mode = DefaultRoundingMode, double roundIncrement = 1.0) const;
 double fromAnyCurrency(const QString &from, QString *currencyCode, bool *ok = 0,
 ParsingMode mode = DefaultParsingMode) const;

static QNumberFormatter default(NumberFormat format = DecimalFormat);
 static QNumberFormatter system(NumberFormat format = DecimalFormat);

private:
 // Create default locale
 QNumberFormatter(NumberFormat format = DecimalFormat);
 // Create locale with backend
 QNumberFormatter(QNumberFormatterPrivate &dd);
 friend class QNumberFormatterPrivate;
 friend class QCustomNumberFormatter;
 QSharedDataPointer<QNumberFormatterPrivate> d;
};

#ifdef QT_USE_ICU
class Q_CORE_EXPORT QCustomNumberFormatter : public QNumberFormatter
{
public:
 // Create default locale
 QCustomNumberFormatter(QNumberFormatter::NumberFormat format,
 const QString &patternOrRules = QString());
 // Create named locale
 QCustomNumberFormatter(const QString &icuLocale, QNumberFormatter::NumberFormat format,
 const QString &patternOrRules = QString());
 ~QCustomNumberFormatter();

QCustomNumberFormatter(const QCustomNumberFormatter &other);
 QCustomNumberFormatter &operator=(const QCustomNumberFormatter &other);

void setCurrencyCode(const QString & currencyCode);

QString pattern() const;
 void setPattern(const QString & pattern);

void setNumberSymbol(QNumberFormatter::NumberSymbol symbol, const QString &value);
 void setNumberAttribute(QNumberFormatter::NumberAttribute attribute, qint32 value);
 void setUsesSignificantDigits(bool value);

void setRounding(QNumberFormatter::RoundingMode mode, double increment = 1.0);
 void setPadding(qint32 width, const QString &padding,
 QNumberFormatter::PadPosition position = DefaultPadPosition);
};
#endif // QT_USE_ICU

QDateTimeFormatter Design

// New enums required
QLocale {
 // CLDR format length attribute for date/time/number/currency
 enum FormatPattern {
 DefaultPattern,
 FullPattern,
 LongPattern,
 MediumPattern,
 ShortPattern
 };

enum DisplayFormat {
 DisplayPattern,
 DisplayName
 };

// CLDR field width attribute
 enum FieldFormat { // Better name?
 DefaultFormat, // Better name?
 LongName, // e.g. January
 ShortName, // e.g. Jan
 NarrowName, // e.g. J
 LongNumber, // e.g. 01
 ShortNumber // e.g. 1
 };

// CLDR context attribute
 enum FieldContext {
 DefaultContext,
 FormatContext, // Use in a format
 StandaloneContext // Use standalone
 };

// Have QCalendarSystem API to return yearType() ?
 enum YearType {
 StandardYear,
 LeapYear
 }

 enum Calendar {
 DefaultCalendar = -1,
 GregorianCalendar = 0,
 ,
 LastCalendar = xxx
 };
}

QDateTimeFormatter {

 QString datePattern(QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QString timePattern(QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QString dateTimePattern(QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;

 QString toString(const QDate &date, QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QString toString(const QDate &date, const QString &pattern) const;

 QString toString(const QTime &time, QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QString toString(const QTime &time, const QString &pattern) const;

 QString toString(const QDateTime &dateTime, QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QString toString(const QDateTime &dateTime, const QString &pattern) const;

 QDate toDate(const QString &string, QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QDate toDate(const QString &string, const QString &pattern) const;

 QTime toTime(const QString &string, QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QTime toTime(const QString &string, const QString &format) const;

 QDateTime toDateTime(const QString &string, QLocale::FormatPattern pattern = QLocale::DefaultPattern) const;
 QDateTime toDateTime(const QString &string, const QString &pattern) const;

 QString monthName(int month,
 QLocale::FieldFormat format = QLocale::DefaultFormat,
 QLocale::FieldContext context = QLocale::DefaultContext) const;

 QString dayName(int day,
 QLocale::FieldFormat format = QLocale::DefaultFormat,
 QLocale::FieldContext context = QLocale::DefaultContext) const;

 QString dayPeriodName(const QTime &time,
 QLocale::FieldFormat format = QLocale::DefaultFormat,
 QLocale::FieldContext context = QLocale::DefaultContext) const;
}

QCalendarSystem Design

Todo: Needs QTime and QTimeZone to be integrated.

class Q_CORE_EXPORT QCalendarSystem
{
public:
 explicit QCalendarSystem();
 ~QCalendarSystem();

 static QCalendarSystem createCalendar(QLocale::Calendar calendar, const QTimeZone &timeZone = QTimeZone());

 QLocale::Calendar calendarType() const;
 QString calendarCode() const; // ICU Code
 static QString calendarCode(QLocale::Calendar calendar) const;

 QDate epoch() const;
 QDate earliestValidDate() const;
 QDate latestValidDate() const;

 int maximumValue(Qt::DateTimeComponent component) const;
 int minimumValue(Qt::DateTimeComponent component) const;

 bool isValid(const QDate &date) const;
 bool isValid(int year, int month, int day) const;
 bool isValid(int year, int dayOfYear) const;

 QDate date(int year, int month, int day) const;
 QDate date(int year, int dayOfYear) const;

 void getDate(const QDate &date, int *year, int *month, int *day) const;

 int year(const QDate &date) const;
 int month(const QDate &date) const;
 int day(const QDate &date) const;

 int quarter(const QDate &date) const;
 int quarter(int year, int month, int day) const;

 int dayOfYear(const QDate &date) const;
 int dayOfYear(int year, int month, int day) const;

 int dayOfWeek(const QDate &date) const;
 int dayOfWeek(int year, int month, int day) const;

 int weekNumber(const QDate &date, int *yearNum = 0,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;
 int weekNumber(int year, int month, int day, int *yearNum = 0,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;

 int monthsInYear(const QDate &date) const;
 int monthsInYear(int year) const;

 int weeksInYear(const QDate &date,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;
 int weeksInYear(int year,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;

 int daysInYear(const QDate &date) const;
 int daysInYear(int year) const;

 int daysInMonth(const QDate &date) const;
 int daysInMonth(int year, int month) const;

 int daysInWeek() const;

 bool isLeapYear(const QDate &date) const;
 bool isLeapYear(int year) const;

 QDate addYears(const QDate &date, int years) const;
 QDate addMonths(const QDate &date, int months) const;
 QDate addDays(const QDate &date, int days) const;

 int yearsDifference(const QDate &fromDate, const QDate &toDate) const;
 int monthsDifference(const QDate &fromDate, const QDate &toDate) const;
 qint64 daysDifference(const QDate &fromDate, const QDate &toDate) const;

 void dateDifference(const QDate &fromDate, const QDate &toDate,
 int *years, int *months, int *days, int '''direction) const;

 QDate firstDayOfYear(const QDate &date) const;
 QDate firstDayOfYear(int year) const;
 QDate lastDayOfYear(const QDate &date) const;
 QDate lastDayOfYear(int year) const;

 QDate firstDayOfMonth(const QDate &date) const;
 QDate firstDayOfMonth(int year, int month) const;
 QDate lastDayOfMonth(const QDate &date) const;
 QDate lastDayOfMonth(int year, int month) const;

private:
 QCalendarSystemPrivate''' const d;
};

QTimeZone Design

See Qt-5-QTimeZone.

Qt {
 enum TimeSpec {
 LocalTime,
 UTC,
 OffsetFromUTC,
 TimeZone, // NEW A time in a specified TimeZone
 ClockTime // NEW The time on the clock, i.e. with no zone
 };
}

class Q_CORE_EXPORT QTimeZone
{
public:
 explicit QTimeZone();
 QTimeZone(const QTimeZone &other);
 ~QTimeZone();

 QTimeZone &operator=(const QTimeZone &other);

 bool operator==(const QTimeZone &other) const; // Exact match, including ID
 bool operator!=(const QTimeZone &other) const;
 bool isEquivalentTo(const QTimeZone &zone) const; // Offsets and Transitions match, ID optional (i.e. same Rules)

 bool isValid() const;
 bool isInvalid() const;
 bool isDefault() const;
 bool isSystem() const;

 QString id() const;
 QString name(/* style and lang and standard/dst */) const;
 QLocale::Country region() const;
 QString regionCode() const; // ISO code
 QString comment() const;

 bool hasLocation() const;
 float latitude() const;
 float longitude() const;

 // Offsets as at given datetime, either UTC or Local decided by Qt::TimeSpec of datetime, null datetime = current
 qint32 offset(const QDateTime &at) const;
 qint32 offset(time_t at) const;
 qint32 currentOffset() const; // convenience offset at current datetime

 qint32 utcOffset(const QDateTime &at) const;
 qint32 utcOffset(time_t at) const;

 qint32 dstOffset(const QDateTime &at) const;
 qint32 dstOffset(time_t at) const;

 bool hasDst() const;
 bool isDst(const QDateTime &at) const;
 bool isDst(time_t at) const;
 bool isCurrentlyDst() const; // convenience, as at current datetime

 bool hasTransitions() const;

 QDateTime nextTransitionTime(const QDateTime &dt) const;
 QDateTime previousTransitionTime(const QDateTime &dt) const;
 QList<QDateTime> transitionTimes(const QDateTime &from = QDateTime(), const QDateTime &to = QDateTime()) const; // inclusive

 static QTimeZone defaultTimeZone();
 static QTimeZone setDefaultTimeZone(QTimeZone tz);
 static QTimeZone resetDefaultTimeZone();
 static QTimeZone systemTimeZone();
 static QTimeZone createTimeZone(const QString &id);
 static QTimeZone createTimeZone(const QString &id, const QString &name, int utcOffset, const QString &comment = QString());

 static QList<QString> availableTimeZones();
 static QList<QString> availableTimeZones(QLocale::Country region);
 static QList<QString> availableTimeZones(const QString &regionCode);
 static QList<QString> availableTimeZones(qint32 utcOffset);

private:
 // Held in QSharedData
 QByteArray m_id;
 TimeZone *m_icuTimeZone;
};

class Q_CORE_EXPORT QDateTime
{
 QDateTime(const QDate &, Qt::TimeSpec spec, const QTimeZone &tz = QTimeZone());
 QDateTime(const QDate &, const QTime &, Qt::TimeSpec spec, const QTimeZone &tz = QTimeZone(), bool isSecondOccurrence = false);

 QTimeZone timeZone() const;

 // Effective offset, i.e. if Qt::TimeZone self.timeZone().offset(self), if Qt:OffsetFromUtc the internal utcOffset()
 qint32 offset();

 // Effective dst, i.e. if Qt::TimeZone self.timeZone().isDst(self)
 bool isDst();

 bool isSecondOccurrence() const;
 void setSecondOccurrence(bool isSecondOccurrence);

 void setTimeSpec(Qt::TimeSpec spec, const QTimeZone &tz = QTimeZone());
 QDateTime toTimeSpec(Qt::TimeSpec spec, const QTimeZone &tz) const;

 // Sets TimeSpec to OffsetFromUtc
 void setOffsetFromUtc(qint32 seconds);

 // Convenience methods, do we really need these?
 void setTimeZone(const QTimeZone &tz);
 QDateTime toTimeZone(const QTimeZone &tz) const;
 QDateTime toOffsetFromUtc() const;
 QDateTime toOffsetFromUtc(int utcOffset) const;
 QDateTime toClockTime() const;
 QDateTime toTimeSpec(const QDateTime &dt) const;

private:
 QTimeZone m_timeZone;
 bool m_secondOccurrence;
};

QLocale Design

class QLocalePrivate
{
private:
 Locale '''m_icuLocale;
 QNumberFormatter m_decimal;
 QNumberFormatter m_currency;
 QNumberFormatter m_percentage;
 QNumberFormatter m_scientific;
 QDateTimeFormatter m_dateTime;
}

QString / QByteArray Design

QDateTime Design

API

Working Notes

This area is a scratchpad for working through the possible implementation details. This is only kept here for reference purposes.

Object Model in ICU / macOS

ICU has the following class structure which is reflected in macOS:

Locale / NSLocale- Simply an id, i.e. country, language, script and variant codes and names, no settings held.

  • NumberFormat / NSNumberFormatter - Parse/Format numbers using the settings for the embedded Locale object. Variants for Number, Currency, Percent, Scientific formats.
  • DecimalFormatSymbols - Used in DecimalNumberFormat sub-class to hold number settings and Locale.
  • UDate / NSDate - Date/Time container, a counter of ms since Unix epoch
  • TimeZone / NSTimeZone - Time Zone container, holding the id and offset rules
  • Calendar / NSCalendar - Converts UDate to/from local Date/Time using embedded TimeZone
  • DateFormat / NSDateFormatter - Parse/Format date/time using settings for embedded NumberFormat and Calendar.
  • Collator - Collates strings

All Format classes share a common base class

In particular note that the ICU API does not have the concept of a Locale Settings container holding all the settings used in a locale, i.e. a fully resolved CLDR locale xml structure. Nor is there a global Locale that applies to all settings and formatters. Instead the Format classes embed the locale they apply to and just the settings they require. This matches the UNIX locale model where you can set different locales to use for different groups of settings. To obtain all the settings from ICU you need to create/query the Format classes for DateTime, Number, Currency, Percentage, and Scientific.

Further, for any given Locale ID, there may be settings available for some but not all Format classes, i.e. Number settings may exist but Date/Time settings may not. Any given Locale ID combination of language, region and script is valid, but settings may not exist for them in ICU.

System Locale

ICU while using the system locale code when the default locale is created (e.g. en_US or de_DE) does not use any local system override settings, i.e. when a user has changed their default date format in system settings. Windows in particular also has some quirks especially in date formatting that differ from ICU. To maintain consistency with other native apps Qt will need to continue using the platform locale services for the system locale calls. We will fallback to ICU when the host system does not provide required settings or functions, for custom locales, and where it makes sense to be consistent. ICU will be the platform system on Linux, but we will pay attention to the traditional ENVVARS.

In some cases we will choose to use the host format/parse methods, in others we may choose to obtain the host settings and pass these to ICU, in extreme cases we may ignore the host and always use ICU for consistency.

It is intended to support KDE as a System Locale, this might be implemented by parsing a JSON or QSettings file which would be portable for Gnome and other platforms as well.

Platform support will be implemented as a last step as we could ship 5.1 solely using ICU as an interim measure.

Code use examples

The API could extend the current model, keeping a flat structure in QLocale, but this doesn't scale well as more features get added, and gets ugly due to the legacy date/time methods:

 // Use system/default locale
 QLocale()::toString(123.45);
 QLocale()::toFloat("123.45");
 QLocale()::toCurrencyString(123.45);
 QLocale()::toCurrencyFloat("123.45");
 QLocale()::toPercentageString(123.45);
 QLocale()::toPercentageFloat("123.45");
 QLocale()::toScientificString(123.45);
 QLocale()::toScientificFloat("123.45");
 QLocale()::toString(QDate(2000,1,1), "yyyy-MM-dd"); // Uses Qt format codes
 QLocale()::toString(QDate(2000,1,1), "yyyy-MM-dd", QLocale::IcuFormat); // Use CLDR / ICU format codes
 QLocale()::toDate("2000-01-01", "yyyy-MM-dd"); // Uses Qt format codes
 QLocale()::toDate("2000-01-01", "yyyy-MM-dd", QLocale::IcuFormat); // Use CLDR / ICU format codes

An alternative is to make the number formatter take an enum, which reduced the number of methods, but that is just as ugly:

 // Use system/default locale
 QLocale()::toString(123.45); // Decimal number by default
 QLocale()::toString(123.45, QLocale::CurrencyFormat);
 QLocale()::toString(123.45, QLocale::PercentageFormat);
 QLocale()::toString(123.45, QLocale::ScientificFormat);

An API design based on ICU would have separate formatter classes with QLocale providing direct access methods to the formatter classes for each number format type, with the nested API allowing access to the component parts. The API is thus consistent for each number format type. The API in each formatter is also "clean", i.e. has no legacy support, so it is clearer where for example to use the old Qt date format codes (only in the deprecated QLocale methods) and where to use the ICU date format codes (only in QDateTimeFormatter).

 // Use system/default locale
 QLocale()::locale().language(); // default level locale

 QLocale()::number().locale().language(); // number specific locale, falls back to default
 QLocale()::number().decimalSymbol();
 QLocale()::number().toString(123.45);
 QLocale()::number().toFloat("123.45");

 QLocale()::currency().locale().language(); // currency specific locale, falls back to default
 QLocale()::currency().decimalSymbol();
 QLocale()::currency().toString(123.45);
 QLocale()::currency().toFloat("123.45");

 QLocale()::percentage().locale().language(); // percentage specific locale, falls back to default
 QLocale()::percentage().decimalSymbol();
 QLocale()::percentage().toString(123.45);
 QLocale()::percentage().toFloat("123.45%");

 QLocale()::scientific().locale().language(); // scientific specific locale, falls back to default
 QLocale()::scientific().decimalSymbol();
 QLocale()::scientific().toString(123.45);
 QLocale()::scientific().toFloat("£123.45");

 QLocale()::datetime().locale().language(); // datetime specific locale, falls back to default
 QLocale()::datetime().toString(myDate)
 QLocale()::datetime().toString(myDate, "yyyy-MM-dd"); // Knows is CLDR/ICU format codes
 QLocale()::datetime().toDate("2000-01-01", "yyyy-MM-dd"); // Knows is CLDR/ICU format codes
 QLocale()::datetime().calendar().day(myDate);
 QLocale()::datetime().calendar().timeZone();

 QLocale()::calendar().day(myDate); // Convenience, points to datetime().calendar()
 QLocale()::timeZone().name(); // Convenience, points to datetime().calendar().timeZone()

Extending this to the full ICU class model gives flexible formatting options:

 // Custom locale for all options
 QLocale british = QLocale("en_GB");
 british.number().toString(123.45);
 british.currency().toString(123.45);

 // Localise just a currency amount into French
 QNumberFormatter frenchMoney(QLocaleCode("fr_FR"), QLocale::CurrencyFormat);
 frenchMoney.toString(123.45);

 // Localise just a date into Islamic calendar with English Names
 QDateTimeFormatter britishIslamic(QLocaleCode("en_GB@calendar=islamic"));
 britishIslamic.toString(myDate);
 britishIslamic.calendar().day(myDate);

Simple solution

The simplest option is to carry on having all data and methods embedded in the QLocale and QLocalePrivate classes, with a QLocaleSettings class to manage the system or ICU settings. This provides a "flat" API that matches traditional QLocale functionality, but may be less flexible and won't match ICU as well.

QLocalePrivate embeds all required ICU classes

  • QLocaleSettings class exposes all settings
  • QLocaleSettingsPrivate base class provides settings from ICU
  • QLocaleSettingsPrivate* derived system classes override with system settings where available
  • QLocalePrivate creates QLocaleSettings for either system or ICU at construction
  • QCalendarSystem provides date/time conversion methods
  • QTimeZone provides time zone data, but conversions done in QCalendarSystem
  • QLocale exposes public API to access system or ICU formatters, and current QLocaleSettings

This option is not explored further as yet as the full option is preferred.

Full solution

The full solution is to copy the ICU/Mac class model and provide an API hierarchy. Most current methods in QLocale would be deprecated with new access methods provided to the various formatters. Pro's are matches ICU so more flexible to future ICU changes, makes clear is new API, nice and clean separation. Anti is more subclasses, complexity, and memory management issues. The actual solution may lie in the middle ground.

The classes could be:

  • QLocaleCode
  • QNumberFormatter
  • QDateTimeFormatter
  • QCalendarSystem
  • QTimeZone

Each class would embed the matching ICU class to be used, and the API would pass all calls through to the ICU class. The classes may cache some frequently accessed key settings. For the application Default Locale this would be a shared instance with all the other default locale instances, i.e. probably a global static in QLocalePrivate. For the System Locale the private for each formatter class would be sub-classed to override the settings and format methods with the appropriate system methods.

QLocaleSettings is not required in this model, it would merely be a convenience wrapper around the Formatter classes which actually hold the settings to be used.

QDate, QTime, and QTimeZone are simply containers for absolute values, they do not do any calculation or formatting themselves. QTimeZone will have static methods for creating or listing time zones.

QCalendarSystem is purely a calculator that converts QDate, QTime and QTimeZone objects to/from local dates and times. This includes converting time using a time zone. It does not do any parsing/formatting. QDateTimeCalculator may be a more accurate name.

QDateTime combines QDate, QTime, TimeSpec and QTimeZone into one co-ordinated package.

QDateTimeFormatter uses a QCalendarSystem and a QNumberFormatter to format a QDateTime or QDate or QTime into a localized string, or vice versa.

See also http://wiki.qt.io/Qt_5_QTimeZone

Windows

No real class structure, just adapt to fit with ICU/macOS style.

Generic vs Speficic style API

Should the calendar and formatter API's use a generic or explicit model, or a mixture.

Should the number formatter have separate methods for symbols:

QNumberFormatter()::decimalSymbol()
QNumberFormatter()::separatorSymbol()
QNumberFormatter()::positiveSymbol()

Or should it be a single method taking an enum:

QNumberFormatter()::symbol(QLocale::DecimalSymbol)
QNumberFormatter()::symbol(QLocale::SeparatorSymbol)
QNumberFormatter()::symbol(QLocale::PositiveSymbol)

Should the calendar have separate methods:

QCalendarSystem::month()
QCalendarSystem::monthsInYear()
QCalendarSystem::maximumMonthsInYear()
QCalendarSystem::minimumMonthsInYear()
QCalendarSystem::toString(myDate, "MM")

Or common methods:

QCalendarSystem::value(Qt::Month)
QCalendarSystem::maximumValue(Qt::MonthsInYear)
QCalendarSystem::minimumValue(Qt::MonthsInYear)
QCalendarSystem::toString(myDate, Qt::Month)

Or a mixture of both:

QCalendarSystem::month()
QCalendarSystem::monthsInYear()
QCalendarSystem::maximumValue(Qt::MonthsInYear)
QCalendarSystem::minimumValue(Qt::MonthsInYear)
QCalendarSystem::toString(myDate, Qt::Month)

Number Format Symbols and Options

WIP.

ICU has a DecimalFormatSymbols class which contains the following symbols:

ENumberFormatSymbol {
 kDecimalSeparatorSymbol,
 kGroupingSeparatorSymbol,
 kPatternSeparatorSymbol,
 kPercentSymbol,
 kZeroDigitSymbol,
 kDigitSymbol,
 kMinusSignSymbol,
 kPlusSignSymbol,
 kCurrencySymbol,
 kIntlCurrencySymbol,
 kMonetarySeparatorSymbol,
 kExponentialSymbol,
 kPerMillSymbol,
 kPadEscapeSymbol,
 kInfinitySymbol,
 kNaNSymbol,
 kSignificantDigitSymbol,
 kMonetaryGroupingSeparatorSymbol,
 kOneDigitSymbol,
 kTwoDigitSymbol,
 kThreeDigitSymbol,
 kFourDigitSymbol,
 kFiveDigitSymbol,
 kSixDigitSymbol,
 kSevenDigitSymbol,
 kEightDigitSymbol,
 kNineDigitSymbol,
 kFormatSymbolCount
}

The DecimalFormat class has the following enum and methods to alter the number formatting:

enum ERoundingMode {
 kRoundCeiling, kRoundFloor, kRoundDown, kRoundUp,
 kRoundHalfEven, kRoundHalfDown, kRoundHalfUp, kRoundUnnecessary
}

enum EPadPosition {
 kPadBeforePrefix,
 kPadAfterPrefix,
 kPadBeforeSuffix,
 kPadAfterSuffix
}

The following number format symbols and options should be set by the Locale and should not be able to be altered via API:

The following number format options should default to the Locale but should be able to be overridden via API:

Should the settings be in a separate class like DecimalFormatSymbols or just directly in the formatter class api? DFS actually embeds the Locale ID used so setting the DFS sets the Locale for the Formatter.

Should the decimal format symbols be returned by custom API for each symbol:

QNumberFormatter()::decimalSymbol()
QNumberFormatter()::separatorSymbol()
QNumberFormatter()::positiveSymbol()

Or should it be a single method taking an enum:

QNumberFormatter()::symbol(QLocale::DecimalSymbol)
QNumberFormatter()::symbol(QLocale::SeparatorSymbol)
QNumberFormatter()::symbol(QLocale::PositiveSymbol)

Proposed API

Proposed API for the Full Solution using the nested Formatter based API closest matching ICU. This is the API proposed for Qt 5.1.

class QLocale
{
 // CLDR format length attribute for date/time/number/currency
 enum FormatPattern {
 DefaultPattern,
 FullPattern,
 LongPattern,
 MediumPattern,
 ShortPattern
 };

 enum DisplayFormat {
 DisplayPattern,
 DisplayName
 };

 // CLDR field width attribute
 enum FieldFormat {
 DefaultFormat,
 LongName, // e.g. January
 ShortName, // e.g. Jan
 NarrowName, // e.g. J
 LongNumber, // e.g. 01
 ShortNumber // e.g. 1
 };

 // CLDR context attribute
 enum FieldContext {
 DefaultContext,
 FormatContext, // Use in a format
 StandaloneContext // Use standalone
 };

 // Have QCalendarSystem API to return yearType()?
 enum YearType {
 StandardYear,
 LeapYear
 }

enum Calendar {
 DefaultCalendar = -1,
 GregorianCalendar = 0,
 ,
 LastCalendar = xxx
 };
}
class QLocaleCode
{
 QLocaleCode(); // Default locale, i.e. System locale unless overridden
 QLocaleCode(const QString &code); // Specific ICU locale
 QLocaleCode(QLocale::Language language, QLocale::Country country = QLocale::AnyCountry);
 QLocaleCode(QLocale::Language language, QLocale::Script script,
 QLocale::Country country, QLocale::Calendar calendar);
 QLocaleCode(const QLocaleCode &other);
 ~QLocaleCode();

 QString code() const; // simplest possible code, i.e. only include mods if not default???
 QString bcp47Code() const; // Full BCP47 code
 QString unicodeCode() const; // Full Unicode code

 QString name() const; // e.g. en_GB = "British English"
 QString composition() const; // e.g. en_GB = "English (United Kingdom)"

 QLocale::Language language() const;
 QString languageCode() const;
 QString languageName() const;
 QString languageName(QLocale::Language language) const;

 QLocale::Script script() const;
 QString scriptCode() const;
 QString scriptName() const;
 QString scriptName(QLocale::Language language) const;

 // country, region or territory?
 QLocale::Country region() const;
 QString regionCode() const;
 QString regionName() const;
 QString regionName(QLocale::Language language) const;

 QLocale::Calendar calendar() const; // Returns DefaultCalendar if not specified on creation
 QString calendarCode() const;
 QString calendarName() const;
 QString calendarName(QLocale::Language language) const;
}


Draft API

Draft notes on the possible API. This is a scratch work area and is not intended for review.

This is a draft API for the full solution based on ICU/CLDR options, not all of this would be implemented in 5.1, but they need to be planned for.

Current status: Need to remove QLocaleSettings and define formatter classes instead.

class QLocale
{
 // ?
 enum NumberingType {
 NumericNumbering,
 AlgorithmicNumbering
 }

 enum NumberingCategory {
 DefaultNumbering,
 NativeNumbering,
 TraditionalNumbering,
 FinanceNumbering
 }

 enum NumberingSystem {
 ArabicNumbering,
 
 }
}

class QLocaleCode
{
 // TODO
 // Collation 5.2?
 // Currency
 // Numbering System
 // Time Zone
 // Variant
}

// Don't do this in full ICU style solution, split across the formatter classes.

class QLocaleSettings
{
 QLocaleSettings(); // Default locale, i.e. System locale unless overridden
 QLocaleSettings(const QLocaleCode &code); // Specific ICU locale
 ~QLocaleSettings();

 QLocaleSettings &operator=(const QLocaleSettings &other);

 QLocaleCode locale() const; // ?

 // Index Labels - 5.2?
 QString indexSeparator() const;
 QString compressedIndexSeparator() const;
 QString indexRangePattern() const;
 QString indexLabelBefore() const;
 QString indexLabelAfter() const;
 QStringList indexLabel() const; //?

 // Ellipses- 5.2? Use enum with List Patterns???
 QString initialEllipsis() const;
 QString medialEllipsis() const;
 QString finalEllipsis() const;

// More Information - 5.2?
 QString moreInformation() const;

// Delimiter Elements
 QString quotationStart(QLocale::QuotationStyle = QLocale::StandardQuotation) const;
 QString quotationEnd(QLocale::QuotationStyle = QLocale::StandardQuotation) const;

// Measurement Elements
 QLocale::MeasurementSystem measurementSystem(); const; // ?

 // Date Elements
 QString dateFormat(QLocale::FormatPattern format = QLocale::DefaultPattern,
 QLocale::DisplayFormat display = QLocale::DisplayPattern) const;
 QString timeFormat(QLocale::FormatPattern format = QLocale::DefaultPattern,
 QLocale::DisplayFormat display = QLocale::DisplayPattern) const;
 QString dateTimeFormat(QLocale::FormatPattern format = QLocale::DefaultPattern,
 QLocale::DisplayFormat display = QLocale::DisplayPattern) const;
 QString dateTimeFormat(QString format) const; // Uses availableFormats to match format
 // TODO availableFormats - use QLocale::DateTimeComponants?
 QString eraName(QDate date, // ? or int?
 QLocale::FieldFormat format = QLocale::LongName,
 QLocale::FieldContext context = QLocale::FormatContext,
 QLocale::CalendarSystem calendar = QLocale::DefaultCalendar) const;
 QString monthName(int month, QLocale::YearType yearType = QLocale::StandardYear, // ? or QDate?
 QLocale::FieldFormat format = QLocale::LongName,
 QLocale::FieldContext context = QLocale::FormatContext,
 QLocale::Calendar calendar = QLocale::DefaultCalendar) const;
 QString dayName(int day,
 QLocale::FieldFormat format = QLocale::LongName,
 QLocale::FieldContext context = QLocale::FormatContext,
 QLocale::CalendarSystem calendar = QLocale::DefaultCalendar) const;
 QString quarterName(int quarter, // or QDate???
 QLocale::FieldFormat format = QLocale::LongName,
 QLocale::FieldContext context = QLocale::FormatContext,
 QLocale::CalendarSystem calendar = QLocale::DefaultCalendar) const;
 QString dayPeriodName(const QTime &time,
 QLocale::FieldFormat format = QLocale::LongName,
 QLocale::FieldContext context = QLocale::FormatContext,
 QLocale::Calendar calendar = QLocale::DefaultCalendar) const;
 // TODO
 // monthPatterns
 // cyclicNameSets
 // Interval
 // Week
 // use24hour()
 // Relative Names
 // Time Zone Name

// Number Elements - Have *Symbol on end of name ?
 Qlocale::NumberingSystem numberingSystem(QLocale::NumberingCategory = QLocale::DefaultNumbering) const;
 QString decimalSymbol() const;
 QString groupSymbol() const;
 QString listSymbol() const;
 QString percentSign() const;
 QString nativeZeroDigit() const;
 QString patternDigitSymbol() const;
 QString plusSign() const;
 QString minusSign() const;
 QString exponentialSymbol() const;
 QString perMilleSymbol() const;
 QString infinitySymbol() const;
 QString nanSymbol() const;
 QString decimalFormat(QLocale::FormatPattern format = QLocale::DefaultPattern,
 QLocale::DisplayFormat display = QLocale::DisplayPattern) const;
 QString scientificFormat(QLocale::FormatPattern format = QLocale::DefaultPattern,
 QLocale::DisplayFormat display = QLocale::DisplayPattern) const;
 QString percentFormat(QLocale::FormatPattern format = QLocale::DefaultPattern,
 QLocale::DisplayFormat display = QLocale::DisplayPattern) const;
 QString currencyDecimalSymbol() const;
 QString currencyGroupSymbol() const;
 QString currencyFormat(QLocale::FormatPattern format = QLocale::DefaultPattern,
 QLocale::DisplayFormat display = QLocale::DisplayPattern) const; //?
 // TODO
 // currencySymbol currencySpacing currencies

Qt::DayOfWeek firstDayOfWeek() const;
 QList<Qt::DayOfWeek> weekdays() const;

Qt::LayoutDirection textDirection() const;

QString currencySymbol(CurrencySymbolFormat = CurrencySymbol) const;

QStringList uiLanguages() const;

bool operator==(const QLocale &other) const;
 bool operator!=(const QLocale &other) const;

// TODO
 // Encoding ?
 // Unit Elements 5.2
 // Collation 5.2
 // Rule-based number formatting 5.2
 // List Patterns 5.2?
 // Context Transform Elements 5.2

private:
 QLocaleCode m_code;
};

class Q_CORE_EXPORT QCalendarSystem
{
public:
 explicit QCalendarSystem(QLocale::Calendar calendar = QLocale::DefaultCalendar);
 ~QCalendarSystem();

QLocale::CalendarSystem calendarSystem() const;

QDate epoch() const;
 QDate earliestValidDate() const;
 QDate latestValidDate() const;
 int maximumMonthsInYear() const;
 int maximumDaysInYear() const;
 int maximumDaysInMonth() const;

bool isValid(const QDate &date) const;
 bool isValid(int year, int month, int day) const;
 bool isValid(int year, int dayOfYear) const;

QDate date(int year, int month, int day) const;
 QDate date(int year, int dayOfYear) const;

void getDate(const QDate &date, int *year, int *month, int *day) const;

int year(const QDate &date) const;
 int month(const QDate &date) const;
 int day(const QDate &date) const;

int quarter(const QDate &date) const;
 int quarter(int year, int month, int day) const;

int dayOfYear(const QDate &date) const;
 int dayOfYear(int year, int month, int day) const;

int dayOfWeek(const QDate &date) const;
 int dayOfWeek(int year, int month, int day) const;

int weekNumber(const QDate &date, int *yearNum = 0,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;
 int weekNumber(int year, int month, int day, int *yearNum = 0,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;

int monthsInYear(const QDate &date) const;
 int monthsInYear(int year) const;

int weeksInYear(const QDate &date,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;
 int weeksInYear(int year,
 QLocale::WeekNumberSystem system = QLocale::DefaultWeekNumber) const;

int daysInYear(const QDate &date) const;
 int daysInYear(int year) const;

int daysInMonth(const QDate &date) const;
 int daysInMonth(int year, int month) const;

int daysInWeek() const;

bool isLeapYear(const QDate &date) const;
 bool isLeapYear(int year) const;

QDate addYears(const QDate &date, int years) const;
 QDate addMonths(const QDate &date, int months) const;
 QDate addDays(const QDate &date, int days) const;

int yearsDifference(const QDate &fromDate, const QDate &toDate) const;
 int monthsDifference(const QDate &fromDate, const QDate &toDate) const;
 qint64 daysDifference(const QDate &fromDate, const QDate &toDate) const;

void dateDifference(const QDate &fromDate, const QDate &toDate,
 int *years, int *months, int *days, int '''direction) const;

 QDate firstDayOfYear(const QDate &date) const;
 QDate firstDayOfYear(int year) const;
 QDate lastDayOfYear(const QDate &date) const;
 QDate lastDayOfYear(int year) const;

 QDate firstDayOfMonth(const QDate &date) const;
 QDate firstDayOfMonth(int year, int month) const;
 QDate lastDayOfMonth(const QDate &date) const;
 QDate lastDayOfMonth(int year, int month) const;

private:
 QCalendarSystemPrivate''' const d;
};

// See http://wiki.qt.io/Qt_5_QTimeZone

Additional information

http://www.layt.net/john/blog/odysseus/icu_did_u_c_me