Last active
August 29, 2015 14:10
-
-
Save tlux/c9b01dbdcdff90549714 to your computer and use it in GitHub Desktop.
Struct representing a Duration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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