Class: RelativeTime

Inherits:
Object
  • Object
show all
Defined in:
lib/timerizer.rb

Overview

Represents a relative amount of time. For example, ‘`5 days`’, ‘`4 years`’, and ‘`5 years, 4 hours, 3 minutes, 2 seconds`’ are all RelativeTimes.

Constant Summary collapse

@@units =
{
  :second     => :seconds,
  :minute     => :minutes,
  :hour       => :hours,
  :day        => :days,
  :week       => :weeks,
  :month      => :months,
  :year       => :years,
  :decade     => :decades,
  :century    => :centuries,
  :millennium => :millennia
}
@@in_seconds =
{
    :second => 1,
    :minute => 60,
    :hour   => 3600,
    :day    => 86400,
    :week   => 604800
}
@@in_months =
{
  :month      => 1,
  :year       => 12,
  :decade     => 120,
  :century    => 1200,
  :millennium => 12000
}
@@average_seconds =

Average amount of time in a given unit. Used internally within the #average and #unaverage methods.

{
  :month => 2629746,
  :year  => 31556952
}
@@syntaxes =

Default syntax formats that can be used with #to_s

See Also:

{
  :micro => {
    :units => {
      :seconds => 's',
      :minutes => 'm',
      :hours => 'h',
      :days => 'd',
      :weeks => 'w',
      :months => 'mn',
      :years => 'y',
    },
    :separator => '',
    :delimiter => ' ',
    :count => 1
  },
  :short => {
    :units => {
      :seconds => 'sec',
      :minutes => 'min',
      :hours => 'hr',
      :days => 'd',
      :weeks => 'wk',
      :months => 'mn',
      :years => 'yr',
      :centuries => 'ct',
      :millenia => 'ml'
    },
    :separator => '',
    :delimiter => ' ',
    :count => 2
  },
  :long => {
    :units => {
      :seconds => ['second', 'seconds'],
      :minutes => ['minute', 'minutes'],
      :hours => ['hour', 'hours'],
      :days => ['day', 'days'],
      :weeks => ['week', 'weeks'],
      :months => ['month', 'months'],
      :years => ['year', 'years'],
      :centuries => ['century', 'centuries'],
      :millennia => ['millenium', 'millenia'],
    }
  }
}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#new(hash) ⇒ RelativeTime #new(count, unit) ⇒ RelativeTime

Initialize a new instance of RelativeTime.

Overloads:

  • #new(hash) ⇒ RelativeTime

    Parameters:

    • units (Hash)

      The base units to initialize with

  • #new(count, unit) ⇒ RelativeTime

    Parameters:

    • count (Integer)

      The number of units to initialize with

    • unit (Symbol)

      The unit to initialize. See units



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

def initialize(count = 0, unit = :second)
  if count.is_a? Hash
    units = count
    units.default = 0
    @seconds, @months = units.values_at(:seconds, :months)
  else
    @seconds = @months = 0

    if @@in_seconds.has_key?(unit)
      @seconds = count * @@in_seconds.fetch(unit)
    elsif @@in_months.has_key?(unit)
      @months = count * @@in_months.fetch(unit)
    end
  end
end

Class Method Details

.unitsObject

All potential units. Key is the unit name, and the value is its plural form.



89
90
91
# File 'lib/timerizer.rb', line 89

def self.units
  @@units
end

.units_in_monthsObject

Unit values in months. If a unit is not present in this hash, it is assumed to be in the @@in_seconds hash.



99
100
101
# File 'lib/timerizer.rb', line 99

def self.units_in_months
  @@in_months
end

.units_in_secondsObject

Unit values in seconds. If a unit is not present in this hash, it is assumed to be in the @@in_months hash.



94
95
96
# File 'lib/timerizer.rb', line 94

def self.units_in_seconds
  @@in_seconds
end

Instance Method Details

#+(time) ⇒ Object

Add two RelativeTimes together.

Raises:

See Also:



312
313
314
315
316
317
318
# File 'lib/timerizer.rb', line 312

def +(time)
  raise ArgumentError unless time.is_a?(RelativeTime)
  RelativeTime.new({
    :seconds => @seconds + time.get(:seconds),
    :months => @months + time.get(:months)
  })
end

#-(time) ⇒ Object

Find the difference between two RelativeTimes.

Raises:

See Also:



323
324
325
326
327
328
329
# File 'lib/timerizer.rb', line 323

def -(time)
  raise ArgumentError unless time.is_a?(RelativeTime)
  RelativeTime.new({
    :seconds => @seconds - time.get(:seconds),
    :months => @months - time.get(:months)
  })
end

#==(time) ⇒ Boolean

Note:

Be weary of rounding; this method compares both RelativeTimes’ base units

Compares two RelativeTimes to determine if they are equal

Parameters:

Returns:

  • (Boolean)

    True if both RelativeTimes are equal



131
132
133
134
135
136
137
# File 'lib/timerizer.rb', line 131

def ==(time)
  if time.is_a?(RelativeTime)
    @seconds == time.get(:seconds) && @months == time.get(:months)
  else
    false
  end
end

#after(time) ⇒ Time

Return the time after the given time according to the current RelativeTime.

Parameters:

  • time (Time)

    The starting time

Returns:

  • (Time)

    The time after the current RelativeTime and the given time

See Also:



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/timerizer.rb', line 195

def after(time)
  time = time.to_time + @seconds

  new_year = time.year + self.years
  new_month = time.month + self.months
  while new_month > 12
    new_year += 1
    new_month -= 12
  end
  if Date.valid_date?(new_year, new_month, time.day)
    new_day = time.day
  else
    new_day = Date.new(new_year, new_month).days_in_month
  end


  new_time = Time.new(
    new_year, new_month, new_day,
    time.hour, time.min, time.sec
  )
  Time.at(new_time.to_i, time.nsec/1000.0)
end

#agoTime

Return the time between the RelativeTime and the current time.

Returns:

  • (Time)

    The difference between the current RelativeTime and Time#now

See Also:



187
188
189
# File 'lib/timerizer.rb', line 187

def ago
  self.before(Time.now)
end

#averageRelativeTime

Average second-based units to month-based units.

Examples:

5.weeks.average
  => 1 month, 4 days, 13 hours, 30 minutes, 54 seconds

Returns:

See Also:



265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/timerizer.rb', line 265

def average
  if @seconds > 0
    months = (@seconds / @@average_seconds[:month])
    seconds = @seconds - months.months.unaverage.get(:seconds)
    RelativeTime.new(
      :seconds => seconds,
      :months => months + @months
    )
  else
    self
  end
end

#average!Object

Destructively average second-based units to month-based units.

See Also:



280
281
282
283
284
285
# File 'lib/timerizer.rb', line 280

def average!
  averaged = self.average
  @seconds = averaged.get(:seconds)
  @months = averaged.get(:months)
  self
end

#before(time) ⇒ Time

Determines the time between RelativeTime and the given time.

Examples:

5 hours before January 1st, 2000 at noon

5.minutes.before(Time.new(2000, 1, 1, 12, 00, 00))
  => 2000-01-01 11:55:00 -0800

Parameters:

  • time (Time)

    The initial time.

Returns:

  • (Time)

    The difference between the current RelativeTime and the given time

See Also:



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/timerizer.rb', line 162

def before(time)
  time = time.to_time - @seconds

  new_month = time.month - self.months
  new_year = time.year - self.years
  while new_month < 1
    new_month += 12
    new_year -= 1
  end
  if Date.valid_date?(new_year, new_month, time.day)
    new_day = time.day
  else
    new_day = Date.new(new_year, new_month).days_in_month
  end

  new_time = Time.new(
    new_year, new_month, new_day,
    time.hour, time.min, time.sec
  )
  Time.at(new_time.to_i, time.nsec/1000)
end

#from_nowTime

Return the time after the current time and the RelativeTime.

Returns:

  • (Time)

    The time after the current time



220
221
222
# File 'lib/timerizer.rb', line 220

def from_now
  self.after(Time.now)
end

#get(unit) ⇒ Integer

Return the number of base units in a RelativeTime.

Parameters:

  • unit (Symbol)

    The unit to return, either :seconds or :months

Returns:

  • (Integer)

    The requested unit count

Raises:

  • (ArgumentError)

    Unit requested was not :seconds or :months



143
144
145
146
147
148
149
150
151
# File 'lib/timerizer.rb', line 143

def get(unit)
  if unit == :seconds
    @seconds
  elsif unit == :months
    @months
  else
    raise ArgumentError
  end
end

#to_s(syntax) ⇒ Object #to_s(hash) ⇒ Object

Convert RelativeTime to a human-readable format.

Examples:

(14.months 49.hours).to_s
  => 2 years, 2 months, 3 days, 1 hour
(1.day 3.hours 4.minutes).to_s(:short)
  => 1d 3hr

Overloads:

  • #to_s(syntax) ⇒ Object

    Parameters:

    • syntax (Symbol)

      The syntax from @@syntaxes to use

  • #to_s(hash) ⇒ Object

    Parameters:

    • hash (Hash)

      The custom hash to use

    Options Hash (hash):

    • :units (Hash)

      The unit names to use. See @@syntaxes for examples

    • :count (Integer)

      The maximum number of units to output. ‘1` would output only the unit of greatest example (such as the hour value in `1.hour 3.minutes 2.seconds`).

    • :separator (String)

      The separator to use in between a unit and its value

    • :delimiter (String)

      The delimiter to use in between different unit-value pairs

Raises:

  • KeyError Symbol argument isn’t in @@syntaxes

  • ArgumentError Argument isn’t a hash (if not a symbol)

See Also:



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/timerizer.rb', line 358

def to_s(syntax = :long)
  if syntax.is_a? Symbol
    syntax = @@syntaxes.fetch(syntax)
  end

  raise ArgumentError unless syntax.is_a? Hash
  times = []

  if syntax[:count].nil? || syntax[:count] == :all
    syntax[:count] = @@units.count
  end
  units = syntax.fetch(:units)

  count = 0
  units = Hash[units.to_a.reverse]
  units.each do |unit, (singular, plural)|
    if count < syntax.fetch(:count)
      time = self.respond_to?(unit) ? self.send(unit) : 0

      if time > 1 && !plural.nil?
        times << [time, plural]
        count += 1
      elsif time > 0
        times << [time, singular]
        count += 1
      end
    end
  end

  times.map do |time|
    time.join(syntax[:separator] || ' ')
  end.join(syntax[:delimiter] || ', ')
end

#to_wallWallClock

Converts RelativeTime to WallClock

Examples:

(17.hours 30.minutes).to_wall
  # => 5:30:00 PM

Returns:

Raises:



336
337
338
339
# File 'lib/timerizer.rb', line 336

def to_wall
  raise WallClock::TimeOutOfBoundsError if @months > 0
  WallClock.new(:second => @seconds)
end

#unaverageRelativeTime

Average month-based units to second-based units.

Examples:

1.month.unaverage
  => 4 weeks, 2 days, 10 hours, 29 minutes, 6 seconds

Returns:

See Also:



294
295
296
297
298
# File 'lib/timerizer.rb', line 294

def unaverage
  seconds = @@average_seconds[:month] * @months
  seconds += @seconds
  RelativeTime.new(:seconds => seconds)
end

#unaverage!Object

Destructively average month-based units to second-based units.

See Also:



302
303
304
305
306
307
# File 'lib/timerizer.rb', line 302

def unaverage!
  unaveraged = self.average
  @seconds = unaverage.get(:seconds)
  @months = unaverage.get(:months)
  self
end