Skip to content

Instantly share code, notes, and snippets.

@tlux
Last active August 29, 2015 14:10
Show Gist options
  • Select an option

  • Save tlux/c9b01dbdcdff90549714 to your computer and use it in GitHub Desktop.

Select an option

Save tlux/c9b01dbdcdff90549714 to your computer and use it in GitHub Desktop.
Struct representing a Duration
# composed_of :duration, class_name: 'Duration', mapping: %w(duration to_i), constructor: :parse, converter: :parse
class Duration
SEPARATOR = ':'
SECONDS = 0...60
MINUTES = 0...60
HOURS = 0...24
include Comparable
attr_reader :seconds, :minutes, :hours, :days
def initialize(seconds, minutes = 0, hours = 0, days = 0)
@seconds = Integer(seconds)
raise ArgumentError, 'invalid seconds' unless SECONDS.include?(@seconds)
@minutes = Integer(minutes)
raise ArgumentError, 'invalid minutes' unless MINUTES.include?(@minutes)
@hours = Integer(hours)
raise ArgumentError, 'invalid hours' unless HOURS.include?(@hours)
@days = Integer(days)
end
def self.from_seconds(total_seconds)
total_seconds = Integer(total_seconds)
seconds = total_seconds % SECONDS.size
minutes = (total_seconds / SECONDS.size) % MINUTES.size
hours = (total_seconds / (SECONDS.size * MINUTES.size)) % HOURS.size
days = total_seconds / (SECONDS.size * MINUTES.size * HOURS.size)
self.new(seconds, minutes, hours, days)
end
def self.parse(input)
case input
when self.class then input
when Integer then from_seconds(input)
else
args = input.split(SEPARATOR).reverse
raise 'invalid duration' if args.count > 4
new(*args)
end
end
def inspect
"#<#{self.class.name} days: #{days}, hours: #{hours}, minutes: #{minutes}, seconds: #{seconds}>"
end
def total_seconds
seconds +
minutes * SECONDS.size +
hours * MINUTES.size * SECONDS.size +
days * HOURS.size * MINUTES.size * SECONDS.size
end
def to_i
total_seconds
end
# Calculation and Comparison
def <=>(other)
return nil unless other.respond_to?(:to_i)
self.to_i <=> other.to_i
end
def +(other)
self.class.from_seconds(total_seconds + other.to_i)
end
def -(other)
self.class.from_seconds(total_seconds - other.to_i)
end
def *(other)
self.class.from_seconds(total_seconds * other)
end
def /(other)
self.class.from_seconds(total_seconds / other)
end
def next
self.class.from_seconds(total_seconds.next)
end
alias_method :succ, :next
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment