A small, dependency-free JavaScript library (ESM, with TypeScript types) that reads short Chinese and English schedule sentences and returns structured dates, times and repeat rules.
每周一三五早上7点跑步 → 跑步 · 07:00 · weekly on Mon/Wed/Fri
明天下午三点开会 → 开会 · tomorrow 15:00
每隔两天提醒我浇花 → 浇花 · every 2 days (flagged: could mean every 3)
8点吃药 → 吃药 · 08:00 or 20:00? (it asks rather than guesses)
Gym every Monday and Thursday at 6:30am → Gym · weekly on Mon/Thu · 06:30
It is rule-based: no model, no network, and it gives the same answer every time. It works in Node 18+ and modern browsers.
- Never guess an unclear time. "8点" or "at 8" could mean 08:00 or 20:00, so you get
timeOptions: ["08:00", "20:00"]andtime: null. The parser only picks one when the words settle it: 早上/下午/晚上, am/pm, a 24-hour time, or a task word such as 早饭 or dinner. - Never turn an unsupported rhythm into a different one. "每小时喝水" keeps the task, drops the rhythm and adds the note
unsupportedRepeat. - Everything that is not a "when" is the title. Filler such as 提醒我, 帮我记一下 and "remind me to" is removed.
It is not published to npm. Clone it, or copy src/ into your project:
git clone https://github.com/jackships/zh-schedule-parser.git
cd zh-schedule-parser
npm test
node examples/cli.js "每周一三五早上7点跑步" "明天下午3点开会,5点半取快递"import { parse, parseMany, nextOccurrences } from "./src/index.js";
const now = new Date(2026, 9, 7, 10, 0); // Wed 2026-10-07 10:00, local time
parse("每周一三五早上7点跑步", { now });
// {
// input: "每周一三五早上7点跑步",
// title: "跑步",
// date: "2026-10-09", // first day it happens (Wednesday's 07:00 has already passed)
// time: "07:00",
// timeOptions: [],
// repeat: { freq: "weekly", weekdays: [1, 3, 5] }, // 0 = Sunday … 6 = Saturday
// endAfterDays: null, lead: null, duration: null, relativeMinutes: null,
// alarm: false, dateExplicit: false, notes: []
// }
parseMany("明天下午3点开会,提前10分钟提醒我;下午5点半取快递", { now });
// [ { title: "开会", date: "2026-10-08", time: "15:00", lead: 10, ... },
// { title: "取快递", date: "2026-10-08", time: "17:30", ... } ]
nextOccurrences(parse("每个月第二个周二晚上7点读书会", { now }), { now, count: 3 });
// [ { date: "2026-10-13", time: "19:00" },
// { date: "2026-11-10", time: "19:00" },
// { date: "2026-12-08", time: "19:00" } ]now defaults to the current time. Dates and times are wall-clock values in the runtime's local time zone. The tests pass in UTC, New York, Shanghai and Auckland.
| Chinese | English | |
|---|---|---|
| Days | 今天 明天 后天 大后天 · 周五 / 这周五 / 下周五 / 下下周二 · 10月20号 · 15号 · 下个月5号 · 三天后 · 两周后 | today, tomorrow, the day after tomorrow · Friday / next Friday · Oct 20 · 20 Oct · 12/3 (US order) · the 25th · in 3 days |
| Times | 早上7点 · 下午三点 · 晚上十点半 · 九点一刻 · 三点三刻 · 3点45分 · 9:25 · 中午 · 凌晨两点 | 7pm · 6:30am · 18:30 · at 8 · noon · midnight · tonight at 9 |
| Windows | 三点到五点之间 (reminds at the start) | 3 to 5pm · between 2 and 4pm |
| Weekly | 每天 · 天天 · 每晚 · 每周一三五 · 每周二四 · 周一到周五 · 每周末 | every day / night / morning · weekdays · every Monday and Thursday · Mondays |
| Other repeats | 工作日 · 隔天 · 每三天 · 每隔两天 · 隔周六 · 每两周的周日 · 每月1号 · 每月月底 · 每月最后一个周五 · 每年3月8号 | every other day · every 3 days · every other Saturday · every 2 weeks · the 1st of every month · the last Friday of every month · every year on June 12 |
| Several times | 早上8点、中午12点、晚上6点吃药 → three plans · 早中晚 / 早晚各一次 (suggested times, flagged) | at 8am and 8pm · twice a day · three times a day |
| Relative | 半小时后 · 20分钟后 · 一个小时后 | in 20 minutes · in half an hour |
| Extras | 提前10分钟提醒 (lead) · 读书20分钟 (duration) · 接下来7天 / 连续3周 (ends) · 从下周一开始 (start) · 闹钟 (alarm) | 15 minutes early · for 30 minutes · for the next 5 days · starting Monday · set an alarm |
| Fixed holidays | 元旦 · 情人节 · 劳动节 · 儿童节 · 教师节 · 国庆节 · 平安夜 · 圣诞节 (+ 前一天 / 后一天) | Christmas (Eve) · New Year's (Eve) · Halloween · Valentine's (+ the day before / after) |
Several plans in one message are split at ;, 。, "然后" or "then", and at a comma only when both sides say when and what. This keeps "早上8点、中午12点吃药" as one plan. A later clause without its own day shares the earlier clause's day, and an unclear hour after an earlier time on the same day takes the later reading ("3点开会,5点半取快递" gives 17:30, with the note timeFromContext).
Each plan has a notes array that tells a UI what to ask or show:
| Note | Meaning |
|---|---|
vagueTime |
No concrete time (晚上, 周末, later, 吃完饭后). Check timeOptions when it is set. |
rolledToTomorrow |
A bare time that has already passed today was moved to tomorrow. |
weekdayIsToday |
"周三…" said on a Wednesday, so it means today. |
unsupportedRepeat |
The rhythm was dropped (hourly, "until …", 每月 without a day). |
suggestedTimes |
The times were proposed from 早中晚 / twice a day. |
ambiguousInterval |
每隔N天 was read as every N days. Some people mean every N+1. |
endsAfterDays |
The repeat stops by itself (endAfterDays, counted from date). |
timeFromContext |
An unclear hour was settled by the clause before it. |
multipleTasks |
parse() only: the message held more than one plan. Use parseMany(). |
- Lunar dates (农历八月十五), lunar holidays (春节, 中秋) and the official Chinese holiday and 调休 calendar.
workdaysmeans Monday to Friday here. - Place or person triggers ("到公司时", "next time I see Sam").
- Other time zones ("纽约时间9点").
- Editing existing plans ("把跑步改到周二"). This library only reads new plans.
corpus/sentences.json holds 117 sentences (76 Chinese, 41 English) with the expected result for a fixed reference time. You can reuse it to test another parser in any language. npm test runs it together with the API tests (135 tests, built-in node:test, no dependencies).
一个零依赖的 JavaScript 小库,把「每周一三五早上7点跑步」「明天下午三点开会」「每隔两天提醒我浇花」这类短句解析成结构化的日期、时间和重复规则,也支持英文。
- 时间不清楚就不猜:"8点吃药"会返回
timeOptions: ["08:00", "20:00"],交给界面让用户选。 - 不支持的节奏不硬套:"每小时喝水"会保留任务,去掉节奏,并加上
unsupportedRepeat提示。 - 能识别每天、每周几、工作日、隔天、每 N 天、隔周、每月几号、每月最后一个周五、每年几月几号,以及"提前10分钟"、"读书20分钟"、"接下来7天"、"从下周一开始"、"闹钟"等说法。
nextOccurrences()可以算出接下来几次的具体日期。- 不支持农历、法定节假日和调休表、地点或人物触发。
测试语料在 corpus/sentences.json(117 句,其中中文 76 句),可以直接拿去测别的解析器。
The same approach powers the one-sentence reminders in Lili, an iOS planner (not on the App Store yet): https://y1y2u3u4.github.io/lili-site/
MIT © 2026 Jack Ships