Class: Horologium::Instant
- Inherits:
-
Object
- Object
- Horologium::Instant
- Includes:
- PreciseValue
- Defined in:
- lib/horologium/instant.rb,
sig/horologium/instant.rbs
Overview
A single point on the timeline, independent of any scale. It is stored as a TAI Julian Date, in days, at a fixed precision.
An Instant is frozen. Its precision is set when it is built, from the
precision in effect unless you pass one. At :standard the Julian Date is
a Numeric::TwoPartFloat, at :exact a Numeric::Exact.
You can add or subtract a Duration, and subtract another Instant to get
the Duration between them. Adding two instants raises DimensionalError.
Mixing a :standard and an :exact operand gives an :exact result.
Instance Attribute Summary
Attributes included from PreciseValue
Class Method Summary collapse
-
.from_tai_julian_date(high, low = 0.0, precision: Horologium.current_precision) ⇒ Horologium::Instant
Builds an instant from a TAI Julian Date, split into a high and a low part in days.
Instance Method Summary collapse
-
#+(duration) ⇒ Horologium::Instant
Adds a duration and returns a later instant.
-
#-(other) ⇒ Horologium::Instant, Horologium::Duration
Subtracts a duration to get an earlier instant, or another instant to get the Duration between them.
-
#add(left, right) ⇒ Numeric::TwoPartFloat, Numeric::Exact
Adds two values.
-
#equal_within?(other, tolerance) ⇒ Boolean
Whether two instants fall within a tolerance of each other.
-
#seconds_to_days(duration, precision) ⇒ Horologium::Numeric::TwoPartFloat, Horologium::Numeric::Exact
The duration's seconds counted in days, at the given precision.
-
#subtract(left, right) ⇒ Numeric::TwoPartFloat, Numeric::Exact
Subtracts two values, promoting to exact the same way #add does.
Methods included from PreciseValue
#<=>, #eql?, #hash, #initialize
Class Method Details
.from_tai_julian_date(high, low = 0.0, precision: Horologium.current_precision) ⇒ Horologium::Instant
Builds an instant from a TAI Julian Date, split into a high and a low
part in days. At :exact the two parts are kept as a Rational, with no
loss. At :standard they are normalized so the high part sits on the
integer-day grid and the low part holds the fraction, in [-0.5, 0.5].
35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/horologium/instant.rb', line 35 def self.from_tai_julian_date( high, low = 0.0, precision: Horologium.current_precision ) value = case Numeric::Precision.validate!(precision) when :exact Numeric::Exact.new(Numeric::TwoPartFloat.new(high, low)) else Numeric::TwoPartFloat.normalize(high, low) end new(value, precision) end |
Instance Method Details
#+(duration) ⇒ Horologium::Instant
Adds a duration and returns a later instant.
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/horologium/instant.rb', line 58 def +(duration) # rubocop:disable Naming/BinaryOperatorParameterName unless duration.is_a?(Duration) raise DimensionalError, "cannot add a #{duration.class} to an Instant; " \ "only a Duration shifts an Instant" end precision = Numeric::Precision.resolve(self.precision, duration.precision) days = seconds_to_days(duration, precision) self.class.new(add(value, days), precision) end |
#-(arg0) ⇒ Instant #-(arg0) ⇒ Duration
Subtracts a duration to get an earlier instant, or another instant to get the Duration between them.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/horologium/instant.rb', line 83 def -(other) case other when Duration precision = Numeric::Precision.resolve(self.precision, other.precision) days = seconds_to_days(other, precision) self.class.new(subtract(value, days), precision) when Instant precision = Numeric::Precision.resolve(self.precision, other.precision) gap = subtract(value, other.value) Duration.new(gap * Duration::SECONDS_PER_DAY, precision) else raise DimensionalError, "cannot subtract a #{other.class} from an Instant; " \ "subtract a Duration or another Instant" end end |
#add(left, right) ⇒ Numeric::TwoPartFloat, Numeric::Exact
Adds two values. Two standard values add as two-part floats; if either is exact, both are promoted to exact Rationals first.
138 139 140 141 142 143 144 145 |
# File 'lib/horologium/instant.rb', line 138 def add(left, right) if left.is_a?(Numeric::TwoPartFloat) && right.is_a?(Numeric::TwoPartFloat) left + right else Numeric::Precision.coerce(left, to: :exact) + Numeric::Precision.coerce(right, to: :exact) end end |
#equal_within?(other, tolerance) ⇒ Boolean
Whether two instants fall within a tolerance of each other. Use this
rather than == in scientific code.
110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/horologium/instant.rb', line 110 def equal_within?(other, tolerance) unless other.is_a?(Instant) raise DimensionalError, "cannot compare an Instant with a #{other.class}" end unless tolerance.is_a?(Duration) raise DimensionalError, "a tolerance must be a Duration, got a #{tolerance.class}" end (self - other).abs <= tolerance end |
#seconds_to_days(duration, precision) ⇒ Horologium::Numeric::TwoPartFloat, Horologium::Numeric::Exact
The duration's seconds counted in days, at the given precision. A Julian Date counts days, so a duration is scaled before it is added.
131 132 133 134 |
# File 'lib/horologium/instant.rb', line 131 def seconds_to_days(duration, precision) Numeric::Precision.coerce(duration.value, to: precision) / Duration::SECONDS_PER_DAY end |
#subtract(left, right) ⇒ Numeric::TwoPartFloat, Numeric::Exact
Subtracts two values, promoting to exact the same way #add does.
148 149 150 151 152 153 154 155 |
# File 'lib/horologium/instant.rb', line 148 def subtract(left, right) if left.is_a?(Numeric::TwoPartFloat) && right.is_a?(Numeric::TwoPartFloat) left - right else Numeric::Precision.coerce(left, to: :exact) - Numeric::Precision.coerce(right, to: :exact) end end |