class Duration

Class to manage durations (or intervals in SQL vocabulary)

Constants

FIELDS
SECONDS_IN_MONTH
SECONDS_IN_YEAR

365.25 * 86_400 == 31_557_600

Attributes

sign[R]

Public Class Methods

new(*args) click to toggle source
# File lib/duration.rb, line 13
def initialize(*args)
  @years, @months, @days, @hours, @minutes, @seconds = 0, 0, 0, 0, 0, 0
  @sign = 1
  if args.size == 1 and args[0].is_a? String
    self.parse(args[0])
  else
    @years   = (args.shift || 0).to_i
    @months  = (args.shift || 0).to_i
    @days    = (args.shift || 0).to_i
    @hours   = (args.shift || 0).to_i
    @minutes = (args.shift || 0).to_i
    @seconds = (args.shift || 0).to_i      
  end
end

Public Instance Methods

normalize(normalize_method = :right) click to toggle source
# File lib/duration.rb, line 141
def normalize(normalize_method = :right)
  self.dup.normalize!(normalize_method)
end
normalize!(normalize_method = :right) click to toggle source

Normalize seconds, minutes, hours and month with their fixed relations

# File lib/duration.rb, line 101
def normalize!(normalize_method = :right)
  if normalize_method == :seconds
    count = self.to_f
    @years = (count / SECONDS_IN_YEAR).floor
    count -= @years * SECONDS_IN_YEAR
    @months = (count / SECONDS_IN_MONTH).floor
    count -= @months * SECONDS_IN_MONTH
    @days = (count / 86_400).floor
    count -= @days * 86_400
    @hours = (count / 3_600).floor
    count -= @hours * 3_600
    @minutes = (count / 60).floor
    count -= @minutes * 60
    @seconds = count
  else
    if @seconds >= 60
      minutes = (@seconds / 60).floor
      @seconds -= minutes * 60
      @minutes += minutes
    end
    if @minutes >= 60
      hours = (@minutes / 60).floor
      @minutes -= hours * 60
      @hours += hours
    end
    if @hours >= 24
      days = (@hours / 24).floor
      @hours -= days * 24
      @days += days
    end
    # No way to convert correctly days in month
    if @months >= 12
      years = (@months / 12).floor
      @months -= years * 12
      @years += years
    end
  end
  return self
end
parse(string) click to toggle source
# File lib/duration.rb, line 28
def parse(string)
  unless string.match(/^\-?P(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+(\.\d+)?S)?)?$/)
    raise ArgumentError.new("Malformed string")
  end
  strings = string.split('T')
  strings[0].gsub(/\d+[YMD]/) do |token|
    code, count = token.to_s[-1..-1], token.to_s[0..-2].to_i
    if code == "Y"
      @years = count
    elsif code == "M"
      @months = count
    elsif code == "D"
      @days = count
    end
    token
  end
  strings[1].to_s.gsub(/(\d+[HM]|\d+(\.\d+)?S)/) do |token|
    code, count = token.to_s[-1..-1], token.to_s[0..-2]
    if code == "H"
      @hours = count.to_i
    elsif code == "M"
      @minutes = count.to_i
    elsif code == "S"
      @seconds = count.to_f
    end
    token
  end
  self.sign = (string.match(/^\-/) ? -1 : 1)
  return self
end
sign=(val) click to toggle source
# File lib/duration.rb, line 59
def sign=(val)
  @sign = (val >= 0 ? 1 : -1)
end
to_f() click to toggle source

Computes a duration in seconds based on theoric and statistic values Because the relation between some of date parts isn't fixed (such as the number of days in a month), the order relationship between durations is only partial, and the result of a comparison between two durations may be undetermined.

# File lib/duration.rb, line 82
def to_f
  count = @seconds
  count += 60 * @minutes
  # 60 * 60 = 3_600
  count += 3_600 * @hours
  # 60 * 60 * 24 = 86_400
  count += 86_400 * @days
  # 365.25/12 * 86_400 == 31_557_600/12 == 2_629_800
  count += SECONDS_IN_MONTH * @months
  # 365.25 * 86_400 == 31_557_600
  count += SECONDS_IN_YEAR * @years
  return @sign * count
end
to_hash() click to toggle source

Export all values to hash

# File lib/duration.rb, line 74
def to_hash
  {:years=>@years, :months=>@months, :days=>@days, :hours=>@hours, :minutes=>@minutes, :seconds=>@seconds, :sign=>@sign}
end
to_i() click to toggle source
# File lib/duration.rb, line 96
def to_i
  self.to_f.to_i
end
to_s(compression = :normal) click to toggle source
# File lib/duration.rb, line 63
def to_s(compression = :normal)
  if compression == :maximum
    return (@sign > 0  ? "" : "-")+"P#{@years.to_s+'Y' if @years > 0}#{@months.to_s+'M' if @months > 0}#{@days.to_s+'D' if @days > 0}"+((@hours.zero? and @minutes.zero? and @seconds.zero?) ? '' : "T#{(@hours.to_s+'H') if @hours > 0}#{(@minutes.to_s+'M') if @minutes > 0}#{((@seconds.floor != @seconds ? @seconds.to_s : @seconds.to_i.to_s)+'S') if @seconds > 0}")
  elsif compression == :minimum
    return (@sign > 0  ? "" : "-")+"P"+@years.to_s+"Y"+@months.to_s+"M"+@days.to_s+"DT"+@hours.to_s+"H"+@minutes.to_s+"M"+@seconds.to_s+"S"
  else
    return (@sign > 0  ? "" : "-")+"P"+((@years.zero? and @months.zero? and @days.zero?) ? '' : @years.to_s+"Y"+@months.to_s+"M"+@days.to_s+"D")+((@hours.zero? and @minutes.zero? and @seconds.zero?) ? '' : "T"+@hours.to_s+"H"+@minutes.to_s+"M"+(@seconds.round != @seconds ? @seconds.to_s : @seconds.to_i.to_s)+"S")
  end
end