when-words
Human-friendly date and time formatting as pure functions. Converts timestamps to
relative phrases ("3 hours ago"), duration strings ("2 hours, 30 minutes"), parses
duration input ("2h30m" to seconds), generates contextual date labels ("Yesterday"),
and formats smart date ranges ("January 15 -- February 15, 2024").
Design principles
- Pure functions only — every function takes explicit timestamps or values; no
function ever accesses the system clock.
- UTC throughout — all calendar math uses UTC. No timezone handling.
- No inter-node dependencies — each node is a standalone leaf function.
- Clarity over performance — reference code prioritizes readability.
Node graph
All 5 nodes are leaf nodes with no inter-node dependencies:
time-ago (standalone)
duration (standalone)
parse-duration (standalone)
human-date (standalone)
date-range (standalone)
Node table
| Node |
Function |
Purpose |
Tests |
time-ago |
timeAgo(timestamp, reference) |
Relative time string ("3 hours ago", "in 2 days") |
35 |
duration |
duration(seconds, options?) |
Format seconds as human duration ("2 hours, 30 minutes") |
22 |
parse-duration |
parseDuration(input) |
Parse duration string to seconds ("2h30m" to 9000) |
24 |
human-date |
humanDate(timestamp, reference) |
Contextual date label ("Yesterday", "March 5") |
18 |
date-range |
dateRange(start, end) |
Smart date range ("January 15--22, 2024") |
9 |
Total: 5 nodes, ~108 tests, 100% line and function coverage.
Subset extraction
Every node is independent — any single node or combination works alone without
pulling in other nodes. Common subsets:
- time-ago only — relative timestamps for feeds, notifications
- duration + parse-duration — bidirectional duration formatting
- human-date + date-range — calendar/scheduling UI labels
- all — full library
Input format
The skill accepts $ARGUMENTS in this format:
<nodes> [--lang <language>]
<nodes> — Space-separated node names, or all for the complete library.
Valid names: time-ago, duration, parse-duration, human-date, date-range.
--lang <language> — Target language (default: typescript).
Supported: python, rust, go, typescript.
Examples:
time-ago --lang python
duration parse-duration --lang rust
all --lang go
Translation workflow
- Read this file for overview and node selection
- Read
nodes/<name>/spec.md for behavioral spec and test vectors
- Read
nodes/<name>/to-<lang>.md for language-specific translation hints
- Consult
reference/src/<name>.ts only if the spec is ambiguous
Generated Code Documentation
Every public function, class, type, and interface in generated code must have
idiomatic doc comments in the target language's standard format:
| Language |
Format |
| TypeScript |
JSDoc (/** */) with @param, @returns |
| Python |
Google-style docstrings with Args, Returns, Raises |
| Kotlin/Java |
KDoc/JavaDoc (/** */) with @param, @return, @throws |
| C# |
XML doc comments (///) with <summary>, <param>, <returns> |
| Go |
GoDoc comments (starting with the function/type name) |
| Rust |
/// doc comments with # Arguments, # Returns, # Errors |
| C++ |
Doxygen (/** or ///) with @brief, @param, @return |
| Swift |
DocC (///) with - Parameters:, - Returns:, - Throws: |
Doc comments should describe what the function does, its parameters, return
value, and error conditions. Derive content from the node spec — do not invent
behavior not in the spec.
Each generated file must include a provenance header as the first comment,
in the file's idiomatic comment style:
Generated by {agent} using {model}
From special:when-words (https://github.com/caryden/special)
Node: {node-name}
Replace {agent}, {model}, and {node-name} with actual values. The
provenance trace makes generated code traceable back to the skill and model
that produced it.
Error handling
duration throws on negative input
parseDuration throws on: empty string, unrecognized input, negative values,
bare numbers without units, unrecognized unit names
timeAgo, humanDate, dateRange are total functions (no error cases)
dateRange auto-swaps if start > end
Reference info
- Language: TypeScript (Bun runtime)
- Coverage: 100% line and function coverage
- Test runner:
bun test
- No external dependencies
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: when-words3description: Generate native human-friendly date and time formatting — relative time, durations, date parsing, date ranges — from a verified TypeScript reference Use when this capability is needed.4---56# when-words78Human-friendly date and time formatting as pure functions. Converts timestamps to9relative phrases ("3 hours ago"), duration strings ("2 hours, 30 minutes"), parses10duration input ("2h30m" to seconds), generates contextual date labels ("Yesterday"),11and formats smart date ranges ("January 15 -- February 15, 2024").1213## Design principles1415- **Pure functions only** — every function takes explicit timestamps or values; no16 function ever accesses the system clock.17- **UTC throughout** — all calendar math uses UTC. No timezone handling.18- **No inter-node dependencies** — each node is a standalone leaf function.19- **Clarity over performance** — reference code prioritizes readability.2021## Node graph2223All 5 nodes are **leaf nodes** with no inter-node dependencies:2425```26time-ago (standalone)27duration (standalone)28parse-duration (standalone)29human-date (standalone)30date-range (standalone)31```3233## Node table3435| Node | Function | Purpose | Tests |36|------|----------|---------|-------|37| `time-ago` | `timeAgo(timestamp, reference)` | Relative time string ("3 hours ago", "in 2 days") | 35 |38| `duration` | `duration(seconds, options?)` | Format seconds as human duration ("2 hours, 30 minutes") | 22 |39| `parse-duration` | `parseDuration(input)` | Parse duration string to seconds ("2h30m" to 9000) | 24 |40| `human-date` | `humanDate(timestamp, reference)` | Contextual date label ("Yesterday", "March 5") | 18 |41| `date-range` | `dateRange(start, end)` | Smart date range ("January 15--22, 2024") | 9 |4243**Total: 5 nodes, ~108 tests, 100% line and function coverage.**4445## Subset extraction4647Every node is independent — any single node or combination works alone without48pulling in other nodes. Common subsets:4950- **time-ago only** — relative timestamps for feeds, notifications51- **duration + parse-duration** — bidirectional duration formatting52- **human-date + date-range** — calendar/scheduling UI labels53- **all** — full library5455## Input format5657The skill accepts `$ARGUMENTS` in this format:5859```60<nodes> [--lang <language>]61```6263- `<nodes>` — Space-separated node names, or `all` for the complete library.64 Valid names: `time-ago`, `duration`, `parse-duration`, `human-date`, `date-range`.65- `--lang <language>` — Target language (default: `typescript`).66 Supported: `python`, `rust`, `go`, `typescript`.6768Examples:69- `time-ago --lang python`70- `duration parse-duration --lang rust`71- `all --lang go`7273## Translation workflow74751. Read this file for overview and node selection762. Read `nodes/<name>/spec.md` for behavioral spec and test vectors773. Read `nodes/<name>/to-<lang>.md` for language-specific translation hints784. Consult `reference/src/<name>.ts` only if the spec is ambiguous7980### Generated Code Documentation8182Every public function, class, type, and interface in generated code must have83idiomatic doc comments in the target language's standard format:8485| Language | Format |86|----------|--------|87| TypeScript | JSDoc (`/** */`) with `@param`, `@returns` |88| Python | Google-style docstrings with Args, Returns, Raises |89| Kotlin/Java | KDoc/JavaDoc (`/** */`) with `@param`, `@return`, `@throws` |90| C# | XML doc comments (`///`) with `<summary>`, `<param>`, `<returns>` |91| Go | GoDoc comments (starting with the function/type name) |92| Rust | `///` doc comments with `# Arguments`, `# Returns`, `# Errors` |93| C++ | Doxygen (`/**` or `///`) with `@brief`, `@param`, `@return` |94| Swift | DocC (`///`) with `- Parameters:`, `- Returns:`, `- Throws:` |9596Doc comments should describe **what** the function does, its parameters, return97value, and error conditions. Derive content from the node spec — do not invent98behavior not in the spec.99100Each generated file must include a **provenance header** as the first comment,101in the file's idiomatic comment style:102103```104Generated by {agent} using {model}105From special:when-words (https://github.com/caryden/special)106Node: {node-name}107```108109Replace `{agent}`, `{model}`, and `{node-name}` with actual values. The110provenance trace makes generated code traceable back to the skill and model111that produced it.112113## Error handling114115- `duration` throws on negative input116- `parseDuration` throws on: empty string, unrecognized input, negative values,117 bare numbers without units, unrecognized unit names118- `timeAgo`, `humanDate`, `dateRange` are total functions (no error cases)119- `dateRange` auto-swaps if start > end120121## Reference info122123- **Language:** TypeScript (Bun runtime)124- **Coverage:** 100% line and function coverage125- **Test runner:** `bun test`126- **No external dependencies**127128---129> Converted and distributed by [TomeVault](https://tomevault.io/claim/caryden) — claim your Tome and manage your conversions.130<!-- tomevault:4.0:skill_md:2026-04-13 -->