~aleteoryx/muditaos

e820a7607e802d454eff795249d0bd4d3f2839ab — Maciej-Mudita 2 years ago 24f63ba
[MOS-893] Fix invalid time description in the SMS thread

The sending/receiving time information will be displayed as follows:
- Only an hour –> for today
- Yesterday and hour –> for messages received/sent yesterday
- Day of the week and hour –> for messages received/sent within
last 7 days
- Date (day+month) and hour –> for messages older than week,
but still from the same year
- Date (day+month+year) and hour –> for messages
rom previous years
M image/system_a/data/lang/Deutsch.json => image/system_a/data/lang/Deutsch.json +2 -0
@@ 90,6 90,8 @@
  "locale_24hour_min": "%H:%M",
  "locale_date_full": "%d.%m.%Y",
  "locale_date_short": "%d.%m",
  "locale_date_DD_MM_YY": "%d.%m.%y",
  "locale_date_MM_DD_YY": "%m.%d.%y",
  "common_AM": "AM",
  "common_PM": "PM",
  "duration_min_0sec": "%M:%0S",

M image/system_a/data/lang/English.json => image/system_a/data/lang/English.json +2 -0
@@ 95,6 95,8 @@
  "locale_24hour_min": "%H:%M",
  "locale_date_DD_MM_YYYY": "%d.%m.%Y",
  "locale_date_MM_DD_YYYY": "%m.%d.%Y",
  "locale_date_DD_MM_YY": "%d.%m.%y",
  "locale_date_MM_DD_YY": "%m.%d.%y",
  "locale_date_DD_MM": "%d.%m",
  "locale_date_MM_DD": "%m.%d",
  "locale_date_Day_DD_Mon": "%A, %d %b",

M image/system_a/data/lang/Espanol.json => image/system_a/data/lang/Espanol.json +2 -0
@@ 90,6 90,8 @@
  "locale_24hour_min": "%H:%M",
  "locale_date_full": "%d.%m.%Y",
  "locale_date_short": "%d.%m",
  "locale_date_DD_MM_YY": "%d.%m.%y",
  "locale_date_MM_DD_YY": "%m.%d.%y",
  "common_AM": "AM",
  "common_PM": "PM",
  "duration_min_0sec": "%M:%0S",

M image/system_a/data/lang/Francais.json => image/system_a/data/lang/Francais.json +2 -0
@@ 90,6 90,8 @@
  "locale_24hour_min": "%H:%M",
  "locale_date_DD_MM_YYYY": "%d.%m.%Y",
  "locale_date_MM_DD_YYYY": "%m.%d.%Y",
  "locale_date_DD_MM_YY": "%d.%m.%y",
  "locale_date_MM_DD_YY": "%m.%d.%y",
  "locale_date_DD_MM": "%d.%m",
  "locale_date_MM_DD": "%m.%d",
  "locale_date_Day_DD_Mon": "%A, %d %b",

M image/system_a/data/lang/Polski.json => image/system_a/data/lang/Polski.json +2 -19
@@ 93,6 93,8 @@
  "locale_24hour_min": "%H:%M",
  "locale_date_full": "%d.%m.%Y",
  "locale_date_short": "%d.%m",
  "locale_date_DD_MM_YY": "%d.%m.%y",
  "locale_date_MM_DD_YY": "%m.%d.%y",
  "common_AM": "AM",
  "common_PM": "PM",
  "duration_min_0sec": "%M:%0S",


@@ 128,25 130,6 @@
  "app_alarm_clock_snooze_15_min": "15 min",
  "app_alarm_clock_custom_repeat_title": "Własne powtarzanie",
  "app_calendar_title_main": "Kalendarz",
  "common_paused": "Pauza",
  "locale_12hour_min": "%I:%M %p",
  "locale_12hour_min_short": "%I:%M",
  "locale_24hour_min": "%H:%M",
  "locale_date_full": "%d.%m.%Y",
  "locale_date_short": "%d.%m",
  "common_AM": "AM",
  "common_PM": "PM",
  "duration_min_0sec": "%M:%0S",
  "duration_0min_0sec": "%0M:%0S",
  "duration_0hmin_0sec": "%0N:%0S",
  "duration_hour_0min_0sec": "%H:%0M:%0S",
  "brightness_text": "JASNOŚĆ",
  "phone_needs_rebooting": "Twój telefon musi zostać ponownie uruchomiony. Wciśnij dowolny przycisk, aby potwierdzić, a następnie wyjmij baterię na 10 sekund, by przeprowadzić pełny reset.",
  "home_modes_connected": "POŁĄCZONY",
  "home_modes_notdisturb": "NIE PRZESZKADZAĆ",
  "home_modes_offline": "OFFLINE",
  "home_modes_message_only": "Tylko wiadomości",
  "statusbar_battery_charging": "Ładowanie",
  "app_calendar_repeat_two_weeks": "Co dwa tygodnie",
  "app_calendar_repeat_month": "Co miesiąc",
  "app_calendar_repeat_year": "Co rok",

M image/system_a/data/lang/Svenska.json => image/system_a/data/lang/Svenska.json +2 -0
@@ 75,6 75,8 @@
  "locale_24hour_min": "%H:%M",
  "locale_date_DD_MM_YYYY": "%d.%m.%Y",
  "locale_date_MM_DD_YYYY": "%m.%d.%Y",
  "locale_date_DD_MM_YY": "%d.%m.%y",
  "locale_date_MM_DD_YY": "%m.%d.%y",
  "locale_date_DD_MM": "%d.%m",
  "locale_date_MM_DD": "%m.%d",
  "locale_date_Day_DD_Mon": "%A, %d %b",

M module-apps/application-calllog/widgets/CalllogItem.cpp => module-apps/application-calllog/widgets/CalllogItem.cpp +1 -1
@@ 72,7 72,7 @@ namespace gui
        imageCallType[static_cast<uint32_t>(callType)]->setVisible(true);

        using namespace utils::time;
        timestamp->setText(*TimestampFactory().createTimestamp(TimestampType::DateTime, call->date));
        timestamp->setText(TimestampFactory().createTimestamp(TimestampType::DateOrTime, call->date)->str());
    }

} /* namespace gui */

M module-apps/application-messages/data/MessagesStyle.hpp => module-apps/application-messages/data/MessagesStyle.hpp +1 -0
@@ 77,6 77,7 @@ namespace style

        namespace smsOutput
        {
            inline constexpr gui::Length sms_label_high              = 44;
            inline constexpr gui::Length sms_radius                  = 8;
            inline constexpr gui::Length default_h                   = 30;
            inline constexpr gui::Length sms_max_width               = 320;

M module-apps/application-messages/models/ThreadsSearchResultsModel.cpp => module-apps/application-messages/models/ThreadsSearchResultsModel.cpp +3 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationMessages.hpp"


@@ 35,7 35,8 @@ namespace gui::model
        {
            using namespace utils::time;
            ret->setContact(threadStruct->contact->getFormattedName());
            ret->setTimestamp(*TimestampFactory().createTimestamp(TimestampType::DateTime, threadStruct->thread->date));
            ret->setTimestamp(
                TimestampFactory().createTimestamp(TimestampType::DateOrTime, threadStruct->thread->date)->str());
            // The only thing that differs with ThreadModel actually - here show what was found
            ret->setPreview(threadStruct->thread->snippet);
        }

M module-apps/application-messages/widgets/SMSOutputWidget.cpp => module-apps/application-messages/widgets/SMSOutputWidget.cpp +9 -3
@@ 66,6 66,9 @@ namespace gui
            break;
        }

        smsBubble->setMaximumSize(std::min(style::messages::smsOutput::sms_max_width,
                                           style::listview::item_width_with_scroll - timeLabel->getTextNeedSpace()),
                                  style::messages::smsOutput::sms_max_height);
        smsBubble->setText(record->body);

        focusChangedCallback = [this]([[maybe_unused]] Item &item) {


@@ 116,7 119,7 @@ namespace gui
    {
        if (timeLabel != nullptr) {
            timeLabel->setMinimumWidth(timeLabel->getTextNeedSpace());
            timeLabel->setMinimumHeight(style::messages::smsOutput::default_h);
            timeLabel->setMinimumHeight(style::messages::smsOutput::sms_label_high);
            uint16_t timeLabelMargin = body->getWidth() - (smsBubble->getWidth() + timeLabel->getTextNeedSpace());

            if (body->getReverseOrder()) {


@@ 136,10 139,13 @@ namespace gui
        timeLabel->activeItem = false;
        timeLabel->setFont(style::window::font::verysmall);
        using namespace utils::time;
        timeLabel->setText(*TimestampFactory().createTimestamp(TimestampType::Time, timestamp));
        timeLabel->setText(TimestampFactory().createTimestamp(TimestampType::DateAndTime, timestamp)->str());
        timeLabel->setVisible(false);
        timeLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        timeLabel->setAlignment(gui::Alignment(body->getReverseOrder() ? gui::Alignment::Horizontal::Left
                                                                       : gui::Alignment::Horizontal::Right,
                                               gui::Alignment::Vertical::Center));
        timeLabel->setEdges(RectangleEdge::None);
        timeLabel->setTextType(TextType::MultiLine);
    }

    void SMSOutputWidget::errorIconBuild()

M module-apps/application-messages/widgets/ThreadItem.cpp => module-apps/application-messages/widgets/ThreadItem.cpp +3 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "MessagesStyle.hpp"


@@ 51,7 51,8 @@ namespace gui

        setContactName(getNumberImportance());
        using namespace utils::time;
        timestamp->setText(*TimestampFactory().createTimestamp(TimestampType::DateTime, threadStruct->thread->date));
        timestamp->setText(
            TimestampFactory().createTimestamp(TimestampType::DateOrTime, threadStruct->thread->date)->str());
        setPreview();
    }


M module-apps/application-notes/widgets/NotesItem.cpp => module-apps/application-notes/widgets/NotesItem.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NotesItem.hpp"


@@ 57,7 57,7 @@ namespace gui
    void NotesItem::setDateText(std::uint32_t timestamp)
    {
        using namespace utils::time;
        auto dt = TimestampFactory().createTimestamp(TimestampType::DateTime, timestamp);
        auto dt = TimestampFactory().createTimestamp(TimestampType::DateOrTime, timestamp);
        date->setText(*dt);
    }


M module-apps/application-notes/windows/NotePreviewWindow.cpp => module-apps/application-notes/windows/NotePreviewWindow.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NotePreviewWindow.hpp"


@@ 108,7 108,7 @@ namespace app::notes
    void NotePreviewWindow::setEditDateText(std::uint32_t timestamp)
    {
        using namespace utils::time;
        auto dateTime = TimestampFactory().createTimestamp(TimestampType::DateTime, timestamp);
        auto dateTime = TimestampFactory().createTimestamp(TimestampType::DateOrTime, timestamp);
        auto dt       = dynamic_cast<DateTime *>(dateTime.get());
        if (dt == nullptr) {
            return;

M module-services/service-db/test/lang/English.json => module-services/service-db/test/lang/English.json +2 -0
@@ 95,6 95,8 @@
  "locale_24hour_min": "%H:%M",
  "locale_date_DD_MM_YYYY": "%d.%m.%Y",
  "locale_date_MM_DD_YYYY": "%m.%d.%Y",
  "locale_date_DD_MM_YY": "%d.%m.%y",
  "locale_date_MM_DD_YY": "%m.%d.%y",
  "locale_date_DD_MM": "%d.%m",
  "locale_date_MM_DD": "%m.%d",
  "locale_date_Day_DD_Mon": "%A, %d %b",

M module-utils/time/test/unittest_time.cpp => module-utils/time/test/unittest_time.cpp +155 -45
@@ 38,8 38,10 @@ const std::regex reg12h("^(00|[1-9]|1[0-2]):[0-5][0-9] (A|P)M$");
const std::regex reg12hShort("^(00|[1-9]|1[0-2]):[0-5][0-9]$");
const std::regex reg24h("^(00|[1-9]|1[0-9]|2[0-4]):[0-5][0-9]$");
const std::regex regexDDMMYYYY("^([0-2]\\d|3[0-1])\\.(0[1-9]|1[0-2])\\.\\d{4}$");
const std::regex regexDDMMYY("^([0-2]\\d|3[0-1])\\.(0[1-9]|1[0-2])\\.\\d{2}$");
const std::regex regexDDMM("^([0-2]\\d|3[0-1])\\.(0[1-9]|1[0-2])$");
const std::regex regexMMDDYYYY("^(0[1-9]|1[0-2])\\.([0-2]\\d|3[0-1])\\.\\d{4}$");
const std::regex regexMMDDYY("^(0[1-9]|1[0-2])\\.([0-2]\\d|3[0-1])\\.\\d{2}$");
const std::regex regexMMDD("^(0[1-9]|1[0-2])\\.([0-2]\\d|3[0-1])$");
const std::regex regexDaysOfWeek("^(sun|Sun|mon|Mon|t(ues|hurs)|(T(ues|hurs))|Fri|"
                                 "fri)(day|\\.)?$|wed(\\.|nesday)?$|Wed(\\.|nesday)?$|"


@@ 108,6 110,8 @@ TEST_CASE("TimeStamp")
            REQUIRE(timestamp.str(Locale::format(Locale::TimeFormat::FormatTime24H)) == "23:41");
            REQUIRE(timestamp.str(Locale::format(Locale::TimeFormat::FormatLocaleDate_DD_MM_YYYY)) == "14.06.2021");
            REQUIRE(timestamp.str(Locale::format(Locale::TimeFormat::FormatLocaleDate_MM_DD_YYYY)) == "06.14.2021");
            REQUIRE(timestamp.str(Locale::format(Locale::TimeFormat::FormatLocaleDate_DD_MM_YY)) == "14.06.21");
            REQUIRE(timestamp.str(Locale::format(Locale::TimeFormat::FormatLocaleDate_MM_DD_YY)) == "06.14.21");
            REQUIRE(timestamp.str(Locale::format(Locale::TimeFormat::FormatLocaleDate_DD_MM)) == "14.06");
            REQUIRE(timestamp.str(Locale::format(Locale::TimeFormat::FormatLocaleDate_MM_DD)) == "06.14");
        }


@@ 400,65 404,171 @@ TEST_CASE("DateTime formatting")
        auto newTimeTimeinfo        = currentTimeTimeinfo;
        timeSettings.timeFormat12h  = true;
        timeSettings.dateFormatDDMM = true;
        SECTION("for the current time")

        SECTION("Date or time formatting depending on the reference time difference")
        {
            std::regex reg = reg12h;
            auto newTime   = std::mktime(&newTimeTimeinfo);
            DateTime datetime(timeSettings, newTime, currentTime);
            SECTION("for the current time")
            {
                std::regex reg = reg12h;
                auto newTime   = std::mktime(&newTimeTimeinfo);
                DateOrTime datetime(timeSettings, newTime, currentTime);

                REQUIRE(std::regex_match(std::string(datetime.str()), reg));
                timeSettings.timeFormat12h = false;
                reg                        = reg24h;
                REQUIRE(std::regex_match(std::string(datetime.str()), reg));
            }

            REQUIRE(std::regex_match(std::string(datetime.str()), reg));
            timeSettings.timeFormat12h = false;
            reg                        = reg24h;
            REQUIRE(std::regex_match(std::string(datetime.str()), reg));
        }
            SECTION("for the time yesterday")
            {
                newTimeTimeinfo.tm_mday -= 1;
                auto newTime = std::mktime(&newTimeTimeinfo);
                DateOrTime datetime(timeSettings, newTime, currentTime);

        SECTION("for the time yesterday")
        {
            newTimeTimeinfo.tm_mday -= 1;
            auto newTime = std::mktime(&newTimeTimeinfo);
            DateTime datetime(timeSettings, newTime, currentTime);
                REQUIRE(datetime.str() == "Yesterday");
            }

            REQUIRE(datetime.str() == "Yesterday");
        }
            SECTION("for a time earlier than yesterday but still in the same week")
            {
                newTimeTimeinfo.tm_mday -= 6;
                const auto newTime = std::mktime(&newTimeTimeinfo);
                DateOrTime datetime(timeSettings, newTime, currentTime);
                REQUIRE(std::regex_match(std::string(datetime.str()), regexDaysOfWeek));
            }

        SECTION("for a time earlier than yesterday but still in the same week")
        {
            newTimeTimeinfo.tm_mday -= 6;
            const auto newTime = std::mktime(&newTimeTimeinfo);
            DateTime datetime(timeSettings, newTime, currentTime);
            REQUIRE(std::regex_match(std::string(datetime.str()), regexDaysOfWeek));
            SECTION("for a time earlier than a week but still in the same year")
            {
                std::regex regexSameYear = regexDDMM;
                if (newTimeTimeinfo.tm_mon == 0) {
                    newTimeTimeinfo.tm_mon = 1;
                }
                else {
                    newTimeTimeinfo.tm_mon -= 1;
                }
                auto newTime = std::mktime(&newTimeTimeinfo);

                DateOrTime datetime(timeSettings, newTime, currentTime);
                REQUIRE(std::regex_match(std::string(datetime.str()), regexSameYear));
                timeSettings.dateFormatDDMM = false;
                regexSameYear               = regexMMDD;
                REQUIRE(std::regex_match(std::string(datetime.str()), regexSameYear));
            }

            SECTION("for the time last year")
            {
                std::regex regexPreviousYear = regexDDMMYYYY;
                newTimeTimeinfo.tm_year -= 1;
                auto newTime = std::mktime(&newTimeTimeinfo);

                DateOrTime datetime(timeSettings, newTime, currentTime);

                REQUIRE(std::regex_match(std::string(datetime.str()), regexPreviousYear));
                timeSettings.dateFormatDDMM = false;
                regexPreviousYear           = regexMMDDYYYY;
                REQUIRE(std::regex_match(std::string(datetime.str()), regexPreviousYear));
            }
        }

        SECTION("for a time earlier than a week but still in the same year")
        SECTION("Date and time formatting")
        {
            std::regex regexSameYear = regexDDMM;
            if (newTimeTimeinfo.tm_mon == 0) {
                newTimeTimeinfo.tm_mon = 1;
            SECTION("for the current time")
            {
                std::regex reg = reg12h;
                auto newTime   = std::mktime(&newTimeTimeinfo);
                DateAndTime datetime(timeSettings, newTime, currentTime);

                REQUIRE(std::regex_match(std::string(datetime.str()), reg));
                timeSettings.timeFormat12h = false;
                reg                        = reg24h;
                REQUIRE(std::regex_match(std::string(datetime.str()), reg));
            }
            else {
                newTimeTimeinfo.tm_mon -= 1;

            SECTION("for the time yesterday")
            {
                newTimeTimeinfo.tm_mday -= 1;
                auto newTime = std::mktime(&newTimeTimeinfo);
                DateAndTime datetime(timeSettings, newTime, currentTime);

                const std::string dateTimeStr = datetime.str();
                const int pos                 = dateTimeStr.find_first_of('\n');
                const std::string timeStr = dateTimeStr.substr(pos + 1), dateStr = dateTimeStr.substr(0, pos);

                REQUIRE(dateStr == "Yesterday");
                REQUIRE(std::regex_match(timeStr, reg12h));
            }
            auto newTime = std::mktime(&newTimeTimeinfo);

            DateTime datetime(timeSettings, newTime, currentTime);
            REQUIRE(std::regex_match(std::string(datetime.str()), regexSameYear));
            timeSettings.dateFormatDDMM = false;
            regexSameYear               = regexMMDD;
            REQUIRE(std::regex_match(std::string(datetime.str()), regexSameYear));
        }
            SECTION("for a time earlier than yesterday but still in the same week")
            {
                newTimeTimeinfo.tm_mday -= 6;
                const auto newTime = std::mktime(&newTimeTimeinfo);
                DateAndTime datetime(timeSettings, newTime, currentTime);

        SECTION("for the time last year")
        {
            std::regex regexPreviousYear = regexDDMMYYYY;
            newTimeTimeinfo.tm_year -= 1;
            auto newTime = std::mktime(&newTimeTimeinfo);
                const std::string dateTimeStr = datetime.str();
                const int pos                 = dateTimeStr.find_first_of('\n');
                const std::string timeStr = dateTimeStr.substr(pos + 1), dateStr = dateTimeStr.substr(0, pos);

            DateTime datetime(timeSettings, newTime, currentTime);
                REQUIRE(std::regex_match(dateStr, regexDaysOfWeek));
                REQUIRE(std::regex_match(timeStr, reg12h));
            }

            REQUIRE(std::regex_match(std::string(datetime.str()), regexPreviousYear));
            timeSettings.dateFormatDDMM = false;
            regexPreviousYear           = regexMMDDYYYY;
            REQUIRE(std::regex_match(std::string(datetime.str()), regexPreviousYear));
            SECTION("for a time earlier than a week but still in the same year")
            {
                std::regex regexSameYear = regexDDMM;
                if (newTimeTimeinfo.tm_mon == 0) {
                    newTimeTimeinfo.tm_mon = 1;
                }
                else {
                    newTimeTimeinfo.tm_mon -= 1;
                }
                auto newTime = std::mktime(&newTimeTimeinfo);

                DateAndTime datetime(timeSettings, newTime, currentTime);

                std::string dateTimeStr = datetime.str();
                int pos                 = dateTimeStr.find_first_of('\n');
                std::string timeStr = dateTimeStr.substr(pos + 1), dateStr = dateTimeStr.substr(0, pos);

                REQUIRE(std::regex_match(dateStr, regexSameYear));
                REQUIRE(std::regex_match(timeStr, reg12h));

                timeSettings.dateFormatDDMM = false;
                regexSameYear               = regexMMDD;

                dateTimeStr = datetime.str();
                pos         = dateTimeStr.find_first_of('\n');
                timeStr     = dateTimeStr.substr(pos + 1);
                dateStr     = dateTimeStr.substr(0, pos);

                REQUIRE(std::regex_match(dateStr, regexSameYear));
                REQUIRE(std::regex_match(timeStr, reg12h));
            }

            SECTION("for the time last year")
            {
                std::regex regexPreviousYear = regexDDMMYY;
                newTimeTimeinfo.tm_year -= 1;
                auto newTime = std::mktime(&newTimeTimeinfo);

                DateAndTime datetime(timeSettings, newTime, currentTime);

                std::string dateTimeStr = datetime.str();
                int pos                 = dateTimeStr.find_first_of('\n');
                std::string timeStr = dateTimeStr.substr(pos + 1), dateStr = dateTimeStr.substr(0, pos);

                REQUIRE(std::regex_match(dateStr, regexPreviousYear));
                REQUIRE(std::regex_match(timeStr, reg12h));

                timeSettings.dateFormatDDMM = false;
                regexPreviousYear           = regexMMDDYY;

                dateTimeStr = datetime.str();
                pos         = dateTimeStr.find_first_of('\n');
                timeStr     = dateTimeStr.substr(pos + 1);
                dateStr     = dateTimeStr.substr(0, pos);

                REQUIRE(std::regex_match(dateStr, regexPreviousYear));
                REQUIRE(std::regex_match(timeStr, reg12h));
            }
        }
    }
}

M module-utils/time/time/time_conversion.cpp => module-utils/time/time/time_conversion.cpp +35 -6
@@ 190,13 190,13 @@ namespace utils::time
        return std::abs(dayDifference) < Locale::num_days;
    }

    UTF8 DateTime::str(std::string format) const
    UTF8 DateOrTime::str(std::string format) const
    {
        if (format.compare("") != 0) {
            return Timestamp::str(format);
        }
        if (isToday()) {
            auto localeFormat =
            const auto localeFormat =
                timeSettings.isTimeFormat12h() ? Locale::TimeFormat::FormatTime12H : Locale::TimeFormat::FormatTime24H;
            return Timestamp::str(Locale::format(localeFormat));
        }


@@ 207,15 207,44 @@ namespace utils::time
            return Timestamp::day();
        }
        if (isCurrentYear()) {
            auto localeFormat = timeSettings.isDateFormatDDMM() ? Locale::TimeFormat::FormatLocaleDate_DD_MM
                                                                : Locale::TimeFormat::FormatLocaleDate_MM_DD;
            const auto localeFormat = timeSettings.isDateFormatDDMM() ? Locale::TimeFormat::FormatLocaleDate_DD_MM
                                                                      : Locale::TimeFormat::FormatLocaleDate_MM_DD;
            return Timestamp::str(Locale::format(localeFormat));
        }
        auto localeFormat = timeSettings.isDateFormatDDMM() ? Locale::TimeFormat::FormatLocaleDate_DD_MM_YYYY
                                                            : Locale::TimeFormat::FormatLocaleDate_MM_DD_YYYY;
        const auto localeFormat = timeSettings.isDateFormatDDMM() ? Locale::TimeFormat::FormatLocaleDate_DD_MM_YYYY
                                                                  : Locale::TimeFormat::FormatLocaleDate_MM_DD_YYYY;
        return Timestamp::str(Locale::format(localeFormat));
    }

    UTF8 DateAndTime::str(std::string format) const
    {
        if (!format.empty()) {
            return Timestamp::str(format);
        }
        const auto timeFormat =
            timeSettings.isTimeFormat12h() ? Locale::TimeFormat::FormatTime12H : Locale::TimeFormat::FormatTime24H;
        const auto time    = Timestamp::str(Locale::format(timeFormat));
        const UTF8 newline = "\n";

        if (isToday()) {
            return time;
        }
        if (isYesterday()) {
            return Locale::yesterday() + newline + time;
        }
        if (isCurrentWeek()) {
            return Timestamp::day(true) + newline + time;
        }
        if (isCurrentYear()) {
            const auto localeFormat = timeSettings.isDateFormatDDMM() ? Locale::TimeFormat::FormatLocaleDate_DD_MM
                                                                      : Locale::TimeFormat::FormatLocaleDate_MM_DD;
            return Timestamp::str(Locale::format(localeFormat)) + newline + time;
        }
        const auto localeFormat = timeSettings.isDateFormatDDMM() ? Locale::TimeFormat::FormatLocaleDate_DD_MM_YY
                                                                  : Locale::TimeFormat::FormatLocaleDate_MM_DD_YY;
        return Timestamp::str(Locale::format(localeFormat)) + newline + time;
    }

    UTF8 Date::str(std::string format) const
    {
        if (!format.empty()) {

M module-utils/time/time/time_conversion.hpp => module-utils/time/time/time_conversion.hpp +30 -2
@@ 147,14 147,42 @@ namespace utils
                return os;
            }

            UTF8 str(std::string fmt = "") const override;

            bool isToday() const;
            bool isYesterday() const;
            bool isCurrentYear() const;
            bool isCurrentWeek() const;
        };

        class DateOrTime : public DateTime
        {
          public:
            /// shows date or time in past in relation to reference value
            ///
            /// @val - timestamp to show
            /// @reference - reference timestamp
            /// @timeSettings - time settings interface
            DateOrTime(const TimeSettingsInterface &timeSettings, time_t val, time_t reference = std::time(nullptr))
                : DateTime(timeSettings, val, reference)
            {}

            UTF8 str(std::string fmt = "") const override;
        };

        class DateAndTime : public DateTime
        {
          public:
            /// shows date and time in past in relation to reference value
            ///
            /// @val - timestamp to show
            /// @reference - reference timestamp
            /// @timeSettings - time settings interface
            DateAndTime(const TimeSettingsInterface &timeSettings, time_t val, time_t reference = std::time(nullptr))
                : DateTime(timeSettings, val, reference)
            {}

            UTF8 str(std::string fmt = "") const override;
        };

        class Date : public DateTime
        {
          public:

M module-utils/time/time/time_conversion_factory.cpp => module-utils/time/time/time_conversion_factory.cpp +5 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "time_conversion_factory.hpp"


@@ 23,8 23,10 @@ namespace utils::time
            return std::make_unique<DateText>(*timeSettings, time);
        case TimestampType::Time:
            return std::make_unique<Time>(*timeSettings, time);
        case TimestampType::DateTime:
            return std::make_unique<DateTime>(*timeSettings, time);
        case TimestampType::DateOrTime:
            return std::make_unique<DateOrTime>(*timeSettings, time);
        case TimestampType::DateAndTime:
            return std::make_unique<DateAndTime>(*timeSettings, time);
        case TimestampType::Clock:
            return std::make_unique<Clock>(*timeSettings, time);
        }

M module-utils/time/time/time_conversion_factory.hpp => module-utils/time/time/time_conversion_factory.hpp +8 -7
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,12 11,13 @@ namespace utils::time
    /// Most types are system locale dependant
    enum class TimestampType
    {
        Timestamp, /// no default formatting, no system locale dependencies
        Clock,     /// actual time
        Time,      /// past time
        DateTime,  /// system date time formatting
        Date,      /// date
        DateText,  /// date in textual represnatation
        Timestamp,   /// no default formatting, no system locale dependencies
        Clock,       /// actual time
        Time,        /// past time
        DateOrTime,  /// system date or time formatting depending on the difference from the reference time
        DateAndTime, /// system date and time formatting
        Date,        /// date
        DateText,    /// date in textual represnatation
    };
    class TimestampFactory
    {

M module-utils/time/time/time_locale.hpp => module-utils/time/time/time_locale.hpp +6 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 24,7 24,7 @@ namespace utils

          private:
            static const int num_monts      = 12;
            static const int num_formatters = 9;
            static const int num_formatters = 11;
            // imo it would be nicer to have datetime locales in different json with thiny bit nicer and more effective
            // getters
            const std::array<std::string, num_days> daysShort = {


@@ 56,6 56,8 @@ namespace utils
                                                                       "locale_24hour_min",
                                                                       "locale_date_DD_MM_YYYY",
                                                                       "locale_date_MM_DD_YYYY",
                                                                       "locale_date_DD_MM_YY",
                                                                       "locale_date_MM_DD_YY",
                                                                       "locale_date_DD_MM",
                                                                       "locale_date_MM_DD",
                                                                       "locale_date_Day_DD_Mon",


@@ 109,6 111,8 @@ namespace utils
                FormatTime24H,      // H:M in 24h format
                FormatLocaleDate_DD_MM_YYYY,
                FormatLocaleDate_MM_DD_YYYY,
                FormatLocaleDate_DD_MM_YY,
                FormatLocaleDate_MM_DD_YY,
                FormatLocaleDate_DD_MM,
                FormatLocaleDate_MM_DD,
                FormatDate_Day_DD_Month,

M pure_changelog.md => pure_changelog.md +2 -1
@@ 3,7 3,8 @@
## Unreleased
### Added
* Always display network access technology and signal strength on the status bar
* Add days of the week to the list of SMS, calls and notes
* Added days of the week to the list of SMS, calls and notes
* Added date formatting of received/sent SMS

### Changed / Improved
* Improved dialog with network via USSD