philiprehberger-duration

Tests Gem Version Last updated

Immutable Duration value object with parsing, arithmetic, and formatting

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-duration"

Or install directly:

gem install philiprehberger-duration

Usage

require "philiprehberger/duration"

d = Philiprehberger::Duration.parse("2h 30m")
d.to_seconds   # => 9000.0
d.to_human     # => "2 hours, 30 minutes"
d.to_iso8601   # => "PT2H30M"

Parsing

Duration = Philiprehberger::Duration

Duration.parse("2 weeks 3 days")  # human string
Duration.parse("1 day 3 hours")   # human string
Duration.parse("PT2H30M")         # ISO 8601
Duration.parse("P2W")             # ISO 8601 weeks
Duration.parse(3600)              # numeric seconds

Use Duration.parse? for a non-raising variant that returns nil on invalid input:

Duration.parse?("2h 30m")  # => Duration("2 hours, 30 minutes")
Duration.parse?("xyz")     # => nil
Duration.parse?("")        # => nil
Duration.parse?(nil)       # => nil

Arithmetic

d1 = Duration.parse("2h")
d2 = Duration.parse("30m")

d1 + d2   # => Duration("2 hours, 30 minutes")
d1 - d2   # => Duration("1 hour, 30 minutes")
d2 * 3    # => Duration("1 hour, 30 minutes")
d1 / 2    # => Duration("1 hour")

Comparison

Duration.parse("2h") > Duration.parse("1h")   # => true
Duration.parse("60m") == Duration.parse("1h")  # => true

Between Two Times

Duration.between(start_time, end_time).to_human  # => "3 hours, 15 minutes"

Component Accessors

d = Philiprehberger::Duration.parse("2 weeks 1 day 2 hours 30 minutes 45 seconds")
d.weeks    # => 2
d.days     # => 1
d.hours    # => 2
d.minutes  # => 30
d.seconds  # => 45
d.to_hash  # => { weeks: 2, days: 1, hours: 2, minutes: 30, seconds: 45 }

Rounding

d = Philiprehberger::Duration.parse("1h 45m")
d.round(:hour).to_human  # => "2 hours"

Total Conversion

d = Philiprehberger::Duration.parse("2h 30m")
d.to_minutes  # => 150.0
d.to_hours    # => 2.5
d.to_days     # => 0.104166...
d.to_weeks    # => 0.014880...
d.to_i        # => 9000
d.to_f        # => 9000.0

Constructing from Components

d = Philiprehberger::Duration.from_hash(weeks: 1, days: 2, hours: 3)
d.to_human  # => "1 week, 2 days, 3 hours"

Custom Formatting

d = Philiprehberger::Duration.parse("1 day 2h 3m 4s")
d.format("%D days %T")  # => "1 days 02:03:04"
d.format("%H:%M:%S")    # => "02:03:04"
Philiprehberger::Duration.zero.zero?  # => true

API

Method Description
Duration.parse(input) Parse human string, ISO 8601, or numeric seconds
Duration.parse?(input) Non-raising variant of parse — returns nil on nil, empty, or invalid input
Duration.from_hash(**components) Construct from named components (weeks, days, hours, minutes, seconds)
Duration.between(time_a, time_b) Duration between two Time objects
Duration.zero Zero-length duration
#zero? Whether the duration is zero
#format(pattern) strftime-style formatter (%W %D %H %M %S %T %s %%)
#to_seconds Total seconds as float
#to_minutes Total minutes as float
#to_hours Total hours as float
#to_days Total days as float
#to_weeks Total weeks as float
#to_i Total seconds as integer
#to_f Total seconds as float
#to_human Human-readable string
#to_iso8601 ISO 8601 formatted string
#weeks Extracted week component
#days Extracted day component (0-6)
#hours Extracted hour component (0-23)
#minutes Extracted minute component (0-59)
#seconds Extracted second component (0-59)
#to_hash Components as { weeks:, days:, hours:, minutes:, seconds: }
#round(unit) Round to nearest :week, :day, :hour, :minute, or :second
#+, #-, #*, #/ Arithmetic operations
<, >, ==, <=> Comparison (via Comparable)

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT