A Rust crate for dates with varying precision: year-only, year+month, or full date.
Real-world date data often isn't complete. Vendors, forms, and manual records frequently give you a month and year but no day, or just a year. The typical response is to invent the missing parts — pick the first of the month, or the last — but that introduces data you never actually had. Once fabricated precision is stored alongside real precision, the two are indistinguishable.
I built this because I kept running into it in data pipelines and wanted a type that carries exactly the precision the source provides, nothing more.
FuzzyDate is an enum with three variants — year, month, and day precision.
FuzzyDateRange pairs two FuzzyDate values, each end independent.
Because dates of different precision can't be meaningfully compared for equality, the API works with bounds and overlap instead:
# use fuzzy_date::FuzzyDate;
# fn main() -> Result<(), Box<dyn std::error::Error>> {
let month: FuzzyDate = "2026-02".parse()?;
assert_eq!(month.lower_bound(), (2026, 2, 1));
assert_eq!(month.upper_bound_inclusive(), (2026, 2, 28));
let day: FuzzyDate = "2026-02-13".parse()?;
assert!(month.contains(&day)); // Feb 13 falls within February
# Ok(())
# }Ordering sorts by lower bound; when two dates share the same lower bound,
less-precise sorts first (2026 < 2026-02 < 2026-02-01).
[dependencies]
fuzzy_date = "0.1"# fn main() -> Result<(), Box<dyn std::error::Error>> {
use fuzzy_date::FuzzyDate;
let a: FuzzyDate = "2026-02-13".parse()?; // day precision
let b: FuzzyDate = "2026-02".parse()?; // month precision
let c: FuzzyDate = "2026".parse()?; // year precision
// Components reflect actual precision — nothing is invented
assert_eq!(b.year().get(), 2026);
assert_eq!(b.month().map(|m| m.get()), Some(2));
assert_eq!(b.day(), None);
# Ok(())
# }Serialize/deserialize as ISO strings preserving precision. For database
storage, to_columns() / from_columns() map to three nullable columns
(year required, month and day optional). Ranges use six.
| Format | Example | Precision |
|---|---|---|
YYYY |
2026 |
Year |
YYYY-MM |
2026-02 |
Month |
YYYY-MM-DD |
2026-02-13 |
Day |
MM/YYYY |
02/2026 |
Month |
MM/DD/YYYY |
02/13/2026 |
Day |
Ranges parse as {start}/{end} — e.g. 2020-03/2026-02-13.
For fuller examples including ranges, database integration, construction, and error handling, see docs/examples.md.