ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Super Productivity 重复任务(Recurring Events)实现方案解析:扁平字段向 RRULE 规范化演进的引擎与同步安全设计

Super Productivity 重复任务(Recurring Events)实现方案解析:扁平字段向 RRULE 规范化演进的引擎与同步安全设计 Super Productivity 重复任务Recurring Events实现方案解析扁平字段向 RRULE 规范化演进的引擎与同步安全设计【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity本文是 Super Productivity 仓库中docs/research/recurring-events-implementation-plan.md的设计论证design rationale指南。它围绕“重复任务recurring tasks的模型与引擎如何演进”这一核心主题展开从约 14 个互相耦合的扁平字段repeatCycle、repeatEvery、7 个星期布尔量、monthlyWeekOfMonth等演进到与 RFC 5545 RRULE 同构的规范化模型同时保证多端 op-log 同步不破坏、旧客户端兼容不破坏、确定性 ID 不漂移。读完本文你将掌握该方案的三条核心前提、已废弃的“类型化模型”设计及其仍具约束力的正确性约束、当前已实现的能力清单、Phase 1–3 的落地路径、迁移子系统选型op-log schema 而非pfapi-config.js以及 RFC 5545 RRULE 的完整速查。阅读前置本计划是feat/rrule-epic分支的配套文档而非全新提案。分支上已实现的内容正向/反向 RRULE 转换器、旧客户端兼容契约、默认关闭的按设备引擎开关比本文更接近当前事实如需规划或构建请先以该分支的 roadmap 为准。文档已在 2026-08-21 与分支做法对齐分支采用“原始rrule字符串”设计并保留见下文 Decision 说明。1. 三条核心前提Net Premises文档在多次多轴评审后收敛为三条驱动全篇的前提全部可以对照当前仓库源码验证RRULE 引擎已在仓库中。ical.js2.2.1已存在于根package.json第 294 行且是devDependency而非运行时依赖它通过 ical-lazy-loader.ts 懒加载并在两处实际展开 RRULE一是 get-relevant-events-from-ical.ts日历集成时展开 VEVENT 的rrule见其 403–444 行的iter.next()循环与 EXDATE 过滤二是packages/plugin-dev/caldav-calendar-provider/src/plugin.tsCalDAV 提供者插件。caldav-client.service.ts只借用 ical.js 解析 VTODO不做RRULE 展开。早期草案建议“不要引入 rrule.js”这一点后来被 epic 分支推翻分支固定引入rrule2.8.1作为引擎详见 Decision 说明。曾经的“关键缺口”已上线。第 N 个星期几issue #6040、月末最后一天issue #7726、EXDATE 跳过实例deletedInstanceDates均已实现早期 gap-analysis 文档已过时并被并入本文附录 A/B。TaskRepeatCfg是同步状态。模型变更必须走 op-log schema 迁移体系packages/shared-schema/src/migrations/保持确定性 IDrpt_${repeatCfgId}_${dueDay}稳定且不得破坏跨版本同步。这是整个方案风险的主导项。2. 决策记录已废弃类型化、与 RRULE 同构的复发模型已于 2026-08-21 废弃。现行决策是 epic 分支的在传统扁平字段旁附加一个rrule?: string扁平字段作为线格式永远双写dual-write。下文是已废弃的类型化模型方案保留它是因为其正确性约束DTSTART/正午锚定、EXDATE 按日字符串匹配、UNTIL包含性、WKST、月度锚点优先级、奇偶校验门、线格式不重命名与迁移警告对分支的序列化器与引擎同样适用——只是持久化形态不同。被废弃的设想把复发模式变成一个单一的、带类型的结构化字段——一个与RFC 5545 一一对应的判别联合discriminated union取代约 14 个互相依赖的扁平字段repeatCycle、repeatEvery、7 个星期布尔量、monthlyWeekOfMonth、monthlyWeekday、monthlyLastDay、quickSetting。RFC 5545 的RRULE 字符串只在边界处产生/解析.ics导出、CalDAV。原始字符串永远不是持久化/同步字段。2.1 为什么“拒绝原始字符串”的结论已过时2026-08-21 调和早期修订版从三个角度拒绝把原始rrule字符串作为规范字段。对照feat/rrule-epic实际构建的内容每个异议都被一个当初未预见的决策回答了“不可查询。”分支是增量式添加rrule?: string并且永远双写传统扁平字段——它们是旧客户端的线格式roadmap “Dual-engine endgame” 第 4 节。选择器与任何字段级消费方都保有结构化表示投影时无需解析字符串。“不可 diff / 不可修复。”同样靠双写op-log 仍对传统字段做 diffdata-repair.ts仍修复它们。对字符串本身分支用显式策略替代静默损坏引擎是 fail-soft 的格式错误的rrule→ 记录日志并返回null绝不 throwisRRuleValid门控路由无效 → 回退传统引擎在后传统引擎时代对无效字符串的既定行为是暂停 修复提示绝不静默重新排程。“热路径性能。”该异议假设展开引擎是 ical.js异步、懒加载、仅前向。分支的引擎是rrule.jsrrule2.8.1精确锁定版本——同步放在专用的按日粒度、基于 UTC 的出现日工具store/rrule-occurrence.util.ts里运行在现有同步计算器内部选择器路径不引入任何异步依赖。旧推理唯一权衡正确的一点是rrule是一个新的根运行时依赖项目规则通常禁止。维护者拥有该 epic故意接受它并把 rrule.js 当作“引擎”对待——锁定精确版本并以差异规范测试集作为升级绊线设备间解析器版本漂移会制造重复任务见 roadmap 的风险模型。这一取舍已定请勿再争论也不要解除版本锁定。2.2 对目标的诚实告诫按此设计“数据模型更小”只是部分的。模式子模型确实从 ~14 个字段塌缩为 1 个类型化recurrence字段但TaskRepeatCfg仍保持约 18 个字段任务模板 SP 扩展 追踪字段不可削减。真正的收益是不变量消除——判别联合让“字段互相矛盾”变得不可表示删除了隐式优先级这一类 bug例如“Nth-weekday 锚点压过monthlyLastDay”并大幅缩小data-repair.ts。应当推销这一点而不是字节数。“覆盖一切”只有一个例外。RFC 5545 无法表达“完成后的 N 天”因此repeatFromCompletionDateSP 的差异化能力保持为独立的非 RRULE 表示。模型是“RRULE 同构 一个例外”引擎保留两种模式。3. 类型化模型已废弃——保留作为理由记录已废弃——见上方 Decision 说明。保留它是因为下面的不变量分析是对传统字段隐式优先级规则的最佳记录。该草图把TaskRepeatCfgCopytask-repeat-cfg.model.ts——请编辑TaskRepeatCfgCopy而不是Readonly别名中的扁平模式字段替换为一个判别联合加一个结束条件。草图最终命名待定type Weekday MO | TU | WE | TH | FR | SA | SU; type RecurrencePattern | { freq: DAILY; interval: number } | { freq: WEEKLY; interval: number; byDay: Weekday[] } // WKST derived, never persisted — see 1.3 | { freq: MONTHLY; interval: number; on: { monthDay: number } } // BYMONTHDAYn | { freq: MONTHLY; interval: number; on: { lastDay: true } } // BYMONTHDAY-1 | { freq: MONTHLY; interval: number; on: { week: 1 | 2 | 3 | 4 | -1; day: Weekday } } // BYDAYnDD | { freq: YEARLY; interval: number; month: number; day: number }; type RecurrenceEnd | { type: never } | { type: count; count: number } // COUNT | { type: until; until: string }; // UNTIL — DbDateStr, inclusive end-of-day interface RecurrenceConfigPart { // canonical, RRULE-isomorphic, persisted/synced: recurrence: RecurrencePattern; end: RecurrenceEnd; deletedInstanceDates: string[]; // wire name kept verbatim — exDates would be exactly the forbidden rename // SP carve-out — not expressible in RFC 5545: repeatFromCompletionDate?: boolean; }判别符是freq加上月度的on形态。非法组合例如在年度配置上设置星期布尔量变得不可表示。不持久化任何派生字段。UI 表现物星期复选框行、“Ends”控件、quickSetting在表单打开时由recurrence/end计算、保存时写回——纯视图模型。这化解了文档记录的 formly “整模型 emit” 陷阱不再有第二个表示可漂移。repeatFromCompletionDate选择例外引擎见下文。4. 已修正的当前状态已上线能力在src/app/features/task-repeat-cfg/中验证能力状态位置每日 / 每周 / 每月 / 每年 repeatEvery间隔✅get-next-repeat-occurrence.util.ts星期选择每周✅7 个布尔量task-repeat-cfg.model.ts每月第 N 个星期几“第 2 个周二”、“最后一个周五”✅ #6040monthlyWeekOfMonthmonthlyWeekdayget-nth-weekday-of-month.util.ts每月最后一天✅ #7726monthlyLastDayget-next-repeat-occurrence.util.ts 中的月末钳制每月第一天✅快捷设置MONTHLY_FIRST_DAY跳过某次出现EXDATE✅deletedInstanceDates: string[]完成后复发✅SP 独有repeatFromCompletionDate get-effective-repeat-start-date.util.ts等待完成不堆积✅waitForCompletion跳过逾期实例✅skipOverdue暂停 / 恢复✅isPaused子任务模板 继承 / 自动更新标志✅subTaskTemplates、shouldInheritSubtasks、disableAutoUpdateSubtasksDST 安全计算✅全程本地正午锚定见 1.3确定性多设备 ID✅rpt_${repeatCfgId}_${dueDay}get-repeatable-task-id.util.ts人类可读描述✅get-task-repeat-info-text.util.ts“下次到期”预览 历史热力图✅repeat-cfg-preview/、repeat-task-heatmap/真正缺失Phase 3 交付结束条件COUNT/UNTIL、每月多日BYMONTHDAY1,15、.ics/CalDAV RRULE 生成Phase 1。延迟 / YAGNIRDATE、RECURRENCE-ID、BYWEEKNO、BYYEARDAY、亚日频率、完整双向.ics导入。BYSETPOS仅序列化器的月末钳制惯用法需要见 Phase-1 映射表一般引擎侧展开仍延迟。5. 引擎决策保留同步有界引擎已调和对传统/开关关闭的配置成立开关打开的rrule配置路由到分支的同步 rrule.js 引擎见 2.1。ical.js 无论哪种方式都只在边界使用——下文记录原因且仍然成立。出现日运行时保持现有的同步有界循环get-next-repeat-occurrence.util.ts、get-newest-possible-due-date.util.ts改为读取新的类型化recurrence字段而非扁平字段。ical.js仅用于在导出/CalDAV 边界序列化/解析 RRULE 字符串——绝不出现在出现日热路径上。后果同步选择器中不引入懒加载模块的异步依赖引导/投影路径上没有约 76 KB 的开销ical-lazy-loader.ts 的注释明确记录 ical.js 约 76 KB。出现日逻辑几乎不变同样的FREQ/INTERVAL/BYxxx数学只是输入形状不同因此确定性 ID 的奇偶风险很小离线 golden-master 测试就足够——不需要生产影子模式。新的常见模式每月多日、结束条件是对有界引擎的小扩展。奇异 RRULE 部件通用BYSETPOS、BYWEEKNO并不免费延迟它们若确有需要就通过 ical.js 在热路径之外扩展这些稀有配置。6. Phase 1 —— 类型化模型 RRULE 序列化器 奇偶校验架可独立交付序列化器解锁日历双向同步路线图中的“SP 不生成 RRULE”关键路径项。6.1 添加类型化recurrence/end字段增量式尚非规范在现有字段旁添加联合。由于校验使用 typiacreateValidate容忍多余属性而非createValidateEquals——在validation-fn.ts中验证旧客户端读取新字段时既不会拒绝也不会剥离它们——结构性前向兼容。确认data-repair.ts没有任何 pass 会删除它们并添加前向兼容回归规格。6.2 双向序列化器类型化 ⇄ RRULE 字符串纯模块例如task-repeat-cfg/rrule/。typed → RRULE是简单字符串组装或ICAL.Recur.fromData({...}).toString()RRULE → typed用于.ics导入使用 ical.js 解析。字段映射——必须覆盖一切类型化模型RRULE{freq, interval}FREQ...;INTERVAL...WEEKLYbyDayBYDAYMO,WE,...WKST见 6.3MONTHLYon.monthDayBYMONTHDAYnn ≤ 2829–31 → 钳制惯用法BYMONTHDAYn,-1;BYSETPOS1SP 钳制到月末纯BYMONTHDAY会跳过MONTHLYon.lastDayBYMONTHDAY-1MONTHLYon.{week,day}BYDAYweekDD-1最后YEARLY{month, day}BYMONTHm;BYMONTHDAYd2 月 29 日 → 同一钳制惯用法BYMONTH2;BYMONTHDAY29,-1;BYSETPOS1非闰年钳到 2 月 28 日end.count/end.untilCOUNT/UNTILUTC 当日结束deletedInstanceDatesEXDATE仅导出线字段名保持原样repeatFromCompletionDate不可表达—— 序列化器拒绝/标记此类配置天然不兼容导出startTime、remindAt、waitForCompletion、skipOverdue、子任务标志、orderSP 扩展在 RRULE 带外 —— 保留isPaused无 RRULE 等价物 —— 序列化器跳过或标记暂停配置见下lastTaskCreationDay/lastTaskCreation内部创建游标在 RRULE 带外 —— 永不导出也永不由导入的规则派生isPaused会短路所有出现日生成task-repeat-cfg.selectors.ts 中两个选择器都先过滤isPaused因此把暂停配置导出为活跃 RRULE 会承诺 SP 永远不会创建的出现日。三行 MONTHLY并非独立——引擎按固定优先级解析一个锚点nth-weekdaymonthlyWeekOfMonthmonthlyWeekday在 get-next-repeat-occurrence.util.ts 首先检查→monthlyLastDay→startDate的日序。逐行序列化对携带多个锚点的配置发出多个部件会产生BYMONTHDAY-1;BYDAY2TU——在 RFC 5545 中是交集通常是空集。必须只序列化一个锚点按上述优先级顺序epic 分支的legacyTaskRepeatCfgToRRuleswitch 正是这样做的。6.3 DTSTART / 日期基准正确性最容易咬人的部分DTSTART 是锚定日的本地正午去掉时间分量。传统引擎做日期数学时从不使用startTime——它在setHours(12,…)处锚定get-next-repeat-occurrence.util.ts。非正午的 DTSTART 会让 ical.js 在另一个瞬时产生出现日可能跨日/DST 边界滚到不同的日历日→ 奇偶被破坏、ID 偏移。startTime保持为展开后的任务模板字段不参与 DTSTART 日期数学。EXDATE 按日字符串匹配而非瞬时相等用getDbDateStr(occurrence)对照exDates过滤生成的出现日。UNTIL是包含当日的结束。WKST 生效 startDate 的星期——或干脆不发出WKST并重新锚定 DTSTART正如 epic 分支的getAlignedStartDate。不是用户的firstDayOfWeek该设置仅用于显示src/app/core/date-time-format/custom-date-adapter.ts:19。引擎按从 startDate 星期起算的滚动 7 天块计数getDiffInWeeks(startDate, d) % repeatEveryget-next-repeat-occurrence.util.ts因此日历周的WKST会移动双周INTERVAL2出现日。反例startDate 为周三 2026-01-07、INTERVAL2、BYDAYMO,FR——SP 触发周一 01-12WKSTMO触发周一 01-19。6.4 出现日奇偶 golden master闸门差分测试架读取类型化字段的引擎与读取扁平字段的现有引擎产生逐字节一致的出现日覆盖每种配置形态每日、每周多日、repeatEvery1、按日每月、每月第 N 个星期几、每月最后一天、每年、2 月 29 日跨多年窗口、两个 CI 时区、跨越 DST 边界。限制每种形态的出现次数上限防止以后加入亚日频率时测试爆炸。基于完成的配置超出测试架范围不同引擎。迁移以 100% 奇偶为门槛。序列化器往返typed → RRULE → typed做属性测试。7. Phase 2 —— 带版本迁移经 op-log schema 体系已修正机制。不是pfapi-config.js——该文件是deprecated LEGACY CODE其CROSS_MODEL_VERSION是过时的4.4且它require的./migrate/cross-model-migrations路径已不存在。经由活跃的 op-log schema 体系迁移在packages/shared-schema/src/migrations/中添加vN → vN1条目注册表 index.ts同时提供migrateState快照与migrateOperation在途操作并提升CURRENT_SCHEMA_VERSIONschema-version.ts。由src/app/op-log/persistence/schema-migration.service.ts/remote-ops-processing.service.ts应用。转换本身是纯 O(1) 的字符串/结构组装即使配置很多也很便宜迁移不得按配置展开出现日。跨版本叙事解决旧客户端矛盾。2026-08 修正——见 #9664。早期修订把MIN_SUPPORTED_SCHEMA_VERSION称为“强制更新闸门”会推动旧客户端走VERSION_UNSUPPORTED流程。那是反的。它是施加于_本客户端读取的数据_上的下限remote-ops-processing.service.ts:177、operation-log-sync.service.ts:2318、verify-decrypted-op-integrity.ts:139、migrate.ts:49,115发送方只盖CURRENT_SCHEMA_VERSION章且不存在服务端客户端版本闸门。提升它会把已更新的客户端卡死——阻塞发生在上传前的周期内sync-wrapper.service.ts:595-606游标不前进而VERSION_UNSUPPORTED提示故意不带任何补救措施remote-ops-processing.service.ts:540-546“更新本设备无济于事”。它永远不会提示旧设备。这里没有版本闸门。而且CURRENT_SCHEMA_VERSION的提升也不能为_当前已发布_的设备群提供闸门master 是 4提升落到 5——在 v17.0.0–v18.14.0 的容忍带2 3内——这些客户端会未迁移地应用操作提升到 6 会阻塞它们但仍推进其游标永久跳过这些操作。v18.14.0 之后的接收端_确实_会安全阻塞因此一旦该群体淘汰提升就会成为真正的围栏。参见 schema-version.ts 与 operation-log-architecture.md §A.7.11 的 Bump Policy 完整说明。真正解决问题的机制已经在feat/rrule-epic上建好了——不要重新推导。传统排程字段与新表示一起保持填充作为旧客户端的线格式util/legacy-cfg-to-rrule.util.ts遵循精确或空exact-or-null契约规则在传统表达能力范围内时传统字段在相同日期触发超出范围时COUNT/UNTIL、季节性BYMONTH、BYWEEKNO/BYYEARDAY、多日列表、联合外序数写入LEGACY_NEVER_FIRES_FALLBACK哨兵——一个全 false 的WEEKLY配置每个已发布版本都确定性地永不触发。旧客户端什么也不创建而不是在错误日期创建会同步回传的任务。getAlignedStartDate处理startDate的双重职责——既作为月/年日序编码又作为间隔锚点。默认关闭的按设备开关RRuleFeatureFlagService、localStorage、永不同步在 epic 未完成时保持传统引擎权威——是那个开关而不是 schema 闸门让半成品阶段变安全。绝不在线格式上重命名deletedInstanceDates。保持同步字段名exDates是内存/类型化名称EXDATE是导出名称。在整实体 LWW 下赢得冲突的旧客户端会在没有重命名字段的情况下重新发出实体从而把跳过列表在设备群内销毁部分更新浅合并路径是第二个销毁向量。如果更偏好保留字面属性名也可以那样做——要点是不要重命名持久化键。8. Phase 3 —— RRULE 原生功能类型化模型成为规范后新模式 类型化联合的新增 有界引擎的小扩展结束条件。end: {type:count|until}在出现日循环中作为守卫执行越过边界返回null。UI “Ends” 控件由end派生不多持久化任何东西。标签经T/TranslateService仅en.json。每月多日BYMONTHDAY1,15等随模型/引擎成长。Phase 3 测试无超过COUNT/UNTIL的出现日每种频率、两个 CI 时区。决定并测试COUNTvsexDates被跳过的实例是否消耗一个计数ical.js 在 EXDATE 之前计数SP 在生成后过滤——刻意选择“10 个实际任务” vs “10 个计划”并测试之。UNTIL边界结束日包含次日排除。9.repeatFromCompletionDate例外这不是一个独立的_引擎_——它运行同样的FREQ/INTERVAL计算但每个周期把开始日期重新锚定到lastTaskCreationDay见 get-effective-repeat-start-date.util.ts 的源码实现当repeatFromCompletionDate为真且有lastTaskCreationDay时返回后者。所以真正的风险是喂入错误的 DTSTART/锚点而不是“错误的引擎”它没有稳定的 DTSTART→ 不可 RRULE 表达序列化器拒绝它天然不兼容导出。建模为联合变体 repeatFromCompletionDate标志在任何固定锚点路径之前路由到动态锚定计算。诚实的代码削减核算只有固定排程的管道被删除完成路径保留。10. 风险登记表风险严重度缓解模型切换导致出现日偏移 → 实例被重新编号阻断保留现有有界引擎Phase-1 golden master 门控迁移跨版本同步旧客户端读不懂类型化模型阻断不存在版本闸门#9664。线格式上按精确或空契约保持传统字段填充不可表达规则用LEGACY_NEVER_FIRES_FALLBACK按设备开关门控 epic重命名deletedInstanceDates线键导致跳过数据丢失高不重命名持久化键EXDATE仅用于导出repeatFromCompletionDate被喂入固定 DTSTART → 变成固定日历高在任何固定锚点路径之前路由完成模式每周期重新锚定错误迁移子系统pfapi-config.js高使用packages/shared-schema迁移 schema-migration.service.ts异步/仅前向 ical.js 迭代导致热路径回归高ical.js 仅做字符串解析/序列化同步有界引擎保持为运行时DTSTART 携带startTime→ 日序滚动高DTSTART 锚定日本地正午startTime在展开后应用EXDATE 永不匹配瞬时 vs 正午中按getDbDateStr日字符串过滤双周偏移WKST 默认值中startDate 派生的 WKST或省略并重新锚定——见 6.3UNTIL丢掉最后一天中包含当日结束生产影子模式成本n/a不需要——引擎不变离线 golden master 覆盖奇偶新依赖的包体大小n/a类型化模型方案无新依赖epic 确实添加了锁定版rrule——见 Decision 说明11. 可度量的成功标准闸门Phase-1 奇偶类型化字段引擎 扁平字段引擎覆盖完整配置形态语料5 年窗口、两个 CI 时区测试架在 CI 中绿灯。序列化器往返typed → RRULE → typed覆盖每种形态属性测试包括每月第 N 个星期几与最后一天。无新运行时依赖仅 ical.js边界使用。已废弃——epic 故意添加锁定版rrule见 Decision 说明。无同步字段键被重命名deletedInstanceDates保持线格式。前向兼容读取新字段的旧客户端既不报错也不剥离它们回归规格。迁移后repeatFromCompletionDate、waitForCompletion、skipOverdue、子任务模板与跳过列表行为不变回归规格绿灯。Phase-3 结束条件带测试在两个 CI 时区通过UI 结束状态是派生的而非持久化的。12. 涉及文件已验证路径领域文件模型task-repeat-cfg.model.tsTaskRepeatCfgCopy出现日引擎保留重指向类型化字段store/get-next-repeat-occurrence.util.ts、store/get-newest-possible-due-date.util.ts、store/get-first-repeat-occurrence.util.ts、store/get-nth-weekday-of-month.util.ts、store/get-effective-repeat-start-date.util.ts、store/get-effective-last-task-creation-day.util.ts确定性 ID必须保持稳定get-repeatable-task-id.util.ts选择器 / 投影task-repeat-cfg.selectors.ts服务 / 创建task-repeat-cfg.service.ts快捷设置 / 对话框 UIdialog-edit-task-repeat-cfg/表单常量、快捷设置更新、构建选项人类可读文本src/app/features/tasks/task-detail-panel/get-task-repeat-info-text.util.tsRRULE 序列化/解析仅边界ical-lazy-loader.ts复用加载器迁移已修正packages/shared-schema/src/migrations/index.tsindex.ts、schema-version.tsCURRENT_SCHEMA_VERSION——注意MIN_SUPPORTED_SCHEMA_VERSION_不是_跨版本闸门#9664、src/app/op-log/persistence/schema-migration.service.ts旧客户端线兼容已在feat/rrule-epic上建成src/app/features/task-repeat-cfg/util/legacy-cfg-to-rrule.util.tslegacyTaskRepeatCfgToRRule、rruleToLegacyTaskRepeatCfg、LEGACY_NEVER_FIRES_FALLBACK、getAlignedStartDate、src/app/features/config/rrule-feature-flag.service.ts校验 / 修复src/app/op-log/validation/createValidate、data-repair.ts日历写入边界注意早于 #6040/#7726/deletedInstanceDatesARCHITECTURE-DECISIONS.md 第 9 条——日历写入位于插件中、按提供者 opt-in。CalDAV VEVENT 展开已随caldav-calendar-provider插件上线单次出现编辑RECURRENCE-ID/EXDATE仍未解决#8148附录 A —— 竞品对比“用户期待什么”的参考SP 列为 2026-06 时点验证剩余 ❌ 才是真正的目标——结束条件。由原recurring-events-gap-analysis.md/recurring-events-industry-standards.md合并而来。功能Google CalendarTodoistThings 3TickTickSuper Productivity基础日/周/月/年✅✅✅✅✅每 N 间隔✅✅✅✅✅星期选择✅✅✅✅✅每月第 N 个星期几✅✅✅✅✅#6040每月最后一天✅✅✅✅✅#7726N 次后结束✅❌❌✅❌Phase 3指定日期结束✅❌❌✅❌Phase 3完成后复发❌✅✅✅✅例外跳过某次出现✅✅✅✅✅自然语言✅✅❌❌✅信息文本iCal 导出✅✅❌✅❌Phase 1附录 B —— RFC 5545 RRULE 参考iCalendar 规范RFC 5545的RRULE属性是类型化模型镜像、序列化器瞄准的复发标准。核心部件参数含义取值FREQ必填频率YEARLY、MONTHLY、WEEKLY、DAILY、HOURLY、MINUTELY、SECONDLYINTERVAL迭代间隔正整数默认 1COUNT出现次数正整数UNTIL结束日期时间DATE 或 DATE-TIMEWKST周起始日MO…SU默认MOBYxxx 部件参数含义取值BYDAY星期几MO…SU可选序数前缀2TU、-1FRBYMONTH月份1–12BYMONTHDAY月内日序1..31 或 -31..-1负值 从月末倒数BYYEARDAY年内日序1..366 / -366..-1BYWEEKNOISO 周号1..53 / -53..-1BYSETPOS集合内位置1..366 / -366..-1BYDAY序数前缀1MO/1MO 第一个周一-1MO 最后一个周一2TU 第二个周二。示例FREQDAILY;COUNT10 # 每天共 10 次 FREQWEEKLY;UNTIL20241231T235959Z;BYDAYMO,FR FREQWEEKLY;INTERVAL2;BYDAYMO,WE,FR # 每隔一周周一/三/五 FREQMONTHLY;BYMONTHDAY15 # 每月 15 日 FREQMONTHLY;BYMONTHDAY-1 # 每月最后一天 FREQMONTHLY;BYDAY2TU # 每月第二个周二 FREQMONTHLY;BYDAY-1FR # 每月最后一个周五例外EXDATE排除出现日 SP 的deletedInstanceDatesRDATE增加出现日已延迟——见“真正缺失”。【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表