Class: Duration

Inherits:
Object
  • Object
show all
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”

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) ⇒ 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>



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/duration.rb', line 48

def initialize(seconds_or_attr)
	if (h = seconds_or_attr).kind_of? Hash
		seconds =  0
		seconds += h[:weeks]   * WEEK   if h.key? :weeks
		seconds += h[:days]    * DAY    if h.key? :days
		seconds += h[:hours]   * HOUR   if h.key? :hours
		seconds += h[:minutes] * MINUTE if h.key? :minutes
		seconds += h[:seconds] * SECOND if h.key? :seconds
	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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

Intercept certain attribute writers. Intercepts ‘weeks=’, ‘days=’, ‘hours=’, ‘minutes=’, ‘seconds=’, and ‘total=’

Example

d = Duration.new(:days => 6) => #<Duration: 6 days> d.days += 1; d => #<Duration: 1 week>



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/duration.rb', line 109

def method_missing(method, *args)
	case method
	when :weeks=   then initialize(WEEK   * args[0] + (@total - WEEK   * @weeks  ))
	when :days=    then initialize(DAY    * args[0] + (@total - DAY    * @days   ))
	when :hours=   then initialize(HOUR   * args[0] + (@total - HOUR   * @hours  ))
	when :minutes= then initialize(MINUTE * args[0] + (@total - MINUTE * @minutes))
	when :seconds= then initialize(SECOND * args[0] + (@total - SECOND * @seconds))
	when :total=   then initialize(args[0])
	else
		raise NoMethodError, "undefined method `#{method}' for #{inspect}"
	end
end

Instance Attribute Details

#daysObject (readonly)

Returns the value of attribute days.



30
31
32
# File 'lib/duration.rb', line 30

def days
  @days
end

#hoursObject (readonly)

Returns the value of attribute hours.



30
31
32
# File 'lib/duration.rb', line 30

def hours
  @hours
end

#minutesObject (readonly)

Returns the value of attribute minutes.



30
31
32
# File 'lib/duration.rb', line 30

def minutes
  @minutes
end

#secondsObject (readonly)

Returns the value of attribute seconds.



30
31
32
# File 'lib/duration.rb', line 30

def seconds
  @seconds
end

#totalObject (readonly) Also known as: to_i

Returns the value of attribute total.



30
31
32
# File 'lib/duration.rb', line 30

def total
  @total
end

#weeksObject (readonly)

Returns the value of attribute weeks.



30
31
32
# File 'lib/duration.rb', line 30

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>



196
197
198
# File 'lib/duration.rb', line 196

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>



170
171
172
# File 'lib/duration.rb', line 170

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>



183
184
185
# File 'lib/duration.rb', line 183

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>



209
210
211
# File 'lib/duration.rb', line 209

def /(other)
	self.class.new(@total / other.to_i)
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>



157
158
159
# File 'lib/duration.rb', line 157

def inspect
	"#<#{self.class}: #{(s = to_s).empty? ? '...' : s}>"
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 %% - 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!”



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/duration.rb', line 86

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

	fmt.gsub(/%?%(w|d|h|m|s)/) 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”



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/duration.rb', line 131

def to_s
	str = ''

	[['weeks'   ,  @weeks  ],
	 ['days'    ,  @days   ],
	 ['hours'   ,  @hours  ],
	 ['minutes' ,  @minutes],
	 ['seconds' ,  @seconds]].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