Join 3,424 readers in helping fund MetaFilter (Hide)


give me the moon
January 28, 2009 11:32 AM   Subscribe

I copied a short javascript to display the moonphase from a webpage, and made a few minor tweaks, and it appears to have a few problems. For instance, the moon phase occasionally shows up as "undefined"; I don't know enough about javascript to fix it, please help.

So, I wanted to display the moon phase as part of my custom portal. You can see the page here (I display it in a sidebar in Firefox with a userstyle defined in the browser, if you're wondering why it's so oddly laid out and ugly).

I did make a few tweaks to this, but mostly just changing the display text (abbreviated day of week, etc.). I also managed to figure out how to round off the "days old" display (because "the moon is 1.6999999999999993 days old" looks ridiculous). Here is the code as I have it:
function getMoonAge(year, month, day) {	
		d = Math.floor(year/20)
		r = year-(d*20) //r is the remainder of (year/20)
		while (r>9)
		{	r = r-19	}
		r = r*11
		while (r>29)
		{	r = r-30	}
		if (month<3)
		{	month = month+2		}
		r = r+month+day
		if (year<100)
		{	r = r-4		}
		else
		{	r = r-8.3	}
		while(r>29)
		{	r = r-30	}
		while(r<0)
		{	r = r+30	}
		return r
	}
		
function getMoonPhase(moonAge) {	
		if (moonAge<1) return "New"
		if (moonAge<6) return "Waxing Crescent"
		if (moonAge<9) return "First Quarter"
		if (moonAge<13) return "Waxing Gibbous"
		if (moonAge<16) return "Full"
		if (moonAge<20) return "Waning Gibbous"
		if (moonAge<23) return "Last Quarter"
		if (moonAge<25) return "Waning Crescent"
		if (moonAge<29) return "Waning Crescent"
		if (moonAge<1) return "New"
	}

function getMoonPhaseImg(moonAge) {	
		if (moonAge<1) return "New"
		if (moonAge<5) return "Waxing_Crescent"
		if (moonAge<9) return "First_Quarter"
		if (moonAge<13) return "Waxing_Gibbous"
		if (moonAge<16) return "Full"
		if (moonAge<20) return "Waning_Gibbous"
		if (moonAge<22) return "Last_Quarter"
		if (moonAge<25) return "Waning_Crescent"
		if (moonAge<29) return "Waning_Crescent"
		if (moonAge<30) return "New"
}

// name arrays
	monthNames = new Array(13)
	monthNames[1]  = "Jan"
	monthNames[2]  = "Feb"
	monthNames[3]  = "March"
	monthNames[4]  = "April"
	monthNames[5]  = "May"
	monthNames[6]  = "June"
	monthNames[7]  = "July"
	monthNames[8]  = "August"
	monthNames[9]  = "Sept"
	monthNames[10] = "Oct"
	monthNames[11] = "Nov"
	monthNames[12] = "Dec"
			 
	dayNames = new Array(8)
	dayNames[1]  = "Sun"
	dayNames[2]  = "Mon"
	dayNames[3]  = "Tues"
	dayNames[4]  = "Wed"
	dayNames[5]  = "Thurs"
	dayNames[6]  = "Fri"
	dayNames[7]  = "Sat"
		 
function getLongDate(dateObj) {	
	theDay = dayNames[dateObj.getDay()+1]
	theMonth = monthNames[dateObj.getMonth()+1]
	theDate = dateObj.getDate()
	return ""+theDay+", "+theMonth+" "+theDate
}
		
function getNextFull(moonAge) {	
	currMilSecs = (new Date()).getTime()
	daysToGo = 15 - moonAge
	while(daysToGo<2)
	{	daysToGo = daysToGo+29 	}
	milSecsToGo = daysToGo*24*60*60*1000
	nextMoonTime = currMilSecs+milSecsToGo
	nextMoonDate = new Date(nextMoonTime)
	return nextMoonDate
}
		
function getNextNew(moonAge) {	
	currMilSecs = (new Date()).getTime()
	daysToGo = 29 - moonAge
	while(daysToGo<2) 	
	{ 	daysToGo = daysToGo+29 	}
	milSecsToGo = daysToGo*24*60*60*1000
	nextMoonTime = currMilSecs+milSecsToGo
	nextMoonDate = new Date(nextMoonTime)
	return nextMoonDate
}
The main problem: today, this is what I am seeing:
Wed, Jan 28
The moon is 30 days old
undefined [should be moon phase]
New moon: Wed, Feb 25
Full moon: Wed, Feb 11

Yeah, moon phases generally aren't more than 29 days old, and I don't know why it's doing that. The new moon was the 26th, so it should have gone to the next phase by now. The page I copied it from says:

Today is Wednesday, Jan 28.
The moon is
Waxing Cresent. [sic]
The current lunar month is 1.6999999999999993 days old.
Next new moon: Tuesday, Feb 24.
Next full moon: Tuesday, Feb 10.

So I guess I must have messed it up somewhere, but I don't know how. Probably part of the rounding-off thing I tried, seeing those slightly different dates for the next new and full moons.

It's minor (I know from experience it'll be correct by Friday) and I'm the only one who sees it, but it's still annoying. How do I fix it?
posted by timepiece to Computers & Internet (13 answers total)
 
getMoonPhase returns undefined if it's passed a moonAge >= 29. getMoonPhaseImg returns undefined if it's passed something >=30. (This looks buggy of itself.) Your rounding change allowed moonAge to hit 30, hence the problem.
posted by Zed at 11:38 AM on January 28, 2009


Hmmm. OK, is there a way to get it to always round down? Or, I guess, just not display anything after a decimal? (yes, I really know nothing about JS).

I can't even figure out where the rounding stuff I added is at this point - though of course I could compare to the original version.
posted by timepiece at 12:16 PM on January 28, 2009


Add this line to the end of the getMoonPhase function (right before the closing curly brace):

else return "New"

And add this line to the end of the getMoonPhaseImg function:

else return "New"

That should make both of those functions return "New" when passed anything 30 or higher as an argument.
posted by letourneau at 1:00 PM on January 28, 2009 [1 favorite]


Actually, your rounding isn't the issue. Your code doesn't even differ from the source other than in whitespace. The difference is in the javascript that runs that code. The original says:

theMoonPhase = getMoonPhase(theMoonAge);
theMoonPhase = getMoonPhaseImg(theMoonAge);


clobbering its original theMoonPhase value with getMoonPhaseImg's value, so it's never trying to use getMoonPhase's return value, which is undefined for it, too. You're actually trying to use getMoonPhase's return value, which is why you're seeing the problem. Change the last line of getMoonPhase to "<>
Math.floor always rounds downward, as opposed to Math.round, which rounds to nearest.
posted by Zed at 1:10 PM on January 28, 2009


Bah. Change from "< 1" to "< 30".
posted by Zed at 1:11 PM on January 28, 2009



Math.floor always rounds downward, as opposed to Math.round, which rounds to nearest.

In this case if the range of expected values is 0-29 (is it? I haven't read closely, but per Zed it isn't clear) You could also use a modulus operator ("%").

Modulus

operand1 % operand2

The modulus operator returns the remainder of a division operation. The division is performed, but only the remainder is kept. The sign of the result is the sign of the quotient. The modulus operator in JavaScript is also different from the one in other programming languages. It operates not only on integers but also on floating point numbers. You should be aware that the modulus operator occasionally returns inaccurate results. The modulus’ inaccuracies stem from the division operation which sometimes returns inaccurate results:

12 % 5 // evaluates to 2
12.3 % 4 // evaluates to 0.3000000000000007 (inaccuracy)
0 % 99 // evaluates to 0
12.75 % 4.25 // evaluates to 0
11.1 % 2.22 // evaluates to 2.219999999999999

posted by juv3nal at 4:40 PM on January 28, 2009


If the algorithm works, the expected range of values is 0 to about 29.53. The algorithm should take care of keeping it in that range, so no modulo should be necessary -- timepiece just didn't want to look at a long string of decimals.
posted by Zed at 10:00 AM on January 29, 2009


If the algorithm works, the expected range of values is 0 to about 29.53. The algorithm should take care of keeping it in that range, so no modulo should be necessary -- timepiece just didn't want to look at a long string of decimals.

My bad, I still haven't taken a close look at the code, but I took it from this (emph mine):

Yeah, moon phases generally aren't more than 29 days old, and I don't know why it's doing that. The new moon was the 26th, so it should have gone to the next phase by now.

..that there's some value that is >29 which should be reset to somewhere near 0. a floor would just set it to 29, wouldn't it?
posted by juv3nal at 10:28 AM on January 29, 2009


Well, thank you everybody. I'm going to assume it's fixed, though it had reset itself to the next phase by the time I got to make any changes. But Zed's (nice, simple [thank you]) suggestions certainly look like they would fix the problem(s).

Once I'm at home (and am allowed to change the date on the computer) I'll reset to yesterday and see what happens.
posted by timepiece at 11:43 AM on January 29, 2009


The rounding - I just realized, the script I provided was in my HEAD, but there was additional script in the body of the page. That was where the rounding part was. I did change it to math.floor there.
posted by timepiece at 11:49 AM on January 29, 2009


OK, in case anyone is still checking this ... any idea why yesterday the moon was 6 days old, but today, it's 5 days old? It should be at least 7 days old, so even Math.floor or rounding shouldn't have taken it all the way back to 5.

I won't post the entire fixed script, but it's still at http://timepiece.nfshost.com/files/customsidebar.html.
posted by timepiece at 12:25 PM on February 3, 2009


Your function prototype is:
function getMoonAge(year, month, day)

but in your body, you call:
theMoonAge = getMoonAge(theYear, theDay, theMonth)

Not sure if that is causing your problem, but it can't help.
posted by juv3nal at 9:52 PM on February 3, 2009


That does seem to be it. Thanks.
posted by timepiece at 9:11 AM on February 4, 2009


« Older Where can I purchase the best ...   |  Best Slow Cooker Clam Chowder ... Newer »
This thread is closed to new comments.