diff --git a/src/moonfire-db-test.cc b/src/moonfire-db-test.cc index b3bd1bf..d2245f0 100644 --- a/src/moonfire-db-test.cc +++ b/src/moonfire-db-test.cc @@ -291,6 +291,31 @@ TEST(AdjustDaysMapTest, Basic) { EXPECT_THAT(days, testing::ElementsAre()); } +TEST(GetDayBoundsTest, Basic) { + int64_t start_90k; + int64_t end_90k; + std::string error_msg; + + // Normal day. + EXPECT_TRUE(GetDayBounds("2015-12-31", &start_90k, &end_90k, &error_msg)) + << error_msg; + EXPECT_EQ(INT64_C(130639392000000), start_90k); + EXPECT_EQ(INT64_C(130647168000000), end_90k); + + // Spring forward (23-hour day). + EXPECT_TRUE(GetDayBounds("2016-03-13", &start_90k, &end_90k, &error_msg)); + EXPECT_EQ(INT64_C(131207040000000), start_90k); + EXPECT_EQ(INT64_C(131214492000000), end_90k); + + // Fall back (25-hour day). + EXPECT_TRUE(GetDayBounds("2016-11-06", &start_90k, &end_90k, &error_msg)); + EXPECT_EQ(INT64_C(133057404000000), start_90k); + EXPECT_EQ(INT64_C(133065504000000), end_90k); + + // Unparseable day. + EXPECT_FALSE(GetDayBounds("xxxx-xx-xx", &start_90k, &end_90k, &error_msg)); +} + // Basic test of running some queries on an empty database. TEST_F(MoonfireDbTest, EmptyDatabase) { std::string error_message; diff --git a/src/moonfire-db.cc b/src/moonfire-db.cc index 96da195..d2fe1d8 100644 --- a/src/moonfire-db.cc +++ b/src/moonfire-db.cc @@ -47,6 +47,9 @@ namespace moonfire_nvr { namespace { +const char kDayFmt[] = "%Y-%m-%d"; +constexpr size_t kDayFmtBufSize = sizeof("YYYY-mm-DD"); + // Helper for AdjustDaysMap. void AdjustDay(const std::string &day, int64_t delta, std::map *days) { @@ -80,9 +83,8 @@ void AdjustDaysMap(int64_t start_time_90k, int64_t end_time_90k, int sign, memset(&mytm, 0, sizeof(mytm)); time_t start_ts = start_time_90k / kTimeUnitsPerSecond; localtime_r(&start_ts, &mytm); - const char kFmt[] = "%Y-%m-%d"; - char buf[sizeof("YYYY-mm-DD")]; - strftime(buf, sizeof(buf), kFmt, &mytm); + char buf[kDayFmtBufSize]; + strftime(buf, sizeof(buf), kDayFmt, &mytm); // Determine the start of the next day. // Note that mktime(3) normalizes tm_mday, so this should work on the last @@ -106,7 +108,7 @@ void AdjustDaysMap(int64_t start_time_90k, int64_t end_time_90k, int sign, } // Fill |buf| with the second day. - strftime(buf, sizeof(buf), kFmt, &mytm); + strftime(buf, sizeof(buf), kDayFmt, &mytm); // Adjust the second day. auto second_day_delta = sign * (end_time_90k - boundary_90k); @@ -117,6 +119,30 @@ void AdjustDaysMap(int64_t start_time_90k, int64_t end_time_90k, int sign, } // namespace internal +bool GetDayBounds(const std::string &day, int64_t *start_time_90k, + int64_t *end_time_90k, std::string *error_message) { + struct tm mytm; + memset(&mytm, 0, sizeof(mytm)); + if (strptime(day.c_str(), kDayFmt, &mytm) != day.c_str() + day.size()) { + *error_message = StrCat("Unparseable day: ", day); + return false; + } + + mytm.tm_isdst = -1; + mytm.tm_hour = 0; + mytm.tm_min = 0; + mytm.tm_sec = 0; + *start_time_90k = kTimeUnitsPerSecond * static_cast(mktime(&mytm)); + + mytm.tm_isdst = -1; + mytm.tm_hour = 0; + mytm.tm_min = 0; + mytm.tm_sec = 0; + ++mytm.tm_mday; + *end_time_90k = kTimeUnitsPerSecond * static_cast(mktime(&mytm)); + return true; +} + bool MoonfireDatabase::Init(Database *db, std::string *error_message) { CHECK(db_ == nullptr); db_ = db; diff --git a/src/moonfire-db.h b/src/moonfire-db.h index aed7f65..5640c6b 100644 --- a/src/moonfire-db.h +++ b/src/moonfire-db.h @@ -259,6 +259,12 @@ class MoonfireDatabase { std::map video_sample_entries_; }; +// Given a key in the day-to-duration map, produce the start and end times of +// the day. (Typically the end time is 24 hours later than the start; but it's +// 23 or 25 hours for the days of spring forward and fall back, respectively.) +bool GetDayBounds(const std::string &day, int64_t *start_time_90k, + int64_t *end_time_90k, std::string *error_message); + namespace internal { // Adjust a day-to-duration map (see MoonfireDatabase::CameraData::days_)