sansan0/TrendRadar · error · ValueError

period '{period_key}' 的 start 与 end 不能相同: {period['start']}

Error message

period '{period_key}' 的 start 与 end 不能相同: {period['start']}

What it means

Raised by _check_period_overlaps during startup validation when two periods within the same day_plan have overlapping time ranges and overlap.policy is 'error_on_overlap' (default). This is the eager, whole-timeline version of the runtime conflict error at scheduler.py:220: every day plan's periods are pairwise-checked (cross-midnight ranges supported via minute conversion) before the scheduler ever runs.

Source

Thrown at trendradar/core/scheduler.py:354

        # period 引用完整性
        for plan_key, plan in timeline["day_plans"].items():
            for period_key in plan.get("periods", []):
                if period_key not in timeline["periods"]:
                    raise ValueError(
                        f"day_plan[{plan_key}] 引用了不存在的 period: {period_key}"
                    )

        # 时间格式校验
        for period_key, period in timeline["periods"].items():
            if "start" not in period or "end" not in period:
                raise ValueError(
                    f"period '{period_key}' 缺少 start 或 end 字段"
                )
            self._validate_hhmm(period["start"], f"{period_key}.start")
            self._validate_hhmm(period["end"], f"{period_key}.end")
            if period["start"] == period["end"]:
                raise ValueError(
                    f"period '{period_key}' 的 start 与 end 不能相同: {period['start']}"
                )

        # 检查冲突策略下的重叠
        policy = timeline.get("overlap", {}).get("policy", "error_on_overlap")
        if policy == "error_on_overlap":
            self._check_period_overlaps(timeline)

    def _check_period_overlaps(self, timeline: Dict[str, Any]) -> None:
        """
        检查每个日计划中的时间段是否存在重叠

        仅在 overlap.policy == "error_on_overlap" 时调用
        """
        periods = timeline.get("periods", {})

        for plan_key, plan in timeline["day_plans"].items():
            period_keys = plan.get("periods", [])

View on GitHub (pinned to 8ee26026ba)

Solutions

  1. Make the listed ranges disjoint (adjust start/end so no pair overlaps).
  2. If overlap is intended with the later period winning, set overlap: {policy: last_wins} — validation is then skipped and the runtime path logs warnings instead.
  3. Audit every day_plan, not just one: the check iterates all plans and all period pairs.

Example fix

# before (policy default)
day_plans:
  work: {periods: [morning, noon]}
periods:
  morning: {start: "08:00", end: "12:00"}
  noon:    {start: "11:30", end: "13:30"}
# after
periods:
  morning: {start: "08:00", end: "11:30"}
  noon:    {start: "11:30", end: "13:30"}
# or add: overlap: {policy: last_wins}
Defensive patterns

Strategy: validation

Validate before calling

def to_min(t): return int(t[:2]) * 60 + int(t[3:])

def overlaps(s1, e1, s2, e2):
    a1, b1, a2, b2 = to_min(s1), to_min(e1), to_min(s2), to_min(e2)
    if a1 == b1 or a2 == b2:
        return False
    spans = []
    for a, b in ((a1, b1), (a2, b2)):
        if a < b:
            spans.append((a, b))
        else:  # cross-midnight
            spans.append((a, 24 * 60))
            spans.append((0, b))
    # naive pairwise check on split intervals is enough for a lint
    flat = sorted(spans)
    for i in range(1, len(flat)):
        if flat[i][0] < flat[i - 1][1]:
            return True
    return False

policy = timeline.get("overlap", {}).get("policy", "error_on_overlap")
if policy == "error_on_overlap":
    for plan_key, plan in timeline["day_plans"].items():
        keys = plan.get("periods", [])
        for i in range(len(keys)):
            for j in range(i + 1, len(keys)):
                pi, pj = timeline["periods"][keys[i]], timeline["periods"][keys[j]]
                if overlaps(pi["start"], pi["end"], pj["start"], pj["end"]):
                    raise ValueError(f"{plan_key}: {keys[i]} overlaps {keys[j]}")

Try / catch

try:
    scheduler = Scheduler(schedule_config, timeline_data)
except ValueError as e:
    if "存在重叠" in str(e):
        raise SystemExit(f"timeline has overlapping periods: {e}")
    raise

Prevention

When it happens

Trigger: A day_plan listing morning 08:00-12:00 and noon 11:30-13:30; overlap.policy unset or 'error_on_overlap'. Triggered at validation time regardless of current clock, unlike the runtime variant.

Common situations: Same as the runtime overlap case: hand-edited boundaries, periods copied between plans, cross-midnight spans overlapping morning periods (e.g. 22:00-02:00 vs 01:00-03:00).

Related errors


AI-assisted analysis of sansan0/TrendRadar@8ee26026ba (2026-08-15). Data as JSON: /api/errors/71e6ffc2cc3e3477. Report an issue: GitHub.