LCOV - code coverage report
Current view: top level - xenolith/font - XLFontLocale.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 327 328 99.7 %
Date: 2024-05-12 00:16:13 Functions: 49 49 100.0 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "XLFontLocale.h"
      24             : 
      25             : namespace STAPPLER_VERSIONIZED stappler::xenolith::locale {
      26             : 
      27             : class LocaleManager {
      28             : public:
      29             :         using StringMap = memory::dict<memory::u16string, memory::u16string>;
      30             :         using LocaleMap = memory::map<memory::string, StringMap>;
      31             : 
      32             :         using StringIndexMap = memory::dict<size_t, memory::u16string>;
      33             :         using LocaleIndexMap = memory::map<memory::string, StringIndexMap>;
      34             : 
      35             :         using Interface = memory::PoolInterface;
      36             : 
      37             :         static LocaleManager *s_sharedInstance;
      38             : 
      39       12171 :         static LocaleManager *getInstance() {
      40       12171 :                 if (!s_sharedInstance) {
      41          12 :                         auto p = memory::pool::createTagged(nullptr, "LocaleManager");
      42          12 :                         memory::pool::push(p);
      43          12 :                         s_sharedInstance = new LocaleManager(p);
      44          12 :                         memory::pool::pop();
      45             :                 }
      46       12171 :                 return s_sharedInstance;
      47             :         }
      48             : 
      49          12 :         LocaleManager(memory::pool_t *p) : _defaultTime{
      50             :                 "today",
      51             :                 "yesterday",
      52             :                 "jan", "feb", "mar", "apr", "may", "jun",
      53             :                 "jul", "aug", "sep", "oct", "nov", "dec"
      54          12 :         }, _pool(p) {
      55         204 :                 define("ru-ru", {
      56          12 :                         pair("SystemSearch", "Найти"),
      57          12 :                         pair("SystemFontSize", "Размер шрифта"),
      58          12 :                         pair("SystemTheme", "Оформление"),
      59          12 :                         pair("SystemThemeLight", "Светлая тема"),
      60          12 :                         pair("SystemThemeNeutral", "Нейтральная тема"),
      61          12 :                         pair("SystemThemeDark", "Темная тема"),
      62          12 :                         pair("SystemMore", "Ещё"),
      63          12 :                         pair("SystemRestore", "Восстановить"),
      64          12 :                         pair("SystemRemoved", "Удалено"),
      65          12 :                         pair("SystemCopy", "Копировать"),
      66          12 :                         pair("SystemCut", "Вырезать"),
      67          12 :                         pair("SystemPaste", "Вставить"),
      68          12 :                         pair("SystemTapExit", "Нажмите ещё раз для выхода"),
      69             : 
      70          12 :                         pair("SystemErrorOverflowChars", "Слишком много символов"),
      71          12 :                         pair("SystemErrorInvalidChar", "Недопустимый символ"),
      72             : 
      73          12 :                         pair("Shortcut:Megabytes", "Мб"),
      74          12 :                         pair("Shortcut:Pages", "с"),
      75             :                 });
      76             : 
      77         204 :                 define("en-us", {
      78          12 :                         pair("SystemSearch", "Search"),
      79          12 :                         pair("SystemFontSize", "Font size"),
      80          12 :                         pair("SystemTheme", "Theme"),
      81          12 :                         pair("SystemThemeLight", "Light theme"),
      82          12 :                         pair("SystemThemeNeutral", "Neutral theme"),
      83          12 :                         pair("SystemThemeDark", "Dark theme"),
      84          12 :                         pair("SystemMore", "More"),
      85          12 :                         pair("SystemRestore", "Restore"),
      86          12 :                         pair("SystemRemoved", "Removed"),
      87          12 :                         pair("SystemCopy", "Copy"),
      88          12 :                         pair("SystemCut", "Cut"),
      89          12 :                         pair("SystemPaste", "Paste"),
      90          12 :                         pair("SystemTapExit", "Tap one more time to exit"),
      91             : 
      92          12 :                         pair("SystemErrorOverflowChars", "Too many characters"),
      93          12 :                         pair("SystemErrorInvalidChar", "Invalid character"),
      94             : 
      95          12 :                         pair("Shortcut:Megabytes", "Mb"),
      96          12 :                         pair("Shortcut:Pages", "p"),
      97             :                 });
      98          12 :         }
      99             : 
     100          48 :         void define(const StringView &locale, LocaleInitList &&init) {
     101          48 :                 memory::pool::push(_pool);
     102          48 :                 auto it = _strings.find(locale);
     103          48 :                 if (it == _strings.end()) {
     104          24 :                         it = _strings.emplace(locale.str<Interface>(), StringMap()).first;
     105             :                 }
     106         768 :                 for (auto &iit : init) {
     107         720 :                         it->second.emplace(string::toUtf16<Interface>(iit.first), string::toUtf16<Interface>(iit.second));
     108             :                 }
     109          48 :                 memory::pool::pop();
     110          48 :         }
     111             : 
     112          24 :         void define(const StringView &locale, LocaleIndexList &&init) {
     113          24 :                 memory::pool::push(_pool);
     114          24 :                 auto it = _indexes.find(locale);
     115          24 :                 if (it == _indexes.end()) {
     116          24 :                         it = _indexes.emplace(locale.str<Interface>(), StringIndexMap()).first;
     117             :                 }
     118         288 :                 for (auto &iit : init) {
     119         264 :                         it->second.emplace(iit.first, string::toUtf16<Interface>(iit.second));
     120             :                 }
     121          24 :                 memory::pool::pop();
     122          24 :         }
     123             : 
     124          24 :         void define(const StringView &locale, const std::array<StringView, toInt(TimeTokens::Max)> &arr) {
     125          24 :                 memory::pool::push(_pool);
     126          24 :                 auto it = _timeTokens.find(locale);
     127          24 :                 if (it == _timeTokens.end()) {
     128          24 :                         it = _timeTokens.emplace(locale.str<Interface>(), std::array<memory::string, toInt(TimeTokens::Max)>()).first;
     129             :                 }
     130             : 
     131          24 :                 size_t i = 0;
     132         360 :                 for (auto &arr_it : arr) {
     133         336 :                         it->second[i] = arr_it.str<Interface>();
     134         336 :                         ++ i;
     135             :                 }
     136          24 :                 memory::pool::pop();
     137          24 :         }
     138             : 
     139        1021 :         WideStringView string(const WideStringView &str) {
     140        1021 :                 auto it = _strings.find(StringView(_locale));
     141        1021 :                 if (it == _strings.end()) {
     142         793 :                         it = _strings.find(StringView(_default));
     143             :                 }
     144        1021 :                 if (it == _strings.end()) {
     145         793 :                         it = _strings.begin();
     146             :                 }
     147             : 
     148        1021 :                 if (it != _strings.end()) {
     149        1021 :                         auto sit = it->second.find(str);
     150        1021 :                         if (sit != it->second.end()) {
     151         180 :                                 return sit->second;
     152             :                         }
     153             :                 }
     154             : 
     155         841 :                 return WideStringView();
     156             :         }
     157             : 
     158          55 :         WideStringView string(size_t index) {
     159          55 :                 auto it = _indexes.find(StringView(_locale));
     160          55 :                 if (it == _indexes.end()) {
     161          19 :                         it = _indexes.find(StringView(_default));
     162             :                 }
     163          55 :                 if (it == _indexes.end()) {
     164          19 :                         it = _indexes.begin();
     165             :                 }
     166             : 
     167          55 :                 if (it != _indexes.end()) {
     168          48 :                         auto sit = it->second.find(index);
     169          48 :                         if (sit != it->second.end()) {
     170          36 :                                 return sit->second;
     171             :                         }
     172             :                 }
     173             : 
     174          19 :                 return WideStringView();
     175             :         }
     176             : 
     177         144 :         WideStringView numeric(const WideStringView &str, uint32_t num) {
     178         144 :                 auto ruleIt = _numRules.find(StringView(_locale));
     179         144 :                 if (ruleIt == _numRules.end()) {
     180          48 :                         return string(str);
     181             :                 } else {
     182          96 :                         uint8_t numEq = ruleIt->second(num);
     183          96 :                         auto fmt = string(str);
     184          96 :                         WideStringView r(fmt);
     185          96 :                         WideStringView def = r.readUntil<WideStringView::Chars<':'>>();
     186          96 :                         if (r.empty() || numEq == 0) {
     187          60 :                                 return def;
     188             :                         }
     189             : 
     190          72 :                         while (!r.empty()) {
     191          60 :                                 if (r.is(':')) {
     192          60 :                                         ++ r;
     193             :                                 }
     194             : 
     195          60 :                                 WideStringView res = r.readUntil<WideStringView::Chars<':'>>();
     196          60 :                                 if (numEq == 1) {
     197          24 :                                         return res;
     198             :                                 } else {
     199          36 :                                         -- numEq;
     200             :                                 }
     201             :                         }
     202             : 
     203          12 :                         return def;
     204             :                 }
     205             :         }
     206             : 
     207          12 :         void setDefault(const String &def) {
     208          12 :                 _default = def;
     209          12 :         }
     210          12 :         const String &getDefault() {
     211          12 :                 return _default;
     212             :         }
     213             : 
     214          12 :         void setLocale(const String &loc) {
     215          12 :                 if (_locale != loc) {
     216          12 :                         _locale = loc;
     217          12 :                         onLocale(nullptr, loc);
     218             :                 }
     219          12 :         }
     220          12 :         const String &getLocale() {
     221          12 :                 return _locale;
     222             :         }
     223             : 
     224          24 :         void setNumRule(const StringView &locale, const NumRule &rule) {
     225          24 :                 _numRules.emplace(locale.str<Interface>(), rule);
     226          24 :         }
     227             : 
     228        8834 :         bool hasLocaleTagsFast(WideStringView r) {
     229        8834 :                 if (r.empty()) {
     230          24 :                         return false;
     231             :                 }
     232             : 
     233        8810 :                 if (r.is(u"@Locale:")) { // raw locale string
     234          12 :                         return true;
     235        8798 :                 } else if (r.is(u"%=")) {
     236          12 :                         r += 2;
     237          12 :                         auto tmp = r.readChars<WideStringView::CharGroup<CharGroupId::Numbers>>();
     238          12 :                         if (!tmp.empty() && r.is('%')) {
     239          12 :                                 return true;
     240             :                         }
     241             :                 } else {
     242        8786 :                         constexpr size_t maxChars = config::MaxFastLocaleChars;
     243        8786 :                         WideStringView shortView(r.data(), std::min(r.size(), maxChars));
     244        8786 :                         shortView.skipUntil<WideStringView::Chars<'%'>>();
     245        8786 :                         if (shortView.is('%')) {
     246          75 :                                 ++ shortView;
     247          75 :                                 if (shortView.is('=')) {
     248          24 :                                         ++ shortView;
     249          24 :                                         shortView = WideStringView(shortView.data(), std::min(maxChars, r.size() - (shortView.data() - r.data())));
     250          24 :                                         shortView.skipChars<WideStringView::CharGroup<CharGroupId::Numbers>>();
     251          24 :                                         if (shortView.is('%')) {
     252          39 :                                                 return true;
     253             :                                         }
     254             :                                 } else {
     255          51 :                                         shortView = WideStringView(shortView.data(), std::min(maxChars, r.size() - (shortView.data() - r.data())));
     256             :                                         shortView.skipChars<
     257             :                                                 WideStringView::CharGroup<CharGroupId::Alphanumeric>,
     258          51 :                                                 WideStringView::Chars<':', '.', '-', '_', '[', ']', '+', '='>>();
     259          51 :                                         if (shortView.is('%')) {
     260          27 :                                                 return true;
     261             :                                         }
     262             :                                 }
     263             :                         }
     264             :                 }
     265        8747 :                 return false;
     266             :         }
     267             : 
     268        1908 :         bool hasLocaleTags(WideStringView r) {
     269        1908 :                 if (r.empty()) {
     270          12 :                         return false;
     271             :                 }
     272             : 
     273        1896 :                 if (r.is(u"@Locale:")) { // raw locale string
     274          12 :                         return true;
     275        1884 :                 } else if (r.is(u"%=")) {
     276          19 :                         r += 2;
     277          19 :                         auto tmp = r.readChars<WideStringView::CharGroup<CharGroupId::Numbers>>();
     278          19 :                         if (!tmp.empty() && r.is('%')) {
     279          19 :                                 return true;
     280             :                         }
     281             :                 } else {
     282        3061 :                         while (!r.empty()) {
     283        1865 :                                 r.skipUntil<WideStringView::Chars<'%'>>();
     284        1865 :                                 if (r.is('%')) {
     285         937 :                                         ++ r;
     286         937 :                                         if (r.is('=')) {
     287          24 :                                                 ++ r;
     288          24 :                                                 r.skipChars<WideStringView::CharGroup<CharGroupId::Numbers>>();
     289          24 :                                                 if (r.is('%')) {
     290          12 :                                                         return true;
     291             :                                                 }
     292             :                                         } else {
     293             :                                                 r.skipChars<
     294             :                                                         WideStringView::CharGroup<CharGroupId::Alphanumeric>,
     295         913 :                                                         WideStringView::Chars<':', '.', '-', '_', '[', ']', '+', '='>>();
     296         913 :                                                 if (r.is('%')) {
     297         657 :                                                         return true;
     298             :                                                 }
     299             :                                         }
     300             :                                 }
     301             :                         }
     302             :                 }
     303             : 
     304        1196 :                 return false;
     305             :         }
     306             : 
     307         889 :         WideString resolveLocaleTags(WideStringView r) {
     308         889 :                 if (r.is(u"@Locale:")) { // raw locale string
     309          12 :                         r += "@Locale:"_len;
     310          24 :                         return string(r).str<memory::StandartInterface>();
     311             :                 } else {
     312         877 :                         WideString ret; ret.reserve(r.size());
     313        2595 :                         while (!r.empty()) {
     314        1718 :                                 auto tmp = r.readUntil<WideStringView::Chars<'%'>>();
     315        1718 :                                 ret.append(tmp.data(), tmp.size());
     316        1718 :                                 if (r.is('%')) {
     317         884 :                                         ++ r;
     318             :                                         auto token = r.readChars<
     319             :                                                 WideStringView::CharGroup<CharGroupId::Alphanumeric>,
     320         884 :                                                 WideStringView::Chars<':', '.', '-', '_', '[', ']', '+', '='>>();
     321         884 :                                         if (r.is('%')) {
     322         860 :                                                 ++ r;
     323         860 :                                                 WideStringView replacement;
     324         860 :                                                 if (token.is('=')) {
     325          31 :                                                         auto numToken = token;
     326          31 :                                                         ++ numToken;
     327          31 :                                                         if (numToken.is<WideStringView::CharGroup<CharGroupId::Numbers>>()) {
     328          31 :                                                                 numToken.readInteger().unwrap([&, this] (int64_t id) {
     329          31 :                                                                         if (numToken.empty()) {
     330          31 :                                                                                 replacement = string(size_t(id));
     331             :                                                                         }
     332          31 :                                                                 });
     333             :                                                         }
     334         829 :                                                 } else if (token.is(u"Num:")) {
     335          36 :                                                         WideStringView splitMaster(token);
     336          36 :                                                         WideStringView num;
     337         144 :                                                         while (!splitMaster.empty()) {
     338         108 :                                                                 num = splitMaster.readUntil<WideStringView::Chars<':'>>();
     339         108 :                                                                 if (splitMaster.is(':')) {
     340          72 :                                                                         ++ splitMaster;
     341             :                                                                 }
     342             :                                                         }
     343             : 
     344          36 :                                                         if (!num.empty()) {
     345          36 :                                                                 WideStringView validate(num);
     346          36 :                                                                 if (validate.is('-')) {
     347          12 :                                                                         ++ validate;
     348             :                                                                 }
     349          36 :                                                                 validate.skipChars<WideStringView::CharGroup<CharGroupId::Numbers>>();
     350          36 :                                                                 if (validate.empty()) {
     351          36 :                                                                         WideStringView vtoken(token.data(), token.size() - num.size() - 1);
     352          36 :                                                                         num.readInteger().unwrap([&, this] (int64_t id) {
     353          36 :                                                                                 replacement = numeric(vtoken, id);
     354          36 :                                                                         });
     355             :                                                                 }
     356             :                                                         }
     357             :                                                 }
     358             : 
     359         860 :                                                 if (replacement.empty() && !token.is('=')) {
     360         817 :                                                         replacement = string(token);
     361             :                                                 }
     362             : 
     363         860 :                                                 if (replacement.empty()) {
     364         812 :                                                         ret.push_back(u'%');
     365         812 :                                                         ret.append(token.data(), token.size());
     366         812 :                                                         ret.push_back(u'%');
     367             :                                                 } else {
     368          48 :                                                         ret.append(replacement.str<memory::StandartInterface>());
     369             :                                                 }
     370             :                                         } else {
     371          24 :                                                 ret.push_back(u'%');
     372          24 :                                                 ret.append(token.data(), token.size());
     373             :                                         }
     374             :                                 }
     375             :                         }
     376         877 :                         return ret;
     377         877 :                 }
     378             :                 return WideString();
     379             :         }
     380             : 
     381          36 :         String language(const StringView &locale) {
     382          36 :                 if (locale == "ru-ru") {
     383          12 :                         return "Русский";
     384          24 :                 } else if (locale.starts_with("en-")) {
     385          12 :                         return "English";
     386             :                 }
     387          12 :                 return String();
     388             :         }
     389             : 
     390          36 :         String common(const StringView &locale) {
     391          36 :                 String ret = string::StringTraits<memory::StandartInterface>::tolower(locale);
     392          36 :                 if (ret.size() == 2) {
     393          24 :                         if (ret == "en") {
     394          12 :                                 ret.reserve(5);
     395          12 :                                 ret.append("-gb");
     396             :                         } else {
     397          12 :                                 ret.reserve(5);
     398          12 :                                 ret.append("-").append(string::StringTraits<memory::StandartInterface>::tolower(locale));
     399             :                         }
     400             :                 }
     401             : 
     402          36 :                 return ret;
     403           0 :         }
     404             : 
     405          48 :         StringView timeToken(TimeTokens tok) {
     406          48 :                 auto it = _timeTokens.find(StringView(_locale));
     407          48 :                 if (it == _timeTokens.end()) {
     408          24 :                         it = _timeTokens.find(StringView(_default));
     409             :                 }
     410             : 
     411          48 :                 auto &table = it == _timeTokens.end()?_defaultTime:it->second;
     412          48 :                 return table[toInt(tok)];
     413             :         }
     414             : 
     415          96 :         const std::array<memory::string, toInt(TimeTokens::Max)> &timeTokenTable() {
     416          96 :                 auto it = _timeTokens.find(StringView(_locale));
     417          96 :                 if (it == _timeTokens.end()) {
     418          12 :                         it = _timeTokens.find(StringView(_default));
     419             :                 }
     420             : 
     421          96 :                 return it == _timeTokens.end()?_defaultTime:it->second;
     422             :         }
     423             : 
     424             : protected:
     425             :         String _default;
     426             :         String _locale;
     427             : 
     428             :         LocaleMap _strings;
     429             :         LocaleIndexMap _indexes;
     430             :         memory::map<memory::string, NumRule> _numRules;
     431             :         memory::map<memory::string, std::array<memory::string, toInt(TimeTokens::Max)>> _timeTokens;
     432             :         std::array<memory::string, toInt(TimeTokens::Max)> _defaultTime;
     433             : 
     434             :         memory::pool_t *_pool;
     435             : };
     436             : 
     437             : LocaleManager *LocaleManager::s_sharedInstance = nullptr;
     438             : 
     439             : EventHeader onLocale("Locale", "onLocale");
     440             : 
     441          24 : void define(const StringView &locale, LocaleInitList &&init) {
     442          24 :         LocaleManager::getInstance()->define(locale, move(init));
     443          24 : }
     444             : 
     445          24 : void define(const StringView &locale, LocaleIndexList &&init) {
     446          24 :         LocaleManager::getInstance()->define(locale, move(init));
     447          24 : }
     448             : 
     449          24 : void define(const StringView &locale, const std::array<StringView, toInt(TimeTokens::Max)> &arr) {
     450          24 :         LocaleManager::getInstance()->define(locale, arr);
     451          24 : }
     452             : 
     453          48 : WideStringView string(const WideStringView &str) {
     454          48 :         return LocaleManager::getInstance()->string(str);
     455             : }
     456             : 
     457          24 : WideStringView string(size_t idx) {
     458          24 :         return LocaleManager::getInstance()->string(idx);
     459             : }
     460             : 
     461         108 : WideStringView numeric(const WideStringView &str, uint32_t num) {
     462         108 :         return LocaleManager::getInstance()->numeric(str, num);
     463             : }
     464             : 
     465          12 : void setDefault(const String &def) {
     466          12 :         LocaleManager::getInstance()->setDefault(def);
     467          12 : }
     468          12 : const String &getDefault() {
     469          12 :         return LocaleManager::getInstance()->getDefault();
     470             : }
     471             : 
     472          12 : void setLocale(const String &loc) {
     473          12 :         LocaleManager::getInstance()->setLocale(loc);
     474          12 : }
     475          12 : const String &getLocale() {
     476          12 :         return LocaleManager::getInstance()->getLocale();
     477             : }
     478             : 
     479          24 : void setNumRule(const String &locale, NumRule &&rule) {
     480          24 :         LocaleManager::getInstance()->setNumRule(locale, rule);
     481          24 : }
     482             : 
     483        8834 : bool hasLocaleTagsFast(const WideStringView &r) {
     484        8834 :         return LocaleManager::getInstance()->hasLocaleTagsFast(r);
     485             : }
     486             : 
     487        1908 : bool hasLocaleTags(const WideStringView &r) {
     488        1908 :         return LocaleManager::getInstance()->hasLocaleTags(r);
     489             : }
     490             : 
     491         889 : WideString resolveLocaleTags(const WideStringView &r) {
     492         889 :         return LocaleManager::getInstance()->resolveLocaleTags(r);
     493             : }
     494             : 
     495          36 : String language(const StringView &locale) {
     496          36 :         return LocaleManager::getInstance()->language(locale);
     497             : }
     498             : 
     499          36 : String common(const StringView &locale) {
     500          36 :         return LocaleManager::getInstance()->common(locale);
     501             : }
     502             : 
     503          48 : StringView timeToken(TimeTokens tok) {
     504          48 :         return LocaleManager::getInstance()->timeToken(tok);
     505             : }
     506             : 
     507          24 : const std::array<memory::string, toInt(TimeTokens::Max)> &timeTokenTable() {
     508          24 :         return LocaleManager::getInstance()->timeTokenTable();
     509             : }
     510             : 
     511         144 : static bool isToday(struct tm &tm, struct tm &now) {
     512         144 :         return tm.tm_year == now.tm_year && tm.tm_yday == now.tm_yday;
     513             : }
     514             : 
     515         200 : static uint32_t getNumDaysInYear(int y) {
     516         200 :         return ((y & 3) || (((y % 100) == 0) && (((y % 400) != 100)))) ? 355 : 356;
     517             : }
     518             : 
     519         200 : static uint32_t getYday(struct tm &now, int y) {
     520         200 :         auto ndays = getNumDaysInYear(y);
     521         200 :         return ((now.tm_year == y) ? 0 : ndays) + now.tm_yday;
     522             : }
     523             : 
     524         100 : static bool isYesterday(struct tm &tm, struct tm &now) {
     525         100 :         auto n1 = getYday(tm, now.tm_year);
     526         100 :         auto n2 = getYday(now, now.tm_year);
     527             : 
     528         100 :         return n1 + 1 == n2;
     529             : }
     530             : 
     531         288 : static void sp_localtime_r(time_t *sec_now, struct tm *tm_now) {
     532             : #if WIN32
     533             :         localtime_s(tm_now, sec_now);
     534             : #else
     535         288 :         localtime_r(sec_now, tm_now);
     536             : #endif
     537         288 : }
     538             : 
     539             : template <typename T>
     540         144 : static String localDate_impl(const std::array<T, toInt(TimeTokens::Max)> &table, Time t) {
     541         144 :         auto sec_now = time_t(Time::now().toSeconds());
     542             :         struct tm tm_now;
     543         144 :         sp_localtime_r(&sec_now, &tm_now);
     544             : 
     545         144 :         auto sec_time = time_t(t.toSeconds());
     546             :         struct tm tm_time;
     547         144 :         sp_localtime_r(&sec_time, &tm_time);
     548             : 
     549         144 :         if (isToday(tm_time, tm_now)) {
     550          88 :                 return String(table[toInt(TimeTokens::Today)].data(), table[toInt(TimeTokens::Today)].size());
     551         100 :         } else if (isYesterday(tm_time, tm_now)) {
     552          56 :                 return String(table[toInt(TimeTokens::Yesterday)].data(), table[toInt(TimeTokens::Yesterday)].size());
     553             :         }
     554          72 :         if (tm_time.tm_year == tm_now.tm_year) {
     555          48 :                 return toString(tm_time.tm_mday, " ", table[tm_time.tm_mon + 2]);
     556             :         } else {
     557          48 :                 return toString(tm_time.tm_mday, " ", table[tm_time.tm_mon + 2], " ", 1900 + tm_time.tm_year);
     558             :         }
     559             : }
     560             : 
     561          72 : String localDate(Time t) {
     562          72 :         return localDate_impl(LocaleManager::getInstance()->timeTokenTable(), t);
     563             : }
     564             : 
     565          72 : String localDate(const std::array<StringView, toInt(TimeTokens::Max)> &table, Time t) {
     566          72 :         return localDate_impl(table, t);
     567             : }
     568             : 
     569             : }

Generated by: LCOV version 1.14