Skip to content

feat: add humanize() for human-readable relative time formatting #58

Description

@ZialeHub

Summary

timeflow currently provides precise formatting (via strftime-style format strings) and exact elapsed durations. A common complementary need — especially in UIs, logs, notifications, and reports — is relative human-readable formatting: expressing a point in time in natural language relative to the current moment or another reference point.


Proposed API

humanize() — relative to now

datetime.humanize()? -> String

// Examples (relative to current system time):
// "just now"          (< 30 seconds ago)
// "2 minutes ago"
// "an hour ago"
// "3 days ago"
// "last month"
// "2 years ago"
// "in 5 minutes"      (future)
// "in 3 days"
// "in 2 months"

humanize_relative_to(&reference) — relative to a given point

datetime.humanize_relative_to(&other_datetime)? -> String
date.humanize_relative_to(&other_date)? -> String

This is useful for displaying times relative to a known event (e.g. "3 hours after the deployment", "1 week before the deadline") without being tied to the current wall clock.

age_in_years(&reference) — for birthdate arithmetic

let birthdate = Date::new(1990, 6, 15)?;
let today = Date::now()?;
let age = birthdate.age_in_years(&today)?;  // e.g. 34

Threshold Table

Suggested rounding thresholds (inspired by moment.js and timeago.rs):

Elapsed Output
< 30s "just now" / "in a moment"
30s – 90s "a minute ago" / "in a minute"
90s – 45min "X minutes ago"
45min – 90min "an hour ago" / "in an hour"
90min – 22h "X hours ago"
22h – 36h "a day ago" / "tomorrow" / "yesterday"
36h – 26 days "X days ago"
26d – 46d "a month ago" / "next month"
46d – 11 months "X months ago"
11m – 18m "a year ago" / "next year"
> 18 months "X years ago"

Implementation Notes

  • humanize() calls humanize_relative_to(&Self::now()?) internally.
  • The method should be available on Date, Time, and DateTime.
  • For Date::humanize(), time-of-day granularity is not available — the threshold table should stop at "days" for Date.
  • For Time::humanize(), only intra-day units (hours, minutes, seconds) are meaningful.
  • All output strings should be in English for v1. Localisation is explicitly out of scope for this issue.
  • humanize does not need to be configurable in v1 — the threshold table above can be hardcoded. A HumanizeOptions builder could be added later.
  • This feature has no new dependencies — it uses only Self::now() and elapsed() which already exist in timeflow.

Acceptance Criteria

  • humanize() implemented on Date, Time, and DateTime
  • humanize_relative_to(&reference) implemented on all three types
  • age_in_years(&reference) implemented on Date
  • Future times produce "in X ..." phrasing; past times produce "X ... ago"
  • Threshold table is applied correctly (tested with specific elapsed durations)
  • Date::humanize() does not produce sub-day output
  • Time::humanize() does not produce multi-day output
  • Unit tests covering each threshold bucket for past and future
  • No new crate dependencies introduced
  • Doc comments with examples showing past and future output

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