Module: JetstreamBridge::Duration

Defined in:
lib/jetstream_bridge/core/duration.rb

Overview

Utility for parsing human-friendly durations into milliseconds.

Uses auto-detection heuristic by default: integers <1000 are treated as seconds, >=1000 as milliseconds. Strings with unit suffixes are supported (e.g., “30s”, “500ms”, “1h”).

Examples:

Integer with auto-detection

Duration.to_millis(2)                         #=> 2000 (auto: seconds)
Duration.to_millis(1500)                      #=> 1500 (auto: milliseconds)

Explicit units

Duration.to_millis(30, default_unit: :s)      #=> 30000
Duration.to_millis(1_500_000_000, default_unit: :ns) #=> 1500

String with suffix

Duration.to_millis("30s")                     #=> 30000
Duration.to_millis("500ms")                   #=> 500
Duration.to_millis("1h")                      #=> 3_600_000

Normalizing a list

Duration.normalize_list_to_millis(%w[1s 5s 15s]) #=> [1000, 5000, 15000]

Constant Summary collapse

MULTIPLIER_MS =

multipliers to convert 1 unit into milliseconds

{
  'ns' => 1.0e-6,     # nanoseconds to ms
  'us' => 1.0e-3,     # microseconds to ms
  'µs' => 1.0e-3,     # alt microseconds symbol
  'ms' => 1,          # milliseconds to ms
  's' => 1_000,       # seconds to ms
  'm' => 60_000,      # minutes to ms
  'h' => 3_600_000,   # hours to ms
  'd' => 86_400_000   # days to ms
}.freeze
NUMBER_RE =
/\A\d[\d_]*\z/
TOKEN_RE =
/\A(\d[\d_]*(?:\.\d+)?)\s*(ns|us|µs|ms|s|m|h|d)\z/i

Class Method Summary collapse

Class Method Details

.coerce_numeric_to_ms(num, unit) ⇒ Object

Raises:

  • (ArgumentError)


135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/jetstream_bridge/core/duration.rb', line 135

def coerce_numeric_to_ms(num, unit)
  # Handle :auto unit with heuristic: <1000 => seconds, >=1000 => milliseconds
  if unit == :auto
    return (num < 1000 ? num * 1_000 : num).round
  end

  u = unit.to_s
  mult = MULTIPLIER_MS[u]
  raise ArgumentError, "invalid unit for default_unit: #{unit.inspect}" unless mult

  (num * mult).round
end

.float_to_ms(flt, default_unit:) ⇒ Object



117
118
119
# File 'lib/jetstream_bridge/core/duration.rb', line 117

def float_to_ms(flt, default_unit:)
  coerce_numeric_to_ms(flt, default_unit)
end

.int_to_ms(num, default_unit:) ⇒ Object

— internal helpers —



113
114
115
# File 'lib/jetstream_bridge/core/duration.rb', line 113

def int_to_ms(num, default_unit:)
  coerce_numeric_to_ms(num.to_f, default_unit)
end

.normalize_list_to_millis(values, default_unit: :auto) ⇒ Array<Integer>

Normalize an array of durations into integer milliseconds.

Parameters:

  • values (Array<Integer, Float, String>)

    Duration values

  • default_unit (Symbol) (defaults to: :auto)

    Default unit for bare numbers

Returns:

  • (Array<Integer>)

    Durations in milliseconds



71
72
73
74
75
76
# File 'lib/jetstream_bridge/core/duration.rb', line 71

def normalize_list_to_millis(values, default_unit: :auto)
  vals = Array(values)
  return [] if vals.empty?

  vals.map { |v| to_millis(v, default_unit: default_unit) }
end

.normalize_list_to_seconds(values, default_unit: :auto) ⇒ Array<Integer>

Normalize an array of durations into integer seconds.

Parameters:

  • values (Array<Integer, Float, String>)

    Duration values

  • default_unit (Symbol) (defaults to: :auto)

    Default unit for bare numbers

Returns:

  • (Array<Integer>)

    Durations in seconds



104
105
106
107
108
109
# File 'lib/jetstream_bridge/core/duration.rb', line 104

def normalize_list_to_seconds(values, default_unit: :auto)
  vals = Array(values)
  return [] if vals.empty?

  vals.map { |v| to_seconds(v, default_unit: default_unit) }
end

.seconds_from_millis(millis) ⇒ Object



148
149
150
151
# File 'lib/jetstream_bridge/core/duration.rb', line 148

def seconds_from_millis(millis)
  # Always round up to avoid zero-second waits when sub-second durations are provided.
  [(millis / 1000.0).ceil, 1].max
end

.string_to_ms(str, default_unit:) ⇒ Object

Raises:

  • (ArgumentError)


121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/jetstream_bridge/core/duration.rb', line 121

def string_to_ms(str, default_unit:)
  s = str.strip
  # Plain number strings are treated like integers so the :auto
  # heuristic still applies (<1000 => seconds, >=1000 => ms).
  return int_to_ms(s.delete('_').to_i, default_unit: default_unit) if NUMBER_RE.match?(s)

  m = TOKEN_RE.match(s)
  raise ArgumentError, "invalid duration: #{str.inspect}" unless m

  num   = m[1].delete('_').to_f
  unit  = m[2].downcase
  (num * MULTIPLIER_MS.fetch(unit)).round
end

.to_millis(val, default_unit: :auto) ⇒ Integer

Convert a duration value to milliseconds.

Parameters:

  • val (Integer, Float, String)

    Duration value to convert

  • default_unit (Symbol) (defaults to: :auto)

    Unit for bare numbers. :auto (heuristic: <1000 => seconds, >=1000 => ms), :ms, :ns, :us, :s, :m, :h, :d

Returns:

  • (Integer)

    Duration in milliseconds

Raises:

  • (ArgumentError)

    If the value cannot be parsed



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/jetstream_bridge/core/duration.rb', line 53

def to_millis(val, default_unit: :auto)
  case val
  when Integer then int_to_ms(val, default_unit: default_unit)
  when Float   then float_to_ms(val, default_unit: default_unit)
  when String  then string_to_ms(val, default_unit: default_unit)
  else
    raise ArgumentError, "invalid duration type: #{val.class}" unless val.respond_to?(:to_f)

    float_to_ms(val.to_f, default_unit: default_unit)

  end
end

.to_seconds(val, default_unit: :auto) ⇒ Integer?

Convert duration-like value to seconds (rounding up, min 1s).

Retains the nanosecond heuristic used in SubscriptionManager: extremely large integers (>= 1_000_000_000) are treated as nanoseconds when default_unit is :auto.

Parameters:

  • val (Integer, Float, String, nil)

    Duration value (returns nil if nil)

  • default_unit (Symbol) (defaults to: :auto)

    Default unit for bare numbers

Returns:

  • (Integer, nil)

    Duration in seconds (minimum 1), or nil if val is nil



87
88
89
90
91
92
93
94
95
96
97
# File 'lib/jetstream_bridge/core/duration.rb', line 87

def to_seconds(val, default_unit: :auto)
  return nil if val.nil?

  millis = if val.is_a?(Integer) && default_unit == :auto && val >= 1_000_000_000
             to_millis(val, default_unit: :ns)
           else
             to_millis(val, default_unit: default_unit)
           end

  seconds_from_millis(millis)
end