Skip to content

Instantly share code, notes, and snippets.

@mrrrk mrrrk/
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 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.