diff --git a/src/lib/util/kformat.h b/src/lib/util/kformat.h index fc2b4be..c9cf1f7 100644 --- a/src/lib/util/kformat.h +++ b/src/lib/util/kformat.h @@ -1,407 +1,407 @@ /* This file is part of the KDE Frameworks Copyright (C) 2013 Alex Merry Copyright (C) 2013 John Layt Copyright (C) 2010 Michael Leupold Copyright (C) 2009 Michael Pyne Copyright (C) 2008 Albert Astals Cid This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KFORMAT_H #define KFORMAT_H #include #include #include #include class QDate; class QDateTime; class KFormatPrivate; /** * \file kformat.h */ /* The code in this class was copied from the old KLocale and modified by John Layt (and also Alex Merry) in the KDELIBS 4 to KDE Frameworks 5 transition in 2013. Albert Astals Cid is the original author of formatSpelloutDuration() originally named KLocale::prettyFormatDuration(). Michael Pyne is the original author of formatByteSize(). Michael Leupold is the original author of formatRelativeDate(() originally part of KFormat::formatDate(). */ /** * @class KFormat kformat.h KFormat * * KFormat provides support for formatting numbers and datetimes in * formats that are not supported by QLocale. * * @author John Layt , * Michael Pyne , * Albert Astals Cid , * * @short Class for formatting numbers and datetimes. * @since 5.0 */ class KCOREADDONS_EXPORT KFormat Q_DECL_FINAL { Q_GADGET public: /** * These binary units are used in KDE by the formatByteSize() * function. * * NOTE: There are several different units standards: * 1) SI (i.e. metric), powers-of-10. * 2) IEC, powers-of-2, with specific units KiB, MiB, etc. * 3) JEDEC, powers-of-2, used for solid state memory sizing which * is why you see flash cards labels as e.g. 4GB. These (ab)use * the metric units. Although JEDEC only defines KB, MB, GB, if * JEDEC is selected all units will be powers-of-2 with metric * prefixes for clarity in the event of sizes larger than 1024 GB. * * Although 3 different dialects are possible this enum only uses * metric names since adding all 3 different names of essentially the same * unit would be pointless. Use BinaryUnitDialect to control the exact * units returned. * * @see BinaryUnitDialect * @see formatByteSize */ enum BinarySizeUnits { /// Auto-choose a unit such that the result is in the range [0, 1000 or 1024) DefaultBinaryUnits = -1, // The first real unit must be 0 for the current implementation! UnitByte, ///< B 1 byte UnitKiloByte, ///< KiB/KB/kB 1024/1000 bytes. UnitMegaByte, ///< MiB/MB/MB 2^20/10^06 bytes. UnitGigaByte, ///< GiB/GB/GB 2^30/10^09 bytes. UnitTeraByte, ///< TiB/TB/TB 2^40/10^12 bytes. UnitPetaByte, ///< PiB/PB/PB 2^50/10^15 bytes. UnitExaByte, ///< EiB/EB/EB 2^60/10^18 bytes. UnitZettaByte, ///< ZiB/ZB/ZB 2^70/10^21 bytes. UnitYottaByte, ///< YiB/YB/YB 2^80/10^24 bytes. UnitLastUnit = UnitYottaByte }; /** * These units are used in KDE by the formatValue() function. * * @see formatValue * @since 5.49 */ enum class Unit { Other, Bit, ///< "bit" Byte, ///< "B" Meter, ///< "m" Hertz, ///< "Hz" }; /** * These prefixes are used in KDE by the formatValue() * function. * * IEC prefixes are only defined for integral units of information, e.g. * bits and bytes. * * @see BinarySizeUnits * @see formatValue * @since 5.49 */ enum class UnitPrefix { /// Auto-choose a unit such that the result is in the range [0, 1000 or 1024) AutoAdjust = -128, Yocto = 0, ///< --/-/y 10^-24 Zepto, ///< --/-/z 10^-21 Atto, ///< --/-/a 10^-18 Femto, ///< --/-/f 10^-15 Pico, ///< --/-/p 10^-12 Nano, ///< --/-/n 10^-9 Micro, ///< --/-/µ 10^-6 - Mili, ///< --/-/m 10^-3 + Milli, ///< --/-/m 10^-3 Centi, ///< --/-/c 0.01 Deci, ///< --/-/d 0.1 Unity, ///< "" 1 Deca, ///< --/-/da 10 Hecto, ///< --/-/h 100 Kilo, ///< Ki/K/k 1024/1000 Mega, ///< Mi/M/M 2^20/10^06 Giga, ///< Gi/G/G 2^30/10^09 Tera, ///< Ti/T/T 2^40/10^12 Peta, ///< Pi/P/P 2^50/10^15 Exa, ///< Ei/E/E 2^60/10^18 Zetta, ///< Zi/Z/Z 2^70/10^21 Yotta, ///< Yi/Y/Y 2^80/10^24 }; /** * This enum chooses what dialect is used for binary units. * * Note: Although JEDEC abuses the metric prefixes and can therefore be * confusing, it has been used to describe *memory* sizes for quite some time * and programs should therefore use either Default, JEDEC, or IEC 60027-2 * for memory sizes. * * On the other hand network transmission rates are typically in metric so * Default, Metric, or IEC (which is unambiguous) should be chosen. * * Normally choosing DefaultBinaryDialect is the best option as that uses * the user's selection for units. If the user has not selected a preference, * IECBinaryDialect will typically be used. * * @see BinarySizeUnits * @see formatByteSize */ enum BinaryUnitDialect { DefaultBinaryDialect = -1, ///< Used if no specific preference IECBinaryDialect, ///< KiB, MiB, etc. 2^(10*n) JEDECBinaryDialect, ///< KB, MB, etc. 2^(10*n) MetricBinaryDialect, ///< SI Units, kB, MB, etc. 10^(3*n) LastBinaryDialect = MetricBinaryDialect }; /** * Format flags for formatDuration() */ enum DurationFormatOption { DefaultDuration = 0x0, ///< Default formatting in localized 1:23:45 format InitialDuration = 0x1, ///< Default formatting in localized 1h23m45s format ShowMilliseconds = 0x2, ///< Include milliseconds in format, e.g. 1:23:45.678 HideSeconds = 0x4, ///< Hide the seconds, e.g. 1:23 or 1h23m, overrides ShowMilliseconds FoldHours = 0x8 ///< Fold the hours into the minutes, e.g. 83:45 or 83m45s, overrides HideSeconds }; Q_DECLARE_FLAGS(DurationFormatOptions, DurationFormatOption) Q_FLAG(DurationFormatOption) /** * Constructs a KFormat. * * @param locale the locale to use, defaults to the system locale */ explicit KFormat(const QLocale &locale = QLocale()); /** * Copy constructor */ KFormat(const KFormat &other); KFormat& operator=(const KFormat &other); /** * Destructor */ ~KFormat(); /** * Converts @p size from bytes to the appropriate string representation * using the binary unit dialect @p dialect and the specific units @p units. * * Example: * @code * QString metric, iec, jedec, small; * metric = formatByteSize(1000, 1, KFormat::MetricBinaryDialect, KFormat::UnitKiloByte); * iec = formatByteSize(1000, 1, KFormat::IECBinaryDialect, KFormat::UnitKiloByte); * jedec = formatByteSize(1000, 1, KFormat::JEDECBinaryDialect, KFormat::UnitKiloByte); * small = formatByteSize(100); * // metric == "1.0 kB", iec == "1.0 KiB", jedec == "1.0 KB", small == "100 B" * @endcode * * @param size size in bytes * @param precision number of places after the decimal point to use. KDE uses * 1 by default so when in doubt use 1. Whenever KFormat::UnitByte is used * (either explicitly or autoselected from KFormat::DefaultBinaryUnits), * the fractional part is always omitted. * @param dialect binary unit standard to use. Use DefaultBinaryDialect to * use the localized user selection unless you need to use a specific * unit type (such as displaying a flash memory size in JEDEC). * @param units specific unit size to use in result. Use * DefaultBinaryUnits to automatically select a unit that will return * a sanely-sized number. * @return converted size as a translated string including the units. * E.g. "1.23 KiB", "2 GB" (JEDEC), "4.2 kB" (Metric). * @see BinarySizeUnits * @see BinaryUnitDialect */ QString formatByteSize(double size, int precision = 1, KFormat::BinaryUnitDialect dialect = KFormat::DefaultBinaryDialect, KFormat::BinarySizeUnits units = KFormat::DefaultBinaryUnits) const; /** * Given a number of milliseconds, converts that to a string containing * the localized equivalent, e.g. 1:23:45 * * @param msecs Time duration in milliseconds * @param options options to use in the duration format * @return converted duration as a string - e.g. "1:23:45" "1h23m" */ QString formatDuration(quint64 msecs, KFormat::DurationFormatOptions options = KFormat::DefaultDuration) const; /** * Given a number of milliseconds, converts that to a string containing * the localized equivalent to the requested decimal places. * * e.g. given formatDuration(60000), returns "1.0 minutes" * * @param msecs Time duration in milliseconds * @param decimalPlaces Deciaml places to round off to, defaults to 2 * @return converted duration as a string - e.g. "5.5 seconds" "23.0 minutes" */ QString formatDecimalDuration(quint64 msecs, int decimalPlaces = 2) const; /** * Given a number of milliseconds, converts that to a spell-out string containing * the localized equivalent. * * e.g. given formatSpelloutDuration(60001) returns "1 minute" * given formatSpelloutDuration(62005) returns "1 minute and 2 seconds" * given formatSpelloutDuration(90060000) returns "1 day and 1 hour" * * @param msecs Time duration in milliseconds * @return converted duration as a string. * Units not interesting to the user, for example seconds or minutes when the first * unit is day, are not returned because they are irrelevant. The same applies for * seconds when the first unit is hour. */ QString formatSpelloutDuration(quint64 msecs) const; /** * Returns a string formatted to a relative date style. * * If the @p date falls within one week before or after the current date * then a relative date string will be returned, such as: * * Yesterday * * Today * * Tomorrow * * Last Tuesday * * Next Wednesday * * If the @p date falls outside this period then the @p format is used. * * @param date the date to be formatted * @param format the date format to use * * @return the date as a string */ QString formatRelativeDate(const QDate &date, QLocale::FormatType format) const; /** * Returns a string formatted to a relative datetime style. * * If the @p dateTime falls within one week before or after the current date * then a relative date string will be returned, such as: * * Yesterday, 3:00pm * * Today, 3:00pm * * Tomorrow, 3:00pm * * Last Tuesday, 3:00pm * * Next Wednesday, 3:00pm * * If the @p dateTime falls outside this period then the @p format is used. * * @param dateTime the date to be formatted * @param format the date format to use * * @return the date as a string */ QString formatRelativeDateTime(const QDateTime &dateTime, QLocale::FormatType format) const; /** * Converts @p value to the appropriate string representation * * Example: * @code * // sets formatted to "1.0 kbit" * auto formatted = format.formatValue(1000, KFormat::Unit::Bit, 1, KFormat::UnitPrefix::Kilo); * @endcode * * @param value value to be formatted * @param precision number of places after the decimal point to use. KDE uses * 1 by default so when in doubt use 1. * @param unit unit to use in result. * @param prefix specific prefix to use in result. Use UnitPrefix::AutoAdjust * to automatically select an appropriate prefix. * @param dialect prefix standard to use. Use DefaultBinaryDialect to * use the localized user selection unless you need to use a specific * unit type. Only meaningful for KFormat::Unit::Byte, and ignored for * all other units. * @return converted size as a translated string including prefix and unit. * E.g. "1.23 KiB", "2 GB" (JEDEC), "4.2 kB" (Metric), "1.2 kbit". * @see Unit * @see UnitPrefix * @see BinaryUnitDialect * @since 5.49 */ QString formatValue(double value, KFormat::Unit unit, int precision = 1, KFormat::UnitPrefix prefix = KFormat::UnitPrefix::AutoAdjust, KFormat::BinaryUnitDialect dialect = KFormat::DefaultBinaryDialect) const; /** * Converts @p value to the appropriate string representation * * Example: * @code * QString bits, slow, fast; * // sets bits to "1.0 kbit", slow to "1.0 kbit/s" and fast to "12.3 Mbit/s". * bits = format.formatValue(1000, QStringLiteral("bit"), 1, KFormat::UnitPrefix::Kilo); * slow = format.formatValue(1000, QStringLiteral("bit/s"); * fast = format.formatValue(12.3e6, QStringLiteral("bit/s"); * @endcode * * @param value value to be formatted * @param precision number of places after the decimal point to use. KDE uses * 1 by default so when in doubt use 1. * @param unit unit to use in result. * @param prefix specific prefix to use in result. Use UnitPrefix::AutoAdjust * to automatically select an appropriate prefix. * @return converted size as a translated string including prefix and unit. * E.g. "1.2 kbit", "2.4 kB", "12.3 Mbit/s" * @see UnitPrefix * @since 5.49 */ QString formatValue(double value, const QString& unit, int precision = 1, KFormat::UnitPrefix prefix = KFormat::UnitPrefix::AutoAdjust) const; private: QSharedDataPointer d; }; #endif // KFORMAT_H diff --git a/src/lib/util/kformatprivate.cpp b/src/lib/util/kformatprivate.cpp index d4d22f9..a7fc1dd 100644 --- a/src/lib/util/kformatprivate.cpp +++ b/src/lib/util/kformatprivate.cpp @@ -1,576 +1,576 @@ /* This file is part of the KDE Frameworks Copyright (C) 2013 Alex Merry Copyright (C) 2013 John Layt Copyright (C) 2010 Michael Leupold Copyright (C) 2009 Michael Pyne Copyright (C) 2008 Albert Astals Cid This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kformatprivate_p.h" #include #include KFormatPrivate::KFormatPrivate(const QLocale &locale) { m_locale = locale; } KFormatPrivate::~KFormatPrivate() { } constexpr double bpow(int exp) { return (exp > 0) ? 2.0 * bpow(exp - 1) : (exp < 0) ? 0.5 * bpow(exp + 1) : 1.0; } QString KFormatPrivate::formatValue(double value, KFormat::Unit unit, QString unitString, int precision, KFormat::UnitPrefix prefix, KFormat::BinaryUnitDialect dialect) const { if (dialect <= KFormat::DefaultBinaryDialect || dialect > KFormat::LastBinaryDialect) { dialect = KFormat::IECBinaryDialect; } if (static_cast(prefix) < static_cast(KFormat::UnitPrefix::Yocto) || static_cast(prefix) > static_cast(KFormat::UnitPrefix::Yotta)) { prefix = KFormat::UnitPrefix::AutoAdjust; } double multiplier = 1024.0; if (dialect == KFormat::MetricBinaryDialect) { multiplier = 1000.0; } int power = 0; if (prefix == KFormat::UnitPrefix::AutoAdjust) { double adjustValue = qAbs(value); while (adjustValue >= multiplier) { adjustValue /= multiplier; power += 1; } while (adjustValue && adjustValue < 1.0) { adjustValue *= multiplier; power -= 1; } const KFormat::UnitPrefix map[] = { KFormat::UnitPrefix::Yocto, // -8 KFormat::UnitPrefix::Zepto, KFormat::UnitPrefix::Atto, KFormat::UnitPrefix::Femto, KFormat::UnitPrefix::Pico, KFormat::UnitPrefix::Nano, KFormat::UnitPrefix::Micro, - KFormat::UnitPrefix::Mili, + KFormat::UnitPrefix::Milli, KFormat::UnitPrefix::Unity, // 0 KFormat::UnitPrefix::Kilo, KFormat::UnitPrefix::Mega, KFormat::UnitPrefix::Giga, KFormat::UnitPrefix::Tera, KFormat::UnitPrefix::Peta, KFormat::UnitPrefix::Exa, KFormat::UnitPrefix::Zetta, KFormat::UnitPrefix::Yotta, // 8 }; power = std::max(-8, std::min(8, power)); prefix = map[power + 8]; } if (prefix == KFormat::UnitPrefix::Unity && unit == KFormat::Unit::Byte) { precision = 0; } struct PrefixMapEntry { KFormat::UnitPrefix prefix; double decimalFactor; double binaryFactor; QChar prefixChar; }; const PrefixMapEntry map[] = { { KFormat::UnitPrefix::Yocto, 1e-24, bpow(-80), u'y' }, { KFormat::UnitPrefix::Zepto, 1e-21, bpow(-70), u'z' }, { KFormat::UnitPrefix::Atto, 1e-18, bpow(-60), u'a' }, { KFormat::UnitPrefix::Femto, 1e-15, bpow(-50), u'f' }, { KFormat::UnitPrefix::Pico, 1e-12, bpow(-40), u'p' }, { KFormat::UnitPrefix::Nano, 1e-9, bpow(-30), u'n' }, { KFormat::UnitPrefix::Micro, 1e-6, bpow(-20), u'µ' }, - { KFormat::UnitPrefix::Mili, 1e-3, bpow(-10), u'm' }, + { KFormat::UnitPrefix::Milli, 1e-3, bpow(-10), u'm' }, { KFormat::UnitPrefix::Unity, 1.0, 1.0, u'\0' }, { KFormat::UnitPrefix::Kilo, 1e3, bpow(10), u'k' }, { KFormat::UnitPrefix::Mega, 1e6, bpow(20), u'M' }, { KFormat::UnitPrefix::Giga, 1e9, bpow(30), u'G' }, { KFormat::UnitPrefix::Tera, 1e12, bpow(40), u'T' }, { KFormat::UnitPrefix::Peta, 1e15, bpow(50), u'P' }, { KFormat::UnitPrefix::Exa, 1e18, bpow(60), u'E' }, { KFormat::UnitPrefix::Zetta, 1e21, bpow(70), u'Z' }, { KFormat::UnitPrefix::Yotta, 1e24, bpow(80), u'Y' }, }; auto entry = std::find_if(std::begin(map), std::end(map), [prefix](const PrefixMapEntry& e) { return e.prefix == prefix; }); switch (unit) { case KFormat::Unit::Bit: unitString = QStringLiteral("bit"); break; case KFormat::Unit::Byte: unitString = QStringLiteral("B"); break; case KFormat::Unit::Meter: unitString = QStringLiteral("m"); break; case KFormat::Unit::Hertz: unitString = QStringLiteral("Hz"); break; case KFormat::Unit::Other: break; } if (prefix == KFormat::UnitPrefix::Unity) { QString numString = m_locale.toString(value, 'f', precision); //: value without prefix, format " " return tr("%1 %2", "no Prefix").arg(numString, unitString); } QString prefixString; if (dialect == KFormat::MetricBinaryDialect) { value /= entry->decimalFactor; prefixString = entry->prefixChar; } else { value /= entry->binaryFactor; prefixString = QString(entry->prefixChar).toUpper(); if (dialect == KFormat::IECBinaryDialect) { prefixString += u'i'; } } QString numString = m_locale.toString(value, 'f', precision); //: value with prefix, format " " return tr("%1 %2%3", "MetricBinaryDialect").arg(numString, prefixString, unitString); } QString KFormatPrivate::formatByteSize(double size, int precision, KFormat::BinaryUnitDialect dialect, KFormat::BinarySizeUnits units) const { // Current KDE default is IECBinaryDialect if (dialect <= KFormat::DefaultBinaryDialect || dialect > KFormat::LastBinaryDialect) { dialect = KFormat::IECBinaryDialect; } // Current KDE default is to auto-adjust so the size falls in the range 0 to 1000/1024 if (units < KFormat::DefaultBinaryUnits || units > KFormat::UnitLastUnit) { units = KFormat::DefaultBinaryUnits; } int unit = 0; // Selects what unit to use double multiplier = 1024.0; if (dialect == KFormat::MetricBinaryDialect) { multiplier = 1000.0; } // If a specific unit conversion is given, use it directly. Otherwise // search until the result is in [0, multiplier] (or out of our range). if (units == KFormat::DefaultBinaryUnits) { while (qAbs(size) >= multiplier && unit < int(KFormat::UnitYottaByte)) { size /= multiplier; ++unit; } } else { // A specific unit is in use unit = static_cast(units); if (unit > 0) { size /= pow(multiplier, unit); } } // Bytes, no rounding if (unit == 0) { precision = 0; } QString numString = m_locale.toString(size, 'f', precision); // Do not remove "//:" comments below, they are used by the translators. // NB: we cannot pass pluralization arguments, as the size may be negative if (dialect == KFormat::MetricBinaryDialect) { switch (unit) { case KFormat::UnitByte: //: MetricBinaryDialect size in bytes return tr("%1 B", "MetricBinaryDialect").arg(numString); case KFormat::UnitKiloByte: //: MetricBinaryDialect size in 1000 bytes return tr("%1 kB", "MetricBinaryDialect").arg(numString); case KFormat::UnitMegaByte: //: MetricBinaryDialect size in 10^6 bytes return tr("%1 MB", "MetricBinaryDialect").arg(numString); case KFormat::UnitGigaByte: //: MetricBinaryDialect size in 10^9 bytes return tr("%1 GB", "MetricBinaryDialect").arg(numString); case KFormat::UnitTeraByte: //: MetricBinaryDialect size in 10^12 bytes return tr("%1 TB", "MetricBinaryDialect").arg(numString); case KFormat::UnitPetaByte: //: MetricBinaryDialect size in 10^15 bytes return tr("%1 PB", "MetricBinaryDialect").arg(numString); case KFormat::UnitExaByte: //: MetricBinaryDialect size in 10^18 byte return tr("%1 EB", "MetricBinaryDialect").arg(numString); case KFormat::UnitZettaByte: //: MetricBinaryDialect size in 10^21 bytes return tr("%1 ZB", "MetricBinaryDialect").arg(numString); case KFormat::UnitYottaByte: //: MetricBinaryDialect size in 10^24 bytes return tr("%1 YB", "MetricBinaryDialect").arg(numString); } } else if (dialect == KFormat::JEDECBinaryDialect) { switch (unit) { case KFormat::UnitByte: //: JEDECBinaryDialect memory size in bytes return tr("%1 B", "JEDECBinaryDialect").arg(numString); case KFormat::UnitKiloByte: //: JEDECBinaryDialect memory size in 1024 bytes return tr("%1 KB", "JEDECBinaryDialect").arg(numString); case KFormat::UnitMegaByte: //: JEDECBinaryDialect memory size in 10^20 bytes return tr("%1 MB", "JEDECBinaryDialect").arg(numString); case KFormat::UnitGigaByte: //: JEDECBinaryDialect memory size in 10^30 bytes return tr("%1 GB", "JEDECBinaryDialect").arg(numString); case KFormat::UnitTeraByte: //: JEDECBinaryDialect memory size in 10^40 bytes return tr("%1 TB", "JEDECBinaryDialect").arg(numString); case KFormat::UnitPetaByte: //: JEDECBinaryDialect memory size in 10^50 bytes return tr("%1 PB", "JEDECBinaryDialect").arg(numString); case KFormat::UnitExaByte: //: JEDECBinaryDialect memory size in 10^60 bytes return tr("%1 EB", "JEDECBinaryDialect").arg(numString); case KFormat::UnitZettaByte: //: JEDECBinaryDialect memory size in 10^70 bytes return tr("%1 ZB", "JEDECBinaryDialect").arg(numString); case KFormat::UnitYottaByte: //: JEDECBinaryDialect memory size in 10^80 bytes return tr("%1 YB", "JEDECBinaryDialect").arg(numString); } } else { // KFormat::IECBinaryDialect, KFormat::DefaultBinaryDialect switch (unit) { case KFormat::UnitByte: //: IECBinaryDialect size in bytes return tr("%1 B", "IECBinaryDialect").arg(numString); case KFormat::UnitKiloByte: //: IECBinaryDialect size in 1024 bytes return tr("%1 KiB", "IECBinaryDialect").arg(numString); case KFormat::UnitMegaByte: //: IECBinaryDialect size in 10^20 bytes return tr("%1 MiB", "IECBinaryDialect").arg(numString); case KFormat::UnitGigaByte: //: IECBinaryDialect size in 10^30 bytes return tr("%1 GiB", "IECBinaryDialect").arg(numString); case KFormat::UnitTeraByte: //: IECBinaryDialect size in 10^40 bytes return tr("%1 TiB", "IECBinaryDialect").arg(numString); case KFormat::UnitPetaByte: //: IECBinaryDialect size in 10^50 bytes return tr("%1 PiB", "IECBinaryDialect").arg(numString); case KFormat::UnitExaByte: //: IECBinaryDialect size in 10^60 bytes return tr("%1 EiB", "IECBinaryDialect").arg(numString); case KFormat::UnitZettaByte: //: IECBinaryDialect size in 10^70 bytes return tr("%1 ZiB", "IECBinaryDialect").arg(numString); case KFormat::UnitYottaByte: //: IECBinaryDialect size in 10^80 bytes return tr("%1 YiB", "IECBinaryDialect").arg(numString); } } // Should never reach here Q_ASSERT(false); return numString; } enum TimeConstants { MSecsInDay = 86400000, MSecsInHour = 3600000, MSecsInMinute = 60000, MSecsInSecond = 1000 }; QString KFormatPrivate::formatDuration(quint64 msecs, KFormat::DurationFormatOptions options) const { quint64 ms = msecs; if (options & KFormat::HideSeconds) { //round to nearest minute ms = qRound(ms / (qreal)MSecsInMinute) * MSecsInMinute ; } else if (!(options & KFormat::ShowMilliseconds)) { //round to nearest second ms = qRound(ms / (qreal)MSecsInSecond) * MSecsInSecond ; } int hours = ms / MSecsInHour; ms = ms % MSecsInHour; int minutes = ms / MSecsInMinute; ms = ms % MSecsInMinute; int seconds = ms / MSecsInSecond; ms = ms % MSecsInSecond; if ((options & KFormat::InitialDuration) == KFormat::InitialDuration) { if ((options & KFormat::FoldHours) == KFormat::FoldHours && (options & KFormat::ShowMilliseconds) == KFormat::ShowMilliseconds) { //: @item:intext Duration format minutes, seconds and milliseconds return tr("%1m%2.%3s").arg(hours * 60 + minutes, 1, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')) .arg(ms, 3, 10, QLatin1Char('0')); } else if ((options & KFormat::FoldHours) == KFormat::FoldHours) { //: @item:intext Duration format minutes and seconds return tr("%1m%2s").arg(hours * 60 + minutes, 1, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')); } else if ((options & KFormat::HideSeconds) == KFormat::HideSeconds) { //: @item:intext Duration format hours and minutes return tr("%1h%2m").arg(hours, 1, 10, QLatin1Char('0')) .arg(minutes, 2, 10, QLatin1Char('0')); } else if ((options & KFormat::ShowMilliseconds) == KFormat::ShowMilliseconds) { //: @item:intext Duration format hours, minutes, seconds, milliseconds return tr("%1h%2m%3.%4s").arg(hours, 1, 10, QLatin1Char('0')) .arg(minutes, 2, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')) .arg(ms, 3, 10, QLatin1Char('0')); } else { // Default //: @item:intext Duration format hours, minutes, seconds return tr("%1h%2m%3s").arg(hours, 1, 10, QLatin1Char('0')) .arg(minutes, 2, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')); } } else { if ((options & KFormat::FoldHours) == KFormat::FoldHours && (options & KFormat::ShowMilliseconds) == KFormat::ShowMilliseconds) { //: @item:intext Duration format minutes, seconds and milliseconds return tr("%1:%2.%3").arg(hours * 60 + minutes, 1, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')) .arg(ms, 3, 10, QLatin1Char('0')); } else if ((options & KFormat::FoldHours) == KFormat::FoldHours) { //: @item:intext Duration format minutes and seconds return tr("%1:%2").arg(hours * 60 + minutes, 1, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')); } else if ((options & KFormat::HideSeconds) == KFormat::HideSeconds) { //: @item:intext Duration format hours and minutes return tr("%1:%2").arg(hours, 1, 10, QLatin1Char('0')) .arg(minutes, 2, 10, QLatin1Char('0')); } else if ((options & KFormat::ShowMilliseconds) == KFormat::ShowMilliseconds) { //: @item:intext Duration format hours, minutes, seconds, milliseconds return tr("%1:%2:%3.%4").arg(hours, 1, 10, QLatin1Char('0')) .arg(minutes, 2, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')) .arg(ms, 3, 10, QLatin1Char('0')); } else { // Default //: @item:intext Duration format hours, minutes, seconds return tr("%1:%2:%3").arg(hours, 1, 10, QLatin1Char('0')) .arg(minutes, 2, 10, QLatin1Char('0')) .arg(seconds, 2, 10, QLatin1Char('0')); } } Q_UNREACHABLE(); return QString(); } QString KFormatPrivate::formatDecimalDuration(quint64 msecs, int decimalPlaces) const { if (msecs >= MSecsInDay) { //: @item:intext %1 is a real number, e.g. 1.23 days return tr("%1 days").arg(m_locale.toString(msecs / (MSecsInDay * 1.0), 'f', decimalPlaces)); } else if (msecs >= MSecsInHour) { //: @item:intext %1 is a real number, e.g. 1.23 hours return tr("%1 hours").arg(m_locale.toString(msecs / (MSecsInHour * 1.0), 'f', decimalPlaces)); } else if (msecs >= MSecsInMinute) { //: @item:intext %1 is a real number, e.g. 1.23 minutes return tr("%1 minutes").arg(m_locale.toString(msecs / (MSecsInMinute * 1.0), 'f', decimalPlaces)); } else if (msecs >= MSecsInSecond) { //: @item:intext %1 is a real number, e.g. 1.23 seconds return tr("%1 seconds").arg(m_locale.toString(msecs / (MSecsInSecond * 1.0), 'f', decimalPlaces)); } //: @item:intext %1 is a whole number //~ singular %n millisecond //~ plural %n milliseconds return tr("%n millisecond(s)", nullptr, msecs); } enum DurationUnits { Days = 0, Hours, Minutes, Seconds }; static QString formatSingleDuration(DurationUnits units, int n) { // NB: n is guaranteed to be non-negative switch (units) { case Days: //: @item:intext %n is a whole number //~ singular %n day //~ plural %n days return KFormatPrivate::tr("%n day(s)", nullptr, n); case Hours: //: @item:intext %n is a whole number //~ singular %n hour //~ plural %n hours return KFormatPrivate::tr("%n hour(s)", nullptr, n); case Minutes: //: @item:intext %n is a whole number //~ singular %n minute //~ plural %n minutes return KFormatPrivate::tr("%n minute(s)", nullptr, n); case Seconds: //: @item:intext %n is a whole number //~ singular %n second //~ plural %n seconds return KFormatPrivate::tr("%n second(s)", nullptr, n); } Q_ASSERT(false); return QString(); } QString KFormatPrivate::formatSpelloutDuration(quint64 msecs) const { quint64 ms = msecs; int days = ms / MSecsInDay; ms = ms % (MSecsInDay); int hours = ms / MSecsInHour; ms = ms % MSecsInHour; int minutes = ms / MSecsInMinute; ms = ms % MSecsInMinute; int seconds = qRound(ms / 1000.0); // Handle correctly problematic case #1 (look at KFormatTest::prettyFormatDuration()) if (seconds == 60) { return formatSpelloutDuration(msecs - ms + MSecsInMinute); } if (days && hours) { /*: @item:intext days and hours. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem */ return tr("%1 and %2").arg(formatSingleDuration(Days, days)) .arg(formatSingleDuration(Hours, hours)); } else if (days) { return formatSingleDuration(Days, days); } else if (hours && minutes) { /*: @item:intext hours and minutes. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem */ return tr("%1 and %2").arg(formatSingleDuration(Hours, hours)) .arg(formatSingleDuration(Minutes, minutes)); } else if (hours) { return formatSingleDuration(Hours, hours); } else if (minutes && seconds) { /*: @item:intext minutes and seconds. This uses the previous item:intext messages. If this does not fit the grammar of your language please contact the i18n team to solve the problem */ return tr("%1 and %2").arg(formatSingleDuration(Minutes, minutes)) .arg(formatSingleDuration(Seconds, seconds)); } else if (minutes) { return formatSingleDuration(Minutes, minutes); } else { return formatSingleDuration(Seconds, seconds); } } QString KFormatPrivate::formatRelativeDate(const QDate &date, QLocale::FormatType format) const { if (!date.isValid()) { return tr("Invalid date", "used when a relative date string can't be generated because the date is invalid"); } const qint64 daysTo = QDate::currentDate().daysTo(date); if (daysTo > 7 || daysTo < -7) { return m_locale.toString(date, format); } switch (daysTo) { case 1: return tr("Tomorrow"); case 0: return tr("Today"); case -1: return tr("Yesterday"); } if (daysTo < -1) { switch (date.dayOfWeek()) { case 1: return tr("Last Monday", "most recent such day before today"); case 2: return tr("Last Tuesday", "most recent such day before today"); case 3: return tr("Last Wednesday", "most recent such day before today"); case 4: return tr("Last Thursday", "most recent such day before today"); case 5: return tr("Last Friday", "most recent such day before today"); case 6: return tr("Last Saturday", "most recent such day before today"); case 7: return tr("Last Sunday", "most recent such day before today"); } } else if (daysTo > 1) { switch (date.dayOfWeek()) { case 1: return tr("Next Monday", "the next such day after today"); case 2: return tr("Next Tuesday", "the next such day after today"); case 3: return tr("Next Wednesday", "the next such day after today"); case 4: return tr("Next Thursday", "the next such day after today"); case 5: return tr("Next Friday", "the next such day after today"); case 6: return tr("Next Saturday", "the next such day after today"); case 7: return tr("Next Sunday", "the next such day after today"); } } Q_UNREACHABLE(); } QString KFormatPrivate::formatRelativeDateTime(const QDateTime &dateTime, QLocale::FormatType format) const { const qint64 daysTo = QDate::currentDate().daysTo(dateTime.date()); if (daysTo > 7 || daysTo < -7) { return m_locale.toString(dateTime, format); } /*: relative datetime with %1 result of formatReleativeDate() and %2 the formatted time If this does not fit the grammar of your language please contact the i18n team to solve the problem */ return tr("%1, %2").arg(formatRelativeDate(dateTime.date(), format)) .arg(m_locale.toString(dateTime.time(), format)); }