Loading page…
Rust walkthroughs
Loading page…
chrono::Duration::seconds handle overflow compared to std::time::Duration::from_secs?chrono::Duration::seconds accepts negative values and returns a signed duration, while std::time::Duration::from_secs accepts only positive values and panics on overflow when the resulting duration would exceed the maximum representable value. The key distinction is that chrono::Duration is signed, allowing negative durations and handling overflow through wrapping or explicit checks, whereas std::time::Duration is unsigned with strict overflow detection that panics in debug mode. This reflects their different design goals: chrono::Duration represents time differences that can be positive or negative, while std::time::Duration represents spans of time that are inherently non-negative.
use chrono::Duration as ChronoDuration;
use std::time::Duration as StdDuration;
fn main() {
// std::time::Duration: unsigned, positive only
let std_dur = StdDuration::from_secs(60);
println!("std duration: {:?}", std_dur);
// Duration { secs: 60, nanos: 0 }
// chrono::Duration: signed, can be negative
let chrono_dur = ChronoDuration::seconds(60);
println!("chrono duration: {:?}", chrono_dur);
// Duration { secs: 60, nanos: 0 }
// chrono can represent negative durations
let negative = ChronoDuration::seconds(-60);
println!("negative duration: {:?}", negative);
// Duration { secs: -60, nanos: 0 }
// std::time::Duration cannot be negative
// let std_negative = StdDuration::from_secs(-60); // Won't compile: can't use negative
}chrono::Duration is signed; std::time::Duration is unsigned and cannot represent negative values.
use chrono::Duration as ChronoDuration;
use std::time::Duration as StdDuration;
fn signed_vs_unsigned() {
// chrono::Duration: represents time difference
// Can be positive (future) or negative (past)
let future = ChronoDuration::seconds(300); // 5 minutes in future
let past = ChronoDuration::seconds(-300); // 5 minutes in past
// Useful for DateTime arithmetic
use chrono::Utc;
let now = Utc::now();
let earlier = now - ChronoDuration::seconds(60); // 1 minute ago
let later = now + ChronoDuration::seconds(60); // 1 minute from now
// std::time::Duration: represents elapsed time
// Always non-negative
let elapsed = StdDuration::from_secs(300); // 5 minutes elapsed
// Useful for timeouts, intervals
let timeout = StdDuration::from_secs(30);
let interval = StdDuration::from_secs(60);
}chrono::Duration represents time differences; std::time::Duration represents elapsed time spans.
use std::time::Duration as StdDuration;
fn debug_overflow() {
// In debug mode, overflow panics
// Maximum Duration is approximately 584 years
// This will panic in debug mode:
// let overflow = StdDuration::from_secs(u64::MAX);
// panic: "overflow when creating duration"
// Safe construction:
let max_duration = StdDuration::MAX;
println!("Max duration: {:?}", max_duration);
// Duration { secs: 18446744073709551615, nanos: 999999999 }
}
use chrono::Duration as ChronoDuration;
fn chrono_overflow() {
// chrono::Duration uses i64 internally
// Overflow wraps in release mode, panics in debug
let large = ChronoDuration::seconds(i64::MAX);
println!("Large chrono duration: {:?}", large);
// chrono::Duration::MAX is about 292 billion years
// i64::MAX / (365.25 * 24 * 3600) ≈ 292 billion years
}Both panic on overflow in debug mode, but chrono::Duration has different internal representation.
use std::time::Duration as StdDuration;
use chrono::Duration as ChronoDuration;
fn checked_creation() {
// std::time::Duration has checked_add, checked_sub, etc.
// But from_secs doesn't have a checked variant directly
// Manual check for std:
let secs: u64 = 1_000_000_000;
if secs <= StdDuration::MAX.as_secs() {
let dur = StdDuration::from_secs(secs);
println!("Created duration: {:?}", dur);
}
// chrono::Duration has checked methods
let checked = ChronoDuration::try_seconds(i64::MAX);
println!("Checked result: {:?}", checked);
// Some(Duration { ... })
let overflow = ChronoDuration::try_seconds(i64::MAX / 2 + 1)
.and_then(|d| d.checked_add(&ChronoDuration::seconds(i64::MAX / 2 + 1)));
println!("Overflow checked: {:?}", overflow);
// None (would overflow)
}chrono::Duration::try_seconds provides checked creation; std::time::Duration::from_secs requires manual validation.
use chrono::Duration;
fn try_vs_unchecked() {
// seconds(): direct conversion, no overflow check
let dur1 = Duration::seconds(1_000_000);
// Always succeeds for reasonable values
// try_seconds(): checked conversion
let dur2 = Duration::try_seconds(1_000_000);
// Some(Duration { ... })
// For extreme values:
// seconds() may wrap or panic
// try_seconds() returns None
// Example with near-max value:
let large = Duration::try_seconds(i64::MAX - 1);
println!("Large duration: {:?}", large);
// Some(Duration { secs: 9223372036854775806, nanos: 0 })
// Overflow in addition:
let dur = Duration::seconds(i64::MAX / 2);
let doubled = dur.checked_add(&dur);
println!("Doubled: {:?}", doubled);
// None - would overflow
}try_seconds returns Option<Duration> for safe construction; seconds assumes valid input.
use chrono::Duration;
use std::time::Duration as StdDuration;
fn negative_handling() {
// chrono::Duration handles negatives naturally
let negative = Duration::seconds(-100);
assert!(negative < Duration::zero());
// Can negate durations
let positive = -negative;
assert_eq!(positive, Duration::seconds(100));
// Can subtract larger from smaller
let small = Duration::seconds(10);
let large = Duration::seconds(100);
let result = small - large;
assert_eq!(result, Duration::seconds(-90));
// std::time::Duration cannot be negative
// Operations that would go negative panic or saturate
let std_small = StdDuration::from_secs(10);
let std_large = StdDuration::from_secs(100);
// checked_sub returns None if would go negative
let checked = std_small.checked_sub(std_large);
println!("Checked sub: {:?}", checked);
// None
// saturating_sub returns zero instead of negative
let saturated = std_small.saturating_sub(std_large);
println!("Saturated sub: {:?}", saturated);
// Duration { secs: 0, nanos: 0 }
// Regular subtraction panics in debug mode
// let overflow = std_small - std_large; // panic!
}chrono::Duration supports negative values naturally; std::time::Duration panics or saturates on negative results.
use chrono::Duration;
use std::time::Duration as StdDuration;
fn internal_representation() {
// std::time::Duration stores:
// - secs: u64
// - nanos: u32 (0-999,999,999)
// chrono::Duration stores internally:
// - A single i64 representing total milliseconds
// OR (depending on version):
// - secs: i64
// - nanos: i32 (can be negative for negative durations)
// This allows chrono to represent:
// - Negative durations
// - Durations near zero with nanosecond precision
let chrono_nano = Duration::nanoseconds(1);
let chrono_neg_nano = Duration::nanoseconds(-1);
println!("Positive nano: {:?}", chrono_nano);
println!("Negative nano: {:?}", chrono_neg_nano);
// std::time::Duration nanos must be < 1 second
let std_nano = StdDuration::from_nanos(1);
// No negative nanos possible
}chrono::Duration uses signed integers internally; std::time::Duration uses unsigned integers.
use chrono::Duration;
use std::time::Duration as StdDuration;
fn arithmetic_comparison() {
// chrono::Duration arithmetic
let a = Duration::seconds(100);
let b = Duration::seconds(50);
let sum = a + b;
let diff = a - b; // Can be negative
let neg_diff = b - a; // Also valid: Duration::seconds(-50)
println!("Sum: {:?}", sum);
println!("Diff: {:?}", diff);
println!("Neg diff: {:?}", neg_diff);
// std::time::Duration arithmetic
let std_a = StdDuration::from_secs(100);
let std_b = StdDuration::from_secs(50);
let std_sum = std_a + std_b; // OK
let std_diff = std_a - std_b; // OK
// let std_neg = std_b - std_a; // PANIC in debug!
// Safe arithmetic with std:
let safe_diff = std_b.checked_sub(std_a);
println!("Safe diff: {:?}", safe_diff); // None
// Multiplication
let chrono_multiplied = Duration::seconds(10) * 5;
let std_multiplied = StdDuration::from_secs(10) * 5;
// Division
let chrono_divided = Duration::seconds(100) / 5;
let std_divided = StdDuration::from_secs(100) / 5;
}chrono::Duration supports negative results; std::time::Duration requires checked operations for safety.
use chrono::Duration;
use std::time::Duration as StdDuration;
fn comparison_operations() {
// Both support standard comparison
// chrono::Duration
let a = Duration::seconds(100);
let b = Duration::seconds(50);
assert!(a > b);
// Including negative
let c = Duration::seconds(-100);
assert!(c < Duration::zero());
// std::time::Duration
let std_a = StdDuration::from_secs(100);
let std_b = StdDuration::from_secs(50);
assert!(std_a > std_b);
// No negative, so always >= zero
assert!(std_b >= StdDuration::ZERO);
// chrono::Duration has zero() constant
let zero = Duration::zero();
assert_eq!(zero, Duration::seconds(0));
// std::time::Duration has ZERO constant
let std_zero = StdDuration::ZERO;
assert_eq!(std_zero, StdDuration::from_secs(0));
}Both support full comparison; chrono::Duration can be negative, std::time::Duration cannot.
use chrono::Duration;
use std::time::Duration as StdDuration;
fn conversion() {
// chrono::Duration -> std::time::Duration
let chrono_dur = Duration::seconds(100);
// to_std() returns Option (None if negative)
let std_dur = chrono_dur.to_std();
println!("To std: {:?}", std_dur);
// Some(Duration { secs: 100, nanos: 0 })
// Negative chrono Duration cannot convert to std
let negative = Duration::seconds(-100);
let cannot_convert = negative.to_std();
println!("Negative to std: {:?}", cannot_convert);
// None
// std::time::Duration -> chrono::Duration
let std_input = StdDuration::from_secs(100);
let chrono_from_std = Duration::from_std(std_input);
println!("From std: {:?}", chrono_from_std);
// Some(Duration { secs: 100, nanos: 0 })
// chrono also has from_std that always succeeds
// since std Duration is always non-negative
let chrono_direct = Duration::from_std(StdDuration::from_secs(50));
println!("Direct: {:?}", chrono_direct);
// Duration { secs: 50, nanos: 0 }
}Conversion from chrono::Duration to std::time::Duration can fail on negative values.
use chrono::{DateTime, Duration, Utc};
fn datetime_arithmetic() {
let now: DateTime<Utc> = Utc::now();
// chrono::Duration enables natural DateTime math
let future = now + Duration::seconds(3600); // 1 hour from now
let past = now - Duration::seconds(3600); // 1 hour ago
// Difference between DateTimes is chrono::Duration
let diff = future - now;
println!("Difference: {:?}", diff);
// Duration { secs: 3600, nanos: 0 }
// This can be negative
let negative_diff = past - now;
println!("Negative diff: {:?}", negative_diff);
// Duration { secs: -3600, nanos: 0 }
// Comparing DateTime differences
if diff > Duration::zero() {
println!("Future is after now");
}
if negative_diff < Duration::zero() {
println!("Past is before now");
}
}chrono::Duration integrates with DateTime for intuitive time arithmetic.
use std::time::Duration as StdDuration;
use chrono::Duration as ChronoDuration;
fn timeout_context() {
// std::time::Duration is designed for timeouts
// tokio::time::sleep requires std::time::Duration
// Correct: use std::time::Duration directly
let timeout = StdDuration::from_secs(30);
// tokio::time::sleep(timeout).await;
// If you have chrono::Duration:
let chrono_timeout = ChronoDuration::seconds(30);
let std_timeout = chrono_timeout.to_std().unwrap();
// tokio::time::sleep(std_timeout).await;
// For negative durations (invalid timeout):
let negative = ChronoDuration::seconds(-30);
let converted = negative.to_std();
println!("Negative timeout: {:?}", converted);
// None - can't use negative timeout
}Timeout APIs use std::time::Duration; convert from chrono::Duration with .to_std().
use chrono::Duration;
use std::time::Duration as StdDuration;
fn real_world_overflow() {
// Scenario: parsing user input for duration
fn parse_duration_user_input(secs: &str) -> Result<StdDuration, String> {
let secs: u64 = secs.parse().map_err(|_| "Invalid number")?;
// Check against maximum
const MAX_SECS: u64 = u64::MAX / 1_000_000_000; // Conservative limit
if secs > MAX_SECS {
return Err("Duration too large".to_string());
}
Ok(StdDuration::from_secs(secs))
}
// Scenario: calculating time differences
fn calculate_time_diff(earlier: i64, later: i64) -> Duration {
Duration::seconds(later - earlier) // Can be negative
}
// Scenario: accumulating durations
fn accumulate_durations() -> Duration {
let durations = vec
![
Duration::seconds(100),
Duration::seconds(-50),
Duration::seconds(200),
Duration::seconds(-75),
];
durations.into_iter().fold(Duration::zero(), |acc, d| {
acc.checked_add(&d).unwrap_or(Duration::max_value())
})
}
// Scenario: std Duration accumulation with overflow protection
fn accumulate_std_durations() -> Option<StdDuration> {
let durations = vec
![
StdDuration::from_secs(100),
StdDuration::from_secs(200),
StdDuration::from_secs(300),
];
durations.into_iter().try_fold(StdDuration::ZERO, |acc, d| {
acc.checked_add(d)
})
}
let result = accumulate_durations();
println!("Accumulated: {:?}", result);
let std_result = accumulate_std_durations();
println!("Std accumulated: {:?}", std_result);
}Real-world usage requires overflow handling appropriate to each type's semantics.
use chrono::Duration;
use std::time::Duration as StdDuration;
fn maximum_values() {
// std::time::Duration maximum
println!("std::time::Duration::MAX:");
println!(" Seconds: {}", StdDuration::MAX.as_secs());
println!(" Nanos: {}", StdDuration::MAX.subsec_nanos());
// Approximately 584 years
// chrono::Duration maximum
// Uses i64 internally, so approximately ±292 billion years
println!("\nchrono::Duration maximum:");
println!(" i64::MAX seconds: {}", i64::MAX);
// 9,223,372,036,854,775,807 seconds
// ≈ 292,277,024,603 years
// Creating near-max durations
let near_max_std = StdDuration::from_secs(StdDuration::MAX.as_secs());
println!("\nNear max std: {:?}", near_max_std);
let near_max_chrono = Duration::seconds(i64::MAX - 1);
println!("Near max chrono: {:?}", near_max_chrono);
// For practical purposes, both are effectively unbounded
// Real applications should set their own limits
}Both types have extremely large maximum values that exceed practical limits.
use chrono::Duration;
use std::time::Duration as StdDuration;
fn checked_methods() {
// std::time::Duration checked methods
let a = StdDuration::from_secs(100);
let b = StdDuration::from_secs(50);
let sum = a.checked_add(b);
println!("Checked add: {:?}", sum); // Some(...)
let diff = a.checked_sub(b);
println!("Checked sub: {:?}", diff); // Some(...)
let overflow = b.checked_sub(a);
println!("Checked underflow: {:?}", overflow); // None
let mul = a.checked_mul(10);
println!("Checked mul: {:?}", mul); // Some(...)
// saturating methods
let saturated = b.saturating_sub(a);
println!("Saturated sub: {:?}", saturated); // Duration::ZERO
// chrono::Duration checked methods
let chrono_a = Duration::seconds(100);
let chrono_b = Duration::seconds(50);
let chrono_sum = chrono_a.checked_add(&chrono_b);
println!("Chrono checked add: {:?}", chrono_sum);
let chrono_diff = chrono_a.checked_sub(&chrono_b);
println!("Chrono checked sub: {:?}", chrono_diff);
// chrono checked methods handle overflow
let large = Duration::seconds(i64::MAX / 2);
let overflow = large.checked_add(&large);
println!("Chrono overflow: {:?}", overflow); // None
}Both types provide checked arithmetic methods for safe operations that might overflow.
Key differences:
| Aspect | chrono::Duration::seconds | std::time::Duration::from_secs |
|--------|-----------------------------|----------------------------------|
| Input type | i64 (can be negative) | u64 (positive only) |
| Return type | Duration (signed) | Duration (unsigned) |
| Overflow (debug) | May panic | Panics |
| Overflow (release) | Wraps | May wrap or panic |
| Negative values | Supported | Not possible |
| Checked creation | try_seconds | Manual check required |
| Use case | Time differences | Time spans, timeouts |
Design philosophy:
// chrono::Duration: Time differences (can be before or after)
// - Represents: "How much time between two events?"
// - Can be negative: "2 hours ago" vs "2 hours from now"
// - Used with DateTime arithmetic
// std::time::Duration: Time spans (always positive)
// - Represents: "How long did something take?"
// - Cannot be negative: "Wait 30 seconds" makes sense, "Wait -30 seconds" doesn't
// - Used for timeouts, intervals, sleepWhen to use each:
// Use chrono::Duration for:
// - DateTime arithmetic
// - Representing time differences
// - Scheduling relative times
// - When negative values are meaningful
// Use std::time::Duration for:
// - Async/await timeouts
// - Thread sleep
// - Intervals
// - Measuring elapsed timeOverflow handling summary:
// chrono::Duration::seconds(i64):
// - Accepts any i64 value
// - Internally stores as i64 seconds + i32 nanoseconds
// - Use try_seconds() for checked conversion
// - Use checked_add/checked_sub for safe arithmetic
// std::time::Duration::from_secs(u64):
// - Accepts any u64 value
// - Panics in debug if value is too large for internal representation
// - No checked creation method (manually validate)
// - Use checked_add/checked_sub/checked_mul for safe arithmeticKey insight: The fundamental difference is that chrono::Duration::seconds is designed for temporal arithmetic where negative values represent "before" vs "after" a reference point, while std::time::Duration::from_secs represents elapsed time which is inherently non-negative. This design choice propagates through their overflow behavior: chrono::Duration must handle both positive and negative overflow, while std::time::Duration only concerns itself with upper bounds. The signed/unsigned distinction at the API level (i64 vs u64 input) makes this clear: if you need to represent a time difference that could be either direction, use chrono::Duration; if you need to represent a span of time for a timeout or interval, use std::time::Duration.