Matt Hackmann

Calculate Moon Rise and Set in PHP

Posted On

If you've been paying attention to my Twitter account, you've probably seen a reference or two about how I've been calculating the positions of astronomical bodies. Well, today I release part of that to you.

Download: Source

The above code is pretty much a direct port of Keith Burnett's implementation here. Outside of porting, my only changes were some code clean up and having the timezone be calculated automatically for the longitude given. This means that you will always get back a time stamp that is local to the point of origin.

How to Use

Moon::calculateMoonTimes(month, day, year, latitude, longitude);

Code: php
  1. <?php
  2. date_default_timezone_set('America/Chicago');
  3. include('moon.php');
  4. print_r(Moon::calculateMoonTimes(6, 28, 2011, 36.754478, -96.110291));
  5. ?>

The above code will output the following result:

Code: output
  1. stdClass Object ( [moonrise] =&gt; 1309246800 [moonset] =&gt; 1309300560 )

Moonrise and moonset, as stated previously, are a Unix time stamp local to the latitude and longitude given.



Would it be possible do you to change the code to use DateTime, so 2038 -problem would be avoided?


Useful code, thanks for sharing this. But it doesn't quite work for me.

You seem to calculate the rise and set times in UTC, but then pass this to mktime. mktime expects a local time. The result is a timestamp which is out by the local offset to UTC, and attempting to format it with date produces an incorrect result.

For example, moon rise at my location today is 11:45 UTC. My timezone is +0100 (UK, daylight saving). Passing 11:45 to mktime produces 1340707500. This then gives

echo date("D M j Y G:i T", 1340707500);

prints Tue, Jun 26 2012 11:45 BST

when actually it is 11:45 UTC, 12:45 BST. The correct timestamp would be 1340711100. To get this you need to pass your calculated times to gmmktime.

I'm also not clear what the timezone calculation is supposed to achieve, but in any event it is too crude to be useful. In reality timezone boundaries do not accurately follow 15 degree parallels, and not all timezone offsets are integer numbers of hours. Something like

$offset = date_offset_get(new DateTime);

might be more reliable.


Very nice site!

Shad Keene
Shad Keene

Do you have an illumination percentage option available?



Following the hints in @tipichris comments I fixed the time offset problem by removing the lines $timezone = (int)($lon / 15); and $date -= $timezone / 24; from the class function calculateMoonTimes. The timezone should be set in the script calling the class via date_default_timezone_set()Now it works great!


What is the output if there was no moonrise event that day?


When moonrise will be tomorrow, this function always returns 00:00 of tomorrow instead of the correct time of moonrise. Changing line

while ($hour < 25 && (false == $set || false == $rise))


while ($hour <= 25 && (false == $set || false == $rise))

can solve this problem because duration between two successive moonrises is about 24 hours and 50 minutes.


Also this doesn't work for places with extreme latitude (poles for example, in this case rise ant set computed by this script will be equal).


As others have mentioned, the timezone calculation is flawed. The timezone offset value must be passed into the function, and must be in minutes, to account for locations that do not use a 1-hour boundary for the timezone. Then replace the line:

$timezone = (int)($lon / 15);


$timezone = $tzoffset / 60;

Where tzOffset is the timezone offset in minutes.

Leave A Comment