Skip to content

Instantly share code, notes, and snippets.



Created Dec 2, 2017
What would you like to do?
Calculate lunar (moon) phase
import math, datetime
class Moon:
# average length of a lunar cycle (Wikipedia)
def lunar_month_days(): return 29.530588853
def day_of_cycle(test_date=None):
if test_date is None:
test_date =
# the closer the base date (a known new-moon) is to the dates we're interested with, the better
base_new_moon = datetime.datetime(2017, 8, 21, 18, 30, 0)
days = (test_date - base_new_moon).total_seconds() / (24.0 * 60.0 * 60.0)
return days % Moon.lunar_month_days()
def illumination(day_of_cycle):
# translate daily position as an angle between 0 and 360°
angle = 2 * math.pi * day_of_cycle / Moon.lunar_month_days()
# move and resize cosine curve so it represents waxing and waning as value from 0 to 1
return (1 + (-math.cos(angle))) / 2.0
# This isn't strictly accurate because new moon and full moon do not actually last for 1/8th of a cycle
def phase(day_of_cycle):
proportion = float(day_of_cycle) / Moon.lunar_month_days()
# add 0.5 to put value in 'middle' of phase
# - which might put it past end of array, so use bitwise AND to lop it off
index = int(proportion * 8.0 + 0.5) & 7
return {
0: "New Moon",
1: "Waxing Crescent",
2: "First Quarter",
3: "Waxing Gibbous",
4: "Full Moon",
5: "Waning Gibbous",
6: "Last Quarter",
7: "Waning Crescent"

This comment has been minimized.

Copy link
Owner Author

@mrrrk mrrrk commented Dec 2, 2017

This uses the average synodic cycle time (time between new moons) along with the time of a recent new moon and assumes you're happy with the approximate result being out by a few hours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.