Loading pageā¦
Rust walkthroughs
Loading pageā¦
chrono::Duration differ from std::time::Duration for date arithmetic?chrono::Duration provides signed duration arithmetic with negative time spans and rich calendar operations, while std::time::Duration represents only non-negative time spans suitable for timers and basic time math. The key distinction is that chrono::Duration can represent negative durations (enabling DateTime - DateTime = Duration calculations), carries milliseconds, seconds, and days with proper sign handling, and integrates with chrono's DateTime and Date types for calendar-aware operations. std::time::Duration is a primitive duration type focused on monotonic clock operations, lacks negative values, and doesn't directly support calendar arithmetic like month or year calculationsāany subtraction that would yield a negative duration panics rather than returning a negative value.
use std::time::Duration as StdDuration;
use chrono::Duration as ChronoDuration;
fn main() {
// std::time::Duration: non-negative only
let std_duration = StdDuration::new(5, 0); // 5 seconds
// chrono::Duration: signed, can be negative
let chrono_duration = ChronoDuration::seconds(5); // 5 seconds
let negative_duration = ChronoDuration::seconds(-5); // -5 seconds
println!("std duration: {:?}", std_duration);
println!("chrono duration: {:?}", chrono_duration);
println!("negative: {:?}", negative_duration);
}std::time::Duration cannot represent negative values; chrono::Duration can.
use std::time::Duration as StdDuration;
use chrono::Duration as ChronoDuration;
fn main() {
// chrono::Duration handles negative values
let minus_five = ChronoDuration::seconds(-5);
println!("Negative: {:?}", minus_five);
// Can negate a chrono::Duration
let positive = -minus_five;
println!("Negated: {:?}", positive);
// std::time::Duration has no negative representation
// Duration::new(-5, 0) would not compile
// There's no way to create a negative StdDuration
// Checking sign
if minus_five.num_seconds() < 0 {
println!("Duration is negative");
}
}chrono::Duration supports full sign arithmetic; std::time::Duration is unsigned-only.
use std::time::Duration as StdDuration;
use chrono::Duration as ChronoDuration;
fn main() {
// std::time::Duration arithmetic
let d1 = StdDuration::from_secs(10);
let d2 = StdDuration::from_secs(3);
let sum = d1 + d2; // Addition
let diff = d1 - d2; // Subtraction (panics if d2 > d1!)
let scaled = d1 * 2; // Multiplication by scalar
// Division by scalar
let div = d1 / 2;
println!("std sum: {:?}", sum);
println!("std diff: {:?}", diff);
// chrono::Duration arithmetic
let c1 = ChronoDuration::seconds(10);
let c2 = ChronoDuration::seconds(3);
let csum = c1 + c2;
let cdiff = c1 - c2; // Returns negative if c2 > c1
let cneg = c2 - c1; // Negative result: -7 seconds
println!("chrono sum: {:?}", csum);
println!("chrono diff: {:?}", cdiff);
println!("chrono negative diff: {:?}", cneg);
}std::time::Duration subtraction panics on underflow; chrono::Duration returns negative values.
use std::time::Duration as StdDuration;
use chrono::Duration as ChronoDuration;
fn main() {
// std::time::Duration: use checked_sub to avoid panic
let d1 = StdDuration::from_secs(3);
let d2 = StdDuration::from_secs(5);
// d1 - d2 would panic!
// Use checked_sub instead:
match d1.checked_sub(d2) {
Some(result) => println!("Result: {:?}", result),
None => println!("Would underflow"),
}
// chrono::Duration: subtraction just returns negative
let c1 = ChronoDuration::seconds(3);
let c2 = ChronoDuration::seconds(5);
let cdiff = c1 - c2; // Returns -2 seconds
println!("chrono result: {:?}", cdiff);
println!("Is negative: {}", cdiff.num_seconds() < 0);
}std::time::Duration::checked_sub returns Option; chrono::Duration subtraction always succeeds.
use std::time::Duration as StdDuration;
use chrono::Duration as ChronoDuration;
fn main() {
// chrono::Duration to std::time::Duration
let chrono_dur = ChronoDuration::seconds(5);
// to_std() returns Option<StdDuration>
// None if chrono::Duration is negative
match chrono_dur.to_std() {
Some(std_dur) => println!("Converted to std: {:?}", std_dur),
None => println!("Cannot convert negative duration"),
}
// Negative chrono::Duration cannot convert
let negative = ChronoDuration::seconds(-5);
match negative.to_std() {
Some(std_dur) => println!("Converted: {:?}", std_dur),
None => println!("Cannot convert negative to std"),
}
// std::time::Duration to chrono::Duration
let std_dur = StdDuration::from_secs(5);
let chrono_dur = ChronoDuration::from_std(std_dur);
println!("Converted to chrono: {:?}", chrono_dur);
}Use to_std() and from_std() for conversions; negative durations convert to None.
use chrono::{DateTime, Duration, Utc, TimeZone, NaiveDate, NaiveDateTime};
fn main() {
let dt1: DateTime<Utc> = Utc.with_ymd_and_hms(2024, 3, 15, 10, 30, 0).unwrap();
// chrono::Duration + DateTime
let later = dt1 + Duration::hours(5);
println!("5 hours later: {}", later);
// DateTime - Duration
let earlier = dt1 - Duration::hours(5);
println!("5 hours earlier: {}", earlier);
// DateTime - DateTime = Duration
let dt2: DateTime<Utc> = Utc.with_ymd_and_hms(2024, 3, 15, 15, 30, 0).unwrap();
let diff: Duration = dt2 - dt1;
println!("Difference: {:?}", diff);
println!("Seconds: {}", diff.num_seconds());
// Negative duration from reversed subtraction
let neg_diff: Duration = dt1 - dt2;
println!("Negative diff: {:?}", neg_diff);
println!("Negative seconds: {}", neg_diff.num_seconds());
}chrono::Duration enables DateTime - DateTime = Duration arithmetic; std::time::Duration cannot.
use chrono::{Duration, NaiveDate, Days};
fn main() {
let date = NaiveDate::from_ymd_opt(2024, 3, 15).unwrap();
// Add days
let later = date + Duration::days(7);
println!("7 days later: {}", later);
// Subtract days
let earlier = date - Duration::days(7);
println!("7 days earlier: {}", earlier);
// Days type for day-only arithmetic (unsigned)
let days_later = date + Days::new(7);
println!("Using Days: {}", days_later);
// Negative duration goes backwards
let negative_offset = date + Duration::days(-7);
println!("Negative offset: {}", negative_offset);
}chrono::Duration works with Date types; use Days for unsigned day arithmetic.
use chrono::Duration;
fn main() {
let duration = Duration::new(3661, 500_000_000);
// 3661.5 seconds = 1 hour, 1 minute, 1.5 seconds
// Component extraction
let num_seconds = duration.num_seconds(); // Total seconds
let num_milliseconds = duration.num_milliseconds();
let num_microseconds = duration.num_microseconds();
let num_nanoseconds = duration.num_nanoseconds();
println!("Total seconds: {}", num_seconds);
println!("Total milliseconds: {}", num_milliseconds);
println!("Total microseconds: {}", num_microseconds);
println!("Total nanoseconds: {}", num_nanoseconds);
// Split into components
let seconds_part = duration.num_seconds() % 60;
let minutes_part = (duration.num_seconds() / 60) % 60;
let hours_part = duration.num_seconds() / 3600;
println!("Components: {}h {}m {}s", hours_part, minutes_part, seconds_part);
}chrono::Duration provides methods for extracting total time in various units.
use chrono::Duration;
fn main() {
// Various ways to create chrono::Duration
let ns = Duration::nanoseconds(100);
let us = Duration::microseconds(100);
let ms = Duration::milliseconds(100);
let sec = Duration::seconds(100);
let min = Duration::minutes(100);
let hr = Duration::hours(100);
let day = Duration::days(100);
let week = Duration::weeks(100);
// std::time::Duration constructors
use std::time::Duration;
let std_ns = Duration::from_nanos(100);
let std_us = Duration::from_micros(100);
let std_ms = Duration::from_millis(100);
let std_sec = Duration::from_secs(100);
// Note: std::time::Duration lacks minutes(), hours(), days()
// Must use: Duration::from_secs(60 * minutes)
}chrono::Duration has constructors for days, weeks, hours, minutes; std::time::Duration does not.
use chrono::Duration;
fn main() {
let negative = Duration::seconds(-100);
// Get absolute value
let absolute = negative.abs();
println!("Original: {:?}", negative);
println!("Absolute: {:?}", absolute);
// Duration::abs() returns the magnitude
let also_abs = Duration::seconds(-500).abs();
println!("Also absolute: {:?}", also_abs);
// std::time::Duration has no abs() - it's always positive
use std::time::Duration as StdDuration;
let positive = StdDuration::from_secs(100);
// No abs() method needed - always positive
}chrono::Duration::abs() returns the magnitude of a possibly-negative duration.
use chrono::Duration;
fn main() {
let positive = Duration::seconds(100);
let negative = Duration::seconds(-100);
let zero = Duration::zero();
// Check if zero
println!("Zero is zero: {}", zero.is_zero());
println!("Positive is zero: {}", positive.is_zero());
// Check sign via num_seconds
fn describe_duration(d: Duration) {
let secs = d.num_seconds();
if secs > 0 {
println!("Positive duration: {} seconds", secs);
} else if secs < 0 {
println!("Negative duration: {} seconds", secs);
} else {
println!("Zero duration");
}
}
describe_duration(positive);
describe_duration(negative);
describe_duration(zero);
}Use is_zero() and num_seconds() sign to check duration polarity.
use chrono::Duration;
fn main() {
// chrono::Duration is integer-based internally
// For fractional seconds, use milliseconds/microseconds/nanoseconds
let one_and_half = Duration::milliseconds(1500); // 1.5 seconds
println!("1.5 seconds: {:?}", one_and_half);
// num_seconds() returns integer (truncates)
println!("num_seconds: {}", one_and_half.num_seconds()); // 1
// num_milliseconds() gives precision
println!("num_milliseconds: {}", one_and_half.num_milliseconds()); // 1500
// For true floating-point, use chrono::Duration::num_seconds() as f64
let secs_f64 = one_and_half.num_milliseconds() as f64 / 1000.0;
println!("Seconds as f64: {}", secs_f64);
// std::time::Duration has subsecond_nanos for precision
use std::time::Duration as StdDuration;
let std_dur = StdDuration::new(1, 500_000_000);
println!("std secs: {}, nanos: {}", std_dur.as_secs(), std_dur.subsec_nanos());
}Both types use integer nanoseconds internally; use milliseconds or nanoseconds for precision.
use chrono::Duration;
use std::cmp::Ordering;
fn main() {
let d1 = Duration::seconds(10);
let d2 = Duration::seconds(5);
let d3 = Duration::seconds(10);
// Equality
println!("d1 == d3: {}", d1 == d3);
println!("d1 == d2: {}", d1 == d2);
// Ordering
println!("d1 cmp d2: {:?}", d1.cmp(&d2));
println!("d2 cmp d1: {:?}", d2.cmp(&d1));
// Works with negative values
let neg = Duration::seconds(-5);
println!("neg < d2: {}", neg < d2);
println!("neg < Duration::zero(): {}", neg < Duration::zero());
// std::time::Duration also implements Ord and Eq
use std::time::Duration as StdDuration;
let s1 = StdDuration::from_secs(10);
let s2 = StdDuration::from_secs(5);
println!("std cmp: {:?}", s1.cmp(&s2));
}Both types implement Ord and Eq; chrono::Duration comparisons handle negatives correctly.
use chrono::{DateTime, Duration, Utc, FixedOffset, TimeZone};
fn main() {
// Duration arithmetic is timezone-aware
let utc_time: DateTime<Utc> = Utc.with_ymd_and_hms(2024, 3, 15, 12, 0, 0).unwrap();
// Add duration across timezone boundaries
let later = utc_time + Duration::hours(5);
println!("UTC + 5h: {}", later);
// Convert to different timezone
let offset = FixedOffset::east_opt(5 * 3600).unwrap(); // UTC+5
let local_time = utc_time.with_timezone(&offset);
println!("Local time: {}", local_time);
// Duration arithmetic in local timezone
let local_later = local_time + Duration::hours(2);
println!("Local + 2h: {}", local_later);
// The underlying instant is the same
// Only the display timezone differs
}Duration arithmetic preserves timezone information correctly.
use chrono::{Duration, NaiveDate, NaiveTime, NaiveDateTime};
fn main() {
// Duration is NOT a date/time - it's a time span
let duration = Duration::days(7);
println!("Duration: {:?}", duration);
// Can't create a date from Duration alone
// Duration::days(7) is "7 days", not "March 7"
// Use Date + Duration
let date = NaiveDate::from_ymd_opt(2024, 3, 15).unwrap();
let week_later = date + Duration::weeks(1);
println!("Week later: {}", week_later);
// DateTime combines Date + Time
let dt = NaiveDateTime::new(
NaiveDate::from_ymd_opt(2024, 3, 15).unwrap(),
NaiveTime::from_hms_opt(10, 30, 0).unwrap()
);
let later = dt + Duration::hours(3);
println!("3 hours later: {}", later);
}Duration represents a time span, not a specific date or time.
use chrono::{Duration, NaiveDate, Months};
fn main() {
// Duration::days() is day-based, not month-aware
// 30 days is not the same as 1 month
let date = NaiveDate::from_ymd_opt(2024, 1, 31).unwrap();
// Duration::days(30) - just adds 30 days
let thirty_days = date + Duration::days(30);
println!("30 days: {}", thirty_days); // March 1
// Months type handles month boundaries
let one_month = date + Months::single();
println!("1 month: {}", one_month); // February 29 (leap year)
// Duration::days(31) is also not 1 month
let thirty_one_days = date + Duration::days(31);
println!("31 days: {}", thirty_one_days); // March 2
// No Duration::months() or Duration::years()
// Use Months type or year-based arithmetic
let next_year = (date + Months::new(12))
.with_year(date.year() + 1)
.unwrap();
}Duration doesn't understand months; use Months type for calendar arithmetic.
use chrono::Duration;
use std::time::Duration as StdDuration;
use std::time::Instant;
fn main() {
// Both types are stack-allocated and efficient
// chrono::Duration stores: i64 seconds + i32 nanoseconds
// std::time::Duration stores: u64 seconds + u32 nanoseconds
// Size comparison
println!("Size of chrono::Duration: {} bytes", std::mem::size_of::<Duration>());
println!("Size of std::time::Duration: {} bytes", std::mem::size_of::<StdDuration>());
// chrono::Duration: 16 bytes (i64 + i32 + padding)
// std::time::Duration: 16 bytes (u64 + u32 + padding)
// Performance difference is negligible for most operations
// Both use integer arithmetic internally
}Both types have similar size and performance; choose based on semantics, not speed.
use chrono::Duration;
fn main() {
let duration = Duration::new(3661, 500_000_000);
// Debug format
println!("Debug: {:?}", duration);
// Custom formatting requires manual implementation
fn format_duration(d: Duration) -> String {
let total_secs = d.num_seconds();
let hours = total_secs / 3600;
let mins = (total_secs % 3600) / 60;
let secs = total_secs % 60;
let nanos = (d - Duration::seconds(total_secs)).num_nanoseconds().unwrap_or(0);
if nanos != 0 {
format!("{}h {}m {}.{:09}s", hours, mins, secs, nanos.abs())
} else {
format!("{}h {}m {}s", hours, mins, secs)
}
}
println!("Formatted: {}", format_duration(duration));
// std::time::Duration has Debug format only
use std::time::Duration as StdDuration;
let std_dur = StdDuration::new(3661, 500_000_000);
println!("std Debug: {:?}", std_dur);
}Neither type has rich formatting; implement custom display for human-readable output.
use chrono::{DateTime, Duration, Utc, TimeZone};
fn timeout_example() {
// std::time::Duration is appropriate for timeouts
use std::time::Duration as StdDuration;
// std::thread::sleep, tokio::time::sleep use std::time::Duration
// std::thread::sleep(StdDuration::from_secs(5));
// Convert chrono::Duration for use with sleep
let chrono_dur = Duration::seconds(5);
let std_dur = chrono_dur.to_std().unwrap();
// std::thread::sleep(std_dur);
println!("Can use with sleep APIs: {:?}", std_dur);
}
fn main() {
// Use chrono::Duration for date/time math
use chrono::{NaiveDate, Duration};
let deadline = Utc::now() + Duration::hours(24);
println!("Deadline: {}", deadline);
let time_remaining = deadline - Utc::now();
println!("Time remaining: {:?}", time_remaining);
// Can be negative if deadline passed
if time_remaining.num_seconds() < 0 {
println!("Deadline has passed!");
} else {
println!("Seconds remaining: {}", time_remaining.num_seconds());
}
// Convert to std::time::Duration for timeout APIs
if time_remaining.num_seconds() > 0 {
let timeout = time_remaining.to_std().unwrap();
println!("Timeout duration: {:?}", timeout);
}
}Use std::time::Duration for timeout/sleep APIs; chrono::Duration for date arithmetic.
use chrono::{DateTime, Duration, Utc, TimeZone, Local};
fn calculate_elapsed(start: DateTime<Utc>) -> Duration {
Utc::now() - start
}
fn format_elapsed(elapsed: Duration) -> String {
let total_secs = elapsed.num_seconds();
if total_secs < 0 {
let abs_elapsed = elapsed.abs();
return format!("-{}", format_elapsed(abs_elapsed));
}
let hours = total_secs / 3600;
let mins = (total_secs % 3600) / 60;
let secs = total_secs % 60;
if hours > 0 {
format!("{}h {}m {}s", hours, mins, secs)
} else if mins > 0 {
format!("{}m {}s", mins, secs)
} else {
format!("{}s", secs)
}
}
fn main() {
let start = Utc.with_ymd_and_hms(2024, 3, 15, 10, 0, 0).single().unwrap();
let elapsed = calculate_elapsed(start);
println!("Elapsed: {}", format_elapsed(elapsed));
// Negative elapsed time (future event)
let future = Utc::now() + Duration::hours(1);
let future_elapsed = calculate_elapsed(future);
println!("Future elapsed: {}", format_elapsed(future_elapsed));
}chrono::Duration enables natural elapsed time calculations with sign handling.
use chrono::{NaiveDateTime, Duration, NaiveDate, Days};
struct Schedule {
base_date: NaiveDate,
intervals: Vec<Duration>,
}
impl Schedule {
fn new(base_date: NaiveDate) -> Self {
Schedule {
base_date,
intervals: Vec::new(),
}
}
fn add_interval(&mut self, offset: Duration) {
self.intervals.push(offset);
}
fn get_events(&self) -> Vec<(NaiveDate, Duration)> {
self.intervals
.iter()
.map(|offset| (self.base_date + *offset, *offset))
.collect()
}
fn get_event_at(&self, offset: Duration) -> NaiveDate {
self.base_date + offset
}
}
fn main() {
let mut schedule = Schedule::new(NaiveDate::from_ymd_opt(2024, 3, 15).unwrap());
schedule.add_interval(Duration::days(0)); // Today
schedule.add_interval(Duration::days(7)); // Week 1
schedule.add_interval(Duration::days(14)); // Week 2
schedule.add_interval(Duration::days(-7)); // Last week
println!("Events:");
for (date, offset) in schedule.get_events() {
println!(" {} (offset: {:?})", date, offset);
}
// Negative offset works correctly
let past_event = schedule.get_event_at(Duration::days(-30));
println!("Event 30 days before base: {}", past_event);
}chrono::Duration's signed values enable schedule calculations with past and future offsets.
Type comparison:
| Feature | std::time::Duration | chrono::Duration |
|---------|----------------------|-------------------|
| Sign | Unsigned only | Signed (positive/negative) |
| Minimum | Zero | Any negative value |
| Subtraction underflow | Panic | Returns negative |
| DateTime arithmetic | No direct support | Full support |
| Days/weeks constructors | No | Yes |
| to_std() | N/A | Returns Option<StdDuration> |
| from_std() | N/A | Always succeeds |
| Use with async/sleep | Direct support | Convert to std |
| Calendar awareness | No | No (use Months) |
When to use each:
| Use case | Recommended type |
|----------|-----------------|
| Timer/sleep operations | std::time::Duration |
| DateTime subtraction | chrono::Duration |
| Negative time spans | chrono::Duration |
| Timeout calculations | std::time::Duration |
| Date arithmetic | chrono::Duration |
| Schedule offsets | chrono::Duration |
| Elapsed time (past/future) | chrono::Duration |
| Platform time APIs | std::time::Duration |
Key insight: chrono::Duration and std::time::Duration serve complementary purposesāchrono::Duration handles signed time spans for calendar arithmetic where DateTime - DateTime = Duration naturally produces negative values when comparing past events, while std::time::Duration provides an unsigned primitive for monotonic clocks and timeout operations where negative values don't make sense. The conversion methods to_std() and from_std() bridge the gap, with to_std() returning None for negative durations since std::time::Duration cannot represent them. chrono::Duration's constructors for days and weeks fill gaps in std::time::Duration's API, while neither type understands months or yearsāuse chrono::Months for calendar-aware date arithmetic. For async code and timeouts, convert chrono::Duration to std::time::Duration before passing to sleep or timeout functions. The rule of thumb: use chrono::Duration when working with DateTime and Date types, and std::time::Duration when working with Instant, SystemTime, and async/timer APIs.