Time and IDs

English dates

Dates typed in English read as times, in the user's time zone and US or UK style: next friday at 3pm, tomorrow 9am, in 2 weeks, 3 days ago.

Version 1.0.0 · 3 functions · 2 crates

#dates#time#forms#text

Functions

What use dates_english.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use dates_parse, dates_parse_iso from dates_english (Quillo 0.6).

dates_parse(text: str, now_ms: int, utc_offset_minutes: int, dialect: str) → intor an error

The time text means, in milliseconds since 1970, counted from now_ms (now()) in the user's time zone, utc_offset_minutes from UTC (-300 for New York in winter, 60 for Paris). dialect is "us" or "uk": "04/01/27" is April 1 in the US and 4 January in the UK, and "next friday" is this coming Friday in the US and the one after in the UK. A time with no hour is midnight.

dates_parse_iso(text: str, now_ms: int, utc_offset_minutes: int, dialect: str) → stror an error

As dates_parse, the time as ISO 8601 text with the user's offset: "2026-11-06T15:00:00+01:00".

dates_duration(text: str) → anyor an error

A length of time typed in English: "2 weeks", "15m ago", "3 months", as { seconds, days, months }, one of them set and the others 0 (months and days are not a fixed number of seconds). "ago" makes it negative.

Get it

$ quillo install dates_english               # dates_english.qwer into this folder$ quillo install dates_english --example     # and its example, dates_english.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/dates_english.qwer$ curl -O https://quillolang.org/plugins/files/dates_english.qwe

Then, in your program, all of it or only what you call:

main.qwe
use dates_english.qweruse dates_parse, dates_parse_iso from dates_english      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[dependencies]
interim = { version = "0.1", features = ["chrono"] }chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }

Example

dates_english.qwe
// Reminders typed as people say them: "next friday at 3pm", "tomorrow 9am", "in 2 weeks".////   quillo run dates_english.qwe      then open http://localhost:8080/remind?when=tomorrow%209amuse "dates_english.qwer"// Wednesday 28 October 2026, 10:00 in Paris (UTC+1): fixed, so the answers below do not move.let NOW = 1793178000000let PARIS = 60route GET /remind ? { when: str } = { when, at: dates_parse_iso(when, NOW, PARIS, "uk")? }route GET /examples = ["next friday at 3pm", "friday 3pm", "in 2 weeks", "3 days ago", "1 April 2027", "04/01/27"] | map((w) => { text: w, uk: dates_parse_iso(w, NOW, PARIS, "uk") ?? "?", us: dates_parse_iso(w, NOW, PARIS, "us") ?? "?" })route GET /due ? { length: str = "2 weeks" } = dates_duration(length)?
$ quillo run dates_english.qwe$ curl 'localhost:8080/remind?when=tomorrow%209am'$ curl 'localhost:8080/examples'

Source

dates_english.qwer
//! Dates and times typed in English read as times: "next friday at 3pm", "tomorrow 9am", "in 2 weeks", "3 days ago", "April 1 2027", for reminders and due dates.//! version: 1.0.0//!//! [dependencies]//! interim = { version = "0.1", features = ["chrono"] }//! chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }use chrono::{DateTime, FixedOffset, TimeZone, Utc};use interim::{parse_date_string, Dialect, Interval};use serde_json::{json, Value};fn base(now_ms: i64, utc_offset_minutes: i64) -> Result<DateTime<FixedOffset>, String> {    let tz = FixedOffset::east_opt((utc_offset_minutes.clamp(-1080, 1080) * 60) as i32).ok_or("bad offset")?;    let t = Utc.timestamp_millis_opt(now_ms).single().ok_or("now_ms is out of range")?;    Ok(t.with_timezone(&tz))}fn dialect(d: &str) -> Dialect {    if d.eq_ignore_ascii_case("us") { Dialect::Us } else { Dialect::Uk }}fn parse(text: &str, now_ms: i64, utc_offset_minutes: i64, d: &str) -> Result<DateTime<FixedOffset>, String> {    if text.len() > 200 {        return Err("up to 200 characters".into());    }    let mut t = text.trim().to_lowercase();    // "at 3pm" and "in 2 weeks" are how people write them; the parser wants "3pm" and "2 weeks".    t = t.replace(" at ", " ");    if let Some(rest) = t.strip_prefix("in ") {        t = rest.to_string();    }    parse_date_string(&t, base(now_ms, utc_offset_minutes)?, dialect(d)).map_err(|e| format!("{:?}: {}", text, e))}/// The time `text` means, in milliseconds since 1970, counted from `now_ms` (`now()`) in the user's time zone, `utc_offset_minutes` from UTC (-300 for New York in winter, 60 for Paris). `dialect` is "us" or "uk": "04/01/27" is April 1 in the US and 4 January in the UK, and "next friday" is this coming Friday in the US and the one after in the UK. A time with no hour is midnight.pub fn dates_parse(text: &str, now_ms: i64, utc_offset_minutes: i64, dialect: &str) -> Result<i64, String> {    Ok(parse(text, now_ms, utc_offset_minutes, dialect)?.timestamp_millis())}/// As `dates_parse`, the time as ISO 8601 text with the user's offset: "2026-11-06T15:00:00+01:00".pub fn dates_parse_iso(text: &str, now_ms: i64, utc_offset_minutes: i64, dialect: &str) -> Result<String, String> {    Ok(parse(text, now_ms, utc_offset_minutes, dialect)?.to_rfc3339())}/// A length of time typed in English: "2 weeks", "15m ago", "3 months", as `{ seconds, days, months }`, one of them set and the others 0 (months and days are not a fixed number of seconds). "ago" makes it negative.pub fn dates_duration(text: &str) -> Result<Value, String> {    let i = interim::parse_duration(&text.trim().to_lowercase()).map_err(|e| format!("{:?}: {}", text, e))?;    Ok(match i {        Interval::Seconds(s) => json!({ "seconds": s, "days": 0, "months": 0 }),        Interval::Days(d) => json!({ "seconds": 0, "days": d, "months": 0 }),        Interval::Months(m) => json!({ "seconds": 0, "days": 0, "months": m }),    })}
Thread
810Useful? Like it, or add to its thread below.

Thread 0 posts

On English dates: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.