Rust时间格式化库fancy-duration的使用,提供优雅且灵活的持续时间(duration)显示与解析功能

Rust时间格式化库fancy-duration的使用,提供优雅且灵活的持续时间(duration)显示与解析功能

"fancy duration"是一种持续时间的文本描述。例如,“1h 20m 30s"可以读作"一小时二十分钟三十秒”。通过多种方式可以透明地表达持续时间类型;支持chrono和time crate,以及通过serde将字符串类型序列化和反序列化。时间支持从年到纳秒的范围。

以下是使用示例。您可以将类似duration的类型包装在FancyDuration结构体中,或者使用允许monkeypatch方法的类型,这些方法允许您直接在目标类型上工作。例如,使用AsFancyDuration注入fancy_duration调用来执行构造(可以格式化或转换为字符串),并使用ParseFancyDuration注入parse_fancy_duration构造函数来将字符串解析为您喜欢的类型。支持std::time::Duration、time::Duration和chrono::Duration(某些功能可能需要启用),您可以通过实现AsTimes特性使更多类型符合条件。

示例代码

use std::time::Duration;
use fancy_duration::FancyDuration;

pub fn main() {
    assert_eq!(FancyDuration(Duration::new(20, 0)).to_string(), "20s");
    assert_eq!(FancyDuration(Duration::new(600, 0)).to_string(), "10m");
    assert_eq!(FancyDuration(Duration::new(120, 0)).to_string(), "2m");
    assert_eq!(FancyDuration(Duration::new(185, 0)).to_string(), "3m 5s");
    assert_eq!(FancyDuration::<Duration>::parse("3m 5s").unwrap().duration(), Duration::new(185, 0));
    assert_eq!(FancyDuration(Duration::new(185, 0)).to_string(), "3m 5s");

    // these traits are also implemented for chrono and time
    use fancy_duration::{ParseFancyDuration, AsFancyDuration};
    assert_eq!(Duration::new(20, 0).fancy_duration().to_string(), "20s");
    assert_eq!(Duration::new(600, 0).fancy_duration().to_string(), "10m");
    assert_eq!(Duration::new(120, 0).fancy_duration().to_string(), "2m");
    assert_eq!(Duration::new(185, 0).fancy_duration().to_string(), "3m 5s");
    assert_eq!(Duration::parse_fancy_duration("3m 5s".to_string()).unwrap(), Duration::new(185, 0));
    assert_eq!(Duration::new(185, 0).fancy_duration().to_string(), "3m 5s");

    #[cfg(feature = "time")]
    {
        // also works with time::Duration from the `time` crate
        assert_eq!(FancyDuration(time::Duration::new(20, 0)).to_string(), "20s");
        assert_eq!(FancyDuration(time::Duration::new(600, 0)).to_string(), "10m");
        assert_eq!(FancyDuration(time::Duration::new(120, 0)).to_string(), "2m");
        assert_eq!(FancyDuration(time::Duration::new(185, 0)).to_string(), "3m 5s");
        assert_eq!(FancyDuration::<time::Duration>::parse("3m 5s").unwrap().duration(), time::Duration::new(185, 0));
        assert_eq!(FancyDuration(time::Duration::new(185, 0)).to_string(), "3m 5s");
    }

    #[cfg(feature = "chrono")]
    {
        // also works with chrono!
        assert_eq!(FancyDuration(chrono::Duration::seconds(20)).to_string(), "20s");
        assert_eq!(FancyDuration(chrono::Duration::seconds(600)).to_string(), "10m");
        assert_eq!(FancyDuration(chrono::Duration::seconds(120)).to_string(), "2m");
        assert_eq!(FancyDuration(chrono::Duration::seconds(185)).to_string(), "3m 5s");
        assert_eq!(FancyDuration::<chrono::Duration>::parse("3m 5s").unwrap().duration(), chrono::Duration::seconds(185));
        assert_eq!(FancyDuration(chrono::Duration::seconds(185)).to_string(), "3m 5s");
    }
}

完整示例代码

// 添加依赖到Cargo.toml
// fancy-duration = "0.9.2"

use std::time::Duration;
use fancy_duration::{FancyDuration, AsFancyDuration, ParseFancyDuration};

fn main() {
    // 基本用法
    let duration = Duration::new(125, 0); // 2分钟5秒
    let fancy = FancyDuration(duration);
    println!("Formatted duration: {}", fancy.to_string()); // 输出: 2m 5s

    // 使用AsFancyDuration trait
    let fancy2 = duration.fancy_duration();
    println!("Alternative formatting: {}", fancy2.to_string()); // 输出: 2m 5s

    // 解析字符串为Duration
    let parsed = Duration::parse_fancy_duration("1h 30m").unwrap();
    println!("Parsed duration: {:?}", parsed); // 输出: 5400s (1.5小时)

    // 使用chrono (需要启用chrono特性)
    #[cfg(feature = "chrono")]
    {
        use chrono::Duration as ChronoDuration;
        let chrono_duration = ChronoDuration::seconds(3665); // 1小时1分钟5秒
        let chrono_fancy = FancyDuration(chrono_duration);
        println!("Chrono duration: {}", chrono_fancy.to_string()); // 输出: 1h 1m 5s
    }

    // 使用time crate (需要启用time特性)
    #[cfg(feature = "time")]
    {
        use time::Duration as TimeDuration;
        let time_duration = TimeDuration::new(185, 0); // 3分钟5秒
        let time_fancy = FancyDuration(time_duration);
        println!("Time duration: {}", time_fancy.to_string()); // 输出: 3m 5s
    }
}

性能基准

每个间隔测试增加了将被格式化的内容数量。第一组解析测试解析一个包含多个项的字符串,其他测试在单次迭代中解析5个静态项。

基准测试系统是Ryzen 5900X,64GB内存,运行Linux 6.6.8桌面配置。

截至0.9.1版本:

fancy duration format seconds: std
                        time:   [644.13 ps 645.34 ps 646.96 ps]
fancy duration format seconds: time
                        time:   [6.6756 ns 6.6945 ns 6.7181 ns]
fancy duration format seconds: chrono
                        time:   [658.36 ps 658.73 ps 659.15 ps]
fancy duration format minutes: std
                        time:   [666.00 ps 666.50 ps 667.03 ps]
fancy duration format minutes: time
                        time:   [6.6301 ns 6.6324 ns 6.6349 ns]
fancy duration format minutes: chrono
                        time:   [646.99 ps 647.24 ps 647.50 ps]
fancy duration format hours: std
                        time:   [658.35 ps 660.83 ps 662.88 ps]
fancy duration format hours: time
                        time:   [6.5761 ns 6.5792 ns 6.5826 ns]
fancy duration format hours: chrono
                        time:   [648.05 ps 648.38 ps 648.74 ps]
fancy duration format days: std
                        time:   [641.06 ps 641.33 ps 641.62 ps]
fancy duration format days: time
                        time:   [6.5374 ns 6.5410 ns 6.5452 ns]
fancy duration format days: chrono
                        time:   [662.60 ps 665.24 ps 667.41 ps]
fancy duration format weeks: std
                        time:   [641.06 ps 641.32 ps 641.61 ps]
fancy duration format weeks: time
                        time:   [6.7987 ns 6.8158 ns 6.8288 ns]
fancy duration format weeks: chrono
                        time:   [660.01 ps 662.64 ps 665.05 ps]
fancy duration format months: std
                        time:   [641.66 ps 642.02 ps 642.41 ps]
fancy duration format months: time
                        time:   [6.5435 ns 6.5498 ns 6.5573 ns]
fancy duration format months: chrono
                        time:   [672.00 ps 672.39 ps 672.83 ps]
fancy duration parse one: std
                        time:   [332.00 ns 332.48 ns 332.98 ns]
fancy duration parse one: time
                        time:   [346.25 ns 346.58 ns 346.92 ns]
fancy duration parse one: chrono
                        time:   [369.81 ns 371.26 ns 372.65 ns]
fancy duration parse 5 distinct items: std
                        time:   [1.9150 µs 1.9183 µs 1.9214 µs]
fancy duration parse 5 distinct items: time
                        time:   [1.8446 µs 1.8474 µs 1.8503 µs]
fancy duration parse 5 distinct items: chrono
                        time:   [1.8281 µs 1.8311 µs 1.8345 µs]

安装

在项目目录中运行以下Cargo命令:

cargo add fancy-duration

或者在Cargo.toml中添加以下行:

fancy-duration = "0.9.2"

作者

Erik Hollensbe git@hollensbe.org

许可证

MIT


1 回复

Rust时间格式化库fancy-duration使用指南

fancy-duration是一个Rust库,用于优雅且灵活地格式化和解析持续时间(Duration)。它提供了比标准库更人性化的持续时间显示方式,并支持自定义格式。

安装

在Cargo.toml中添加依赖:

[dependencies]
fancy-duration = "0.7"

基本使用

格式化持续时间

use std::time::Duration;
use fancy_duration::FancyDuration;

fn main() {
    let duration = Duration::new(3665, 0); // 1小时1分钟5秒
    
    // 默认格式化
    println!("{}", FancyDuration(duration));
    // 输出: "1h 1m 5s"
    
    // 紧凑模式
    println!("{}", FancyDuration(duration).compact());
    // 输出: "1h1m5s"
}

解析持续时间

use fancy_duration::parse_fancy_duration;

fn main() {
    let duration = parse_fancy_duration("1h 30m").unwrap();
    println!("总秒数: {}", duration.as_secs());
    // 输出: "总秒数: 5400"
}

高级功能

自定义格式

use std::time::Duration;
use fancy_duration::{FancyDuration, FancyDurationFormat};

fn main() {
    let duration = Duration::new(93784, 0); // 1天2小时3分钟4秒
    
    let custom_format = FancyDurationFormat::new()
        .with_use_days(true)
        .with_use_hours(true)
        .with_use_minutes(true)
        .with_use_seconds(false)
        .with_separator(", ");
    
    println!("{}", FancyDuration(duration).format(custom_format));
    // 输出: "1d, 2h, 3m"
}

本地化支持

use std::time::Duration;
use fancy_duration::{FancyDuration, FancyDurationFormat};

fn main() {
    let duration = Duration::new(3723, 0); // 1小时2分钟3秒
    
    let french_format = FancyDurationFormat::new()
        .with_days_label(" jours")
        .with_hours_label(" heures")
        .with_minutes_label(" minutes")
        .with_seconds_label(" secondes");
    
    println!("{}", FancyDuration(duration).format(french_format));
    // 输出: "1 heures 2 minutes 3 secondes"
}

精确控制显示单位

use std::time::Duration;
use fancy_duration::{FancyDuration, FancyDurationFormat};

fn main() {
    let duration = Duration::new(93784, 0); // 1天2小时3分钟4秒
    
    let format = FancyDurationFormat::new()
        .with_max_units(2); // 只显示最大的两个单位
    
    println!("{}", FancyDuration(duration).format(format));
    // 输出: "1d 2h" (跳过了分钟和秒)
}

错误处理

解析时可能会出错,需要适当处理:

use fancy_duration::parse_fancy_duration;

fn main() {
    match parse_fancy_duration("1hour 30mins") {
        Ok(duration) => println!("解析成功: {}秒", duration.as_secs()),
        Err(e) => println!("解析错误: {}", e),
    }
}

性能考虑

fancy-duration在格式化时几乎没有性能开销,因为它只是对std::time::Duration的轻量级包装。解析操作会有一些开销,但通常可以忽略不计。

这个库特别适合需要向用户显示友好时间间隔的应用,如日志系统、监控工具或任何需要展示时间数据的CLI/GUI应用。

完整示例代码

use std::time::Duration;
use fancy_duration::{FancyDuration, FancyDurationFormat, parse_fancy_duration};

fn main() {
    // 基本格式化示例
    let duration1 = Duration::new(3665, 0); // 1小时1分钟5秒
    println!("基本格式化:");
    println!("默认: {}", FancyDuration(duration1));
    println!("紧凑模式: {}", FancyDuration(duration1).compact());
    
    // 解析示例
    println!("\n解析示例:");
    let parsed = parse_fancy_duration("1h 30m").unwrap();
    println!("解析结果: {}秒", parsed.as_secs());
    
    // 自定义格式示例
    println!("\n自定义格式:");
    let duration2 = Duration::new(93784, 0); // 1天2小时3分钟4秒
    let custom_format = FancyDurationFormat::new()
        .with_use_days(true)
        .with_use_hours(true)
        .with_use_minutes(true)
        .with_use_seconds(false)
        .with_separator(", ");
    println!("自定义格式: {}", FancyDuration(duration2).format(custom_format));
    
    // 本地化示例
    println!("\n本地化示例:");
    let french_format = FancyDurationFormat::new()
        .with_days_label(" jours")
        .with_hours_label(" heures")
        .with_minutes_label(" minutes")
        .with_seconds_label(" secondes");
    println!("法语格式: {}", FancyDuration(duration1).format(french_format));
    
    // 控制显示单位示例
    println!("\n控制显示单位:");
    let max_units_format = FancyDurationFormat::new().with_max_units(2);
    println!("只显示2个单位: {}", FancyDuration(duration2).format(max_units_format));
    
    // 错误处理示例
    println!("\n错误处理:");
    match parse_fancy_duration("1hour 30mins") {
        Ok(d) => println!("解析成功: {}秒", d.as_secs()),
        Err(e) => println!("解析错误: {}", e),
    }
}

输出结果示例:

基本格式化:
默认: 1h 1m 5s
紧凑模式: 1h1m5s

解析示例:
解析结果: 5400秒

自定义格式:
自定义格式: 1d, 2h, 3m

本地化示例:
法语格式: 1 heures 1 minutes 5 secondes

控制显示单位:
只显示2个单位: 1d 2h

错误处理:
解析错误: invalid duration string
回到顶部