diff --git a/src/moon.ts b/src/moon.ts index f4c4d01..1479d01 100644 --- a/src/moon.ts +++ b/src/moon.ts @@ -996,8 +996,11 @@ export const isFullMoon = (datetime: Date): boolean => getLunarPhase(datetime) = * @returns The date of the next full Moon. */ export const getNextFullMoon = (datetime: Date): Date => { - // Amend the date to midnight on the given date: - let date = new Date(new Date(datetime).setHours(0, 0, 0, 0)) + // The search begins at the datetime given, and not at the midnight before it: the midnight of + // the host system is not the midnight of UTC, and so the search would begin at an instant that + // depends on where the host is, and it would resolve a full Moon that has already passed earlier + // on the same day as the one that follows it: + let date = new Date(datetime) // The maximum number of days in a synodic month is 29, so if we increment the // date by 1 hour until we reach a full Moon, we will eventually reach @@ -1045,6 +1048,14 @@ export const getNextFullMoon = (datetime: Date): Date => { } } + // The Moon reads as full over a window of ~20 hours centred on the syzygy, and so the datetime + // given may lie within it but beyond the syzygy itself, e.g., the full Moon resolved from it has + // already passed. The search resumes two days on, which clears the remainder of that window, for + // the full Moon that follows it: + if (fullMoon <= datetime) { + return getNextFullMoon(new Date(fullMoon.getTime() + 2 * 24 * 60 * 60 * 1000)) + } + return fullMoon } diff --git a/tests/moon.spec.ts b/tests/moon.spec.ts index d671a16..f86948d 100644 --- a/tests/moon.spec.ts +++ b/tests/moon.spec.ts @@ -488,6 +488,18 @@ describe('getNextFullMoon', () => { const nextFullMoon = getNextFullMoon(datetime) expect(nextFullMoon.toISOString()).toBe('2021-05-26T11:31:30.000Z') }) + + it('should return a full Moon that is still to come for a datetime just after one', () => { + // The Moon reads as full for ~20 hours about the syzygy, and so a datetime within that window + // but beyond the syzygy resolved the full Moon that had already passed: + const passed = new Date('2024-08-19T18:26:00.000Z') + + for (const hours of [1, 6, 12, 18]) { + const datetime = new Date(passed.getTime() + hours * 60 * 60 * 1000) + + expect(getNextFullMoon(datetime).getTime()).toBeGreaterThan(datetime.getTime()) + } + }) }) /*****************************************************************************************************************/