Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/Mount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "EndSwitches.hpp"
#include "Mount.hpp"
#include "Sidereal.hpp"
#include "core/CalendarMath.hpp"
#include "libs/MappedDict/MappedDict.hpp"

PUSH_NO_WARNINGS
Expand Down Expand Up @@ -4239,13 +4240,16 @@ DayTime Mount::calculateLst()
{
DayTime timeUTC = getUtcTime();
LocalDate localDate = getLocalDate();
DayTime lst = Sidereal::calculateByDateAndTime(longitude().getTotalHours(), localDate.year, localDate.month, localDate.day, &timeUTC);
// The sidereal formula needs the UTC date, which differs from the local date for |UTC offset| hours each day.
core::CalendarDate utcDate
= core::localToUtcDate({localDate.year, localDate.month, localDate.day}, getLocalTime().getTotalSeconds(), _localUtcOffset);
DayTime lst = Sidereal::calculateByDateAndTime(longitude().getTotalHours(), utcDate.year, utcDate.month, utcDate.day, &timeUTC);
// LOG(DEBUG_INFO,
// "[MOUNT]: Calculating LST. UTC time: %s. Date: %d-%d-%d. Longitude: %s",
// "[MOUNT]: Calculating LST. UTC time: %s. UTC date: %d-%d-%d. Longitude: %s",
// timeUTC.ToString(),
// localDate.year,
// localDate.month,
// localDate.day,
// utcDate.year,
// utcDate.month,
// utcDate.day,
// longitude().ToString());
// LOG(DEBUG_INFO, "[MOUNT]: LST is: %s", lst.ToString());
return lst;
Expand Down
18 changes: 18 additions & 0 deletions src/core/CalendarMath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,22 @@ CalendarDate addDays(CalendarDate date, long days)
return date;
}

CalendarDate localToUtcDate(CalendarDate localDate, long localSecondsOfDay, int utcOffsetHours)
{
long utcSeconds = localSecondsOfDay - utcOffsetHours * 3600L;
long dayShift = 0;
while (utcSeconds < 0)
{
utcSeconds += 86400L;
dayShift--;
}
while (utcSeconds >= 86400L)
{
utcSeconds -= 86400L;
dayShift++;
}

return addDays(localDate, dayShift);
}

} // namespace core
5 changes: 5 additions & 0 deletions src/core/CalendarMath.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ struct CalendarDate {
/// Handles month-length tables and leap-year logic including century rules.
CalendarDate addDays(CalendarDate date, long days);

/// Convert a local calendar date to the UTC calendar date.
/// localSecondsOfDay is the local time of day in seconds since local midnight (0..86399).
/// utcOffsetHours is the local offset from UTC (local = UTC + offset).
CalendarDate localToUtcDate(CalendarDate localDate, long localSecondsOfDay, int utcOffsetHours);

} // namespace core
80 changes: 80 additions & 0 deletions unit_tests/test_core/test_calendar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,83 @@ TEST(CalendarMathTest, NegativeDays)
EXPECT_EQ(12, result.month);
EXPECT_EQ(31, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateEastAfterMidnight)
{
// UTC+2, local 2026-10-14 01:30 is UTC 2026-10-13 23:30
CalendarDate result = core::localToUtcDate({2026, 10, 14}, 1 * 3600L + 30 * 60L, 2);
EXPECT_EQ(2026, result.year);
EXPECT_EQ(10, result.month);
EXPECT_EQ(13, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateEastSameDay)
{
// UTC+2, local 2026-10-14 03:30 is UTC 2026-10-14 01:30
CalendarDate result = core::localToUtcDate({2026, 10, 14}, 3 * 3600L + 30 * 60L, 2);
EXPECT_EQ(2026, result.year);
EXPECT_EQ(10, result.month);
EXPECT_EQ(14, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateWestEvening)
{
// UTC-5, local 2026-01-15 20:00 is UTC 2026-01-16 01:00
CalendarDate result = core::localToUtcDate({2026, 1, 15}, 20 * 3600L, -5);
EXPECT_EQ(2026, result.year);
EXPECT_EQ(1, result.month);
EXPECT_EQ(16, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateWestSameDay)
{
// UTC-5, local 2026-01-15 18:59:59 is UTC 2026-01-15 23:59:59
CalendarDate result = core::localToUtcDate({2026, 1, 15}, 18 * 3600L + 59 * 60L + 59, -5);
EXPECT_EQ(2026, result.year);
EXPECT_EQ(1, result.month);
EXPECT_EQ(15, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateUtcZero)
{
CalendarDate result = core::localToUtcDate({2026, 10, 14}, 0, 0);
EXPECT_EQ(14, result.day);
result = core::localToUtcDate({2026, 10, 14}, 86399L, 0);
EXPECT_EQ(14, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateEastYearBoundary)
{
// UTC+10, local 2027-01-01 05:00 is UTC 2026-12-31 19:00
CalendarDate result = core::localToUtcDate({2027, 1, 1}, 5 * 3600L, 10);
EXPECT_EQ(2026, result.year);
EXPECT_EQ(12, result.month);
EXPECT_EQ(31, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateWestLeapDay)
{
// UTC-8, local 2028-02-28 17:00 is UTC 2028-02-29 01:00
CalendarDate result = core::localToUtcDate({2028, 2, 28}, 17 * 3600L, -8);
EXPECT_EQ(2028, result.year);
EXPECT_EQ(2, result.month);
EXPECT_EQ(29, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateWestYearBoundary)
{
// UTC-12, local 2026-12-31 12:00 is UTC 2027-01-01 00:00
CalendarDate result = core::localToUtcDate({2026, 12, 31}, 12 * 3600L, -12);
EXPECT_EQ(2027, result.year);
EXPECT_EQ(1, result.month);
EXPECT_EQ(1, result.day);
}

TEST(CalendarMathTest, LocalToUtcDateExactUtcMidnight)
{
// UTC+2, local 02:00 is exactly UTC 00:00 on the same date; 01:59:59 is still the day before
CalendarDate result = core::localToUtcDate({2026, 10, 14}, 2 * 3600L, 2);
EXPECT_EQ(14, result.day);
result = core::localToUtcDate({2026, 10, 14}, 2 * 3600L - 1, 2);
EXPECT_EQ(13, result.day);
}
Loading