Humanize
Durations, file sizes and relative times as people write them and back: 2h 30m, 1.5 GB, 2 GiB, 3 days ago, in 2 hours.
Version 1.0.0 · 5 functions · 2 crates
Functions
What use humanize.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use humanize_duration, humanize_parse_duration from humanize (Quillo 0.6).
humanize_duration(seconds: float) → stror an errorseconds as a short duration: 9000 is "2h 30m", 90061 is "1day 1h 1m 1s"; fractions of a second as "ms". Negative is an error.
humanize_parse_duration(text: str) → floator an errorA duration typed by a person, in seconds: "2h 30m", "1day 3hours", "90s", "15 minutes", "500ms" (0.5). An error says what it could not read.
humanize_bytes(bytes: int, si: bool) → strbytes as a size: "1.5 GB" in powers of 1000 when si is true (as disks and downloads count), "1.4 GiB" in powers of 1024 when false (as memory counts).
humanize_parse_bytes(text: str) → intor an errorA size typed by a person, in bytes: "1.5 GB" (1,500,000,000), "2 GiB" (2,147,483,648), "512 KB", "300" (bytes). Case does not matter.
humanize_ago(time_ms: int, now_ms: int) → strHow long ago (or how far ahead) time_ms is from now_ms, both in milliseconds since 1970 as now() gives: "just now", "5 minutes ago", "yesterday", "3 days ago", "in 2 hours", "last month", "2 years ago".
Get it
$ quillo install humanize # humanize.qwer into this folder$ quillo install humanize --example # and its example, humanize.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/humanize.qwer$ curl -O https://quillolang.org/plugins/files/humanize.qweThen, in your program, all of it or only what you call:
use humanize.qweruse humanize_duration, humanize_parse_duration from humanize // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
humantime = "2.1"bytesize = "2"Example
// A file list with sizes and ages as people read them, and settings typed as people write them.//// quillo run humanize.qwe then open http://localhost:8080/filesuse "humanize.qwer"let t = now()let files = [ { name: "backup.tar.gz", bytes: 1500000000, at: t - 3 * 86400000 }, { name: "photo.jpg", bytes: 2400000, at: t - 5 * 60000 }, { name: "notes.txt", bytes: 812, at: t - 10000 },]route GET /files = files | map((f) => { name: f.name, size: humanize_bytes(f.bytes, true), memory: humanize_bytes(f.bytes, false), changed: humanize_ago(f.at, t) })// Settings a person typed: "session lasts 2h 30m", "uploads up to 25 MB".route GET /settings ? { session: str = "2h 30m", upload: str = "25 MB" } = { session_seconds: humanize_parse_duration(session)?, upload_bytes: humanize_parse_bytes(upload)?, shown: humanize_duration(humanize_parse_duration(session)?)?,}route GET /ahead = { soon: humanize_ago(now() + 2 * 3600000, now()), later: humanize_ago(now() + 400 * 86400000, now()) }$ quillo run humanize.qwe$ curl 'localhost:8080/files'$ curl 'localhost:8080/settings?session=1day%203hours&upload=2%20GiB'Source
//! Numbers as people write them and back: durations ("2h 30m"), file sizes ("1.5 GB") and relative times ("3 days ago", "in 2 hours").//! version: 1.0.0//!//! [dependencies]//! humantime = "2.1"//! bytesize = "2"use bytesize::ByteSize;use std::time::Duration;/// `seconds` as a short duration: 9000 is "2h 30m", 90061 is "1day 1h 1m 1s"; fractions of a second as "ms". Negative is an error.pub fn humanize_duration(seconds: f64) -> Result<String, String> { if !(0.0..1e15).contains(&seconds) { return Err("seconds from 0 up".into()); } let d = Duration::from_millis((seconds * 1000.0).round() as u64); Ok(humantime::format_duration(d).to_string())}/// A duration typed by a person, in seconds: "2h 30m", "1day 3hours", "90s", "15 minutes", "500ms" (0.5). An error says what it could not read.pub fn humanize_parse_duration(text: &str) -> Result<f64, String> { humantime::parse_duration(text.trim()).map(|d| d.as_secs_f64()).map_err(|e| format!("{:?}: {}", text, e))}/// `bytes` as a size: "1.5 GB" in powers of 1000 when `si` is true (as disks and downloads count), "1.4 GiB" in powers of 1024 when false (as memory counts).pub fn humanize_bytes(bytes: i64, si: bool) -> String { let b = ByteSize::b(bytes.max(0) as u64); let s = if si { b.display().si().to_string() } else { b.display().iec().to_string() }; if bytes < 0 { format!("-{}", s) } else { s }}/// A size typed by a person, in bytes: "1.5 GB" (1,500,000,000), "2 GiB" (2,147,483,648), "512 KB", "300" (bytes). Case does not matter.pub fn humanize_parse_bytes(text: &str) -> Result<i64, String> { let t = text.trim(); let n: ByteSize = t.parse().map_err(|e: String| format!("{:?}: {}", text, e))?; i64::try_from(n.as_u64()).map_err(|_| "too large".into())}/// How long ago (or how far ahead) `time_ms` is from `now_ms`, both in milliseconds since 1970 as `now()` gives: "just now", "5 minutes ago", "yesterday", "3 days ago", "in 2 hours", "last month", "2 years ago".pub fn humanize_ago(time_ms: i64, now_ms: i64) -> String { let diff = (now_ms as i128 - time_ms as i128) / 1000; let past = diff >= 0; let s = diff.unsigned_abs() as u64; if s < 45 { return "just now".into(); } let (n, unit) = match s { s if s < 90 => (1, "minute"), s if s < 3600 => ((s + 30) / 60, "minute"), s if s < 5400 => (1, "hour"), s if s < 86400 => ((s + 1800) / 3600, "hour"), s if s < 2 * 86400 => return if past { "yesterday".into() } else { "tomorrow".into() }, s if s < 7 * 86400 => ((s + 43200) / 86400, "day"), s if s < 30 * 86400 => ((s + 3 * 86400) / (7 * 86400), "week"), s if s < 365 * 86400 => ((s + 15 * 86400) / (30 * 86400), "month"), s => ((s + 182 * 86400) / (365 * 86400), "year"), }; let n = n.max(1); if n == 1 && (unit == "week" || unit == "month" || unit == "year") { return format!("{} {}", if past { "last" } else { "next" }, unit); } let words = if n == 1 { format!("{} {}", if unit == "hour" { "an" } else { "a" }, unit) } else { format!("{} {}s", n, unit) }; if past { format!("{} ago", words) } else { format!("in {}", words) }}Related
case.qwer · 1.0.0Text in snake_case, camelCase, PascalCase, kebab-case, CONSTANT_CASE, Title Case and Sentence case: column names as API keys and labels.7 views · 8 downloadsInflection inflect.qwer · 1.0.0English words in the right form for counts and labels: plurals and singulars, 3 items, ordinals such as 1st and 22nd, and a or an by how a word sounds.6 views · 7 downloadsJapanese kana kana.qwer · 1.0.0Japanese kana: romaji typed as hiragana or katakana, kana written in Hepburn romaji, hiragana and katakana each to the other, half-width katakana from bank and till systems made full-width, and which scripts a text uses.6 views · 10 downloadsProfanity filter profanity.qwer · 1.0.0Offensive words found and masked in comments, usernames and chat, with l33t spellings and spaced-out letters caught, and a severity per kind for moderation queues.8 views · 8 downloadsSpam filter spam_filter.qwer · 1.0.0A spam filter that learns from your own messages: naive Bayes trained on what users mark, kept in a file, scoring comments and forms from 0 to 1.7 views · 7 downloadsSpellcheck spellcheck.qwer · 1.0.0Spelling checked against a word list you load, in any language: unknown words found with their places, suggestions ranked by closeness and frequency (SymSpell's method), text corrected for "did you mean", and your own words added.7 views · 8 downloadsThread 0 posts
On Humanize: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.