License | AGPL-3 |
---|---|
Maintainer | swiss-ephemeris@lfborjas.com |
Portability | POSIX |
Safe Haskell | None |
Language | Haskell2010 |
Functions and types for conversion between Haskell time types and Swiss Ephemeris time values.
Since: 1.4.0.0
Synopsis
- data TimeStandard
- data JulianDay (s :: TimeStandard)
- type JulianDayUT = JulianDay 'UT
- type JulianDayTT = JulianDay 'TT
- type JulianDayUT1 = JulianDay 'UT1
- getJulianDay :: JulianDay s -> Double
- data SiderealTime
- getSiderealTime :: SiderealTime -> Double
- data SingTimeStandard :: TimeStandard -> Type where
- STT :: SingTimeStandard 'TT
- SUT1 :: SingTimeStandard 'UT1
- SUT :: SingTimeStandard 'UT
- class SingTSI a where
- singTS :: SingTimeStandard a
- class MonadFail m => ToJulianDay m jd from where
- toJulianDay :: from -> IO (m (JulianDay jd))
- class FromJulianDay jd to where
- fromJulianDay :: JulianDay jd -> IO to
- data ConversionResult dt
- getConversionResult :: ConversionResult dt -> Either String dt
- mkJulianDay :: SingTimeStandard ts -> Double -> JulianDay ts
- coerceUT :: JulianDay 'UT -> JulianDay 'UT1
- julianNoon :: JulianDay ts -> JulianDay ts
- julianMidnight :: JulianDay ts -> JulianDay ts
- utcToJulianDays :: MonadFail m => UTCTime -> IO (m (JulianDay 'TT, JulianDay 'UT1))
- dayFromJulianDay :: JulianDay ts -> Day
- dayToJulianDay :: Day -> JulianDay ts
- gregorianToFakeJulianDayTT :: Integer -> Int -> Int -> Double -> JulianDay 'TT
- gregorianFromFakeJulianDayTT :: JulianDay 'TT -> (Integer, Int, Int, Double)
- gregorianToJulianDayUT :: Integer -> Int -> Int -> Double -> JulianDay 'UT
- gregorianFromJulianDayUT :: JulianDay 'UT -> (Integer, Int, Int, Double)
- utcToJulianDayUT :: UTCTime -> JulianDay 'UT
- julianDayUTToUTC :: JulianDay 'UT -> UTCTime
- utcToJulian :: UTCTime -> JulianDay 'UT
- julianToUTC :: JulianDay 'UT -> UTCTime
- addDeltaTime :: JulianDay 'UT1 -> Double -> JulianDay 'TT
- subtractDeltaTime :: JulianDay 'TT -> Double -> JulianDay 'UT1
- unsafeDeltaTime :: JulianDay 'UT1 -> IO Double
- deltaTime :: JulianDay 'UT1 -> IO Double
- safeDeltaTime :: MonadFail m => EphemerisOption -> JulianDay 'UT1 -> IO (m Double)
- deltaTimeSE :: MonadFail m => JulianDay 'UT1 -> IO (m Double)
- universalToTerrestrial :: JulianDay 'UT1 -> IO (JulianDay 'TT)
- universalToTerrestrialSafe :: MonadFail m => EphemerisOption -> JulianDay 'UT1 -> IO (m (JulianDay 'TT))
- universalToTerrestrialSE :: MonadFail m => JulianDay 'UT1 -> IO (m (JulianDay 'TT))
- julianToSiderealSimple :: JulianDay 'UT1 -> IO SiderealTime
- julianToSidereal :: JulianDay 'UT1 -> ObliquityInformation -> IO SiderealTime
The many faces of time
This module offers conversions between some Haskell time values, and astronomical
time values as defined by SwissEphemeris
. The most important types in this
module are TimeStandard
, which refers to different "standards" of time
such as Universal Time and Terrestial Time, and JulianDay
, which codifies
an absolute floating point number of fractional "days" since an epoch
in the distant past. A SiderealTime
is also provided, though it figures
less prominently in the Swiss Ephemeris API, and the conversions are more
self-explanatory.
As far as this library is concerned, a Julian Day
can represent either a moment in Universal Time,
which takes into account the Earth's rotation (either the
more specific UT1
standard, or a generic UT
time whose precision is left up
to the caller -- we provide ways of converting a UTCTime
into a JulianDayUT
, for example,)
or Terrestrial Time, which is independent of the Earth's rotation and is used
in astronomical measurements from a theoretical point on the surface of the Earth.
Most functionality in Swiss Ephemeris uses Terrestrial Time (the documentation
also refers to it using the now-superseded moniker of Ephemeris Time, but current
versions of the library actually don't use the time standard by that name, and instead
adhere to TT.)
An absolute moment in time will not be the same in UT1 and TT: TT is ahead of
UT1 by a quantity known as Delta Time,
which is not neatly predictable but which is expected to increase with the passage of time;
given this reality, functions in this module make it mostly impossible to "coerce" a Julian Day
obtained from a moment in Universal Time to Terrestrial Time (and vice-versa: ) Delta Time must be calculated,
and leap seconds in UT must be taken into account.
Swiss Ephemeris provides functions to do these conversions safely by consulting historical data (hence the IO
restriction,)
and the ToJulian
and FromJulian
typeclasses govern the interface for conversion for any
given type: currently only UTCTime
from the Haskell time taxonomy is supported: a Day
can trivially be first converted to/from UTCTime
, and other values such as Haskell's
own notion of UniversalTime
don't have immediate astronomical significance.
The only somewhat "safe" coercion between time standards that doesn't go through IO
is between UT
and UT1
, though for UTCTime
, this will be off by less than a second
due to the nature of UTC vs. UT1.
For convenience, we provide a way of converting between Day
and any JulianDay
values purely,
which relies on temporally unsound assumptions about the difference
between the supported time standards; this works fine for dates, but is categorically
wrong whenever a time of day is necessary. Go through the typeclass methods in that case.
Some further reading:
- https://wiki.haskell.org/Time
- https://en.wikipedia.org/wiki/Terrestrial_Time
- https://en.wikipedia.org/wiki/Universal_Time
- https://en.wikipedia.org/wiki/Leap_second
- https://en.wikipedia.org/wiki/%CE%94T_(timekeeping)
- https://www.nist.gov/pml/time-and-frequency-division/time-realization/leap-seconds
- https://www.ietf.org/timezones/data/leap-seconds.list
data TimeStandard Source #
Various standards for measuring time that can be expressed as Julian Days.
TT | Terrestrial Time (successor to Ephemeris Time) |
UT1 | Universal Time, explicitly in its |
UT | Universal Time, in any of its forms; depending on how it was constructed (in most cases, UTC) |
Instances
Eq TimeStandard Source # | |
Defined in SwissEphemeris.Time (==) :: TimeStandard -> TimeStandard -> Bool # (/=) :: TimeStandard -> TimeStandard -> Bool # | |
Show TimeStandard Source # | |
Defined in SwissEphemeris.Time showsPrec :: Int -> TimeStandard -> ShowS # show :: TimeStandard -> String # showList :: [TimeStandard] -> ShowS # |
data JulianDay (s :: TimeStandard) Source #
A JulianDay
can have different provenances, witnessed
by its accompanying phantom type:
Instances
Enum (JulianDay s) Source # | |
Defined in SwissEphemeris.Time succ :: JulianDay s -> JulianDay s # pred :: JulianDay s -> JulianDay s # toEnum :: Int -> JulianDay s # fromEnum :: JulianDay s -> Int # enumFrom :: JulianDay s -> [JulianDay s] # enumFromThen :: JulianDay s -> JulianDay s -> [JulianDay s] # enumFromTo :: JulianDay s -> JulianDay s -> [JulianDay s] # enumFromThenTo :: JulianDay s -> JulianDay s -> JulianDay s -> [JulianDay s] # | |
Eq (JulianDay s) Source # | |
Ord (JulianDay s) Source # | |
Defined in SwissEphemeris.Time | |
Show (JulianDay s) Source # | |
type JulianDayUT = JulianDay 'UT Source #
A generic universal time as a Julian Day
type JulianDayTT = JulianDay 'TT Source #
A terrestrial time as a Julian Day
type JulianDayUT1 = JulianDay 'UT1 Source #
A UT1
universal time as a Julian Day
getJulianDay :: JulianDay s -> Double Source #
data SiderealTime Source #
Represents an instant in sidereal time
Instances
Eq SiderealTime Source # | |
Defined in SwissEphemeris.Time (==) :: SiderealTime -> SiderealTime -> Bool # (/=) :: SiderealTime -> SiderealTime -> Bool # | |
Ord SiderealTime Source # | |
Defined in SwissEphemeris.Time compare :: SiderealTime -> SiderealTime -> Ordering # (<) :: SiderealTime -> SiderealTime -> Bool # (<=) :: SiderealTime -> SiderealTime -> Bool # (>) :: SiderealTime -> SiderealTime -> Bool # (>=) :: SiderealTime -> SiderealTime -> Bool # max :: SiderealTime -> SiderealTime -> SiderealTime # min :: SiderealTime -> SiderealTime -> SiderealTime # | |
Show SiderealTime Source # | |
Defined in SwissEphemeris.Time showsPrec :: Int -> SiderealTime -> ShowS # show :: SiderealTime -> String # showList :: [SiderealTime] -> ShowS # |
getSiderealTime :: SiderealTime -> Double Source #
singletons
data SingTimeStandard :: TimeStandard -> Type where Source #
Singletons for pseudo-dependent type programming with time standards.
STT :: SingTimeStandard 'TT | |
SUT1 :: SingTimeStandard 'UT1 | |
SUT :: SingTimeStandard 'UT |
class SingTSI a where Source #
Typeclass to recover the singleton for a given time standard
singTS :: SingTimeStandard a Source #
Instances
SingTSI 'TT Source # | |
Defined in SwissEphemeris.Time singTS :: SingTimeStandard 'TT Source # | |
SingTSI 'UT1 Source # | |
Defined in SwissEphemeris.Time singTS :: SingTimeStandard 'UT1 Source # | |
SingTSI 'UT Source # | |
Defined in SwissEphemeris.Time singTS :: SingTimeStandard 'UT Source # |
Impure conversion typeclasses
class MonadFail m => ToJulianDay m jd from where Source #
Conversion from a temporal value of type from
to a JulianDay
in the TimeStandard
jd
.
It's bound to IO _and_ a containing MonadFail
since
in the general case, we need to interact with
the outside world, and may fail, when consulting
the necessary data.
How can it fail? In short: at least for valid temporal
values constructed via the time
library, pretty much only
if you have an old version of Swiss Ephemeris that's not aware
of a recent leap second.
toJulianDay :: from -> IO (m (JulianDay jd)) Source #
Instances
MonadFail m => ToJulianDay m 'TT UTCTime Source # | |
Defined in SwissEphemeris.Time | |
MonadFail m => ToJulianDay m 'UT1 UTCTime Source # | |
Defined in SwissEphemeris.Time | |
MonadFail m => ToJulianDay m 'UT UTCTime Source # | |
Defined in SwissEphemeris.Time |
class FromJulianDay jd to where Source #
Conversion from a JulianDay
in the TimeStandard
jd
to a temporal value of type to
It's bound to IO since historical data may need to be consulted;
however, as per the underlying library, it cannot fail.
fromJulianDay :: JulianDay jd -> IO to Source #
Instances
FromJulianDay 'TT UTCTime Source # | |
Defined in SwissEphemeris.Time | |
FromJulianDay 'UT1 UTCTime Source # | |
Defined in SwissEphemeris.Time | |
FromJulianDay 'UT UTCTime Source # | |
Defined in SwissEphemeris.Time |
Wrapper for fail-able conversions
data ConversionResult dt Source #
A type that encodes an attempt to convert between temporal types.
Instances
getConversionResult :: ConversionResult dt -> Either String dt Source #
Pure utility functions
mkJulianDay :: SingTimeStandard ts -> Double -> JulianDay ts Source #
Constructor with chaperone: you have to provide a witness to a time standard
to produce a JulianDay
directly from a Double
. This is mostly
intended for internal use, if you find yourself using this function,
you're probably producing an unreliable value: consider using the
ToJulianDay
instance of a reliable temporal type
(like UTCTime
,) before reaching for this function.
coerceUT :: JulianDay 'UT -> JulianDay 'UT1 Source #
Coerce a value obtained directly from UTC (without
consulting historical data) into a UT1 Julian Day.
The difference should be less than 1 second, and
if you've used Haskell's own UTCTime
as the source
it should be negligible for most use cases.
If you want to be precise... you'll have to go into IO
.
julianNoon :: JulianDay ts -> JulianDay ts Source #
Historically, Julian Days started at noon, which is why the point with no fractional part is noon (not midnight).
julianMidnight :: JulianDay ts -> JulianDay ts Source #
The half-day in Julian Days is midnight, so midnight of a given date is halfway through the _previous_ day.
Impure conversion functions
utcToJulianDays :: MonadFail m => UTCTime -> IO (m (JulianDay 'TT, JulianDay 'UT1)) Source #
Convert a UTCTime
into a tuple of Terrestrial Time and UT1 Julian Days;
the underlying C function can return errors if:
- Any of the individual date components are invalid
- The given date has a leap second that it is not aware of (due to either input error or the library not being out of date.)
A legitimately obtained UTCTime
(i.e. not crafted by hand, but by some means
of validated time input/ingestion) is very unlikely to error out in the former
of those scenarios, but there is a chance it may fail in the latter; if you
encounter this, the first step would be to update the Swiss Ephemeris library,
since they bundle an array of leap seconds; otherwise, you can provide a file
called seleapsec.txt
in your configured ephemeris path,
see: 8.3. Handling of leap seconds and the file seleapsec.txt
Pure conversion functions
Lossy conversion of a Day
value
dayFromJulianDay :: JulianDay ts -> Day Source #
Convenience "pure" function that takes an arbitrary
JulianDay
value in any time standard, converts it to noon,
and then to the corresponding 'Day.' Exploits the same circumstantial
truths about time as dayToJulianDay
dayToJulianDay :: Day -> JulianDay ts Source #
Convenience "pure" function that pretends
that a day at noon can be converted to any JulianDay;
in reality, it pretends that a JulianDay in UT stands
in for any other (e.g. in UT1
or TT
) -- this is "good enough"
for a day at noon since, at least in 2021, UT is only off
by less than a second from UT1, and only behind TT by a few
seconds
Fake
(innacurate) conversions of datetime components
gregorianToFakeJulianDayTT :: Integer -> Int -> Int -> Double -> JulianDay 'TT Source #
If you care about accuracy, don't use this function!!! It's merely provided
as a facility for testing or situations where you don't really care about
the truth: the actual Julian Day produced by this function is an absolute,
universal time, we just naughtily repackage it as a terrestrial time here.
If you want a real TerrestrialTime, either convert a valid temporal value
through the toJulianDay
polymorphic function, or use universalToTerrestrial
if you already have a UT1
value.
gregorianFromFakeJulianDayTT :: JulianDay 'TT -> (Integer, Int, Int, Double) Source #
This is a bit of a misnomer: the "fake" value isn't the input,
it's the output: it produces a value as if the input was in UT, thus
running afoul of both leap seconds and delta time. Only useful
in contexts where accuracy is not valued. To get a somewhat more
trustworthy value, and still not have to go into IO
, check out
dayFromJulianDay
, which produces only the Day
part of a date.
Lossy UT conversions of datetime components
gregorianToJulianDayUT :: Integer -> Int -> Int -> Double -> JulianDay 'UT Source #
Given components of a gregorian day (and time,)
produce a JulianDay
in the generic UT
time standard;
the precision of the resulting Julian Day will only be as good
as its input; if obtained by other means than via a UTCTime
,
it's likely to be off by up to a second when compared with a UT1
value.
(on the other hand, it doesn't consult any data so it's not bound to IO
)
This is provided for convenience, but if you have date components, you'd
be better off producing a valid UTCTime
to send to the toJulian
family of functions, via e.g. fromGregorianValid
and makeTimeOfDayValid
gregorianFromJulianDayUT :: JulianDay 'UT -> (Integer, Int, Int, Double) Source #
Given a JulianDay in UT, produce the equivalent Gregorian date's components.
Lossy UT conversions of an UTC
value
utcToJulianDayUT :: UTCTime -> JulianDay 'UT Source #
Given a UTCTime
, produce a JulianDay
purely.
It can only be said to be in UT
, since Haskell's
UTC is an approximation of UT1
, off to up to a second.
If you want precision, use utcToJulianDays
(which returns
both the UT1
and TT
timestamps,) or utcToJulianUT1
.
Keep in mind though, that they're both in IO
and may
return errors.
utcToJulian :: UTCTime -> JulianDay 'UT Source #
See utcToJulianDayUT
-- this function is provided
for convenience in contexts where the ~1s accuracy gain
is not worth the more complicated type signature of
toJulian
, but you'll get a "lesser" JulianDay
that's only as precise as its input.
julianToUTC :: JulianDay 'UT -> UTCTime Source #
See julianDayUTToUTC
-- this function is provided
for convenience in contexts where a slightly innacurate
JulianDay is worth it to stay in a pure context, otherwise,
see fromJulian
.
Delta Time
addDeltaTime :: JulianDay 'UT1 -> Double -> JulianDay 'TT Source #
Add time to catch up UT to TT; doesn't make sense for other time standards.
subtractDeltaTime :: JulianDay 'TT -> Double -> JulianDay 'UT1 Source #
Subtract time to 'slow down' TT to UT; doesn't make sense for other time standards.
unsafeDeltaTime :: JulianDay 'UT1 -> IO Double Source #
Somewhat naïve delta time calculation: if no ephemeris
mode has been selected, it will use the default tidal
acceleration value as per the DE431 JPL ephemeris,
otherwise, it will use whatever ephemeris is currently set.
It's considered unsafe since switching ephemeris modes will
result in an incongruent delta time. See safeDeltaTime
safeDeltaTime :: MonadFail m => EphemerisOption -> JulianDay 'UT1 -> IO (m Double) Source #
Same as deltaTime
, but fails if the given EphemerisOption
doesn't agree with the current ephemeris mode.
deltaTimeSE :: MonadFail m => JulianDay 'UT1 -> IO (m Double) Source #
Try to produce a delta time for the SwissEphemeris
ephemeris mode,
will fail if the current mode isn't set to SwissEphemeris
.
universalToTerrestrial :: JulianDay 'UT1 -> IO (JulianDay 'TT) Source #
Convert between an instant in UT1 to TT, as a JulianDay
, may
produce inaccurate results if an ephemeris mode isn't set explicitly.
universalToTerrestrialSafe :: MonadFail m => EphemerisOption -> JulianDay 'UT1 -> IO (m (JulianDay 'TT)) Source #
Convert between an instant in UT1 to TT, as a JulianDay
, using an explicit
ephemeris mode; fails if not currently working in the expected mode.
universalToTerrestrialSE :: MonadFail m => JulianDay 'UT1 -> IO (m (JulianDay 'TT)) Source #
universaltoTerrestrialSafe
, set to SwissEphemeris
Sidereal time
julianToSiderealSimple :: JulianDay 'UT1 -> IO SiderealTime Source #
Given JulianDay
, get SiderealTime
. May consult ephemerides data, hence it being in IO,
will have to calculate obliquity at the given julian time, so it'll be slightly slower than
calculateSiderealTime
.
julianToSidereal :: JulianDay 'UT1 -> ObliquityInformation -> IO SiderealTime Source #
Given a JulianDay
and ObliquityInformation
, calculate the equivalent SiderealTime
.
prefer it over calculateSiderealTimeSimple
if you already obtained ObliquityInformation
for another calculation.