kal-time is a tiny Rust library for parsing human-friendly time and
timespan expressions into chrono::DateTime<FixedOffset>. It supports
relative parsing against a caller-provided reference moment and
gracefully fills missing segments (date, time, or offset) using either
zeroes or the supplied reference.
Use it when you want a CLI or automation tool to accept terse inputs
such as "9h..10h" for “today between 9 and 10” or "30m" to fix the
minute field to 00:30 while reusing the current day/hour, without
forcing users to type full ISO timestamps.
This is more a tiny piece of code I use between many different project. It has no ambition to become anything big, and the quality is alpha level.
cargo build— compile the library and surface warnings.cargo test— execute unit tests embedded alongside the modules.cargo fmtandcargo clippy— enforce formatting and linting prior to review.
Each snippet shows how a public helper parses input and what kind of timestamp it produces.
Use parse_with_reference to interpret partial or relative
expressions against a known moment.
use chrono::TimeZone;
use chrono::Utc;
use kal_time::parse_with_reference;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let reference = Utc.with_ymd_and_hms(2025, 10, 22, 9, 10, 11).unwrap();
let parsed = parse_with_reference("30m", &reference)?;
println!("{}", parsed);
// => 2025-10-22 09:30:00 +00:00
Ok(())
}parse assumes the local clock when fields are missing; full
timestamps stay in the caller’s local offset.
use kal_time::parse;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let parsed = parse("2025-10-22 14:30")?;
println!("{}", parsed);
// => 2025-10-22 14:30:00 +<local offset>
Ok(())
}parse_utc mirrors parse but always anchors missing pieces to the
current UTC reference.
use kal_time::parse_utc;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let parsed = parse_utc("9h")?;
println!("{}", parsed);
// => <today’s date> 09:00:00 +00:00
Ok(())
}parse_timespan expands a range like start..end into start/stop
instants, defaulting to a 1-day window when no end is supplied.
use kal_time::parse_timespan;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (start, stop) = parse_timespan("2025-10-20..2025-10-22 12:00")?;
println!("start: {}", start);
println!("stop: {}", stop);
// => start: 2025-10-20 00:00:00 +<local offset>
// => stop: 2025-10-22 12:00:00 +<local offset>
Ok(())
}kt-parse is a thin wrapper around the library, useful in scripts and
shell pipelines.
Run kt-parse --help for a quick usage reminder.
$ kt-parse time 9h 1761104400 2025-10-22 09:00:00 +00:00
You can supply a fully specified reference (RFC3339 or similar) when you need deterministic results regardless of the machine clock.
$ kt-parse time 30m 2025-10-22T09:10:11+00:00 1761105011 2025-10-22 09:30:11 +00:00
Timespans print two lines: start then end. Relative fields reuse the reference on a per-field basis.
$ kt-parse timespan 9h..10h 2025-10-22T09:10:11+00:00 1761104400 2025-10-22 09:00:00 +00:00 1761108000 2025-10-22 10:00:00 +00:00
Missing fields in the end segment now borrow the fully resolved start
instant (commit 1741734), so terse ranges stay on the expected day.
$ kt-parse timespan 10:15..30 2025-10-27T09:00:00+00:00 1761560100 2025-10-27 10:15:00 +00:00 1761561000 2025-10-27 10:30:00 +00:00 $ kt-parse timespan '2025-10-27 10:30..11:30' 2025-10-01T00:00:00+00:00 1761561000 2025-10-27 10:30:00 +00:00 1761564600 2025-10-27 11:30:00 +00:00