Class: Duration

Inherits:
Object show all
Includes:
Comparable, Enumerable
Defined in:
lib/more/facets/duration.rb

Overview

Duration

Duration is a simple class that provides ways of easily manipulating durations
(timespans) and formatting them as well.

Usage:

require 'duration'
=> true
d = Duration.new(60 * 60 * 24 * 10 + 120 + 30)
=> #<Duration: 1 week, 3 days, 2 minutes and 30 seconds>
d.to_s
=> "1 week, 3 days, 2 minutes and 30 seconds"
[d.weeks, d.days]
=> [1, 3]
d.days = 7; d
=> #<Duration: 2 weeks, 2 minutes and 30 seconds>
d.strftime('%w w, %d d, %h h, %m m, %s s')
=> "2 w, 0 d, 0 h, 2 m, 30 s"

Direct Known Subclasses

BigDuration

Constant Summary collapse

WEEK =
60 * 60 * 24 * 7
DAY =
60 * 60 * 24
HOUR =
60 * 60
MINUTE =
60
SECOND =
1

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Enumerable

#**, #accumulate, cart, cartesian_product, #cartesian_product, #cluster_by, #collect_if, #collect_with_index, combinations, #combos, #commonality, #compact_collect, #count, #divide, #each_by, #each_combination, #each_combo, #each_pair, #each_permutation, #eachn, #elementwise, #entropy, #every, #every!, #filter_collect, #frequency, #group_by, #ideal_entropy, #inject!, #injecting, #map_send, #mash, #mode, #modulate, #none?, #nonuniq, #occur, #one?, #permutation, #permutation_number, #probability, #split, #sum, #threaded_map, #threaded_map_send, #to_elem, #to_h, #to_hash, #uniq_by

Methods included from Comparable

#at_least, #at_most, #cap, #clip, #cmp

Constructor Details

#initialize(seconds_or_attr = 0) ⇒ Duration

Initialize Duration class.

Example

d = Duration.new(60 * 60 * 24 * 10 + 120 + 30)
=> #<Duration: 1 week, 3 days, 2 minutes and 30 seconds>
d = Duration.new(:weeks => 1, :days => 3, :minutes => 2, :seconds => 30)
=> #<Duration: 1 week, 3 days, 2 minutes and 30 seconds>


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/more/facets/duration.rb', line 65

def initialize(seconds_or_attr = 0)
    if seconds_or_attr.kind_of? Hash
        # Part->time map table.
        h = {:weeks => WEEK, :days => DAY, :hours => HOUR, :minutes => MINUTE, :seconds => SECOND}

        # Loop through each valid part, ignore all others.
        seconds = seconds_or_attr.inject(0) do |sec, args|
            # Grab the part of the duration (week, day, whatever) and the number of seconds for it.
            part, time = args

            # Map each part to their number of seconds and the given value.
            # {:weeks => 2} maps to h[:weeks] -- so... weeks = WEEK * 2
            if h.key?(prt = part.to_s.to_sym) then sec + time * h[prt] else 0 end
        end
    else
        seconds = seconds_or_attr
    end

    @total, array = seconds.to_f.round, []
    @seconds = [WEEK, DAY, HOUR, MINUTE].inject(@total) do |left, part|
        array << left / part; left % part
    end

    @weeks, @days, @hours, @minutes = array
end

Instance Attribute Details

#daysObject

Returns the value of attribute days.



48
49
50
# File 'lib/more/facets/duration.rb', line 48

def days
  @days
end

#hoursObject

Returns the value of attribute hours.



48
49
50
# File 'lib/more/facets/duration.rb', line 48

def hours
  @hours
end

#minutesObject

Returns the value of attribute minutes.



48
49
50
# File 'lib/more/facets/duration.rb', line 48

def minutes
  @minutes
end

#totalObject (readonly) Also known as: to_i

Returns the value of attribute total.



48
49
50
# File 'lib/more/facets/duration.rb', line 48

def total
  @total
end

#weeksObject

Returns the value of attribute weeks.



48
49
50
# File 'lib/more/facets/duration.rb', line 48

def weeks
  @weeks
end

Instance Method Details

#*(other) ⇒ Object

Multiply two Durations.

Example

d = Duration.new(30)
=> #<Duration: 30 seconds>
d * 2
=> #<Duration: 1 minute>


326
327
328
# File 'lib/more/facets/duration.rb', line 326

def *(other)
    self.class.new(@total * other.to_i)
end

#+(other) ⇒ Object

Add to Duration.

Example

d = Duration.new(30)
=> #<Duration: 30 seconds>
d + 30
=> #<Duration: 1 minute>


300
301
302
# File 'lib/more/facets/duration.rb', line 300

def +(other)
    self.class.new(@total + other.to_i)
end

#-(other) ⇒ Object

Subtract from Duration.

Example

d = Duration.new(30)
=> #<Duration: 30 seconds>
d - 15
=> #<Duration: 15 seconds>


313
314
315
# File 'lib/more/facets/duration.rb', line 313

def -(other)
    self.class.new(@total - other.to_i)
end

#/(other) ⇒ Object

Divide two Durations.

Example

d = Duration.new(30)
=> #<Duration: 30 seconds>
d / 2
=> #<Duration: 15 seconds>


339
340
341
# File 'lib/more/facets/duration.rb', line 339

def /(other)
    self.class.new(@total / other.to_i)
end

#<=>(other) ⇒ Object

Calls ‘<=>’ on Duration#total.

Example

5.days == 24.hours * 5
=> true


188
189
190
# File 'lib/more/facets/duration.rb', line 188

def <=>(other)
    @total <=> other.to_i
end

#eachObject

For iterating through the duration set of weeks, days, hours, minutes, and seconds.

Example

Duration.new(:weeks => 1, :seconds => 30).each do |part, time|
    puts "part: #{part}, time: #{time}"
end

Output

part: weeks, time: 1
part: days, time: 0
part: hours, time: 0
part: minutes, time: 0
part: seconds, time: 30


170
171
172
173
174
175
176
177
178
179
# File 'lib/more/facets/duration.rb', line 170

def each
    [['weeks'   ,  @weeks  ],
     ['days'    ,  @days   ],
     ['hours'   ,  @hours  ],
     ['minutes' ,  @minutes],
     ['seconds' ,  @seconds]].each do |part, time|
         # Yield to block
        yield part, time
    end
end

#inspectObject

Inspection string–Similar to #to_s except that it has the class name.

Example

Duration.new(:seconds => 140)
=> #<Duration: 2 minutes and 20 seconds>


287
288
289
# File 'lib/more/facets/duration.rb', line 287

def inspect
    "#<#{self.class}: #{(s = to_s).empty? ? '...' : s}>"
end

#seconds(part = nil) ⇒ Object

Get the number of seconds of a given part, or simply just get the number of seconds.

Example

d = Duration.new(:weeks => 1, :days => 1, :hours => 1, :seconds => 30)
=> #<Duration: 1 week, 1 day, 1 hour and 30 seconds>
d.seconds(:weeks)
=> 604800
d.seconds(:days)
=> 86400
d.seconds(:hours)
=> 3600
d.seconds
=> 30


142
143
144
145
146
147
148
149
150
151
# File 'lib/more/facets/duration.rb', line 142

def seconds(part = nil)
    # Table mapping
    h = {:weeks => WEEK, :days => DAY, :hours => HOUR, :minutes => MINUTE}

    if [:weeks, :days, :hours, :minutes].include? part
        __send__(part) * h[part]
    else
        @seconds
    end
end

#seconds=(n) ⇒ Object

Set the number of minutes.

Example

d = Duration.new(0)
=> #<Duration: ...>
d.seconds = 30; d
=> #<Duration: 30 seconds>


253
254
255
# File 'lib/more/facets/duration.rb', line 253

def seconds=(n)
    initialize(:seconds => (@total + n) - @seconds)
end

#strftime(fmt) ⇒ Object

Format duration.

Identifiers

%w -- Number of weeks
%d -- Number of days
%h -- Number of hours
%m -- Number of minutes
%s -- Number of seconds
%t -- Total number of seconds
%x -- Duration#to_s
%% -- Literal `%' character

Example

d = Duration.new(:weeks => 10, :days => 7)
=> #<Duration: 11 weeks>
d.strftime("It's been %w weeks!")
=> "It's been 11 weeks!"


111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/more/facets/duration.rb', line 111

def strftime(fmt)
    h =\
    {'w' => @weeks  ,
     'd' => @days   ,
     'h' => @hours  ,
     'm' => @minutes,
     's' => @seconds,
     't' => @total  ,
     'x' => to_s}

    fmt.gsub(/%?%(w|d|h|m|s|t|x)/) do |match|
        match.size == 3 ? match : h[match[1..1]]
    end.gsub('%%', '%')
end

#to_sObject

Friendly, human-readable string representation of the duration.

Example

d = Duration.new(:seconds => 140)
=> #<Duration: 2 minutes and 20 seconds>
d.to_s
=> "2 minutes and 20 seconds"


266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/more/facets/duration.rb', line 266

def to_s
    str = ''

    each do |part, time|
        # Skip any zero times.
        next if time.zero?

        # Concatenate the part of the time and the time itself.
        str << "#{time} #{time == 1 ? part[0..-2] : part}, "
    end

    str.chomp(', ').sub(/(.+), (.+)/, '\1 and \2')
end