@@ -329,3 +329,46 @@ def test_exact_sun_event(default_date: date, location: Location, tz: BaseTzInfo)
329329 assert next_sunset .date () != default_date , "Next sunset should be tomorrow"
330330 assert next_sunset .date () != default_date , "Next sunset should be tomorrow"
331331 assert next_sunset .date () != default_date , "Next sunset should be tomorrow"
332+
333+
334+ def test_run_at_time_in_past (default_now : datetime , default_date : date , tomorrow_date : date , parser : partial [datetime ]) -> None :
335+ """Test that run_at schedules for next day when time is in the past.
336+
337+ This test reproduces the bug reported in issue #2491 where run_at() with a time
338+ in the past runs immediately instead of scheduling for the next day.
339+
340+ The fix is to have run_at() explicitly pass today=False to parse_datetime,
341+ which forces times in the past to be scheduled for tomorrow.
342+ """
343+ from datetime import time
344+
345+ # Current time is 12:00 (default_now is 12:00:00)
346+ # Test with a time object that's 1 hour in the past (11:00)
347+ past_time = time (11 , 0 , 0 )
348+ # run_at should call parse_datetime with today=False
349+ result = parser (past_time , today = False )
350+
351+ # Since the time is in the past and today=False (behavior for run_at),
352+ # it should be scheduled for tomorrow
353+ assert result .date () == tomorrow_date , f"Expected { tomorrow_date } , got { result .date ()} "
354+ assert result .time () == past_time
355+
356+ # Test with a time string that's in the past
357+ result_str = parser ("11:00:00" , today = False )
358+ assert result_str .date () == tomorrow_date , f"Expected { tomorrow_date } , got { result_str .date ()} "
359+
360+ # Test with a time that's in the future (should be today)
361+ future_time = time (13 , 0 , 0 )
362+ result_future = parser (future_time , today = False )
363+ assert result_future .date () == default_date , f"Expected { default_date } , got { result_future .date ()} "
364+ assert result_future .time () == future_time
365+
366+ # Test with today=True explicitly (should be today even if in the past)
367+ result_today = parser (past_time , today = True )
368+ assert result_today .date () == default_date
369+ assert result_today .time () == past_time
370+
371+ # Test with today=None (default for elevation events - should be today even if past)
372+ result_none = parser (past_time , today = None )
373+ assert result_none .date () == default_date
374+ assert result_none .time () == past_time
0 commit comments