Python Program for Synodic months

I am trying to make an astronomical program for celestial bodies in our solar system (and beyond!).

However I want it to show how many Synodic months since New moon. Example: Since last 2022 there happened 0.50794789457 Synodic months.

So is there any API/library to get such data (it's not a necessity that a module would be required)? .

• Do you mean en.wikipedia.org/wiki/Lunar_month#Synodic_month = 29 d 12 h 44 min and 2.9 s? It's a number that will always be less than about one, correct? Isn't the time between successive new Moons on average one synodic month?
– uhoh
Jan 6 at 10:30
• Yes, @uhoh. I am talking about the same Synodic month. Thanks.
– user47732
Jan 6 at 10:48
• @uhoh Yes. However I did not find such Library while Googling
– user47732
Jan 6 at 11:16
• Mostly the gravity of the sun. If you are serious about any kind of astronomical calculation you need to obtain a copy of "Astronomical Algorithms" by Jean Meeus. They may not be the be-all-and-end-all of astronomical computation. But they are everyone's starting point. Lunar theory, in particular, is complex (far more so than the motion of the planets.) The Moon's orbit is very significantly non-keplarian. Jan 6 at 18:08
• The previously mentioned Astronomical Algorithms book has an algorithm for computing the times of the phases of the moon. You could use that to compute the time between two new moons, and then compute how far along you are in the current Synodic period. An implementation of the moon phase algorithm is here: celestialprogramming.com/moonphases.html . Jan 6 at 20:55

The Python Skyfield API can do this. What follows is a brief python program that will calculate the fraction of time that has passed between the last and the next New Moon.


import pytz
from bisect import bisect
from datetime import datetime, tzinfo, timedelta

from skyfield import api, almanac

def main():

time_zone = pytz.timezone("UTC")
time = time_zone.localize(datetime.utcnow())

start_ts, end_ts = timescale.utc((time - timedelta(days=30),
time + timedelta(days=30)))

phase_times, phase_indices = almanac.find_discrete(
start_ts, end_ts, almanac.moon_phases(ephemeris))

new_moons = [phase_time for phase_time, phase_index
in zip(phase_times.astimezone(time_zone), phase_indices)
if phase_index == 0]

index = bisect(new_moons, time)
prev_moon, next_moon = new_moons[index-1: index + 1]

lunation_fraction = (time - prev_moon) / (next_moon - prev_moon)

print(f"Lunation fraction: {lunation_fraction}")

if __name__ == '__main__':
main()

• Incredible script! +n! I've never heard of pytz or bisect but wow how useful. For those new to Python, if you click "install" at anaconda.com/products/distribution you'll get a nice selection of scientific libraries, but you'll have to install Skyfield separately.
– uhoh
Jan 8 at 3:41
• Thanks a lot!. However what does Pytz and bisect do?
– user47732
Jan 8 at 9:20
• @ScienceAJ I use the pytz module to turn an ordinary datetime object into one that contains time zone information that skyfield's timescale objects are happy with. bisect takes a sorted list, and a value and returns the index a value would have if you inserted it in its sorted place; I used it in this case because in the sixty-day timespan the code searches for New Moons, there's a small chance of finding three. Jan 8 at 10:57
• @notovny Thanks a lot!. I shall implement the code in my program
– user47732
Jan 8 at 11:44