KCoreAddons

kformatprivate.cpp
1/*
2 This file is part of the KDE Frameworks
3
4 SPDX-FileCopyrightText: 2013 Alex Merry <alex.merry@kdemail.net>
5 SPDX-FileCopyrightText: 2013 John Layt <jlayt@kde.org>
6 SPDX-FileCopyrightText: 2010 Michael Leupold <lemma@confuego.org>
7 SPDX-FileCopyrightText: 2009 Michael Pyne <mpyne@kde.org>
8 SPDX-FileCopyrightText: 2008 Albert Astals Cid <aacid@kde.org>
9
10 SPDX-License-Identifier: LGPL-2.0-or-later
11*/
12
13#include "kformatprivate_p.h"
14
15#include <QDateTime>
16#include <QSettings>
17#include <QStandardPaths>
18
19#include <math.h>
20
21KFormatPrivate::KFormatPrivate(const QLocale &locale)
22 : m_locale(locale)
23{
24}
25
26KFormatPrivate::~KFormatPrivate() = default;
27
28constexpr double bpow(int exp)
29{
30 return (exp > 0) ? 2.0 * bpow(exp - 1) : (exp < 0) ? 0.5 * bpow(exp + 1) : 1.0;
31}
32
33QString KFormatPrivate::formatValue(double value,
34 KFormat::Unit unit,
35 QString unitString,
36 int precision,
38 KFormat::BinaryUnitDialect dialect) const
39{
40 if (dialect <= KFormat::DefaultBinaryDialect || dialect > KFormat::LastBinaryDialect) {
42 }
43
44 if (static_cast<int>(prefix) < static_cast<int>(KFormat::UnitPrefix::Yocto) || static_cast<int>(prefix) > static_cast<int>(KFormat::UnitPrefix::Yotta)) {
46 }
47
48 double multiplier = 1024.0;
49 if (dialect == KFormat::MetricBinaryDialect) {
50 multiplier = 1000.0;
51 }
52
53 if (prefix == KFormat::UnitPrefix::AutoAdjust) {
54 int power = 0;
55 double adjustValue = qAbs(value);
56 while (adjustValue >= multiplier) {
57 adjustValue /= multiplier;
58 power += 1;
59 }
60 while (adjustValue && adjustValue < 1.0) {
61 adjustValue *= multiplier;
62 power -= 1;
63 }
64 const KFormat::UnitPrefix map[] = {
82 };
83 power = std::max(-8, std::min(8, power));
84 prefix = map[power + 8];
85 }
86
87 if (prefix == KFormat::UnitPrefix::Unity && unit == KFormat::Unit::Byte) {
88 precision = 0;
89 }
90
91 struct PrefixMapEntry {
93 double decimalFactor;
94 double binaryFactor;
95 QString prefixCharSI;
96 QString prefixCharIEC;
97 };
98
99 const PrefixMapEntry map[] = {
100 {KFormat::UnitPrefix::Yocto, 1e-24, bpow(-80), tr("y", "SI prefix for 10^⁻24"), QString()},
101 {KFormat::UnitPrefix::Zepto, 1e-21, bpow(-70), tr("z", "SI prefix for 10^⁻21"), QString()},
102 {KFormat::UnitPrefix::Atto, 1e-18, bpow(-60), tr("a", "SI prefix for 10^⁻18"), QString()},
103 {KFormat::UnitPrefix::Femto, 1e-15, bpow(-50), tr("f", "SI prefix for 10^⁻15"), QString()},
104 {KFormat::UnitPrefix::Pico, 1e-12, bpow(-40), tr("p", "SI prefix for 10^⁻12"), QString()},
105 {KFormat::UnitPrefix::Nano, 1e-9, bpow(-30), tr("n", "SI prefix for 10^⁻9"), QString()},
106 {KFormat::UnitPrefix::Micro, 1e-6, bpow(-20), tr("µ", "SI prefix for 10^⁻6"), QString()},
107 {KFormat::UnitPrefix::Milli, 1e-3, bpow(-10), tr("m", "SI prefix for 10^⁻3"), QString()},
109 {KFormat::UnitPrefix::Kilo, 1e3, bpow(10), tr("k", "SI prefix for 10^3"), tr("Ki", "IEC binary prefix for 2^10")},
110 {KFormat::UnitPrefix::Mega, 1e6, bpow(20), tr("M", "SI prefix for 10^6"), tr("Mi", "IEC binary prefix for 2^20")},
111 {KFormat::UnitPrefix::Giga, 1e9, bpow(30), tr("G", "SI prefix for 10^9"), tr("Gi", "IEC binary prefix for 2^30")},
112 {KFormat::UnitPrefix::Tera, 1e12, bpow(40), tr("T", "SI prefix for 10^12"), tr("Ti", "IEC binary prefix for 2^40")},
113 {KFormat::UnitPrefix::Peta, 1e15, bpow(50), tr("P", "SI prefix for 10^15"), tr("Pi", "IEC binary prefix for 2^50")},
114 {KFormat::UnitPrefix::Exa, 1e18, bpow(60), tr("E", "SI prefix for 10^18"), tr("Ei", "IEC binary prefix for 2^60")},
115 {KFormat::UnitPrefix::Zetta, 1e21, bpow(70), tr("Z", "SI prefix for 10^21"), tr("Zi", "IEC binary prefix for 2^70")},
116 {KFormat::UnitPrefix::Yotta, 1e24, bpow(80), tr("Y", "SI prefix for 10^24"), tr("Yi", "IEC binary prefix for 2^80")},
117 };
118
119 auto entry = std::find_if(std::begin(map), std::end(map), [prefix](const PrefixMapEntry &e) {
120 return e.prefix == prefix;
121 });
122
123 switch (unit) {
125 unitString = tr("bit", "Symbol of binary digit");
126 break;
128 unitString = tr("B", "Symbol of byte");
129 break;
131 unitString = tr("m", "Symbol of meter");
132 break;
134 unitString = tr("Hz", "Symbol of hertz");
135 break;
136 case KFormat::Unit::Other:
137 break;
138 }
139
140 if (prefix == KFormat::UnitPrefix::Unity) {
141 QString numString = m_locale.toString(value, 'f', precision);
142 //: value without prefix, format "<val> <unit>"
143 return tr("%1 %2", "no Prefix").arg(numString, unitString);
144 }
145
146 QString prefixString;
147 if (dialect == KFormat::MetricBinaryDialect) {
148 value /= entry->decimalFactor;
149 prefixString = entry->prefixCharSI;
150 } else {
151 value /= entry->binaryFactor;
152 if (dialect == KFormat::IECBinaryDialect) {
153 prefixString = entry->prefixCharIEC;
154 } else {
155 prefixString = entry->prefixCharSI.toUpper();
156 }
157 }
158
159 QString numString = m_locale.toString(value, 'f', precision);
160
161 //: value with prefix, format "<val> <prefix><unit>"
162 return tr("%1 %2%3", "MetricBinaryDialect").arg(numString, prefixString, unitString);
163}
164
165QString KFormatPrivate::formatByteSize(double size, int precision, KFormat::BinaryUnitDialect dialect, KFormat::BinarySizeUnits units) const
166{
167 // Current KDE default is IECBinaryDialect
168 const auto fallbackDialect = KFormat::IECBinaryDialect;
169
170 if (dialect <= KFormat::DefaultBinaryDialect || dialect > KFormat::LastBinaryDialect) {
171 const auto kdeglobals = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QStringLiteral("kdeglobals"));
172 QSettings settings(kdeglobals, QSettings::IniFormat);
173 dialect = static_cast<KFormat::BinaryUnitDialect>(settings.value("Locale/BinaryUnitDialect", fallbackDialect).toInt());
174 }
175
176 // Current KDE default is to auto-adjust so the size falls in the range 0 to 1000/1024
177 if (units < KFormat::DefaultBinaryUnits || units > KFormat::UnitLastUnit) {
179 }
180
181 int unit = 0; // Selects what unit to use
182 double multiplier = 1024.0;
183
184 if (dialect == KFormat::MetricBinaryDialect) {
185 multiplier = 1000.0;
186 }
187
188 // If a specific unit conversion is given, use it directly. Otherwise
189 // search until the result is in [0, multiplier] (or out of our range).
190 if (units == KFormat::DefaultBinaryUnits) {
191 while (qAbs(size) >= multiplier && unit < int(KFormat::UnitYottaByte)) {
192 size /= multiplier;
193 ++unit;
194 }
195 } else {
196 // A specific unit is in use
197 unit = static_cast<int>(units);
198 if (unit > 0) {
199 size /= pow(multiplier, unit);
200 }
201 }
202
203 // Bytes, no rounding
204 if (unit == 0) {
205 precision = 0;
206 }
207
208 QString numString = m_locale.toString(size, 'f', precision);
209
210 // Do not remove "//:" comments below, they are used by the translators.
211 // NB: we cannot pass pluralization arguments, as the size may be negative
212 if (dialect == KFormat::MetricBinaryDialect) {
213 switch (unit) {
215 //: MetricBinaryDialect size in bytes
216 return tr("%1 B", "MetricBinaryDialect").arg(numString);
218 //: MetricBinaryDialect size in 1000 bytes
219 return tr("%1 kB", "MetricBinaryDialect").arg(numString);
221 //: MetricBinaryDialect size in 10^6 bytes
222 return tr("%1 MB", "MetricBinaryDialect").arg(numString);
224 //: MetricBinaryDialect size in 10^9 bytes
225 return tr("%1 GB", "MetricBinaryDialect").arg(numString);
227 //: MetricBinaryDialect size in 10^12 bytes
228 return tr("%1 TB", "MetricBinaryDialect").arg(numString);
230 //: MetricBinaryDialect size in 10^15 bytes
231 return tr("%1 PB", "MetricBinaryDialect").arg(numString);
233 //: MetricBinaryDialect size in 10^18 byte
234 return tr("%1 EB", "MetricBinaryDialect").arg(numString);
236 //: MetricBinaryDialect size in 10^21 bytes
237 return tr("%1 ZB", "MetricBinaryDialect").arg(numString);
239 //: MetricBinaryDialect size in 10^24 bytes
240 return tr("%1 YB", "MetricBinaryDialect").arg(numString);
241 }
242 } else if (dialect == KFormat::JEDECBinaryDialect) {
243 switch (unit) {
245 //: JEDECBinaryDialect memory size in bytes
246 return tr("%1 B", "JEDECBinaryDialect").arg(numString);
248 //: JEDECBinaryDialect memory size in 1024 bytes
249 return tr("%1 KB", "JEDECBinaryDialect").arg(numString);
251 //: JEDECBinaryDialect memory size in 10^20 bytes
252 return tr("%1 MB", "JEDECBinaryDialect").arg(numString);
254 //: JEDECBinaryDialect memory size in 10^30 bytes
255 return tr("%1 GB", "JEDECBinaryDialect").arg(numString);
257 //: JEDECBinaryDialect memory size in 10^40 bytes
258 return tr("%1 TB", "JEDECBinaryDialect").arg(numString);
260 //: JEDECBinaryDialect memory size in 10^50 bytes
261 return tr("%1 PB", "JEDECBinaryDialect").arg(numString);
263 //: JEDECBinaryDialect memory size in 10^60 bytes
264 return tr("%1 EB", "JEDECBinaryDialect").arg(numString);
266 //: JEDECBinaryDialect memory size in 10^70 bytes
267 return tr("%1 ZB", "JEDECBinaryDialect").arg(numString);
269 //: JEDECBinaryDialect memory size in 10^80 bytes
270 return tr("%1 YB", "JEDECBinaryDialect").arg(numString);
271 }
272 } else { // KFormat::IECBinaryDialect, KFormat::DefaultBinaryDialect
273 switch (unit) {
275 //: IECBinaryDialect size in bytes
276 return tr("%1 B", "IECBinaryDialect").arg(numString);
278 //: IECBinaryDialect size in 1024 bytes
279 return tr("%1 KiB", "IECBinaryDialect").arg(numString);
281 //: IECBinaryDialect size in 10^20 bytes
282 return tr("%1 MiB", "IECBinaryDialect").arg(numString);
284 //: IECBinaryDialect size in 10^30 bytes
285 return tr("%1 GiB", "IECBinaryDialect").arg(numString);
287 //: IECBinaryDialect size in 10^40 bytes
288 return tr("%1 TiB", "IECBinaryDialect").arg(numString);
290 //: IECBinaryDialect size in 10^50 bytes
291 return tr("%1 PiB", "IECBinaryDialect").arg(numString);
293 //: IECBinaryDialect size in 10^60 bytes
294 return tr("%1 EiB", "IECBinaryDialect").arg(numString);
296 //: IECBinaryDialect size in 10^70 bytes
297 return tr("%1 ZiB", "IECBinaryDialect").arg(numString);
299 //: IECBinaryDialect size in 10^80 bytes
300 return tr("%1 YiB", "IECBinaryDialect").arg(numString);
301 }
302 }
303
304 // Should never reach here
305 Q_ASSERT(false);
306 return numString;
307}
308
309enum TimeConstants {
310 MSecsInDay = 86400000,
311 MSecsInHour = 3600000,
312 MSecsInMinute = 60000,
313 MSecsInSecond = 1000,
314};
315
316enum DurationUnits {
317 Days = 0,
318 Hours,
319 Minutes,
320 Seconds,
321};
322
323static QString formatSingleAbbreviatedDuration(DurationUnits units, int n)
324{
325 switch (units) {
326 case Days:
327 //: @item:intext abbreviated amount of days
328 //~ singular %n d
329 //~ plural %n d
330 return KFormatPrivate::tr("%n d", nullptr, n);
331 case Hours:
332 //: @item:intext abbreviated amount of hours
333 //~ singular %n hr
334 //~ plural %n hr
335 return KFormatPrivate::tr("%n hr", nullptr, n);
336 case Minutes:
337 //: @item:intext abbreviated amount of minutes
338 //~ singular %n min
339 //~ plural %n min
340 return KFormatPrivate::tr("%n min", nullptr, n);
341 case Seconds:
342 //: @item:intext abbreviated amount of seconds
343 //~ singular %n sec
344 //~ plural %n sec
345 return KFormatPrivate::tr("%n sec", nullptr, n);
346 }
347 Q_ASSERT(false);
348 return QString();
349}
350QString KFormatPrivate::formatDuration(quint64 msecs, KFormat::DurationFormatOptions options) const
351{
352 quint64 ms = msecs;
353 if (options & KFormat::HideSeconds) {
354 // round to nearest minute
355 ms = qRound64(ms / (qreal)MSecsInMinute) * MSecsInMinute;
356 } else if (!(options & KFormat::ShowMilliseconds)) {
357 // round to nearest second
358 ms = qRound64(ms / (qreal)MSecsInSecond) * MSecsInSecond;
359 }
360
361 int hours = ms / MSecsInHour;
362 ms = ms % MSecsInHour;
363 int minutes = ms / MSecsInMinute;
364 ms = ms % MSecsInMinute;
365 int seconds = ms / MSecsInSecond;
366 ms = ms % MSecsInSecond;
367
370 //: @item:intext Duration format minutes, seconds and milliseconds
371 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'));
372 } else if ((options & KFormat::FoldHours) == KFormat::FoldHours) {
373 //: @item:intext Duration format minutes and seconds
374 return tr("%1m%2s").arg(hours * 60 + minutes, 1, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0'));
375 } else if ((options & KFormat::HideSeconds) == KFormat::HideSeconds) {
376 //: @item:intext Duration format hours and minutes
377 return tr("%1h%2m").arg(hours, 1, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0'));
378 } else if ((options & KFormat::ShowMilliseconds) == KFormat::ShowMilliseconds) {
379 //: @item:intext Duration format hours, minutes, seconds, milliseconds
380 return tr("%1h%2m%3.%4s")
381 .arg(hours, 1, 10, QLatin1Char('0'))
382 .arg(minutes, 2, 10, QLatin1Char('0'))
383 .arg(seconds, 2, 10, QLatin1Char('0'))
384 .arg(ms, 3, 10, QLatin1Char('0'));
385 } else { // Default
386 //: @item:intext Duration format hours, minutes, seconds
387 return tr("%1h%2m%3s").arg(hours, 1, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0'));
388 }
389
390 } else if (options & KFormat::AbbreviatedDuration) {
391 if (options & KFormat::FoldHours) {
392 minutes += 60 * hours;
393 hours = 0;
394 }
395
396 if (hours == 0 && minutes == 0) {
397 return (options & KFormat::HideSeconds) ? formatSingleAbbreviatedDuration(Minutes, minutes) : formatSingleAbbreviatedDuration(Seconds, seconds);
398 }
399
400 if (hours == 0) {
401 if (options & KFormat::HideSeconds) {
402 return formatSingleAbbreviatedDuration(Minutes, minutes);
403 }
404 //: @item:intext abbreviated amount of minutes and abbreviated amount of seconds
405 return tr("%1 %2").arg(formatSingleAbbreviatedDuration(Minutes, minutes), formatSingleAbbreviatedDuration(Seconds, seconds));
406 }
407
408 if (options & KFormat::HideSeconds) {
409 //: @item:intext abbreviated amount of hours and abbreviated amount of minutes
410 return tr("%1 %2").arg(formatSingleAbbreviatedDuration(Hours, hours), formatSingleAbbreviatedDuration(Minutes, minutes));
411 }
412
413 //: @item:intext abbreviated amount of hours, abbreviated amount of minutes and abbreviated amount of seconds
414 return tr("%1 %2 %3")
415 .arg(formatSingleAbbreviatedDuration(Hours, hours),
416 formatSingleAbbreviatedDuration(Minutes, minutes),
417 formatSingleAbbreviatedDuration(Seconds, seconds));
418
419 } else {
421 //: @item:intext Duration format minutes, seconds and milliseconds
422 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'));
423 } else if ((options & KFormat::FoldHours) == KFormat::FoldHours) {
424 //: @item:intext Duration format minutes and seconds
425 return tr("%1:%2").arg(hours * 60 + minutes, 1, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0'));
426 } else if ((options & KFormat::HideSeconds) == KFormat::HideSeconds) {
427 //: @item:intext Duration format hours and minutes
428 return tr("%1:%2").arg(hours, 1, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0'));
429 } else if ((options & KFormat::ShowMilliseconds) == KFormat::ShowMilliseconds) {
430 //: @item:intext Duration format hours, minutes, seconds, milliseconds
431 return tr("%1:%2:%3.%4")
432 .arg(hours, 1, 10, QLatin1Char('0'))
433 .arg(minutes, 2, 10, QLatin1Char('0'))
434 .arg(seconds, 2, 10, QLatin1Char('0'))
435 .arg(ms, 3, 10, QLatin1Char('0'));
436 } else { // Default
437 //: @item:intext Duration format hours, minutes, seconds
438 return tr("%1:%2:%3").arg(hours, 1, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0'));
439 }
440 }
441
442 Q_UNREACHABLE();
443 return QString();
444}
445
446QString KFormatPrivate::formatDecimalDuration(quint64 msecs, int decimalPlaces) const
447{
448 if (msecs >= MSecsInDay) {
449 //: @item:intext %1 is a real number, e.g. 1.23 days
450 return tr("%1 days").arg(m_locale.toString(msecs / (+MSecsInDay * 1.0), 'f', decimalPlaces));
451 } else if (msecs >= MSecsInHour) {
452 //: @item:intext %1 is a real number, e.g. 1.23 hours
453 return tr("%1 hours").arg(m_locale.toString(msecs / (+MSecsInHour * 1.0), 'f', decimalPlaces));
454 } else if (msecs >= MSecsInMinute) {
455 //: @item:intext %1 is a real number, e.g. 1.23 minutes
456 return tr("%1 minutes").arg(m_locale.toString(msecs / (+MSecsInMinute * 1.0), 'f', decimalPlaces));
457 } else if (msecs >= MSecsInSecond) {
458 //: @item:intext %1 is a real number, e.g. 1.23 seconds
459 return tr("%1 seconds").arg(m_locale.toString(msecs / (+MSecsInSecond * 1.0), 'f', decimalPlaces));
460 }
461 //: @item:intext %1 is a whole number
462 //~ singular %n millisecond
463 //~ plural %n milliseconds
464 return tr("%n millisecond(s)", nullptr, msecs);
465}
466
467static QString formatSingleDuration(DurationUnits units, int n)
468{
469 // NB: n is guaranteed to be non-negative
470 switch (units) {
471 case Days:
472 //: @item:intext %n is a whole number
473 //~ singular %n day
474 //~ plural %n days
475 return KFormatPrivate::tr("%n day(s)", nullptr, n);
476 case Hours:
477 //: @item:intext %n is a whole number
478 //~ singular %n hour
479 //~ plural %n hours
480 return KFormatPrivate::tr("%n hour(s)", nullptr, n);
481 case Minutes:
482 //: @item:intext %n is a whole number
483 //~ singular %n minute
484 //~ plural %n minutes
485 return KFormatPrivate::tr("%n minute(s)", nullptr, n);
486 case Seconds:
487 //: @item:intext %n is a whole number
488 //~ singular %n second
489 //~ plural %n seconds
490 return KFormatPrivate::tr("%n second(s)", nullptr, n);
491 }
492 Q_ASSERT(false);
493 return QString();
494}
495
496QString KFormatPrivate::formatSpelloutDuration(quint64 msecs) const
497{
498 quint64 ms = msecs;
499 int days = ms / MSecsInDay;
500 ms = ms % (MSecsInDay);
501 int hours = ms / MSecsInHour;
502 ms = ms % MSecsInHour;
503 int minutes = ms / MSecsInMinute;
504 ms = ms % MSecsInMinute;
505 int seconds = qRound(ms / 1000.0);
506
507 // Handle correctly problematic case #1 (look at KFormatTest::prettyFormatDuration())
508 if (seconds == 60) {
509 return formatSpelloutDuration(msecs - ms + MSecsInMinute);
510 }
511
512 if (days && hours) {
513 /*: @item:intext days and hours. This uses the previous item:intext messages.
514 If this does not fit the grammar of your language please contact the i18n team to solve the problem */
515 return tr("%1 and %2").arg(formatSingleDuration(Days, days), formatSingleDuration(Hours, hours));
516 } else if (days) {
517 return formatSingleDuration(Days, days);
518 } else if (hours && minutes) {
519 /*: @item:intext hours and minutes. This uses the previous item:intext messages.
520 If this does not fit the grammar of your language please contact the i18n team to solve the problem */
521 return tr("%1 and %2").arg(formatSingleDuration(Hours, hours), formatSingleDuration(Minutes, minutes));
522 } else if (hours) {
523 return formatSingleDuration(Hours, hours);
524 } else if (minutes && seconds) {
525 /*: @item:intext minutes and seconds. This uses the previous item:intext messages.
526 If this does not fit the grammar of your language please contact the i18n team to solve the problem */
527 return tr("%1 and %2").arg(formatSingleDuration(Minutes, minutes), formatSingleDuration(Seconds, seconds));
528 } else if (minutes) {
529 return formatSingleDuration(Minutes, minutes);
530 } else {
531 return formatSingleDuration(Seconds, seconds);
532 }
533}
534
535QString KFormatPrivate::formatRelativeDate(const QDate &date, QLocale::FormatType format) const
536{
537 if (!date.isValid()) {
538 return tr("Invalid date", "used when a relative date string can't be generated because the date is invalid");
539 }
540
541 const qint64 daysTo = QDate::currentDate().daysTo(date);
542 if (daysTo > 2 || daysTo < -2) {
543 return m_locale.toString(date, format);
544 }
545
546 switch (daysTo) {
547 case 2:
548 return tr("In two days");
549 case 1:
550 return tr("Tomorrow");
551 case 0:
552 return tr("Today");
553 case -1:
554 return tr("Yesterday");
555 case -2:
556 return tr("Two days ago");
557 }
558 Q_UNREACHABLE();
559}
560
561QString KFormatPrivate::formatRelativeDateTime(const QDateTime &dateTime, QLocale::FormatType format) const
562{
564
565 const auto secsToNow = dateTime.secsTo(now);
566 constexpr int secsInAHour = 60 * 60;
567 if (secsToNow >= 0 && secsToNow < secsInAHour) {
568 const int minutesToNow = secsToNow / 60;
569 if (minutesToNow <= 1) {
570 return tr("Just now");
571 } else {
572 //: @item:intext %1 is a whole number
573 //~ singular %n minute ago
574 //~ plural %n minutes ago
575 return tr("%n minute(s) ago", nullptr, minutesToNow);
576 }
577 }
578
579 const auto timeFormatType = format == QLocale::FormatType::LongFormat ? QLocale::FormatType::ShortFormat : format;
580 const qint64 daysToNow = dateTime.daysTo(now);
581 QString dateString;
582 if (daysToNow < 2 && daysToNow > -2) {
583 dateString = formatRelativeDate(dateTime.date(), format);
584 } else {
585 dateString = m_locale.toString(dateTime.date(), format);
586 }
587
588 /*: relative datetime with %1 result of QLocale.toString(date, format) or formatRelativeDate
589 and %2 result of QLocale.toString(time, timeformatType)
590 If this does not fit the grammar of your language please contact the i18n team to solve the problem */
591 QString formattedDate = tr("%1 at %2").arg(dateString, m_locale.toString(dateTime.time(), timeFormatType));
592
593 return formattedDate.replace(0, 1, formattedDate.at(0).toUpper());
594}
595
596QString KFormatPrivate::formatDistance(double distance, KFormat::DistanceFormatOptions options) const
597{
598 if (((options & KFormat::MetricDistanceUnits) == 0)
599 && (m_locale.measurementSystem() == QLocale::ImperialUSSystem || m_locale.measurementSystem() == QLocale::ImperialUSSystem)) {
600 return formatImperialDistance(distance);
601 }
602 return formatMetricDistance(distance);
603}
604
605[[nodiscard]] QString KFormatPrivate::formatImperialDistance(double distance) const
606{
607 const auto feet = distance / 0.3048;
608 if (feet < 500.0) {
609 return KFormatPrivate::tr("%1 ft", "distance in feet").arg(m_locale.toString((int)std::round(feet)));
610 }
611 const auto miles = distance / 1609.344;
612 if (miles < 10.0) {
613 return KFormatPrivate::tr("%1 mi", "distance in miles").arg(m_locale.toString((int)(std::round(miles * 10.0)) / 10.0));
614 }
615 return KFormatPrivate::tr("%1 mi", "distance in miles").arg(m_locale.toString((int)std::round(miles)));
616}
617
618[[nodiscard]] QString KFormatPrivate::formatMetricDistance(double distance) const
619{
620 if (distance < 1000.0) {
621 return KFormatPrivate::tr("%1 m", "distance in meter").arg(m_locale.toString((int)std::round(distance)));
622 }
623 if (distance < 10000.0) {
624 return KFormatPrivate::tr("%1 km", "distance in kilometer").arg(m_locale.toString((int)(std::round(distance / 100.0)) / 10.0));
625 }
626 return KFormatPrivate::tr("%1 km", "distance in kilometer").arg(m_locale.toString((int)std::round(distance / 1000.0)));
627}
@ MetricDistanceUnits
Force the use of metric unites regardless of the current locale.
Definition kformat.h:203
@ ShowMilliseconds
Include milliseconds in format, e.g. 1:23:45.678.
Definition kformat.h:186
@ FoldHours
Fold the hours into the minutes, e.g. 83:45 or 83m45s, overrides HideSeconds.
Definition kformat.h:188
@ HideSeconds
Hide the seconds, e.g. 1:23 or 1h23m, overrides ShowMilliseconds.
Definition kformat.h:187
@ InitialDuration
Default formatting in localized 1h23m45s format.
Definition kformat.h:185
@ AbbreviatedDuration
Use abbreviated units (e.g.
Definition kformat.h:189
QFlags< DurationFormatOption > DurationFormatOptions
Stores a combination of DurationFormatOption values.
Definition kformat.h:195
Unit
These units are used in KDE by the formatValue() function.
Definition kformat.h:107
@ Bit
"bit"
Definition kformat.h:109
@ Hertz
"Hz"
Definition kformat.h:112
UnitPrefix
These prefixes are used in KDE by the formatValue() function.
Definition kformat.h:126
@ Milli
–/-/m 10^-3
Definition kformat.h:137
@ Zetta
Zi/Z/Z 2^70/10^21.
Definition kformat.h:149
@ Tera
Ti/T/T 2^40/10^12.
Definition kformat.h:146
@ Kilo
Ki/K/k 1024/1000.
Definition kformat.h:143
@ Peta
Pi/P/P 2^50/10^15.
Definition kformat.h:147
@ Giga
Gi/G/G 2^30/10^09.
Definition kformat.h:145
@ Femto
–/-/f 10^-15
Definition kformat.h:133
@ Atto
–/-/a 10^-18
Definition kformat.h:132
@ Nano
–/-/n 10^-9
Definition kformat.h:135
@ Yotta
Yi/Y/Y 2^80/10^24.
Definition kformat.h:150
@ Exa
Ei/E/E 2^60/10^18.
Definition kformat.h:148
@ Yocto
–/-/y 10^-24
Definition kformat.h:130
@ Pico
–/-/p 10^-12
Definition kformat.h:134
@ Zepto
–/-/z 10^-21
Definition kformat.h:131
@ Micro
–/-/µ 10^-6
Definition kformat.h:136
@ AutoAdjust
Auto-choose a unit such that the result is in the range [0, 1000 or 1024)
Definition kformat.h:128
@ Mega
Mi/M/M 2^20/10^06.
Definition kformat.h:144
BinaryUnitDialect
This enum chooses what dialect is used for binary units.
Definition kformat.h:171
@ JEDECBinaryDialect
KB, MB, etc. 2^(10*n)
Definition kformat.h:174
@ IECBinaryDialect
KiB, MiB, etc. 2^(10*n)
Definition kformat.h:173
@ MetricBinaryDialect
SI Units, kB, MB, etc. 10^(3*n)
Definition kformat.h:175
BinarySizeUnits
These binary units are used in KDE by the formatByteSize() function.
Definition kformat.h:84
@ UnitPetaByte
PiB/PB/PB 2^50/10^15 bytes.
Definition kformat.h:94
@ UnitTeraByte
TiB/TB/TB 2^40/10^12 bytes.
Definition kformat.h:93
@ UnitZettaByte
ZiB/ZB/ZB 2^70/10^21 bytes.
Definition kformat.h:96
@ UnitGigaByte
GiB/GB/GB 2^30/10^09 bytes.
Definition kformat.h:92
@ DefaultBinaryUnits
Auto-choose a unit such that the result is in the range [0, 1000 or 1024)
Definition kformat.h:86
@ UnitKiloByte
KiB/KB/kB 1024/1000 bytes.
Definition kformat.h:90
@ UnitYottaByte
YiB/YB/YB 2^80/10^24 bytes.
Definition kformat.h:97
@ UnitMegaByte
MiB/MB/MB 2^20/10^06 bytes.
Definition kformat.h:91
@ UnitByte
B 1 byte.
Definition kformat.h:89
@ UnitExaByte
EiB/EB/EB 2^60/10^18 bytes.
Definition kformat.h:95
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
char32_t toUpper(char32_t ucs4)
QDate currentDate()
qint64 daysTo(QDate d) const const
bool isValid(int year, int month, int day)
QDateTime currentDateTime()
QDate date() const const
qint64 daysTo(const QDateTime &other) const const
qint64 secsTo(const QDateTime &other) const const
QTime time() const const
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString toUpper() const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 31 2025 11:58:24 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.