mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-06-05 04:15:12 -04:00
1764623999 is 2025-12-01 21:19:59 UTC, not 19:59:59. Pointed out by Copilot review on PR #4871. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
85 lines
3.9 KiB
C++
85 lines
3.9 KiB
C++
/*
|
|
* This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "zm_catch2.h"
|
|
|
|
#include "zm_time.h"
|
|
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <string>
|
|
|
|
// Build the local-time "%Y-%m-%d %H:%M:%S" string for a whole-second time_t the
|
|
// same way SetPath() derives the on-disk event directory, so the test stays
|
|
// timezone-independent (it compares against the same localtime conversion the
|
|
// production code uses rather than a hard-coded TZ-specific string).
|
|
static std::string LocalSecondString(time_t sec) {
|
|
tm tm_local = {};
|
|
localtime_r(&sec, &tm_local);
|
|
char buffer[32];
|
|
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_local);
|
|
return std::string(buffer);
|
|
}
|
|
|
|
TEST_CASE("SystemTimePointToMysqlString: whole second has no fractional part") {
|
|
// 1764623999 == 2025-12-01 21:19:59 UTC; exact-second timepoints must format
|
|
// without a fractional component because the Events.StartDateTime column is
|
|
// datetime (second precision).
|
|
SystemTimePoint tp = std::chrono::system_clock::from_time_t(1764623999);
|
|
REQUIRE(SystemTimePointToMysqlString(tp) == LocalSecondString(1764623999));
|
|
}
|
|
|
|
TEST_CASE("SystemTimePointToMysqlString: sub-second part is floored, never rounded up") {
|
|
// refs #4870: a continuous event whose backdated start keyframe lands in the
|
|
// last fraction of a second before local midnight must not be promoted to the
|
|
// next second. MySQL 8 rounds fractional seconds when storing into a
|
|
// datetime(0) column, while Event::SetPath() truncates via to_time_t(); if the
|
|
// string we hand MySQL carries a roundable fractional, the DB row and the
|
|
// on-disk day folder diverge by a day. The string must already be floored to
|
|
// the whole second that SetPath() (to_time_t) uses.
|
|
const time_t base_sec = 1764623999; // floored second SetPath would use
|
|
|
|
// Worst case: 999999us before the next second boundary - MySQL 8 would round
|
|
// this up to base_sec + 1 if we emitted ".999999".
|
|
SystemTimePoint tp_high =
|
|
std::chrono::system_clock::from_time_t(base_sec) + Microseconds(999999);
|
|
REQUIRE(SystemTimePointToMysqlString(tp_high) == LocalSecondString(base_sec));
|
|
|
|
// Half-second: the classic round-vs-truncate divergence point.
|
|
SystemTimePoint tp_half =
|
|
std::chrono::system_clock::from_time_t(base_sec) + Microseconds(500000);
|
|
REQUIRE(SystemTimePointToMysqlString(tp_half) == LocalSecondString(base_sec));
|
|
|
|
// Small fraction: trivially floors with either policy, included for coverage.
|
|
SystemTimePoint tp_low =
|
|
std::chrono::system_clock::from_time_t(base_sec) + Microseconds(1);
|
|
REQUIRE(SystemTimePointToMysqlString(tp_low) == LocalSecondString(base_sec));
|
|
}
|
|
|
|
TEST_CASE("SystemTimePointToMysqlString: matches the second used to build the event path") {
|
|
// The DB StartDateTime and Event::SetPath() must resolve to the same calendar
|
|
// day. SetPath() derives the directory from to_time_t(start_time); the Mysql
|
|
// string must use that identical floored second so the day folder and the
|
|
// StartDateTime row never disagree (refs #4870).
|
|
const time_t base_sec = 1764623999;
|
|
SystemTimePoint tp =
|
|
std::chrono::system_clock::from_time_t(base_sec) + Microseconds(999999);
|
|
|
|
time_t path_sec = std::chrono::system_clock::to_time_t(tp);
|
|
REQUIRE(SystemTimePointToMysqlString(tp) == LocalSecondString(path_sec));
|
|
}
|