Class: Tins::Duration

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

Instance Method Summary collapse

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.

Examples:

Basic parsing

Tins::Duration.parse('6+05:04:03', template: '%S%d+%h:%m:%s') # => 536643
Tins::Duration.parse('6+05:04:03.21', template: '%S%d+%h:%m:%s.%f') # => 536643.21

Parsing negative durations

Tins::Duration.parse('-6+05:04:03', template: '%S%d+%h:%m:%s') # => -536643

Custom template parsing

Tins::Duration.parse('05:04:03.21', template: '%h:%m:%s.%f') # => 18243.21

Raises:

  • (ArgumentError)

    If the string doesn’t match the expected template format



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.

Examples:

Basic formatting

duration = Tins::Duration.new(93784.123)
duration.format('%d+%h:%m:%s.%f') # => "1+02:03:04.123000"

Smart format

duration.format('%D') # => "1+02:03:04.123"

Custom precision

duration.format('%s.%f', precision: 2) # => "04.12"


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

#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_fFloat

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_sString



216
217
218
# File 'lib/tins/duration.rb', line 216

def to_s
  format_smart
end