Kiosk: show all user-visible times as 12-hour AM/PM
Add datetime::formatHm12h/formatTime12h and use them for the Home footer clock, the Timezone page's local-time sample, and the log viewer timestamps. Storage and parsing remain 24-hour HH:MM. Extends the date-format tests with the midnight/noon edge cases.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include "date_format.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
|
||||
namespace radiantwave::datetime {
|
||||
@@ -34,4 +36,35 @@ std::string formatTime(std::time_t t) {
|
||||
return formatDate(t, "%H:%M:%S");
|
||||
}
|
||||
|
||||
std::string formatHm12h(const std::string& hm24) {
|
||||
// Require exactly HH:MM with two decimal digits each.
|
||||
if (hm24.size() != 5 || hm24[2] != ':' ||
|
||||
!std::isdigit(static_cast<unsigned char>(hm24[0])) ||
|
||||
!std::isdigit(static_cast<unsigned char>(hm24[1])) ||
|
||||
!std::isdigit(static_cast<unsigned char>(hm24[3])) ||
|
||||
!std::isdigit(static_cast<unsigned char>(hm24[4]))) {
|
||||
return hm24;
|
||||
}
|
||||
|
||||
const int hour24 = (hm24[0] - '0') * 10 + (hm24[1] - '0');
|
||||
const int minute = (hm24[3] - '0') * 10 + (hm24[4] - '0');
|
||||
if (hour24 > 23 || minute > 59) {
|
||||
return hm24;
|
||||
}
|
||||
|
||||
int hour12 = hour24 % 12;
|
||||
if (hour12 == 0) {
|
||||
hour12 = 12;
|
||||
}
|
||||
const char* period = hour24 < 12 ? "AM" : "PM";
|
||||
|
||||
char buffer[16];
|
||||
std::snprintf(buffer, sizeof(buffer), "%d:%02d %s", hour12, minute, period);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
std::string formatTime12h(std::time_t t) {
|
||||
return formatHm12h(formatDate(t, "%H:%M"));
|
||||
}
|
||||
|
||||
} // namespace radiantwave::datetime
|
||||
|
||||
@@ -26,4 +26,13 @@ std::string formatDate(std::time_t t, const std::string& pattern);
|
||||
// Render `t` as a 24-hour clock: HH:MM:SS.
|
||||
std::string formatTime(std::time_t t);
|
||||
|
||||
// Convert a 24-hour clock `HH:MM` string to a 12-hour display label, e.g.
|
||||
// "00:00" -> "12:00 AM", "13:05" -> "1:05 PM". Storage and parsing remain
|
||||
// 24-hour; this is display-only. Returns `hm24` unchanged when it is not a
|
||||
// valid 24-hour time.
|
||||
std::string formatHm12h(const std::string& hm24);
|
||||
|
||||
// Render `t` in local time as a 12-hour clock label, e.g. "7:05 PM".
|
||||
std::string formatTime12h(std::time_t t);
|
||||
|
||||
} // namespace radiantwave::datetime
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ void Home::render(SDL_Renderer *renderer)
|
||||
internet_ ? std::string("Connected") : std::string("Offline"),
|
||||
audioStatus(),
|
||||
radiantwave::datetime::formatDate(now, dateFormat_),
|
||||
radiantwave::datetime::formatTime(now),
|
||||
radiantwave::datetime::formatTime12h(now),
|
||||
};
|
||||
std::vector<SDL_Color> valueColors(values.size(), footerColor);
|
||||
std::vector<bool> valueBold(values.size(), false);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "log_viewer_page.h"
|
||||
#include "app_state.h"
|
||||
#include "date_format.h"
|
||||
#include "timezone_helper.h"
|
||||
#include "logical_size.h"
|
||||
#include <SDL3/SDL.h>
|
||||
@@ -256,7 +257,11 @@ void LogViewerPage::rebuildWrapped(float maxLineWidth) {
|
||||
using namespace radiantwave::logger;
|
||||
|
||||
for (const auto& entry : rawEntries_) {
|
||||
std::string timeStr = radiantwave::timezone::formatUtcToTimezone(entry.timestamp, currentTzCode_, "%Y-%m-%d %H:%M:%S");
|
||||
std::string timeStr =
|
||||
radiantwave::timezone::formatUtcToTimezone(entry.timestamp, currentTzCode_, "%Y-%m-%d") +
|
||||
" " +
|
||||
radiantwave::datetime::formatHm12h(
|
||||
radiantwave::timezone::formatUtcToTimezone(entry.timestamp, currentTzCode_, "%H:%M"));
|
||||
|
||||
std::string fullLineStr = "[" + timeStr + "] [" + std::string(logLevelToString(entry.level)) + "] [" + entry.component + "] " + entry.message;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "timezone_page.h"
|
||||
#include "app_state.h"
|
||||
#include "date_format.h"
|
||||
#include "logical_size.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <algorithm>
|
||||
@@ -237,7 +238,13 @@ void TimeZonePage::render(SDL_Renderer* renderer) {
|
||||
}
|
||||
|
||||
time_t now = std::time(nullptr);
|
||||
std::string localSample = "Local Time: " + radiantwave::timezone::formatUtcToTimezone(now, currentTzCode_);
|
||||
// Show the configured zone's wall-clock time with an AM/PM label; storage
|
||||
// and the rest of the app stay 24-hour.
|
||||
std::string localSample = "Local Time: " +
|
||||
radiantwave::timezone::formatUtcToTimezone(now, currentTzCode_, "%Y-%m-%d") + " " +
|
||||
radiantwave::datetime::formatHm12h(
|
||||
radiantwave::timezone::formatUtcToTimezone(now, currentTzCode_, "%H:%M")) +
|
||||
" " + radiantwave::timezone::formatUtcToTimezone(now, currentTzCode_, "%Z");
|
||||
sampleText_.ensure(renderer, fontHeader_, localSample, green);
|
||||
if (sampleText_.texture) {
|
||||
SDL_FRect dst{700.0f, currentY, sampleText_.width, sampleText_.height};
|
||||
|
||||
@@ -59,3 +59,59 @@ TEST_F(DateFormatTest, OptionsIncludeDefault) {
|
||||
EXPECT_EQ(options[0].pattern, std::string(DEFAULT_DATE_FORMAT));
|
||||
EXPECT_EQ(options[0].name, "Month/Day/Year");
|
||||
}
|
||||
|
||||
TEST_F(DateFormatTest, Converts24HourTo12HourLabels) {
|
||||
EXPECT_EQ(formatHm12h("00:00"), "12:00 AM");
|
||||
EXPECT_EQ(formatHm12h("00:05"), "12:05 AM");
|
||||
EXPECT_EQ(formatHm12h("11:59"), "11:59 AM");
|
||||
EXPECT_EQ(formatHm12h("12:00"), "12:00 PM");
|
||||
EXPECT_EQ(formatHm12h("12:30"), "12:30 PM");
|
||||
EXPECT_EQ(formatHm12h("13:00"), "1:00 PM");
|
||||
EXPECT_EQ(formatHm12h("23:59"), "11:59 PM");
|
||||
}
|
||||
|
||||
TEST_F(DateFormatTest, LeavesMalformedInputUnchanged) {
|
||||
EXPECT_EQ(formatHm12h(""), "");
|
||||
EXPECT_EQ(formatHm12h("7:05"), "7:05");
|
||||
EXPECT_EQ(formatHm12h("24:00"), "24:00");
|
||||
EXPECT_EQ(formatHm12h("12:60"), "12:60");
|
||||
EXPECT_EQ(formatHm12h("ab:cd"), "ab:cd");
|
||||
}
|
||||
|
||||
TEST_F(DateFormatTest, Formats12HourClock) {
|
||||
std::tm morning = {};
|
||||
morning.tm_year = 2026 - 1900;
|
||||
morning.tm_mon = 8 - 1;
|
||||
morning.tm_mday = 4;
|
||||
morning.tm_hour = 7;
|
||||
morning.tm_min = 5;
|
||||
morning.tm_sec = 0;
|
||||
morning.tm_isdst = -1;
|
||||
time_t tMorning = mktime(&morning);
|
||||
ASSERT_NE(tMorning, static_cast<time_t>(-1));
|
||||
EXPECT_EQ(formatTime12h(tMorning), "7:05 AM");
|
||||
|
||||
std::tm midnight = {};
|
||||
midnight.tm_year = 2026 - 1900;
|
||||
midnight.tm_mon = 8 - 1;
|
||||
midnight.tm_mday = 4;
|
||||
midnight.tm_hour = 0;
|
||||
midnight.tm_min = 0;
|
||||
midnight.tm_sec = 0;
|
||||
midnight.tm_isdst = -1;
|
||||
time_t tMidnight = mktime(&midnight);
|
||||
ASSERT_NE(tMidnight, static_cast<time_t>(-1));
|
||||
EXPECT_EQ(formatTime12h(tMidnight), "12:00 AM");
|
||||
|
||||
std::tm evening = {};
|
||||
evening.tm_year = 2026 - 1900;
|
||||
evening.tm_mon = 8 - 1;
|
||||
evening.tm_mday = 4;
|
||||
evening.tm_hour = 19;
|
||||
evening.tm_min = 5;
|
||||
evening.tm_sec = 0;
|
||||
evening.tm_isdst = -1;
|
||||
time_t tEvening = mktime(&evening);
|
||||
ASSERT_NE(tEvening, static_cast<time_t>(-1));
|
||||
EXPECT_EQ(formatTime12h(tEvening), "7:05 PM");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user