Class: Duration

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

Overview

Class to manage durations (or intervals in SQL vocabulary)

Constant Summary collapse

SECONDS_IN_YEAR =

365.25 * 86_400 == 31_557_600

31_557_600
SECONDS_IN_MONTH =

31_556_926 => 31_556_928

SECONDS_IN_YEAR / 12
FIELDS =
[:years, :months, :days, :hours, :minutes, :seconds]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Duration

Returns a new instance of Duration.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 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

Instance Attribute Details

#signObject

Returns the value of attribute sign.



11
12
13
# File 'lib/duration.rb', line 11

def sign
  @sign
end

Instance Method Details

#normalize(normalize_method = :right) ⇒ Object



141
142
143
# File 'lib/duration.rb', line 141

def normalize(normalize_method = :right)
  self.dup.normalize!(normalize_method)
end

#normalize!(normalize_method = :right) ⇒ Object

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



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# 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) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 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

#to_fObject

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.



82
83
84
85
86
87
88
89
90
91
92
93
94
# 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_hashObject

Export all values to hash



74
75
76
# 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_iObject



96
97
98
# File 'lib/duration.rb', line 96

def to_i
  self.to_f.to_i
end

#to_s(compression = :normal) ⇒ Object



63
64
65
66
67
68
69
70
71
# 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