Class: Duration

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

Overview

Author

Matthew Harris ([email protected])

Project

www.rubyforge.org/projects/duration

Synopsis

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

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>



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/duration.rb', line 50

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.



33
34
35
# File 'lib/duration.rb', line 33

def days
  @days
end

#hoursObject

Returns the value of attribute hours.



33
34
35
# File 'lib/duration.rb', line 33

def hours
  @hours
end

#minutesObject

Returns the value of attribute minutes.



33
34
35
# File 'lib/duration.rb', line 33

def minutes
  @minutes
end

#totalObject (readonly) Also known as: to_i

Returns the value of attribute total.



33
34
35
# File 'lib/duration.rb', line 33

def total
  @total
end

#weeksObject

Returns the value of attribute weeks.



33
34
35
# File 'lib/duration.rb', line 33

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>



311
312
313
# File 'lib/duration.rb', line 311

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>



285
286
287
# File 'lib/duration.rb', line 285

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>



298
299
300
# File 'lib/duration.rb', line 298

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>



324
325
326
# File 'lib/duration.rb', line 324

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

#<=>(other) ⇒ Object

Calls ‘<=>’ on Duration#total.

Example

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



173
174
175
# File 'lib/duration.rb', line 173

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



155
156
157
158
159
160
161
162
163
164
# File 'lib/duration.rb', line 155

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>



272
273
274
# File 'lib/duration.rb', line 272

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



127
128
129
130
131
132
133
134
135
136
# File 'lib/duration.rb', line 127

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>



238
239
240
# File 'lib/duration.rb', line 238

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!”



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/duration.rb', line 96

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”



251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/duration.rb', line 251

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