๐ŸŽ“ A1Learner
๐Ÿ”

Rust Date Recipes

A date recipe is a ready-made pattern for a common date job. Each recipe below uses fixed dates, so every result is stable.

Sort and pick the ends

Dates sort like numbers. The first is the earliest:

let mut days = vec![NaiveDate::from_ymd_opt(2026, 10, 22).unwrap(), NaiveDate::from_ymd_opt(2026, 9, 30).unwrap(), NaiveDate::from_ymd_opt(2026, 11, 1).unwrap()];
days.sort();
println!("sorted = {:?}", days);
println!("earliest = {} latest = {}", days.first().unwrap(), days.last().unwrap());

Days between, both ways

Subtract dates to count the gap. The order sets the sign:

let start = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap();
let end = NaiveDate::from_ymd_opt(2026, 11, 1).unwrap();
println!("forward = {}", (end - start).num_days());
println!("backward = {}", (start - end).num_days());
println!("inclusive = {}", (end - start).num_days() + 1);

Work out an age

Start with the year gap. Take one year back if the birthday is still ahead:

fn age_on(born: NaiveDate, day: NaiveDate) -> i32 {
    let mut age = day.year() - born.year();
    if (day.month(), day.day()) < (born.month(), born.day()) {
        age -= 1;
    }
    age
}
let born = NaiveDate::from_ymd_opt(2000, 10, 23).unwrap();
println!("day before = {}", age_on(born, NaiveDate::from_ymd_opt(2026, 10, 22).unwrap()));
println!("birthday = {}", age_on(born, NaiveDate::from_ymd_opt(2026, 10, 23).unwrap()));

A leap-day birthday

This recipe treats 1 March as the reached day in a plain year. That choice is a product rule, not a chrono rule:

let leap_born = NaiveDate::from_ymd_opt(2000, 2, 29).unwrap();
println!("28 Feb = {}", age_on(leap_born, NaiveDate::from_ymd_opt(2026, 2, 28).unwrap()));
println!("1 Mar = {}", age_on(leap_born, NaiveDate::from_ymd_opt(2026, 3, 1).unwrap()));

The edges of one day

A day can end at its last nanosecond. A range is safer when it ends at the next midnight:

let day = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap();
println!("start = {}", day.and_hms_opt(0, 0, 0).unwrap());
println!("last nano = {}", day.and_hms_nano_opt(23, 59, 59, 999_999_999).unwrap());
println!("next start = {}", day.checked_add_days(Days::new(1)).unwrap().and_hms_opt(0, 0, 0).unwrap());

Find the next Monday

Step one day at a time. Stop when the weekday matches:

let mut next = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap();
while next.weekday() != Weekday::Mon {
    next = next.checked_add_days(Days::new(1)).unwrap();
}
println!("next Monday = {} {}", next, next.weekday());

Example

// Cargo.toml: chrono = "0.4" (verified with chrono 0.4.45)
use chrono::{Datelike, Days, NaiveDate};

fn age_on(born: NaiveDate, day: NaiveDate) -> i32 {
    let mut age = day.year() - born.year();
    if (day.month(), day.day()) < (born.month(), born.day()) {
        age -= 1;
    }
    age
}

fn main() {
    let born = NaiveDate::from_ymd_opt(2000, 10, 23).unwrap();
    let mut events = vec![
        NaiveDate::from_ymd_opt(2026, 11, 1).unwrap(),
        NaiveDate::from_ymd_opt(2026, 10, 22).unwrap(),
        NaiveDate::from_ymd_opt(2026, 10, 30).unwrap(),
    ];
    events.sort();
    let first = events[0];
    let last = events[events.len() - 1];
    println!("first event = {} {}", first, first.weekday());
    println!("last event = {} {}", last, last.weekday());
    println!("span = {} days ({} including both ends)", (last - first).num_days(), (last - first).num_days() + 1);
    println!("age on first event = {}", age_on(born, first));
    println!("first day starts = {}", first.and_hms_opt(0, 0, 0).unwrap());
    println!("range ends at next midnight = {}", last.checked_add_days(Days::new(1)).unwrap().and_hms_opt(0, 0, 0).unwrap());
}

Key points

  • Sort dates directly.
  • Their order is calendar order.
  • A signed day count shows direction.
  • Swap the dates and the sign flips.
  • Add one for an inclusive count.
  • The count then includes both end dates.
  • Age is not just a year gap.
  • The birthday must have happened in the later year.
  • Use a half-open day range.
  • It starts at one midnight and ends at the next midnight.
  • A date recipe is a reusable pattern that solves one everyday date job.

In this example, three event dates are sorted. The first is 22 October. The last is 1 November. The span is 10 days, or 11 dates with both ends counted. The guest is still 25 on the first date. The birthday falls one day later.

Note

Date-only recipes dodge clock changes.

Work out the local date first when โ€œtodayโ€ means a place.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Rust Playground

Exercise

A stay runs from 22 October to 1 November. How many dates does it cover, counting both ends?