Class: Tins::Duration
- Includes:
- Comparable
- Defined in:
- lib/tins/duration.rb
Overview
A class to represent durations with support for formatting and parsing time intervals.
Class Method Summary collapse
-
.parse(string, template: '%S%d+%h:%m:%s.%f') ⇒ Integer, Float
Returns the number of seconds represented by the given duration string according to the provided template format.
Instance Method Summary collapse
-
#<=>(other) ⇒ Integer
The <=> method compares this object with another object after converting both to floats.
-
#days? ⇒ TrueClass, FalseClass
Returns true if the duration includes days, false otherwise.
-
#format(template = '%S%d+%h:%m:%s.%f', precision: nil) ⇒ String
Formats the duration according to the given template and precision.
-
#format_smart ⇒ String
private
The format_smart method provides intelligent formatting based on the duration’s components.
-
#fractional_seconds? ⇒ TrueClass, FalseClass
Returns true if the duration includes fractional seconds.
-
#hours? ⇒ TrueClass, FalseClass
Returns true if the duration has any hours component.
-
#initialize(seconds) ⇒ Duration
constructor
Initializes a new Duration object with the specified number of seconds.
-
#minutes? ⇒ TrueClass, FalseClass
Returns true if the duration has minutes, false otherwise.
-
#negative? ⇒ TrueClass, FalseClass
Returns true if the duration is negative.
-
#seconds? ⇒ TrueClass, FalseClass
Returns true if the duration has a positive seconds component.
-
#to_f ⇒ Float
Converts the original seconds value to a floating-point number.
-
#to_s ⇒ String
The formatted duration string.
Constructor Details
#initialize(seconds) ⇒ Duration
Initializes a new Duration object with the specified number of seconds.
represent
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/tins/duration.rb', line 78 def initialize(seconds) @negative = seconds < 0 seconds = seconds.abs @original_seconds = seconds @days, @hours, @minutes, @seconds, @fractional_seconds = [ 86_400, 3600, 60, 1, 0 ].inject([ [], seconds ]) {|(r, s), d| if d > 0 dd, rest = s.divmod(d) r << dd [ r, rest ] else r << s end } end |
Class Method Details
.parse(string, template: '%S%d+%h:%m:%s.%f') ⇒ Integer, Float
Returns the number of seconds represented by the given duration string according to the provided template format.
The parser supports the following directives in templates:
-
‘%S` - Sign indicator (optional negative sign)
-
‘%d` - Days component (integer)
-
‘%h` - Hours component (integer)
-
‘%m` - Minutes component (integer)
-
‘%s` - Seconds component (integer)
-
‘%f` - Fractional seconds component (decimal)
-
‘%%` - Literal percent character
The parser is greedy and consumes as much of the input string as possible for each directive. If a directive expects a specific format but doesn’t find it, an ArgumentError is raised.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/tins/duration.rb', line 43 def self.parse(string, template: '%S%d+%h:%m:%s.%f') s, t = string.to_s.dup, template.dup d, sd = 0, 1 loop do t.sub!(/\A(%[Sdhmsf%]|.)/) { |directive| case directive when '%S' then s.sub!(/\A-?/) { sd *= -1 if _1 == ?-; '' } when '%d' then s.sub!(/\A\d+/) { d += 86_400 * _1.to_i; '' } when '%h' then s.sub!(/\A\d+/) { d += 3_600 * _1.to_i; '' } when '%m' then s.sub!(/\A\d+/) { d += 60 * _1.to_i; '' } when '%s' then s.sub!(/\A\d+/) { d += _1.to_i; '' } when '%f' then s.sub!(/\A\d+/) { d += Float(?. + _1); '' } when '%%' then if s[0] == ?% s[0] = '' else raise "expected %s, got #{s.inspect}" end else if directive == s[0] s[0] = '' else raise ArgumentError, "expected #{t.inspect}, got #{s.inspect}" end end '' } or break end sd * d end |
Instance Method Details
#<=>(other) ⇒ Integer
The <=> method compares this object with another object after converting both to floats.
equal, 1 if this object is greater than other
108 109 110 |
# File 'lib/tins/duration.rb', line 108 def <=>(other) to_f <=> other.to_f end |
#days? ⇒ TrueClass, FalseClass
Returns true if the duration includes days, false otherwise.
otherwise
124 125 126 |
# File 'lib/tins/duration.rb', line 124 def days? @days > 0 end |
#format(template = '%S%d+%h:%m:%s.%f', precision: nil) ⇒ String
Formats the duration according to the given template and precision.
The template string supports the following directives:
-
‘%S` - Sign indicator (negative sign if duration is negative)
-
‘%d` - Days component
-
‘%h` - Hours component (zero-padded to 2 digits)
-
‘%m` - Minutes component (zero-padded to 2 digits)
-
‘%s` - Seconds component (zero-padded to 2 digits)
-
‘%f` - Fractional seconds component (without the leading decimal point)
-
‘%D` - Smart format (automatically includes days, fractional seconds, and sign)
-
‘%%` - Literal percent character
When using ‘%f`, the fractional part will be formatted according to the precision parameter.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/tins/duration.rb', line 189 def format(template = '%S%d+%h:%m:%s.%f', precision: nil) result = template.gsub(/%[DdhmSs%]/) { |directive| case directive when '%S' then ?- if negative? when '%d' then @days when '%h' then '%02u' % @hours when '%m' then '%02u' % @minutes when '%s' then '%02u' % @seconds when '%D' then format_smart when '%%' then '%' end } if result.include?('%f') if precision fractional_seconds = "%.#{precision}f" % @fractional_seconds else fractional_seconds = '%f' % @fractional_seconds end result.gsub!('%f', fractional_seconds[2..-1]) end result end |
#format_smart ⇒ String (private)
The format_smart method provides intelligent formatting based on the duration’s components. It automatically determines which components are present and formats them accordingly, making it ideal for human-readable output where you want to avoid showing zero values.
The smart format follows these rules:
-
If days are present, includes the day component (e.g., “1+02:03:04”) -
If fractional seconds are present, includes them with 3 decimal places (e.g., “.123”)
-
If the duration is negative, includes the sign prefix
This method is used internally by #to_s and can also be called directly for smart formatting using the %D directive.
inclusion
238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/tins/duration.rb', line 238 def format_smart template = '%h:%m:%s' precision = nil if days? template.prepend '%d+' end if fractional_seconds? template << '.%f' precision = 3 end template.prepend '%S' format template, precision: precision end |
#fractional_seconds? ⇒ TrueClass, FalseClass
Returns true if the duration includes fractional seconds.
false otherwise
155 156 157 |
# File 'lib/tins/duration.rb', line 155 def fractional_seconds? @fractional_seconds > 0 end |
#hours? ⇒ TrueClass, FalseClass
Returns true if the duration has any hours component
otherwise
132 133 134 |
# File 'lib/tins/duration.rb', line 132 def hours? @hours > 0 end |
#minutes? ⇒ TrueClass, FalseClass
Returns true if the duration has minutes, false otherwise.
139 140 141 |
# File 'lib/tins/duration.rb', line 139 def minutes? @minutes > 0 end |
#negative? ⇒ TrueClass, FalseClass
Returns true if the duration is negative.
negative time interval, false otherwise
116 117 118 |
# File 'lib/tins/duration.rb', line 116 def negative? @negative end |
#seconds? ⇒ TrueClass, FalseClass
Returns true if the duration has a positive seconds component.
false otherwise
147 148 149 |
# File 'lib/tins/duration.rb', line 147 def seconds? @seconds > 0 end |
#to_f ⇒ Float
Converts the original seconds value to a floating-point number.
97 98 99 |
# File 'lib/tins/duration.rb', line 97 def to_f @original_seconds.to_f end |
#to_s ⇒ String
Returns the formatted duration string.
216 217 218 |
# File 'lib/tins/duration.rb', line 216 def to_s format_smart end |