views:

725

answers:

7

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.

+7  A: 

I think you searched on wrong google:

Jack
@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).
Webinator
A: 

A quick google revealed this.

nosklo
A: 

I found a Python solution here (untested, no idea if it works; you'd have to verify this yourself).

def moon_phase(month, day, year):
    ages = [18, 0, 11, 22, 3, 14, 25, 6, 17, 28, 9, 20, 1, 12, 23, 4, 15, 26, 7]
    offsets = [-1, 1, 0, 1, 2, 3, 4, 5, 7, 7, 9, 9]
    description = ["new (totally dark)",
      "waxing crescent (increasing to full)",
      "in its first quarter (increasing to full)",
      "waxing gibbous (increasing to full)",
      "full (full light)",
      "waning gibbous (decreasing from full)",
      "in its last quarter (decreasing from full)",
      "waning crescent (decreasing from full)"]
    months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]

    if day == 31:
        day = 1
    days_into_phase = ((ages[(year + 1) % 19] + ((day + offsets[month-1]) % 30) + (year < 1900)) % 30)
    index = int((days_into_phase + 2) * 16/59.0)
    if index > 7:
        index = 7
    status = description[index]

    # light should be 100% 15 days into phase
    light = int(2 * days_into_phase * 100/29)
    if light > 100:
        light = abs(light - 200);
    date = "%d%s%d" % (day, months[month-1], year)

    return date, status, light

month = 3
day = 26
year = 2010  # use yyyy format

date, status, light = moon_phase(month, day, year)
print "moon phase on %s is %s, light = %d%s" % (date, status, light, '%')
ChristopheD
Yeah, this looks like what I want. Unfortunately the author didn't state over which range it was valid. I'll have to test it I guess.
Scott Bailey
+2  A: 

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.

keturn
"Computers are pretty fast at computing." - I love it! I may have to quote that one.
Mark Ransom
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
+2  A: 

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.

keturn
Hey that's awesome. Thanks for the link keturn.
Scott Bailey
+1  A: 

I know that you're looking for Python but if you can understand C# there's an open source project out there called Chronos XP which does this very well.

Repo Man
I can read pretty much anything besides Perl. LOL. But Chronos XP seems to be more of an astrology app than astronomy.
Scott Bailey
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.
Repo Man
+1  A: 

If you don't need high accuracy, you can always (ab)use a lunar (or lunisolar) calendar class (e.g., HijriCalendar or ChineseLunisolarCalendar in Microsoft .NET) to calculate the (approximate) moon phase of any date, as the calendar's "day-of-month" property, being a lunar (or lunisolar) calendar day, always corresponds to the moon phase (e.g., day 1 is the new moon, day 15 is the full moon, etc.)

Anonymous