Skip to content

feat: implement std::str::FromStr for Date, Time and DateTime #54

Description

@ZialeHub

Summary

timeflow currently exposes TryFrom<&str> for all three types, which enables Date::try_from("2024-10-31"). However, the standard Rust ecosystem primarily relies on std::str::FromStr — the trait that powers the .parse() method — for string-to-type conversions.

Without FromStr, timeflow types cannot participate in a large class of Rust idioms and frameworks out of the box.


Impact of Missing FromStr

The following common patterns do not work today:

// Standard .parse() idiom
let date: Date = "2024-10-31".parse()?;  // ❌ no FromStr

// clap (CLI argument parsing)
#[derive(Parser)]
struct Args {
    #[arg(long)]
    since: Date,  // ❌ clap requires FromStr
}

// envy / figment (environment variable / config deserialization)
// serde_qs (query string deserialization)
// axum / actix Path and Query extractors that use FromStr

Proposed Implementation

impl std::str::FromStr for Date {
    type Err = SpanError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::try_from(s)
    }
}

Identical delegating implementations for Time and DateTime. The actual parsing logic already exists in TryFrom<&str> — this is purely a trait surface addition.


Relation to the build() Method

The README and several doc comments reference Date::build("2024-10-31") and DateTime::build("2024-10-31 06:32:28"), but no build method exists on any type. The closest equivalent is TryFrom<&str>. Adding FromStr provides the idiomatic Rust spelling and also makes it trivial to add a build convenience method as a thin wrapper:

impl Date {
    pub fn build(s: &str) -> Result<Self, SpanError> {
        s.parse()
    }
}

This would make all README examples compile as written.


Implementation Notes

  • FromStr must use BASE_*_FORMAT globals so it respects any SpanBuilder configuration.
  • Error type must be SpanError (already std::error::Error via thiserror), satisfying the FromStr::Err: Error bound.
  • No new parsing logic needed — delegate entirely to TryFrom<&str>.
  • Consider also implementing FromStr for DateUnit, TimeUnit, DateTimeUnit (e.g. "Year".parse::<DateUnit>()) as a separate follow-up.

Acceptance Criteria

  • std::str::FromStr implemented for Date, Time, and DateTime
  • "2024-10-31".parse::<Date>() compiles and returns the correct value
  • Parsing uses the active BASE_*_FORMAT global
  • Invalid strings return a descriptive SpanError::ParseFromStr
  • Date::build(s), Time::build(s), DateTime::build(s) convenience methods added as thin wrappers, matching README documentation
  • Unit tests: valid parse, invalid format, empty string, format set via SpanBuilder
  • README examples updated to use build() or .parse() consistently

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-domainbusiness logic / coreB-ideaDiscussion; or implementation attempt, to be reviewed before further work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions