🎓 A1Learner
🔍

Rust Date Format Codes

`%Y`, `%m`, and `%d` are format codes. One format string can shape a whole log line or file name, and the same string can also read text back into a value.

The date codes

One fixed date shows the core codes. `%e` pads with a space:

let date = NaiveDate::from_ymd_opt(2026, 10, 5).unwrap();
println!("year = {} month = {} day = {}", date.format("%Y"), date.format("%m"), date.format("%d"));
println!("short day = [{}]", date.format("%e"));
println!("names = {} {} {} {}", date.format("%a"), date.format("%A"), date.format("%b"), date.format("%B"));

The clock codes

Hours, minutes, and seconds have their own codes. `%I` uses the 12-hour clock:

let dt = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap().and_hms_milli_opt(14, 5, 9, 123).unwrap();
println!("24-hour clock = {}", dt.format("%H:%M:%S"));
println!("12-hour clock = {}", dt.format("%I:%M %p"));
println!("whole clock = {}", dt.format("%T"));

Fractions of a second

`%S%.3f` keeps three fraction digits. The dot comes from the fraction code:

let dt = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap().and_hms_milli_opt(14, 5, 9, 123).unwrap();
println!("with millis = {}", dt.format("%S%.3f"));
println!("digits only = {}", dt.format("%f"));
println!("dot form = {}", dt.format("%.f"));

Offset codes need a zone

A naive value has no offset to print. Pin the value to UTC first:

let utc = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap().and_hms_milli_opt(14, 5, 9, 123).unwrap().and_utc();
println!("offset = {}", utc.format("%z"));
println!("colon offset = {}", utc.format("%:z"));
println!("zone = {}", utc.format("%Z"));
println!("epoch code = {}", utc.format("%s"));

Parse with the same string

The format must match the text shape. A wrong shape fails:

let parsed = NaiveDateTime::parse_from_str("22 Oct 2026 14:05", "%d %b %Y %H:%M").unwrap();
println!("parsed = {}", parsed);
println!("wrong shape works = {}", NaiveDateTime::parse_from_str("2026/10/22", "%Y-%m-%d").is_ok());

Words and percent signs

Text in the format string stays in the output. `%j` counts from the start of the year:

let date = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap();
println!("{}", date.format("Day %e of %B, day %j of the year, 100%%"));

Example

// Cargo.toml: chrono = "0.4" (verified with chrono 0.4.45)
use chrono::DateTime;

fn main() {
    let log_time = DateTime::parse_from_str("2026-10-22 14:05:09 +0000", "%Y-%m-%d %H:%M:%S %z").unwrap();
    println!("machine = {}", log_time.format("%Y-%m-%dT%H:%M:%S%:z"));
    println!("human = {}", log_time.format("%A %d %B %Y at %I:%M %p"));
    println!("file name = {}", log_time.format("%Y%m%d-%H%M%S"));
    println!("rfc3339 = {}", log_time.to_rfc3339());
}

Key points

  • Case matters in format codes.
  • `%m` is the month.
  • `%M` is the minute.
  • Use `%e` for a space-padded day.
  • Use `%d` for a zero-padded day.
  • Use `%j` for the day of the year.
  • Use `%s` for epoch seconds on a zoned value.
  • Fraction codes differ.
  • `%.3f` writes a dot and three digits.
  • `%f` writes digits with no dot.
  • Parsing returns a `Result`.
  • Bad text gives an error, not a guessed date.
  • Format codes are short % spellings that shape how a date or time is written and read back.

In this example, one log time is read with a format string. The `%z` code reads its UTC offset. Three new shapes then print the same moment. One shape suits machines. One shape suits people. One shape suits a file name.

Note

Plain words in a format string pass through as text.

Write `%%` when you need one real percent sign.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Which code prints the minute?