moon-schedule

Deterministic UTC Cron and RFC 5545 recurrence rules for MoonBit.

cron
rrule
schedule
calendar
utc
moon add 100kkk/moon-schedule@0.1.0
Download zip
Author
Version
0.1.0
License
MIT
Last updated
last month
Downloads
9
README

#MoonSchedule

MoonSchedule 是一个纯 MoonBit 的确定性 UTC 日程规则引擎。它覆盖五字段 Cron 与 RFC 5545 RRULE 的实用子集,可用于跨 wasmwasm-gcjsnative 后端计算下一次触发时间或一个时间区间内的复发实例。

#为什么需要它

MoonBit 已有日期时间实现,但面向工程定时(Cron)与可互操作日历复发规则(RRULE)的纯 MoonBit 规则引擎仍是生态空缺。MoonSchedule 不读取系统时间、不做时区或 DST 猜测;同一输入在每个后端都会得到相同 UTC 结果。

#安装

发布后:

moon add 100kkk/moon-schedule

本地开发:

moon check --target all --deny-warn moon test --target all --deny-warn

#功能范围

  • DateTime:带分钟精度的 UTC RFC 3339 时间、闰年/月末/星期计算。
  • Cron*、列表、范围、步长、月份和星期英文别名;遵循传统 Cron 的 DOM/DOW OR 语义。
  • RRuleFREQ(DAILY/WEEKLY/MONTHLY/YEARLY)、INTERVALCOUNTUNTILBYDAYBYMONTHDAYBYMONTH
  • CLI:解析、下一次触发模拟、区间枚举。

首版明确不支持时区、DST、秒字段、BYSETPOSEXDATE 和完整 iCalendar 文档;它们是后续 4k 行规模路线图中的独立功能。

#API 示例

let cron = @schedule.Cron::parse("*/15 9-17 * * MON-FRI")
let after = @schedule.DateTime::parse("2025-01-06T09:01Z")
let next = cron.next_after(after)
// 2025-01-06T09:15Z

let rule = @schedule.RRule::parse("FREQ=DAILY;COUNT=3")
let start = @schedule.DateTime::parse("2025-01-01T09:00Z")
let end = @schedule.DateTime::parse("2025-01-05T09:00Z")
let occurrences = rule.occurrences_between(start, end)

#CLI

PowerShell 中请将包含空格或分号的规则整体加引号:

moon run cmd/main -- parse 'cron:*/15 9-17 * * MON-FRI' moon run cmd/main -- next 'cron:0 0 1 JAN *' '2024-12-31T23:59Z' 2 moon run cmd/main -- between 'rrule:FREQ=DAILY;COUNT=3' '2025-01-01T09:00Z' '2025-01-05T09:00Z'

#质量与发布

CI 对四个稳定后端执行检查、构建、格式检查、接口生成、测试和 moon package。项目采用 MIT 许可证。发布前运行:

moon check --target all --deny-warn moon build --target all --deny-warn moon fmt --check moon info git diff --exit-code moon test --target all --deny-warn moon package

#路线图

  1. 规则集合、EXDATE/RDATE 与人类可读描述。
  2. RFC 5545 更多 BY* 字段与性质测试。
  3. 时区适配层、DST 策略与性能基准。

#许可证

MIT。

#
ScheduleError

pub suberror ScheduleError {
InvalidDate(String)
InvalidRule(String)
}

#
Cron

pub struct Cron {
minutes : Array[Bool]
hours : Array[Bool]
days : Array[Bool]
months : Array[Bool]
weekdays : Array[Bool]
days_wildcard : Bool
weekdays_wildcard : Bool
} derive(
Debug
)

#
Cron::matches

fn Cron::matches(self : Cron, at : DateTime) -> Bool

#
Cron::next_after

fn Cron::next_after(self : Cron, after : DateTime) -> DateTime raise ScheduleError

Return the first matching minute strictly after after, searching at most two years.

#
Cron::parse

fn Cron::parse(input : String) -> Cron raise ScheduleError

Parse a five-field UTC cron expression: minute hour day-of-month month day-of-week.

#
DateTime

pub struct DateTime {
year : Int
month : Int
day : Int
hour : Int
minute : Int
} derive(Compare, Eq,
Debug
)

#
DateTime::add_minutes

fn DateTime::add_minutes(self : DateTime, amount : Int) -> DateTime

#
DateTime::new

fn DateTime::new(year : Int, month : Int, day : Int, hour : Int, minute : Int) -> DateTime raise ScheduleError

#
DateTime::next_day

fn DateTime::next_day(self : DateTime) -> DateTime

#
DateTime::parse

fn DateTime::parse(input : String) -> DateTime raise ScheduleError

#
DateTime::to_string

fn DateTime::to_string(self : DateTime) -> String

#
DateTime::weekday

fn DateTime::weekday(self : DateTime) -> Int

#
Frequency

pub enum Frequency {
Daily
Weekly
Monthly
Yearly
} derive(Eq,
Debug
)

#
RRule

pub struct RRule {
frequency : Frequency
interval : Int
count : Int?
until : DateTime?
weekdays : Array[Int]
monthdays : Array[Int]
months : Array[Int]
} derive(
Debug
)

#
RRule::next_after

fn RRule::next_after(self : RRule, after : DateTime) -> DateTime?

#
RRule::occurrences_between

fn RRule::occurrences_between(self : RRule, start : DateTime, end : DateTime) -> Array[DateTime]

Enumerate occurrences in [start, end]; start is the recurrence anchor.

#
RRule::parse

fn RRule::parse(input : String) -> RRule raise ScheduleError

Parse the UTC-safe RFC 5545 subset used by MoonSchedule.

#
days_in_month

fn days_in_month(year : Int, month : Int) -> Int

#
is_leap_year

fn is_leap_year(year : Int) -> Bool