#include "TimeFormatters.h" #include "configuration.h" #include "gps/RTC.h" #include "mesh/NodeDB.h" #include bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo) { // Cache the result - avoid frequent recalculation static uint8_t hoursCached = 0, minutesCached = 0; static uint32_t daysAgoCached = 0; static uint32_t secondsAgoCached = 0; static bool validCached = false; // Abort: if timezone not set if (strlen(config.device.tzdef) == 0) { validCached = false; return validCached; } // Abort: if invalid pointers passed if (hours == nullptr || minutes == nullptr || daysAgo == nullptr) { validCached = false; return validCached; } // Abort: if time seems invalid.. (> 6 months ago, probably seen before RTC set) if (secondsAgo > SEC_PER_DAY * 30UL * 6) { validCached = false; return validCached; } // If repeated request, don't bother recalculating if (secondsAgo - secondsAgoCached < 60 && secondsAgoCached != 0) { if (validCached) { *hours = hoursCached; *minutes = minutesCached; *daysAgo = daysAgoCached; } return validCached; } // Get local time uint32_t secondsRTC = getValidTime(RTCQuality::RTCQualityDevice, true); // Get local time // Abort: if RTC not set if (!secondsRTC) { validCached = false; return validCached; } // Get absolute time when last seen uint32_t secondsSeenAt = secondsRTC - secondsAgo; // Calculate daysAgo *daysAgo = (secondsRTC / SEC_PER_DAY) - (secondsSeenAt / SEC_PER_DAY); // How many "midnights" have passed // Get seconds since midnight uint32_t hms = (secondsRTC - secondsAgo) % SEC_PER_DAY; hms = (hms + SEC_PER_DAY) % SEC_PER_DAY; // Tear apart hms into hours and minutes *hours = hms / SEC_PER_HOUR; *minutes = (hms % SEC_PER_HOUR) / SEC_PER_MIN; // Cache the result daysAgoCached = *daysAgo; hoursCached = *hours; minutesCached = *minutes; secondsAgoCached = secondsAgo; validCached = true; return validCached; } void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength) { // Use an absolute timestamp in some cases. // Particularly useful with E-Ink displays. Static UI, fewer refreshes. uint8_t timestampHours, timestampMinutes; int32_t daysAgo; bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo); if (agoSecs < 120) // last 2 mins? snprintf(timeStr, maxLength, "%u seconds ago", agoSecs); // -- if suitable for timestamp -- else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE); else if (useTimestamp && daysAgo == 0) // Today snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes); else if (useTimestamp && daysAgo == 1) // Yesterday snprintf(timeStr, maxLength, "Seen yesterday"); else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method) snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo); // -- if using time delta instead -- else if (agoSecs < 120 * 60) // last 2 hrs snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60); // Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data. else if ((agoSecs / 60 / 60) < (730 * 6)) snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60); else snprintf(timeStr, maxLength, "unknown age"); }