format_values.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "ui/text/format_values.h"
  8. #include "lang/lang_keys.h"
  9. #include "countries/countries_instance.h"
  10. #include <QtCore/QLocale>
  11. #include <locale>
  12. #include <sstream>
  13. #include <iostream>
  14. namespace Ui {
  15. namespace {
  16. [[nodiscard]] QString FormatTextWithReadyAndTotal(
  17. tr::phrase<lngtag_ready, lngtag_total, lngtag_mb> phrase,
  18. qint64 ready,
  19. qint64 total) {
  20. QString readyStr, totalStr, mb;
  21. if (total >= 1024 * 1024) { // more than 1 mb
  22. const qint64 readyTenthMb = (ready * 10 / (1024 * 1024));
  23. const qint64 totalTenthMb = (total * 10 / (1024 * 1024));
  24. readyStr = QString::number(readyTenthMb / 10)
  25. + '.'
  26. + QString::number(readyTenthMb % 10);
  27. totalStr = QString::number(totalTenthMb / 10)
  28. + '.'
  29. + QString::number(totalTenthMb % 10);
  30. mb = u"MB"_q;
  31. } else if (total >= 1024) {
  32. qint64 readyKb = (ready / 1024), totalKb = (total / 1024);
  33. readyStr = QString::number(readyKb);
  34. totalStr = QString::number(totalKb);
  35. mb = u"KB"_q;
  36. } else {
  37. readyStr = QString::number(ready);
  38. totalStr = QString::number(total);
  39. mb = u"B"_q;
  40. }
  41. return phrase(tr::now, lt_ready, readyStr, lt_total, totalStr, lt_mb, mb);
  42. }
  43. } // namespace
  44. QString FormatSizeText(qint64 size) {
  45. if (size >= 1024 * 1024) { // more than 1 mb
  46. const qint64 sizeTenthMb = (size * 10 / (1024 * 1024));
  47. return QString::number(sizeTenthMb / 10)
  48. + '.'
  49. + QString::number(sizeTenthMb % 10) + u" MB"_q;
  50. }
  51. if (size >= 1024) {
  52. const qint64 sizeTenthKb = (size * 10 / 1024);
  53. return QString::number(sizeTenthKb / 10)
  54. + '.'
  55. + QString::number(sizeTenthKb % 10) + u" KB"_q;
  56. }
  57. return QString::number(size) + u" B"_q;
  58. }
  59. QString FormatDownloadText(qint64 ready, qint64 total) {
  60. return FormatTextWithReadyAndTotal(
  61. tr::lng_save_downloaded,
  62. ready,
  63. total);
  64. }
  65. QString FormatProgressText(qint64 ready, qint64 total) {
  66. return FormatTextWithReadyAndTotal(
  67. tr::lng_media_save_progress,
  68. ready,
  69. total);
  70. }
  71. QString FormatDateTime(QDateTime date) {
  72. const auto now = QDateTime::currentDateTime();
  73. if (date.date() == now.date()) {
  74. return tr::lng_mediaview_today(
  75. tr::now,
  76. lt_time,
  77. QLocale().toString(date.time(), QLocale::ShortFormat));
  78. } else if (date.date().addDays(1) == now.date()) {
  79. return tr::lng_mediaview_yesterday(
  80. tr::now,
  81. lt_time,
  82. QLocale().toString(date.time(), QLocale::ShortFormat));
  83. } else {
  84. return tr::lng_mediaview_date_time(
  85. tr::now,
  86. lt_date,
  87. QLocale().toString(date.date(), QLocale::ShortFormat),
  88. lt_time,
  89. QLocale().toString(date.time(), QLocale::ShortFormat));
  90. }
  91. }
  92. QString FormatDurationText(qint64 duration) {
  93. qint64 hours = (duration / 3600), minutes = (duration % 3600) / 60, seconds = duration % 60;
  94. return (hours ? QString::number(hours) + ':' : QString()) + (minutes >= 10 ? QString() : QString('0')) + QString::number(minutes) + ':' + (seconds >= 10 ? QString() : QString('0')) + QString::number(seconds);
  95. }
  96. QString FormatDurationWords(qint64 duration) {
  97. if (duration > 59) {
  98. auto minutes = (duration / 60);
  99. auto minutesCount = tr::lng_duration_minsec_minutes(tr::now, lt_count, minutes);
  100. auto seconds = (duration % 60);
  101. auto secondsCount = tr::lng_duration_minsec_seconds(tr::now, lt_count, seconds);
  102. return tr::lng_duration_minutes_seconds(tr::now, lt_minutes_count, minutesCount, lt_seconds_count, secondsCount);
  103. }
  104. return tr::lng_seconds(tr::now, lt_count, duration);
  105. }
  106. QString FormatDurationWordsSlowmode(qint64 duration) {
  107. if (duration > 59) {
  108. auto minutes = (duration / 60);
  109. auto minutesCount = tr::lng_duration_minsec_minutes(tr::now, lt_count, minutes);
  110. auto seconds = (duration % 60);
  111. auto secondsCount = tr::lng_duration_minsec_seconds(tr::now, lt_count, seconds);
  112. return tr::lng_duration_minutes_seconds(tr::now, lt_minutes_count, minutesCount, lt_seconds_count, secondsCount);
  113. }
  114. return tr::lng_slowmode_seconds(tr::now, lt_count, duration);
  115. }
  116. QString FormatDurationAndSizeText(qint64 duration, qint64 size) {
  117. return tr::lng_duration_and_size(tr::now, lt_duration, FormatDurationText(duration), lt_size, FormatSizeText(size));
  118. }
  119. QString FormatGifAndSizeText(qint64 size) {
  120. return tr::lng_duration_and_size(tr::now, lt_duration, u"GIF"_q, lt_size, FormatSizeText(size));
  121. }
  122. QString FormatPlayedText(qint64 played, qint64 duration) {
  123. return tr::lng_duration_played(tr::now, lt_played, FormatDurationText(played), lt_duration, FormatDurationText(duration));
  124. }
  125. QString FillAmountAndCurrency(
  126. int64 amount,
  127. const QString &currency,
  128. bool forceStripDotZero) {
  129. // std::abs doesn't work on that one :/
  130. Expects(amount != std::numeric_limits<int64>::min());
  131. if (currency == kCreditsCurrency) {
  132. return QChar(0x2B50) + Lang::FormatCountDecimal(std::abs(amount));
  133. }
  134. const auto rule = LookupCurrencyRule(currency);
  135. const auto prefix = (amount < 0)
  136. ? QString::fromUtf8("\xe2\x88\x92")
  137. : QString();
  138. const auto value = std::abs(amount) / std::pow(10., rule.exponent);
  139. const auto name = (*rule.international)
  140. ? QString::fromUtf8(rule.international)
  141. : currency;
  142. auto result = prefix;
  143. if (rule.left) {
  144. result.append(name);
  145. if (rule.space) result.append(' ');
  146. }
  147. const auto precision = ((!rule.stripDotZero && !forceStripDotZero)
  148. || std::floor(value) != value)
  149. ? rule.exponent
  150. : 0;
  151. result.append(FormatWithSeparators(
  152. value,
  153. precision,
  154. rule.decimal,
  155. rule.thousands));
  156. if (!rule.left) {
  157. if (rule.space) result.append(' ');
  158. result.append(name);
  159. }
  160. return result;
  161. }
  162. CurrencyRule LookupCurrencyRule(const QString &currency) {
  163. static const auto kRules = std::vector<std::pair<QString, CurrencyRule>>{
  164. { u"AED"_q, { "", ',', '.', true, true } },
  165. { u"AFN"_q, {} },
  166. { u"ALL"_q, { "", '.', ',', false } },
  167. { u"AMD"_q, { "", ',', '.', false, true } },
  168. { u"ARS"_q, { "", '.', ',', true, true } },
  169. { u"AUD"_q, { "AU$" } },
  170. { u"AZN"_q, { "", ' ', ',', false, true } },
  171. { u"BAM"_q, { "", '.', ',', false, true } },
  172. { u"BDT"_q, { "", ',', '.', true, true } },
  173. { u"BGN"_q, { "", ' ', ',', false, true } },
  174. { u"BHD"_q, { "", ',', '.', true, true, 3 } },
  175. { u"BND"_q, { "", '.', ',' } },
  176. { u"BOB"_q, { "", '.', ',', true, true } },
  177. { u"BRL"_q, { "R$", '.', ',', true, true } },
  178. { u"BYN"_q, { "", ' ', ',', false, true } },
  179. { u"CAD"_q, { "CA$" } },
  180. { u"CHF"_q, { "", '\'', '.', false, true } },
  181. { u"CLP"_q, { "", '.', ',', true, true, 0 } },
  182. { u"CNY"_q, { "\x43\x4E\xC2\xA5" } },
  183. { u"COP"_q, { "", '.', ',', true, true } },
  184. { u"CRC"_q, { "", '.', ',' } },
  185. { u"CZK"_q, { "", ' ', ',', false, true } },
  186. { u"DKK"_q, { "", '\0', ',', false, true } },
  187. { u"DOP"_q, {} },
  188. { u"DZD"_q, { "", ',', '.', true, true } },
  189. { u"EGP"_q, { "", ',', '.', true, true } },
  190. { u"ETB"_q, {} },
  191. { u"EUR"_q, { "\xE2\x82\xAC", ' ', ',', false, true } },
  192. { u"GBP"_q, { "\xC2\xA3" } },
  193. { u"GEL"_q, { "", ' ', ',', false, true } },
  194. { u"GHS"_q, {} },
  195. { u"GTQ"_q, {} },
  196. { u"HKD"_q, { "HK$" } },
  197. { u"HNL"_q, { "", ',', '.', true, true } },
  198. { u"HRK"_q, { "", '.', ',', false, true } },
  199. { u"HUF"_q, { "", ' ', ',', false, true } },
  200. { u"IDR"_q, { "", '.', ',' } },
  201. { u"ILS"_q, { "\xE2\x82\xAA", ',', '.', true, true } },
  202. { u"INR"_q, { "\xE2\x82\xB9" } },
  203. { u"IQD"_q, { "", ',', '.', true, true, 3 } },
  204. { u"IRR"_q, { "", ',', '/', false, true } },
  205. { u"ISK"_q, { "", '.', ',', false, true, 0 } },
  206. { u"JMD"_q, {} },
  207. { u"JOD"_q, { "", ',', '.', true, false, 3 } },
  208. { u"JPY"_q, { "\xC2\xA5", ',', '.', true, false, 0 } },
  209. { u"KES"_q, {} },
  210. { u"KGS"_q, { "", ' ', '-', false, true } },
  211. { u"KRW"_q, { "\xE2\x82\xA9", ',', '.', true, false, 0 } },
  212. { u"KZT"_q, { "", ' ', '-' } },
  213. { u"LBP"_q, { "", ',', '.', true, true } },
  214. { u"LKR"_q, { "", ',', '.', true, true } },
  215. { u"MAD"_q, { "", ',', '.', true, true } },
  216. { u"MDL"_q, { "", ',', '.', false, true } },
  217. { u"MMK"_q, {} },
  218. { u"MNT"_q, { "", ' ', ',' } },
  219. { u"MOP"_q, {} },
  220. { u"MUR"_q, {} },
  221. { u"MVR"_q, { "", ',', '.', false, true } },
  222. { u"MXN"_q, { "MX$" } },
  223. { u"MYR"_q, {} },
  224. { u"MZN"_q, {} },
  225. { u"NGN"_q, {} },
  226. { u"NIO"_q, { "", ',', '.', true, true } },
  227. { u"NOK"_q, { "", ' ', ',', true, true } },
  228. { u"NPR"_q, {} },
  229. { u"NZD"_q, { "NZ$" } },
  230. { u"PAB"_q, { "", ',', '.', true, true } },
  231. { u"PEN"_q, { "", ',', '.', true, true } },
  232. { u"PHP"_q, {} },
  233. { u"PKR"_q, {} },
  234. { u"PLN"_q, { "", ' ', ',', false, true } },
  235. { u"PYG"_q, { "", '.', ',', true, true, 0 } },
  236. { u"QAR"_q, { "", ',', '.', true, true } },
  237. { u"RON"_q, { "", '.', ',', false, true } },
  238. { u"RSD"_q, { "", '.', ',', false, true } },
  239. { u"RUB"_q, { "", ' ', ',', false, true } },
  240. { u"SAR"_q, { "", ',', '.', true, true } },
  241. { u"SEK"_q, { "", '.', ',', false, true } },
  242. { u"SGD"_q, {} },
  243. { u"SYP"_q, { "", ',', '.', true, true } },
  244. { u"THB"_q, { "\xE0\xB8\xBF" } },
  245. { u"TJS"_q, { "", ' ', ';', false, true } },
  246. { u"TRY"_q, { "", '.', ',', false, true } },
  247. { u"TTD"_q, {} },
  248. { u"TWD"_q, { "NT$" } },
  249. { u"TZS"_q, {} },
  250. { u"UAH"_q, { "", ' ', ',', false } },
  251. { u"UGX"_q, { "", ',', '.', true, false, 0 } },
  252. { u"USD"_q, { "$" } },
  253. { u"UYU"_q, { "", '.', ',', true, true } },
  254. { u"UZS"_q, { "", ' ', ',', false, true } },
  255. { u"VEF"_q, { "", '.', ',', true, true } },
  256. { u"VND"_q, { "\xE2\x82\xAB", '.', ',', false, true, 0 } },
  257. { u"YER"_q, { "", ',', '.', true, true } },
  258. { u"ZAR"_q, { "", ',', '.', true, true } },
  259. //{ u"VUV"_q, { "", ',', '.', false, false, 0 } },
  260. //{ u"WST"_q, {} },
  261. //{ u"XAF"_q, { "FCFA", ',', '.', false, false, 0 } },
  262. //{ u"XCD"_q, {} },
  263. //{ u"XOF"_q, { "CFA", ' ', ',', false, false, 0 } },
  264. //{ u"XPF"_q, { "", ',', '.', false, false, 0 } },
  265. //{ u"ZMW"_q, {} },
  266. //{ u"ANG"_q, {} },
  267. //{ u"RWF"_q, { "", ' ', ',', true, true, 0 } },
  268. //{ u"PGK"_q, {} },
  269. //{ u"TOP"_q, {} },
  270. //{ u"SBD"_q, {} },
  271. //{ u"SCR"_q, {} },
  272. //{ u"SHP"_q, {} },
  273. //{ u"SLL"_q, {} },
  274. //{ u"SOS"_q, {} },
  275. //{ u"SRD"_q, {} },
  276. //{ u"STD"_q, {} },
  277. //{ u"SVC"_q, {} },
  278. //{ u"SZL"_q, {} },
  279. //{ u"AOA"_q, {} },
  280. //{ u"AWG"_q, {} },
  281. //{ u"BBD"_q, {} },
  282. //{ u"BIF"_q, { "", ',', '.', false, false, 0 } },
  283. //{ u"BMD"_q, {} },
  284. //{ u"BSD"_q, {} },
  285. //{ u"BWP"_q, {} },
  286. //{ u"BZD"_q, {} },
  287. //{ u"CDF"_q, { "", ',', '.', false } },
  288. //{ u"CVE"_q, { "", ',', '.', true, false, 0 } },
  289. //{ u"DJF"_q, { "", ',', '.', false, false, 0 } },
  290. //{ u"FJD"_q, {} },
  291. //{ u"FKP"_q, {} },
  292. //{ u"GIP"_q, {} },
  293. //{ u"GMD"_q, { "", ',', '.', false } },
  294. //{ u"GNF"_q, { "", ',', '.', false, false, 0 } },
  295. //{ u"GYD"_q, {} },
  296. //{ u"HTG"_q, {} },
  297. //{ u"KHR"_q, { "", ',', '.', false } },
  298. //{ u"KMF"_q, { "", ',', '.', false, false, 0 } },
  299. //{ u"KYD"_q, {} },
  300. //{ u"LAK"_q, { "", ',', '.', false } },
  301. //{ u"LRD"_q, {} },
  302. //{ u"LSL"_q, { "", ',', '.', false } },
  303. //{ u"MGA"_q, { "", ',', '.', true, false, 0 } },
  304. //{ u"MKD"_q, { "", '.', ',', false, true } },
  305. //{ u"MWK"_q, {} },
  306. //{ u"NAD"_q, {} },
  307. //{ u"CLF"_q, { "", ',', '.', true, false, 4 } },
  308. //{ u"KWD"_q, { "", ',', '.', true, false, 3 } },
  309. //{ u"LYD"_q, { "", ',', '.', true, false, 3 } },
  310. //{ u"OMR"_q, { "", ',', '.', true, false, 3 } },
  311. //{ u"TND"_q, { "", ',', '.', true, false, 3 } },
  312. //{ u"UYI"_q, { "", ',', '.', true, false, 0 } },
  313. //{ u"MRO"_q, { "", ',', '.', true, false, 1 } },
  314. };
  315. static const auto kRulesMap = [] {
  316. // flat_multi_map_pair_type lacks some required constructors :(
  317. auto &&list = kRules | ranges::views::transform([](auto &&pair) {
  318. return base::flat_multi_map_pair_type<QString, CurrencyRule>(
  319. pair.first,
  320. pair.second);
  321. });
  322. return base::flat_map<QString, CurrencyRule>(begin(list), end(list));
  323. }();
  324. const auto i = kRulesMap.find(currency);
  325. return (i != end(kRulesMap)) ? i->second : CurrencyRule{};
  326. }
  327. [[nodiscard]] QString FormatWithSeparators(
  328. double amount,
  329. int precision,
  330. char decimal,
  331. char thousands) {
  332. Expects(decimal != 0);
  333. // Thanks https://stackoverflow.com/a/5058949
  334. struct FormattingHelper : std::numpunct<char> {
  335. FormattingHelper(char decimal, char thousands)
  336. : decimal(decimal)
  337. , thousands(thousands) {
  338. }
  339. char do_decimal_point() const override { return decimal; }
  340. char do_thousands_sep() const override { return thousands; }
  341. std::string do_grouping() const override { return "\3"; }
  342. char decimal = '.';
  343. char thousands = ',';
  344. };
  345. auto stream = std::ostringstream();
  346. stream.imbue(std::locale(
  347. stream.getloc(),
  348. new FormattingHelper(decimal, thousands ? thousands : '?')));
  349. stream.precision(precision);
  350. stream << std::fixed << amount;
  351. auto result = QString::fromStdString(stream.str());
  352. if (!thousands) {
  353. result.replace('?', QString());
  354. }
  355. return result;
  356. }
  357. QString FormatImageSizeText(const QSize &size) {
  358. return QString::number(size.width())
  359. + QChar(215)
  360. + QString::number(size.height());
  361. }
  362. QString FormatPhone(QString phone) {
  363. if (phone.isEmpty()) {
  364. return QString();
  365. }
  366. if (phone.at(0) == '0') {
  367. return phone;
  368. }
  369. phone = phone.remove(QChar::Space);
  370. return Countries::Instance().format({
  371. .phone = (phone.at(0) == '+') ? phone.mid(1) : phone,
  372. }).formatted;
  373. }
  374. QString FormatTTL(float64 ttl) {
  375. if (ttl < 86400) {
  376. return tr::lng_hours(tr::now, lt_count, int(ttl / 3600));
  377. } else if (ttl < 86400 * 7) {
  378. return tr::lng_days(tr::now, lt_count, int(ttl / (86400)));
  379. } else if (ttl < 86400 * 31) {
  380. const auto days = int(ttl / 86400);
  381. if ((int(ttl) % 7) == 0) {
  382. return tr::lng_weeks(tr::now, lt_count, int(days / 7));
  383. } else {
  384. return tr::lng_weeks(tr::now, lt_count, int(days / 7))
  385. + ' '
  386. + tr::lng_days(tr::now, lt_count, int(days % 7));
  387. }
  388. } else if (ttl < (86400 * 31) * 12) {
  389. return tr::lng_months(tr::now, lt_count, int(ttl / (86400 * 31)));
  390. } else {
  391. return tr::lng_years({}, lt_count, std::round(ttl / (86400 * 365)));
  392. }
  393. }
  394. QString FormatTTLAfter(float64 ttl) {
  395. return (ttl <= 3600 * 23)
  396. ? tr::lng_settings_ttl_after_hours(tr::now, lt_count, int(ttl / 3600))
  397. : (ttl <= (86400) * 6)
  398. ? tr::lng_settings_ttl_after_days(
  399. tr::now,
  400. lt_count,
  401. int(ttl / (86400)))
  402. : (ttl <= (86400 * 7) * 3)
  403. ? tr::lng_settings_ttl_after_weeks(
  404. tr::now,
  405. lt_count,
  406. int(ttl / (86400 * 7)))
  407. : (ttl <= (86400 * 31) * 11)
  408. ? tr::lng_settings_ttl_after_months(
  409. tr::now,
  410. lt_count,
  411. int(ttl / (86400 * 31)))
  412. : tr::lng_settings_ttl_after_years(
  413. tr::now,
  414. lt_count,
  415. std::round(ttl / (86400 * 365)));
  416. }
  417. QString FormatTTLTiny(float64 ttl) {
  418. return (ttl <= 3600 * 9)
  419. ? tr::lng_hours_tiny(tr::now, lt_count, int(ttl / 3600))
  420. : (ttl <= (86400) * 6)
  421. ? tr::lng_days_tiny(tr::now, lt_count, int(ttl / (86400)))
  422. : (ttl <= (86400 * 7) * 3)
  423. ? tr::lng_weeks_tiny(tr::now, lt_count, int(ttl / (86400 * 7)))
  424. : (ttl <= (86400 * 31) * 11)
  425. ? tr::lng_months_tiny({}, lt_count, int(ttl / (86400 * 31)))
  426. : (ttl <= 86400 * 366)
  427. ? tr::lng_years_tiny({}, lt_count, std::round(ttl / (86400 * 365)))
  428. : QString();
  429. }
  430. QString FormatMuteFor(float64 sec) {
  431. return (sec <= 60)
  432. ? tr::lng_seconds(tr::now, lt_count, sec)
  433. : (sec <= 60 * 59)
  434. ? tr::lng_minutes(tr::now, lt_count, int(sec / 60))
  435. : FormatTTL(sec);
  436. }
  437. QString FormatMuteForTiny(float64 sec) {
  438. return (sec <= 60)
  439. ? QString()
  440. : (sec <= 60 * 59)
  441. ? tr::lng_minutes_tiny(tr::now, lt_count, std::round(sec / 60))
  442. : (sec <= 3600 * 23)
  443. ? tr::lng_hours_tiny(tr::now, lt_count, std::round(sec / 3600))
  444. : (sec <= 86400 * 6)
  445. ? tr::lng_days_tiny(tr::now, lt_count, std::round(sec / 86400))
  446. : (sec <= (86400 * 7) * 3)
  447. ? tr::lng_weeks_tiny(tr::now, lt_count, std::round(sec / (86400 * 7)))
  448. : (sec <= (86400 * 31) * 11)
  449. ? tr::lng_months_tiny({}, lt_count, std::round(sec / (86400 * 31)))
  450. : (sec <= 86400 * 366)
  451. ? tr::lng_years_tiny({}, lt_count, std::round(sec / (86400 * 365)))
  452. : QString();
  453. }
  454. QString FormatResetCloudPasswordIn(float64 sec) {
  455. return (sec >= 3600) ? FormatTTL(sec) : FormatDurationText(sec);
  456. }
  457. QString FormatDialogsDate(const QDateTime &lastTime) {
  458. // Show all dates that are in the last 20 hours in time format.
  459. constexpr int kRecentlyInSeconds = 20 * 3600;
  460. const auto now = QDateTime::currentDateTime();
  461. const auto nowDate = now.date();
  462. const auto lastDate = lastTime.date();
  463. if ((lastDate == nowDate)
  464. || (std::abs(lastTime.secsTo(now)) < kRecentlyInSeconds)) {
  465. return QLocale().toString(lastTime.time(), QLocale::ShortFormat);
  466. } else if (std::abs(lastDate.daysTo(nowDate)) < 7) {
  467. return langDayOfWeek(lastDate);
  468. } else {
  469. return QLocale().toString(lastDate, QLocale::ShortFormat);
  470. }
  471. }
  472. } // namespace Ui