Skip to content


Folders and files

Last commit message
Last commit date

Latest commit



53 Commits

Repository files navigation


Utilities for processing pre-calculated Ephemeris data (see swiss-ephemeris.) Given an interval and a set of queries, it will efficiently traverse precalculated ephemeris in daily increments, or do faster interpolation of regular ephemeris data for certain queries like moon events and eclipses, and return a Seq of Events. One can then choose to inspect the extracted events to calculate when exactly they happen -- this is recommended as an extra step, only on demand, since the exactitude calculation needs to do some numerical interpolation, vs. simple fast daily perusal.

Since the main use case is to get a sequence of events and presumably examine them, Lenses and Traversals are provided in the Almanac.Optics module to help in the otherwise tiresome deep pattern matching that could ensue. Note that there's no dependency on any actual lens-like library, so you'll have to bring your own to actually do "lens stuff" with the provided optics. To illustrate how the provided optics can be brought to life with a lens library, the tests use microlens. I have only added the most obvious lenses and traversals (no Prisms, since I don't foresee any "construction" happening and thus Traversals do the job nicely and that obviates the need for a dependency on profunctors.)

To use this library, you'll have to get ahold of ephemeris files; you'll also need precalculated ephemeris, which are not officially distributed by, but I've put a couple of centuries of precalculated ephemeris in this project's test/ephe/ directory that you can just copy (they're tiny files.) Functions to generate precalculated ephemeris are also provided by swiss-ephemeris, here's some example usage from laboratorium, which can be run as a CLI.

With those in place, here's some example usage:

import Almanac
import Almanac.Optics
import Control.Applicative 
import Data.Time 
-- You can use any `lens`-compatible library here.
import Lens.Micro
import System.Directory (makeAbsolute)

lunarPhasesAndEclipses :: IO [(String, UTCTime)]
lunarPhasesAndEclipses = do
  let nov2021 = UTCTime (fromGregorian 2021 11 1) 0
      dec2021 = UTCTime (fromGregorian 2021 12 1) 0
      q = mundane
            (Interval nov2021 dec2021)
            [QueryLunarPhase, QueryEclipse] 

  -- Get events, then calculate the exact moment they happen
  exactEvents <- runQuery q >>= eventsWithExactitude

  -- For each event, get the phase name (if it's a lunar phase,)
  -- or the eclipse type (only when it's a lunar eclipse,) as well
  -- as the moment of exactitude. Using optics to deal with the
  -- annoying pattern matching that would ensue.
  let digest = (summarize <$> exactEvents) ^.. traversed . _Just
      summarize evt = 
        let phaseName = show <$> evt ^? eventL._LunarPhaseInfo.lunarPhaseNameL
            eclType   = show <$> evt ^? eventL._EclipseInfo._LunarEclipseType
            exactAt   = evt ^? exactitudeMomentsL._head
        in liftA2 (,) (phaseName <|> eclType) exactAt
  pure digest  

-- NOTE: as mentioned in @swiss-ephemeris@, in a production application
-- you're better off setting environment variables with the location of the
-- ephemeris directory --  it's unnecessary and potentially thread-unsafe
-- in the presence of laziness to set them programmatically.
main :: IO ()
  fullEphePath <- makeAbsolute "./my-ephe"
  -- location of ephe data
  setEphemeridesPath fullEphePath
  -- location of precalculated ephe
  setEphe4Path fullEphePath

  lunarPhasesAndEclipses >>= print
  Would print something like 

See the test directory for more examples: planetary ingresses, changes of direction, transits and queries relevant to a "natal" reference event are provided.

This library was incubated in my laboratorium, with some further refinements.


Query various events in ephemeris data: lunar phases, eclipses, transits, zodiac ingresses, planetary changes of direction (retrograde/direct)







No releases published


No packages published