whenparse 0.1.0

Parse natural-language dates and times (English + Russian) into jiff types.
Documentation
# whenparse

Parse natural-language dates and times — in **English and Russian** — into [`jiff`](https://docs.rs/jiff) types. One direct dependency, no regex, deterministic.

`whenparse` turns strings like `next friday 8pm`, `через 2 недели`, `15 March 2026`, `Q3 2025`, or `2ч30м` into a typed [`When`](#output): a date, a zoned datetime, a range, or a duration.

## Why

- **Explicit anchor.** Every parse takes a `now: &Zoned` you supply, so results are deterministic and testable — no hidden clock.
- **Bilingual.** English and Russian share one grammar; you get both without a flag.
- **Ranges are first-class.** `next week`, `March`, and `Q3` resolve to a real `[start, end)` range instead of a lossy point.
- **Small.** One direct dependency (`jiff`). Hand-rolled scanner and grammar, no regex.

## Install

```toml
[dependencies]
whenparse = "0.1"
jiff = "0.2"
```

## Usage

```rust
use jiff::Zoned;
use whenparse::{parse, parse_with, Dialect, Options, When};

let now = Zoned::now();

// Default options: automatic language, international date order.
if let Ok(When::DateTime(z)) = parse_with("next friday 8pm", &now) {
    println!("{z}");
}

// Russian, ranges, and durations go through the same call.
assert!(matches!(parse_with("через 2 недели", &now), Ok(When::Date(_))));
assert!(matches!(parse_with("Q3 2025", &now), Ok(When::Range { .. })));
assert!(matches!(parse_with("2ч30м", &now), Ok(When::Duration(_))));

// Tune the dialect (US month/day order and "next Friday" semantics).
let opts = Options { dialect: Dialect::Us, ..Options::default() };
let _ = parse("3/15/2026", &now, opts); // -> 2026-03-15
```

## Output

```rust
pub enum When {
    Date(jiff::civil::Date),
    DateTime(jiff::Zoned),
    Range { start: jiff::Zoned, end: jiff::Zoned },
    Duration(jiff::Span),
}
```

## What it understands

| Class | English | Russian |
|---|---|---|
| Named days | `today`, `tomorrow`, `yesterday` | `сегодня`, `завтра`, `послезавтра` |
| Offsets | `in 3 days`, `2 weeks ago`, `in an hour` | `через 3 дня`, `неделю назад`, `через час` |
| Weekdays | `next friday`, `last monday`, `this thursday` | `в следующую пятницу`, `в прошлый вторник` |
| Periods → `Range` | `this` / `next` / `last` `week` \| `month` \| `year` | `на следующей неделе`, `в этом месяце` |
| Absolute | `15 March 2026`, `March 15`, `3/15/2026`, `15.03.2026` | `15 марта 2026`, `15.03.2026` |
| Months / quarters → `Range` | `March`, `Q3`, `Q3 2025` | `март` |
| Clock | `8pm`, `8:30`, `noon`, `at 8` | `в 17:00`, `в 8 вечера`, `в полдень` |
| Combined | `tomorrow at 5pm`, `next friday 8pm` | `завтра в 17:00` |
| Durations → `Duration` | `3 hours 20 minutes`, `2h30m` | `3 часа 20 минут`, `2ч30м` |

ISO 8601 input (`2026-03-15`, `2026-03-15T14:30:00[America/New_York]`) passes straight through to `jiff`.

## Options

- `lang`: `Auto` (default), `En`, or `Ru`.
- `dialect`: `Intl` (default; day/month/year) or `Us` (month/day/year; `next Friday` means the coming Friday). Ambiguous numeric dates auto-correct when only one ordering is valid.
- `week_start`: the first day of the week for period ranges (default `Monday`).

## Not in scope (v0.1)

Time zones by name or abbreviation, recurrence (`every monday`), holidays, and spelled-out fractions (`half an hour`). ISO datetimes are delegated to `jiff`.

## License

Licensed under either of [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE) at your option.