Class: Horologium::Representations::JulianDate

Inherits:
Object
  • Object
show all
Defined in:
lib/horologium/representations/julian_date.rb,
sig/horologium/representations/julian_date.rbs

Overview

The Julian Date: the number of days since noon on 1 January 4713 BC, in the scale it is read in. Astronomy counts time with it, and an ephemeris takes it as input.

An instant is already stored as a Julian Date, so on the way out this representation only picks the type it comes out in. At today's dates a Float keeps the Julian Date to a few tens of microseconds, which is where the other types come in.

Examples:

The type is chosen on the way out

instant = Horologium::Instant.from_julian_date(2_443_144.5, scale: :tai)
instant.as(:julian_date, scale: :tt)                  # => Float
instant.as(:julian_date, scale: :tt, as: :rational)   # => Rational
instant.as(:julian_date, scale: :tt, as: :two_part)   # => TwoPartFloat

Constant Summary collapse

OUTPUTS =

The types a Julian Date can come out as.

Returns:

  • (Array[Symbol])
%i[float rational two_part].freeze
DECIMAL =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

The shape a Julian Date written as a String takes: digits, with an optional sign and an optional decimal fraction. There is no exponent, because a Julian Date is not written with one.

Returns:

  • (Regexp)
/\A[+-]?\d+(\.\d+)?\z/

Class Method Summary collapse

Class Method Details

.parse(value, low, _scale, precision) ⇒ Horologium::Numeric::TwoPartFloat, Horologium::Numeric::Exact

A Julian Date as it was given, in days, at the precision asked for. This is the way in, where render is the way out. The value is a Julian Date in a scale, not in TAI; it is Instant.from_julian_date that reads it back in TAI.

The lossless shapes come first. A String and a Rational say the Julian Date exactly, and a high and a low Float say it to about twice what one Float holds. A single Float is the lossy one: seven of its sixteen digits go to the day count, which leaves the fraction of a day about 40 microseconds. Horologium stores it faithfully and does not guess, but the loss happened in the literal, before the library was reached.

At :standard two Floats are normalized onto the integer-day grid, and a String or a Rational is split so that the two Floats carry as much of it as they can hold. At :exact nothing is dropped, beyond what the input had already lost.

Examples:

Horologium::Representations::JulianDate.parse(
  "2456463.052272",
  nil,
  Horologium::Scales::TAI,
  :exact
)

Parameters:

  • value (String, Rational, Integer, Float)

    the Julian Date, in days, or its high part when a low part follows

  • low (Float, Integer, nil)

    the low part of the Julian Date, in days

  • _scale (Class)

    the scale the value is read in, unused: a Julian Date means the same in every scale

  • precision (Symbol)

    :standard or :exact

  • scale (singleton(Scales::Base))

Returns:

Raises:

  • (ParseError)

    when a String does not spell a Julian Date

  • (ArgumentError)

    when the Julian Date is none of the shapes above

  • (UnknownPrecisionError)

    when the precision is not recognised



100
101
102
103
104
# File 'lib/horologium/representations/julian_date.rb', line 100

def parse(value, low, _scale, precision)
  return two_parts(value, low, precision) unless low.nil?

  single(value, precision)
end

.render(reading, output) ⇒ Float, ...

The Julian Date, in the type asked for.

:float is the default. A Float spends most of its digits on the Julian Date itself, and at today's dates the numbers it can hold are about 40 microseconds apart, which is enough to display, to plot, or to compare at millisecond tolerance. :rational keeps the whole value. :two_part gives the two parts, to pass to a foreign kernel that reads them, such as an ERFA binding or a Chebyshev ephemeris segment. They are only guaranteed to add up to the Julian Date: an instant is built on the integer-day grid, but a scale conversion moves the parts off it. For arithmetic, use Duration.

An :exact reading asked for :two_part is split again, and loses the precision the Rational held beyond the two parts. It only happens when the caller asks for it.

Parameters:

Returns:

Raises:



58
59
60
# File 'lib/horologium/representations/julian_date.rb', line 58

def render(reading, output)
  render_value(reading.value, output)
end

.render_value(value, output) ⇒ Float, ...

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A value in days, in the type asked for. render reads the value off the reading; a representation that shifts the value before rendering it, such as ModifiedJulianDate, hands the shifted value here.

Parameters:

Returns:

Raises:



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/horologium/representations/julian_date.rb', line 117

def render_value(value, output)
  case output
  when :float
    value.to_f
  when :rational
    value.to_r
  when :two_part
    two_part(value)
  else
    raise UnknownOutputError.new(output, OUTPUTS)
  end
end