Browse and export the National Dong Hwa University (NDHU) academic calendar from your terminal.
ndhucal fetches the Office of Academic Affairs calendar, classifies every entry (exams, course selection, registration, holidays, …), and lets you either browse it in an interactive calendar UI or export it non-interactively to a console table, JSON, or an importable iCalendar (.ics) file.
東華大學行事曆 November 2025 filter: all search: — selected: 1
┌ Calendar ──────────────────┐┌ 2025-11-05 ──────────────────────────────┐
│Mo Tu We Th Fr Sa Su ││▌[x] 期中評量 期中評量…2025-11-07 │
│27 28 29 30 31 1 2 ││ │
│ 3 4 5 6 7 8 9 ││ │
│10 11 12 13 14 15 16 ││ │
│17 18 19 20 21 22 23 ││ │
│24 25 26 27 28 29 30 ││ │
└────────────────────────────┘└──────────────────────────────────────────┘
hjkl/arrows move · Tab pane · [ ] month · t today · / search · f filter · …
- Live fetch of the official calendar (no scraping setup; the page data is static).
- Two-level classification — 11 categories → 47 subcategories — so you can slice precisely (e.g.
assessment/midterm-exam,enrollment/add-drop). - Interactive TUI (ratatui): month grid with event-bearing days highlighted, a per-day panel, search, category filter, multi-select, and an export popup.
- Scriptable CLI: filter with flags and export straight to a file or stdout — no UI.
- Three output formats: aligned console
table,json, and RFC 5545ics(works with Google / Apple / Outlook calendars). - Correct UTF-8 everywhere, including on Windows / Big5 consoles.
Requires Rust 1.85+ (2024 edition).
Simply install ndhucal directly from GitHub with Cargo:
cargo install --git https://github.com/000hen/ndhucalThis places the executable in Cargo's binary directory (usually ~/.cargo/bin), so you can run ndhucal from any terminal.
By default ndhucal opens the interactive UI when run in a terminal. Give it an output target (-o, --print, or a pipe) and it runs non-interactively instead.
ndhucal # open the calendar UI
ndhucal --important # UI, pre-filtered to key milestones
ndhucal --category assessment,holiday # UI, pre-filtered to those categories
ndhucal --print # print the table to stdout (no UI)
ndhucal --format ics -o ndhu.ics # export an importable calendar file
ndhucal --important --format json -o dates.json
ndhucal --after today --format ics -o upcoming.ics # only events not yet over| Key | Action |
|---|---|
h j k l / arrows |
move the focused day (calendar) or the cursor (event list) |
Tab |
switch between the calendar grid and the event panel |
[ ] or PgUp PgDn |
previous / next month |
t |
jump to today |
/ |
search — type to filter events by title |
f |
cycle the category filter |
c |
clear search + filter |
space |
select / deselect (one event, or the whole focused day) |
a / n |
select all matching / clear selection |
e |
open the export popup (↑/↓ format, type the path, Enter writes) |
q / Esc |
quit |
Exporting writes your selected events, or all currently-filtered events if nothing is selected.
--url <URL> Calendar page to fetch [default: the AA calendar]
-f, --file <PATH> Parse a saved HTML file instead of fetching
--format <FORMAT> table (default) | json | ics
-o, --output <PATH> Write to a file (UTF-8); implies non-interactive export
--print Print to stdout instead of opening the UI
--tui Force the interactive UI (even with -o/--print)
--important Keep only key milestones
--after <DATE> Keep only events ending on/after a date ("today" or YYYY-MM-DD)
--category <CAT> Keep only these categories (comma-separated)
--subcategory <SUB> Keep only these subcategories (comma-separated)
--search <TEXT> Keep only events whose title contains this text
-h, --help Show full help (with every category/subcategory value)
Filters compose; in the UI they apply as the initial view, and in batch mode they define exactly what is exported.
Every event is classified into a category and a finer subcategory (the kebab labels below are what --category / --subcategory accept):
| Category | 中文 | Subcategories |
|---|---|---|
semester |
學期 | semester-start · classes-begin · semester-end |
enrollment |
選課 | course-selection · add-drop · drop · add-by-signature · course-withdraw · credit-fee |
assessment |
評量 | midterm-exam · final-exam · degree-exam · makeup-exam · exam |
registration |
註冊 | registration · tuition-payment · fee-waiver · student-loan · financial-aid |
academic |
教務 | credit-transfer · dept-transfer · dual-degree · grades · teaching-survey · midterm-warning · self-study · withdrawal |
graduation |
畢業 | graduation-ceremony · early-graduation · departure |
holiday |
節日 | public-holiday · substitute-day |
vacation |
假期 | winter-break · spring-break · summer-break |
student-affairs |
學務 | dormitory · health-check · orientation · military-service · conduct |
campus |
校園 | anniversary · sports-day · drill · election · maintenance · admission |
other |
其他 | other |
Classification is keyword-based (Subcategory::classify in src/model.rs); on the current calendar all 101 entries classify into a real category.
table— aligned, CJK-width-aware columns:Date · Category · Subcategory · Title.json— an array of{ title, start, end, category, subcategory, source }, dates asYYYY-MM-DD.ics— all-dayVEVENTs withCATEGORIESand a ChineseDESCRIPTION; import it into any calendar app.
ndhucal always writes UTF-8. On Windows PowerShell, the > redirect re-encodes the stream to UTF-16 and can mangle Chinese — so prefer -o FILE, which writes the bytes directly:
ndhucal --format ics -o ndhu.ics # ✅ clean UTF-8
ndhucal --format ics > ndhu.ics # ⚠️ PowerShell rewrites this as UTF-16The calendar page renders each entry as a <div class='EventDiv'> carrying inclusive start/end dates (evt_over2('dYYYYMMDD', …)), a category marker, the title, and a Google-Calendar link. ndhucal parses these with scraper, so it reads the reliable machine-readable attributes rather than the visual grid.
Built with ratatui (TUI), clap (CLI), reqwest (HTTP), scraper (HTML), and serde (JSON).
cargo build --release # build to target/release/ndhucal
cargo install --path . # install from the local checkout
cargo test # unit tests + a TUI render smoke-test (ratatui TestBackend)
cargo clippy
cargo run # launch the UI against the live calendar
cargo run -- --file page.html --print # work offline against a saved pageUnofficial tool. Data comes from NDHU's public calendar and may change; always confirm important dates against the official page.