Skip to content

Commit 0c4a571

Browse files
committed
enhance: next_run_at calculation
1 parent b9b37f6 commit 0c4a571

File tree

1 file changed

+22
-16
lines changed

1 file changed

+22
-16
lines changed

camel/triggers/schedule_trigger.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import asyncio
1515
import threading
1616
import time
17-
from datetime import datetime
17+
from datetime import datetime, timedelta
1818
from enum import Enum
1919
from typing import Any, Dict, Optional
2020

@@ -28,13 +28,16 @@
2828
except ImportError:
2929
HAS_CRONITER = False
3030

31+
from camel.logger import get_logger
3132
from camel.triggers.base_trigger import (
3233
BaseTrigger,
3334
TriggerEvent,
3435
TriggerState,
3536
TriggerType,
3637
)
3738

39+
logger = get_logger(__name__)
40+
3841

3942
class ScheduleType(Enum):
4043
"""Enumeration of supported schedule types."""
@@ -254,18 +257,23 @@ def _calculate_next_run(self) -> None:
254257
If cron parsing fails, defaults to next minute as fallback.
255258
"""
256259
try:
260+
now = datetime.now()
261+
257262
if HAS_CRONITER:
258-
cron = croniter(self.cron_expression, datetime.now())
263+
cron = croniter(self.cron_expression, now)
259264
self.next_run_at = cron.get_next(datetime)
260265
else:
261266
# Basic fallback for common patterns
262267
self._calculate_next_run_basic()
263268
except Exception:
264-
# Fallback: calculate next run in 1 minute if cron parsing fails
265-
self.next_run_at = datetime.now().replace(second=0, microsecond=0)
266-
self.next_run_at = self.next_run_at.replace(
267-
minute=self.next_run_at.minute + 1
269+
logger.warning(
270+
"Failed to parse cron expression '%s'. Falling back to "
271+
"next minute calculation.",
272+
self.cron_expression,
268273
)
274+
# Fallback: calculate next run in 1 minute if cron parsing fails
275+
base_time = now.replace(second=0, microsecond=0)
276+
self.next_run_at = base_time + timedelta(minutes=1)
269277

270278
def _calculate_next_run_basic(self) -> None:
271279
"""Basic cron calculation without croniter dependency.
@@ -294,22 +302,20 @@ def _calculate_next_run_basic(self) -> None:
294302
interval = int(minute[2:])
295303
next_minute = ((now.minute // interval) + 1) * interval
296304
if next_minute >= 60:
297-
self.next_run_at = now.replace(
298-
hour=now.hour + 1,
299-
minute=next_minute - 60,
300-
second=0,
301-
microsecond=0,
305+
# Use timedelta to properly handle hour and day rollovers
306+
# e.g. 23:58 with interval 5 -> 0:00, 0:05 ... next day
307+
base_time = now.replace(minute=0, second=0, microsecond=0)
308+
self.next_run_at = base_time + timedelta(
309+
hours=1, minutes=next_minute - 60
302310
)
303311
else:
304312
self.next_run_at = now.replace(
305313
minute=next_minute, second=0, microsecond=0
306314
)
307315
else:
308-
# Default: next minute
309-
self.next_run_at = now.replace(second=0, microsecond=0)
310-
self.next_run_at = self.next_run_at.replace(
311-
minute=self.next_run_at.minute + 1
312-
)
316+
# Default: next minute - use timedelta to handle rollovers
317+
base_time = now.replace(second=0, microsecond=0)
318+
self.next_run_at = base_time + timedelta(minutes=1)
313319

314320
async def activate(self) -> bool:
315321
"""Activate the schedule trigger and start the scheduler thread.

0 commit comments

Comments
 (0)