Moon / Lunar Phase Algorithm

43,338

Solution 1

I ported some code to Python for this a while back. I was going to just link to it, but it turns out that it fell off the web in the meantime, so I had to go dust it off and upload it again. See moon.py which is derived from John Walker's moontool.

I can't find a reference for this for what time spans it's accurate for either, but seems like the authors were pretty rigorous. Which means yes, it does use trig, but I can't imagine what the heck you would be using this for that would make it computationally prohibitive. Python function call overhead is probably more than the cost of the trig operations. Computers are pretty fast at computing.

The algorithms used in the code are drawn from the following sources:

Meeus, Jean. Astronomical Algorithms. Richmond: Willmann-Bell, 1991. ISBN 0-943396-35-2.

A must-have; if you only buy one book, make sure it's this one. Algorithms are presented mathematically, not as computer programs, but source code implementing many of the algorithms in the book can be ordered separately from the publisher in either QuickBasic, Turbo Pascal, or C. Meeus provides many worked examples of calculations which are essential to debugging your code, and frequently presents several algorithms with different tradeoffs among accuracy, speed, complexity, and long-term (century and millennia) validity.

Duffett-Smith, Peter. Practical Astronomy With Your Calculator. 3rd ed. Cambridge: Cambridge University Press, 1981. ISBN 0-521-28411-2.

Despite the word Calculator in the title; this is a valuable reference if you're interested in developing software which calculates planetary positions, orbits, eclipses, and the like. More background information is given than in Meeus, which helps those not already versed in astronomy learn the often-confusing terminology. The algorithms given are simpler and less accurate than those provided by Meeus, but are suitable for most practical work.

Solution 2

If you're like me, you try to be a careful programmer. So it makes you nervous when you see random code scattered across the internet that purports to solve a complex astronomical problem, but doesn't explain why the solution is correct.

You believe that there must be authoritative sources such as books which contain careful, and complete, solutions. For instance:

Meeus, Jean. Astronomical Algorithms. Richmond: Willmann-Bell, 1991. ISBN 0-943396-35-2.

Duffett-Smith, Peter. Practical Astronomy With Your Calculator. 3rd ed. Cambridge: Cambridge University Press, 1981. ISBN 0-521-28411-2.

You place your trust in widely-used, well-tested, open source libraries which can have their errors corrected (unlike static web pages). Here then, is a Python solution to your question based on the PyEphem library, using the Phases of the Moon interface.

#!/usr/bin/python
import datetime
import ephem

def get_phase_on_day(year,month,day):
  """Returns a floating-point number from 0-1. where 0=new, 0.5=full, 1=new"""
  #Ephem stores its date numbers as floating points, which the following uses
  #to conveniently extract the percent time between one new moon and the next
  #This corresponds (somewhat roughly) to the phase of the moon.

  #Use Year, Month, Day as arguments
  date=ephem.Date(datetime.date(year,month,day))

  nnm = ephem.next_new_moon    (date)
  pnm = ephem.previous_new_moon(date)

  lunation=(date-pnm)/(nnm-pnm)

  #Note that there is a ephem.Moon().phase() command, but this returns the
  #percentage of the moon which is illuminated. This is not really what we want.

  return lunation

def get_moons_in_year(year):
  """Returns a list of the full and new moons in a year. The list contains tuples
of either the form (DATE,'full') or the form (DATE,'new')"""
  moons=[]

  date=ephem.Date(datetime.date(year,01,01))
  while date.datetime().year==year:
    date=ephem.next_full_moon(date)
    moons.append( (date,'full') )

  date=ephem.Date(datetime.date(year,01,01))
  while date.datetime().year==year:
    date=ephem.next_new_moon(date)
    moons.append( (date,'new') )

  #Note that previous_first_quarter_moon() and previous_last_quarter_moon()
  #are also methods

  moons.sort(key=lambda x: x[0])

  return moons

print get_phase_on_day(2013,1,1)

print get_moons_in_year(2013)

This returns

0.632652265318

[(2013/1/11 19:43:37, 'new'), (2013/1/27 04:38:22, 'full'), (2013/2/10 07:20:06, 'new'), (2013/2/25 20:26:03, 'full'), (2013/3/11 19:51:00, 'new'), (2013/3/27 09:27:18, 'full'), (2013/4/10 09:35:17, 'new'), (2013/4/25 19:57:06, 'full'), (2013/5/10 00:28:22, 'new'), (2013/5/25 04:24:55, 'full'), (2013/6/8 15:56:19, 'new'), (2013/6/23 11:32:15, 'full'), (2013/7/8 07:14:16, 'new'), (2013/7/22 18:15:31, 'full'), (2013/8/6 21:50:40, 'new'), (2013/8/21 01:44:35, 'full'), (2013/9/5 11:36:07, 'new'), (2013/9/19 11:12:49, 'full'), (2013/10/5 00:34:31, 'new'), (2013/10/18 23:37:39, 'full'), (2013/11/3 12:49:57, 'new'), (2013/11/17 15:15:44, 'full'), (2013/12/3 00:22:22, 'new'), (2013/12/17 09:28:05, 'full'), (2014/1/1 11:14:10, 'new'), (2014/1/16 04:52:10, 'full')]

Solution 3

I think you searched on wrong google:

Solution 4

Also, pyephem — scientific-grade astronomy routines [PyPI], which is a Python package but has the computational guts in C, and that does say

Precision < 0.05" from -1369 to +2950.
Uses table lookup techniques to limit calls to trigonometric functions.

Solution 5

Pyephem by default uses coordinated universal (UTC) time. I wanted a program that would generate a list of full moons that would be accurate in the pacific time zone. The code below will calculate the full moons for a given year and then adjust that using the ephem.localtime() method to calibrate to the desired time zone. It also appears to properly account for daylight savings time as well. Thank you to Richard, this code is similar to what he had written.

#!/usr/bin/python
import datetime
import ephem
import os
import time

# Set time zone to pacific
os.environ['TZ'] = 'US/Pacific'
time.tzset()

print("Time zone calibrated to", os.environ['TZ'])

def get_full_moons_in_year(year):
    """
    Generate a list of full moons for a given year calibrated to the local time zone
    :param year: year to determine the list of full moons
    :return: list of dates as strings in the format YYYY-mm-dd
    """
    moons = []

    date = ephem.Date(datetime.date(year - 1, 12, 31))
    end_date = ephem.Date(datetime.date(year + 1, 1, 1))

    while date <= end_date:
        date = ephem.next_full_moon(date)

        # Convert the moon dates to the local time zone, add to list if moon date still falls in desired year
        local_date = ephem.localtime(date)
        if local_date.year == year:
            # Append the date as a string to the list for easier comparison later
            moons.append(local_date.strftime("%Y-%m-%d"))

    return moons

moons = get_full_moons_in_year(2015)
print(moons)

The code above will return:

Time zone calibrated to US/Pacific
['2015-01-04', '2015-02-03', '2015-03-05', '2015-04-04', '2015-05-03', '2015-06-02', '2015-07-01', '2015-07-31', '2015-08-29', '2015-09-27', '2015-10-27', '2015-11-25', '2015-12-25']
Share:
43,338
Scott Bailey
Author by

Scott Bailey

Updated on November 07, 2021

Comments

  • Scott Bailey
    Scott Bailey over 2 years

    Does anyone know an algorithm to either calculate the moon phase or age on a given date or find the dates for new/full moons in a given year?

    Googling tells me the answer is in some Astronomy book, but I don't really want to buy a whole book when I only need a single page.

    Update:

    I should have qualified my statement about googling a little better. I did find solutions that only worked over some subset of time (like the 1900's); and the trig based solutions that would be more computationally expensive than I'd like.

    S Lott in his Python book has several algorithms for calculating Easter on a given year, most are less than ten lines of code and some work for all days in the Gregorian calendar. Finding the full moon in March is a key piece of finding Easter so I figured there should be an algorithm that doesn't require trig and works for all dates in the Gregorian calendar.

    • nategoose
      nategoose about 14 years
      The same problem was on here bunches of times hidden in the problem of determining the date of Easter, Lint, Good Friday, and/or Passover.
    • Matthew Slattery
      Matthew Slattery about 14 years
      An accurate astronomical model is unavoidably complicated; the calculation for the full moon from which the date of Easter is derived (see en.wikipedia.org/wiki/Paschal_full_moon) uses a simplified model.
    • heltonbiker
      heltonbiker almost 13 years
      A great use for the moon.py cited in the chosen answer is to write a GUI app (I'd go with GTK) to actually show the moon phase with a picture of the moon (actually I am trying to do this, which became considerably easier with this question and its answers...)
  • SyntaxT3rr0r
    SyntaxT3rr0r about 14 years
    @Jack: WOW! +1... The memory. I couldn't tell where I knew that name from: "Ben Daglish". He was one of the best (I said one ;) composer of 8-bit music on the C64 back in the days. Amazing to find a link to him for something completely unrelated (I had to search trough his website until I found the C64 links to remember where I knew that name from).
  • Mark Ransom
    Mark Ransom about 14 years
    "Computers are pretty fast at computing." - I love it! I may have to quote that one.
  • Scott Bailey
    Scott Bailey about 14 years
    Hey that's awesome. Thanks for the link keturn.
  • Scott Bailey
    Scott Bailey about 14 years
    Well I'm working on a calendaring system for Oracle and Postgres. I want to be able to find dates based on moon phase. And that could mean performing this calculation over a bunch of dates. And computationally expensive = visit from the DBA. :)
  • Scott Bailey
    Scott Bailey about 14 years
    I can read pretty much anything besides Perl. LOL. But Chronos XP seems to be more of an astrology app than astronomy.
  • Jacobs Data Solutions
    Jacobs Data Solutions about 14 years
    Look in the file called LunarPhase.cs. That class basically does what it says it does. It's one of the better implementations I've found, which is unfortunate because it's still complex. If you don't want to download the whole source code just do a search for that file name on Google Code.
  • Richard
    Richard over 10 years
    -1 One of the nice features of StackOverflow is that people can come here for answers, rather than links to answers. Additionally, your top link is broken, your second link has no citations to convince a careful programmer that the algorithms listed therein are correct, your third link has citations, but the code has all been altered from the originals. The fourth link's similarly problematic.
  • Richard
    Richard over 10 years
    The important bit here, of course, is that keturn's code and John Walker's code both contain references to authoritative sources against which their code can be checked. These sources are listed here.
  • keturn
    keturn over 10 years
    Richard, I don't mind citing the sources of the algorithms, but if you paste the reviews of those works here, do make it clear that those words are John Walker's, not yours or mine.
  • jkj
    jkj over 10 years
    Whose Google? The hits are REALLY different for different people.
  • Matt Joiner
    Matt Joiner almost 9 years
    moon.py has abysmal Python3 support.
  • keturn
    keturn almost 9 years
    "abysmal", really? How bad could it be, it's basically all arithmetic. But given that it was written in 2001, we're lucky it even has Python2 support.
  • dhasenan
    dhasenan over 7 years
    One of those links is broken and the other three only contain crude approximations. The sci.astro FAQ entry will drift by a day every 2500 years, for instance (it's the same algorithm the Kuwaiti Islamic calendar uses). Daglish's is likely the best, but it only handles the Gregorian day, not the time of day. Maybe enough for some people but not good enough for calendar calculations.
  • vicemagui
    vicemagui about 6 years
    Is ephem.Moon() based on Meeus' book?
  • Pryftan
    Pryftan over 5 years
    Computers are pretty fast at computing. And making errors. So fast in fact, that it's just behind humans... And yes there is of course certain irony here.
  • Pryftan
    Pryftan over 5 years
    @klemens Lol thanks. I won't go that low (if I were I would have to do it for Windows and Microsoft in general and - well that's something I long outgrew and much for the better too) but it's a much needed laugh.