Skip to content
Pyrula

Duration

Duration is a typed time-span value backed by an i64 nanosecond count. It is immutable, hashable, and supports arithmetic and comparison. It interoperates directly with Python’s datetime.timedelta.

from pyrula import Duration

These accept an exact int and scale by the unit’s nanosecond factor with no floating-point rounding:

Duration.nanos(1_000) # 1 000 nanoseconds
Duration.micros(500) # 500 microseconds
Duration.millis(250) # 250 milliseconds

These accept a float and round to the nearest nanosecond:

Duration.seconds(1.5) # 1 500 000 000 ns
Duration.minutes(2.0) # 120 000 000 000 ns
Duration.hours(0.5) # 1 800 000 000 000 ns
Duration.days(1.0) # 86 400 000 000 000 ns

Fractional inputs are fine: Duration.seconds(0.5) is Duration.millis(500).

d = Duration.seconds(1.5)
d.to_nanos() # 1_500_000_000 (int)
d.to_millis() # 1500 (int, truncates sub-ms)
d.to_seconds() # 1.5 (float)

Duration supports +, -, *, and all six comparison operators:

Duration.seconds(1) + Duration.millis(500) # Duration(1500ms)
Duration.seconds(2) - Duration.seconds(1) # Duration(1s)
Duration.seconds(1) * 3 # Duration(3s)
Duration.seconds(1) < Duration.seconds(2) # True
Duration.seconds(2) >= Duration.seconds(2) # True
Duration.seconds(1) == Duration.millis(1000) # True

Duration is hashable and safe to use as a dict key or set element:

seen = {Duration.seconds(1), Duration.seconds(1), Duration.seconds(2)}
len(seen) # 2

Convert to and from Python’s datetime.timedelta:

import datetime as dt
Duration.seconds(1).to_timedelta()
# datetime.timedelta(seconds=1)
Duration.from_timedelta(dt.timedelta(milliseconds=250))
# Duration(250ms)

Note: timedelta resolution is microseconds. to_timedelta() truncates sub-microsecond precision. from_timedelta uses total_seconds() so fractional-second deltas are faithfully converted.

repr picks the largest natural unit that represents the value exactly:

repr(Duration.seconds(1)) # "Duration(1s)"
repr(Duration.millis(250)) # "Duration(250ms)"
repr(Duration.minutes(2)) # "Duration(2m)"
repr(Duration.hours(1)) # "Duration(1h)"
repr(Duration.days(3)) # "Duration(3d)"
repr(Duration.nanos(700)) # "Duration(700ns)"
repr(Duration.seconds(0)) # "Duration(0s)"

Duration mirrors scala.concurrent.duration.FiniteDuration, which also wraps a long nanosecond count and provides unit-converting accessors.

In Scala (with the duration DSL imported):

import scala.concurrent.duration._
val d: FiniteDuration = 5.seconds
d.toMillis // 5000L
d + 500.millis // 5500 milliseconds

In Pyrula:

from pyrula import Duration
d = Duration.seconds(5)
d.to_millis() # 5000
d + Duration.millis(500) # Duration(5500ms)