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

Parameters:

  • seconds (Integer, Float)

    the total number of seconds to



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

Parameters:

  • string (String)

    The duration string to parse.

  • template (String) (defaults to: '%S%d+%h:%m:%s.%f')

    for the duration format, see #format. Default: ‘%S%d+%h:%m:%s.%f’

Returns:

  • (Integer, Float)

    The number of (fractional) seconds of the duration.

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

Parameters:

  • other (Object)

    the object to compare with

Returns:

  • (Integer)

    -1 if this object is less than other, 0 if they are



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

Returns:

  • (TrueClass, FalseClass)

    true if the duration has any days, false



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"

Parameters:

  • template (String) (defaults to: '%S%d+%h:%m:%s.%f')

    the format template to use for formatting Default: ‘%S%d+%h:%m:%s.%f’

  • precision (Integer) (defaults to: nil)

    the precision to use for fractional seconds When nil, uses default formatting with 6 decimal places

Returns:

  • (String)

    the formatted duration string



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

Returns:

  • (TrueClass, FalseClass)

    true if fractional seconds are present,



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

Returns:

  • (TrueClass, FalseClass)

    true if hours are present, false



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.

Returns:

  • (TrueClass, FalseClass)

    true if minutes are greater than 0, 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

Returns:

  • (TrueClass, FalseClass)

    true if the duration represents a



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

Returns:

  • (TrueClass, FalseClass)

    true if seconds are greater than zero,



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.

Returns:

  • (Float)

    the original seconds value as a floating-point number



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

def to_f
  @original_seconds.to_f
end

#to_sString

Returns the formatted duration string.

Returns:

  • (String)

    the formatted duration string



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

def to_s
  format_smart
end