Install
openclaw skills install @thcjp/cron-expert-freeopenclaw skills install @thcjp/cron-expert-free不是教你写cron表达式,而是教你正确使用定时系统。自唤醒、时区锁定、陷阱规避,经验法则一网打尽。
定时系统的正确使用比表达式编写更重要。本技能聚焦定时行为方法论:Agent如何在会话开始时自唤醒检查待执行任务、如何锁定时区避免混乱、如何设计可靠的提醒模式、如何规避DST和月末日期等常见陷阱。
┌─────────────────────────────────────────────────┐
│ cron优选实践专家 (免费版) │
├─────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 自唤醒规则层 │ │
│ │ 会话开始 → 检查任务 → 执行 → 确认 │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 时区锁定 │ │ 提醒模式 │ │
│ │ 统一UTC+8 │ │ 单次/重复 │ │
│ │ 避免DST混乱 │ │ 递增提醒 │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 陷阱规避清单 │ │
│ │ DST夏令时 │ 月末日期 │ 并发竞争 │ │
│ │ 时区漂移 │ 闰年2/29 │ 长任务阻塞 │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
import json
from pathlib import Path
from datetime import datetime
# Agent会话开始时的自唤醒检查
def self_wake_check():
"""会话开始时检查待执行的定时任务"""
store = Path.home() / "workspace" / "scheduler" / "reminders"
store.mkdir(parents=True, exist_ok=True)
reminders_file = store / "reminders.json"
if not reminders_file.exists():
print("无定时任务")
return []
reminders = json.loads(reminders_file.read_text(encoding="utf-8"))
now = datetime.now()
pending = []
for r in reminders:
if r["status"] != "active":
continue
next_run = datetime.fromisoformat(r["next_run"])
if next_run <= now:
pending.append(r)
print(f"⏰ 待执行:{r['name']}(应于{r['next_run']}执行)")
if not pending:
print("无待执行任务")
return pending
# Agent每次会话开始调用
self_wake_check()
配置提醒系统与执行确认:
import json
from pathlib import Path
from datetime import datetime, timedelta
class ReminderSystem:
"""定时提醒系统(免费版核心)"""
TIMEZONE = "Asia/Shanghai" # 时区锁定
def __init__(self):
self.store = Path.home() / "workspace" / "scheduler" / "reminders"
self.store.mkdir(parents=True, exist_ok=True)
self.file = self.store / "reminders.json"
if not self.file.exists():
self.file.write_text("[]", encoding="utf-8")
def add_reminder(self, name, remind_type, config, message):
"""添加提醒"""
reminders = json.loads(self.file.read_text(encoding="utf-8"))
reminder = {
"id": f"rem_{len(reminders)+1:04d}",
"name": name,
"type": remind_type, # once / daily / weekly
"config": config,
"message": message,
"status": "active",
"timezone": self.TIMEZONE,
"created_at": datetime.now().isoformat(),
"next_run": self._calc_next(remind_type, config),
"last_run": None,
"confirm_required": True
}
reminders.append(reminder)
self.file.write_text(json.dumps(reminders, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"✓ 提醒已创建:{reminder['id']} - {name}")
print(f" 类型:{remind_type}")
print(f" 下次提醒:{reminder['next_run']}")
return reminder
def check_and_execute(self):
"""检查并执行到期提醒"""
reminders = json.loads(self.file.read_text(encoding="utf-8"))
now = datetime.now()
executed = []
for r in reminders:
if r["status"] != "active":
continue
next_run = datetime.fromisoformat(r["next_run"])
if next_run <= now:
# 执行提醒
print(f"\n⏰ [{r['name']}] {r['message']}")
r["last_run"] = now.isoformat()
r["next_run"] = self._calc_next(r["type"], r["config"])
# 单次提醒执行后归档
if r["type"] == "once":
r["status"] = "done"
executed.append(r)
self.file.write_text(json.dumps(reminders, ensure_ascii=False, indent=2), encoding="utf-8")
return executed
def confirm_execution(self, reminder_id):
"""确认执行结果"""
reminders = json.loads(self.file.read_text(encoding="utf-8"))
for r in reminders:
if r["id"] == reminder_id:
r["confirmed"] = True
r["confirmed_at"] = datetime.now().isoformat()
self.file.write_text(json.dumps(reminders, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"✓ 已确认:{reminder_id}")
return
print(f"✗ 未找到:{reminder_id}")
def _calc_next(self, remind_type, config):
"""计算下次提醒时间"""
now = datetime.now()
if remind_type == "once":
return datetime.fromisoformat(config["datetime"])
elif remind_type == "daily":
time_str = config.get("time", "09:00")
h, m = map(int, time_str.split(":"))
next_run = now.replace(hour=h, minute=m, second=0, microsecond=0)
if next_run <= now:
next_run += timedelta(days=1)
return next_run.isoformat()
elif remind_type == "weekly":
target_day = config.get("weekday", 0)
time_str = config.get("time", "09:00")
h, m = map(int, time_str.split(":"))
days_ahead = (target_day - now.weekday()) % 7
next_run = now.replace(hour=h, minute=m, second=0, microsecond=0)
if days_ahead == 0 and next_run <= now:
days_ahead = 7
next_run += timedelta(days=days_ahead)
return next_run.isoformat()
return now.isoformat()
# 示例
rs = ReminderSystem()
# 单次提醒
rs.add_reminder("项目截止提醒", "once",
{"datetime": "2026-07-20T17:00:00"},
"项目A将于明天截止,请确认完成状态")
# 每日提醒
rs.add_reminder("每日站会", "daily",
{"time": "09:30"},
"每日站会时间到,请准备今日工作汇报")
# 每周提醒
rs.add_reminder("周报提交", "weekly",
{"weekday": 4, "time": "17:00"},
"周五周报提交截止,请生成本周工作总结")
# 检查并执行
rs.check_and_execute()
配置递增提醒与陷阱规避:
class SmartReminderSystem(ReminderSystem):
"""智能提醒系统(含递增提醒)"""
def add_escalating_reminder(self, name, start_time, intervals, message):
"""递增提醒:间隔逐渐缩短"""
reminders = json.loads(self.file.read_text(encoding="utf-8"))
reminder = {
"id": f"rem_{len(reminders)+1:04d}",
"name": name,
"type": "escalating",
"config": {
"start": start_time,
"intervals": intervals, # [60, 30, 15, 5] 分钟,逐渐缩短
"current_index": 0
},
"message": message,
"status": "active",
"timezone": self.TIMEZONE,
"created_at": datetime.now().isoformat(),
"next_run": start_time,
"last_run": None,
"confirm_required": True
}
reminders.append(reminder)
self.file.write_text(json.dumps(reminders, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"✓ 递增提醒已创建:{reminder['id']} - {name}")
print(f" 间隔序列:{intervals}分钟")
return reminder
def check_escalating(self):
"""检查递增提醒"""
reminders = json.loads(self.file.read_text(encoding="utf-8"))
now = datetime.now()
for r in reminders:
if r["type"] != "escalating" or r["status"] != "active":
continue
next_run = datetime.fromisoformat(r["next_run"])
if next_run <= now:
print(f"\n⏰ [{r['name']}] {r['message']}")
r["last_run"] = now.isoformat()
# 计算下次间隔
config = r["config"]
idx = config["current_index"]
intervals = config["intervals"]
if idx < len(intervals) - 1:
idx += 1
config["current_index"] = idx
r["next_run"] = (now + timedelta(minutes=intervals[idx])).isoformat()
else:
r["next_run"] = (now + timedelta(minutes=intervals[-1])).isoformat()
self.file.write_text(json.dumps(reminders, ensure_ascii=False, indent=2), encoding="utf-8")
# 使用:递增提醒(越来越紧急)
smart = SmartReminderSystem()
smart.add_escalating_reminder(
"紧急部署提醒",
start_time=datetime.now().isoformat(),
intervals=[60, 30, 15, 5, 5, 5], # 1小时→30分→15分→每5分钟
message="生产环境部署待确认,请立即处理"
)
Agent在每次会话开始时执行的自唤醒检查流程:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 读取任务文件 | 加载 reminders.json |
| 2 | 检查待执行 | 比较 next_run 与当前时间 |
| 3 | 执行到期任务 | 输出提醒消息 |
| 4 | 更新状态 | 记录 last_run,计算新 next_run |
| 5 | 确认执行 | 用户确认后标记 confirmed |
输入: 用户提供自唤醒规则所需的指令和必要参数。 处理: 按照skill规范执行自唤醒规则操作,遵循单一意图原则。 输出: 返回自唤醒规则的执行结果,包含操作状态和输出数据。
| 规则 | 说明 |
|---|---|
| 统一时区 | 所有任务使用 Asia/Shanghai |
| 存储格式 | ISO 8601 含时区信息 |
| 显示格式 | 本地时间 + UTC偏移 |
| DST规避 | 不依赖DST自动切换,固定偏移 |
输入: 用户提供时区锁定策略所需的指令和必要参数。 处理: 按照skill规范执行时区锁定策略操作,遵循单一意图原则。 输出: 返回时区锁定策略的执行结果,包含操作状态和输出数据。
| 模式 | 配置 | 适用场景 |
|---|---|---|
| 单次(once) | datetime: 指定时间 | 一次性事件提醒 |
| 每日(daily) | time: 每日时间 | 日常例行提醒 |
| 每周(weekly) | weekday+time | 周期性周提醒 |
| 递增(escalating) | intervals列表 | 紧急程度递增 |
输入: 用户提供提醒模式所需的指令和必要参数。 处理: 按照skill规范执行提醒模式操作,遵循单一意图原则。 输出: 返回提醒模式的执行结果,包含操作状态和输出数据。
| 陷阱 | 问题 | 规避方法 |
|---|---|---|
| DST夏令时 | 时间偏移导致提醒错乱 | 锁定时区,不依赖DST |
| 月末日期 | 2月无30/31号 | 使用L语法或避免月末 |
| 闰年2/29 | 非闰年无2/29 | 使用年度首日替代 |
| 并发竞争 | 多Agent同时执行 | 加锁机制(专业版) |
| 时区漂移 | 不同时区计算不一致 | 统一时区存储 |
| 长任务阻塞 | 任务执行时间超长 | 设置超时(专业版) |
输入: 用户提供陷阱规避清单所需的指令和必要参数。 处理: 按照skill规范执行陷阱规避清单操作,遵循单一意图原则。 输出: 返回陷阱规避清单的执行结果,包含操作状态和输出数据。 能力覆盖范围:本skill的核心能力覆盖以下场景关键词:cron、定时系统优选实践、指南免费版、含自唤醒规则、基础提醒模式、常见陷阱规避、优选实践专家免费、版是面向、的定时系统使用优、选实践指南、不同于表达式编写、本技能聚焦、如何正确使用定时、的方法论与经验法、建立可靠的定时行、为模式、Use、when、模型调用、智能对话、LLM、应用时使用、不适用于需要、确定性的关键决策等。这些关键词对应description中声明的使用场景,均已在上述能力点中提供对应的操作支持。
角色:独立开发者
场景描述:每天早上提醒查看待办事项,下午提醒提交日报。
rs = ReminderSystem()
rs.add_reminder("早间待办", "daily", {"time": "08:00"}, "查看今日待办事项")
rs.add_reminder("晚间日报", "daily", {"time": "18:00"}, "提交今日工作日报")
角色:项目经理
场景描述:项目临近截止,提醒频率逐渐增加。
smart = SmartReminderSystem()
smart.add_escalating_reminder(
"项目A截止",
start_time="2026-07-19T09:00:00",
intervals=[120, 60, 30, 15, 5],
message="项目A即将截止,请确认完成状态"
)
角色:团队负责人
场景描述:每周五下午提醒团队提交周报。
rs.add_reminder("周报提醒", "weekly",
{"weekday": 4, "time": "16:00"},
"周五周报提交截止,请生成本周总结")
自唤醒规则是Agent在每次会话开始时执行的检查流程:读取定时任务文件,比较 next_run 与当前时间,如果有到期任务则立即执行。这确保了即使Agent不是持续运行的,也能在会话开始时补执行错过的定时任务。
不同时区的DST(夏令时)切换会导致时间偏移,可能造成提醒在错误的时间触发。锁定为固定时区(如Asia/Shanghai,UTC+8,无DST)可以避免这类问题。所有时间戳以ISO 8601格式存储,包含时区信息。
递增提醒是间隔逐渐缩短的提醒模式,适用于紧急程度递增的场景。例如项目截止前:提前2小时提醒,然后1小时、30分钟、15分钟、5分钟。通过 intervals 列表定义间隔序列,每次执行后自动切换到下一个更短的间隔。
2月没有30和31号,4/6/9/11月没有31号。如果在这些日期设置定时任务,任务会被跳过。规避方法:(1) 使用专业版的L语法(每月最后一天);(2) 避免在月末设置任务;(3) 使用月初日期替代。
单次(once)提醒在执行后状态自动改为 done,不再参与后续调度。但历史记录保留,可用于审计。如果需要重新启用,可以手动将状态改回 active。
| 依赖项 | 类型 | 是否必需 | 获取方式 |
|---|---|---|---|
| Python标准库 | 内置 | 必需 | Python自带(json/pathlib/datetime) |
| LLM API | API | 必需 | 由Agent平台内置LLM提供 |
本技能基于原始开源定时系统优选实践作品改进,保留原始版权声明:
本改进作品在原始作品基础上进行了深度差异化改造,包括但不限于:
原始MIT license允许使用、复制、修改和分发,需保留版权声明。本改进作品在保留原始版权声明的基础上添加自有署名,完全符合MIT license要求。
本免费体验版限制以下高级功能:
解锁全部功能请使用专业版:cron-expert-pro
| 错误场景 | 原因 | 处理方式 |
|---|---|---|
| 配置错误 | 参数缺失或格式错误 | 检查依赖说明中的配置要求 |
| 运行时错误 | 运行环境不满足 | 确认运行环境符合依赖说明 |
| 网络错误 | 连接超时或不可达 | 执行ping命令测试网络连通性,检查防火墙和代理设置连接后执行ping命令测试网络连通性,检查防火墙和代理设置连接后重新执行命令,参考国内替代方案 |
输入:用户提供操作指令和必要参数
输出:返回执行结果,包含操作状态和输出数据
用户: 执行核心功能
Skill: 正在执行核心功能...
Skill: 执行完成,结果如下: 操作成功