All of this started when I realized that we only have 12, sometimes 13, full moon's in a year. That doesn’t seem like that many.
I set out to build a MacOS app to remind me when a full moon occurs, so that I could take a moment and step outside to appreciate it.
The MacOS app I ended up creating can be found at https://apps.apple.com/us/app/tiny-moon/id6502374344 along with the source code [0], all powered by the Tiny Moon library.
I knew that I wanted the app to work offline, so working with a network request was out of the picture. Taking inspiration from SunCalc [1] and Moontool for Windows [2], I decided to create my own library and wrote Tiny Moon as a Swift Package to power my app.
The app tries to be as minimal as possible, does what it does very fast, and works completely offline.
[0] https://github.com/mannylopez/TinyMoonApp
I used this approach because most platforms supports islamic calendar.
I really like the offline first approach, and would definitely use the library when there’s a need.
Thanks for sharing!
What if there's a bug in the calendar for Ramadan or Salah?
That’s why if a scholar based with intensive research and data, has concluded that X is true. If in the end its really true, they’ll receive two rewards; when its debunked and it was actually false, they receive one reward. The sin is to be ignorant and without any research and data concluded that X is true.
Specifically for Ramadan, the guideline is to use Moon sighting. If you see a new moon, then you begin fasting. If not, then you postpone for a day before starting Ramadan. Usually there’s a committee that does this and they will announce the result. But it doesn’t prevent anyone with the ability to observe to decide when to start Ramadan.
Salah guideline is the sun, e.g the dawn prayer is when there’s thin strike of the sun on the horizon. So to calculate the prayer time you use the sun position relative to your position on earth. If there’s a bug that somehow err the prayer time to ~5 minutes. We can always observe the sun first.
So there’s always this second factor that you can use to validate the first method. Time seems off? Look at the sun. Sun not visible? Estimate with time. Both seems off? Estimate with the variables that you can observe.
“ God does not burden any soul with more than it can bear “ (Al Baqarah 286)
——- So to answer the question, the tolerance depends on your effort on trying to reduce the err.
God knows best
Doing so also allowed me to completely remove and replace my first attempt at implementing the moon phase algorithm when it turned out to be not-accurate enough (the values were off by about 4 hours).
=LET(phase,MOD(ROUND(MOD(NOW(),29.5275)/3.691,0)-2,8)+1,UNICHAR(127760+phase)))
$ curl wttr.in/Moon
by comparison this is an absolute mountain of code. here is a good one - but note that it is only so accurate for each quarter phase (...and not sure why it destroys the formatting):
function approximateMoonPhase(julianDay)
{
// from Meeus p.319
// JDE = 2451 550.09765 + 29.530 588 853 k
// + 0.000 1337 T2
// - 0.000 000 150 T3
// + 0.000 000 000 73 T4
const t = toJulianCenturiesSinceJ2000(julianDay);
const lhs = julianDay - 2451550.09765 + (-0.0001337 + (0.00000015 - 0.00000000073 * t) * t) * t * t;
return lhs / 29.530588853;
}enjoy. its from the same source as your work appears to use...
which, as someone who as implemented a ton of this stuff. its kind of a damning sentiment that we still refer to such an old book instead of learning the problem space and making better solutions... i have a whole book in me about this at some point.
So it's not actually comparable at all?
tbh the small error can be very easily fixed. its close to the difference between a sine wave and its linear piecewise approximation from tip to tip, hence the very high accuracy on the quarters
you could go even further and remove the error from path being nearly a precessing ellipse...
in both cases what you will get is nothing that you would detect by eye or even with a telescope.
for the stated purpose this algorithm does fine in almost every use case including astronomical ones.
1. Moon phase (or "age") is trivial to compute using modular arithmetic, given an absolute moment in time as an offset relative to a reference full moon time.
2. Converting datetime formats to a reference time can get complicated, particularly if you want to handle historical dates where calendar systems changed.
3. Converting phase into named bins is a pretty trivial lookup table to quantize the continuous phase value.
4. Converting phase into an approximate percent illumination is pretty trivial geometry, using a simplified model of the moon as a circle with an elliptical boundary for the shaded versus lit part.
5. Converting percent illumination into apparent brightness requires much more work to actually compute the positions of the sun, moon, and earthbound observer.
6. Finding moonrise and moonset times requires even more work to repeatedly find the positions and search for horizon crossings.
Edit: and computing practical brightness is even more complex as you need to fuse together the above with meteorological data to consider what light actually reaches the observer!
Should I be suspicious of 'LEET code showing up in here?
To your point though, is this your solution? Is the accuracy helped by using more precise numbers or is it more of a structural limitation?
The algorithm in Moontool for Windows [2], which Tiny Moon is based off of, is based off of Meeus' algorithm.
0. https://www.agopax.it/Libri_astronomia/pdf/Astronomical%20Al...
Great library, super documentation and the code seems well written with lots of tests. Kudos, I will definitely check it out for my next ios project.
Thanks Otto
I bought my friends daughter a watch for her birthday. She was, like, 12 or something. I got it at Target.
And it had a moon phase dial! How about that?!
So I was determined to set it accurately for her.
To wit I started dredging through the internet. And to be completely honest, I'm not quite sure how I was doing that, having nothing but a Netcom.com shell account available to me. Perhaps I was using Lynx, or using some mail gateway, or who knows what. It wasn't no 10 seconds on Google, I'll tell you that.
In the end, I stumbled upon some code for my HP-48. Aha! This should do nicely!
Somehow, I got the code into the calculator. Did I type it in? Did I download it over Kermit? Who knows. Times were moving fast back then. Somehow, someway, however, I got that code in and calculated away.
Finally! ACCURATE results. Done with precision and math and engineering. Done Right!
To wit, I then proceeded to get the watch set properly.
And...it was a fashion watch for a 12 year old girl from Target. This may come as a shock to some, I was obviously taken aback by it, however -- the moon phase did not work. It was just a moon on a dial that moved, though some undetermined mechanism. It could not be set independently.
Oh.
Most of them are written in JavaScript only, but specifically written to be easy to port to other languages.
_=min
lphase = lambda y,m,d: _( lkp(ph,p)
for lkp in [lambda t,x: _(v for (kl,v),(kh,w)
in zip(t,t[1:]) if kl <= x < kh)]
for daynum in [lambda y,m,d: daynum(y-1,m+12,d) if m<3 else
y*365 + y//4 - y//100 + y//400 + (153*m+3)//5 + d]
for o in [daynum(y,m,d)-daynum(2000,1,6)]
for p in [o%29.53] # mean; varies significantly!
for ph in [[(0,"new"),(0.5,"waxing crescent"),(7,"first quarter"),
(8,"waxing gibbous"),(14.5,"full"),(15.5,"waning gibbous"),
(21.7,"last quarter"),(22.7,"waning crescent"),(29,"new"),
(30,None)]] )
(For what it's worth - 29.53 * 300 = 8859 exactly, and 2000-01-06 + 8859 days = 2024-04-08, and I am quite sure there was a new moon on the latter date because of the solar eclipse.)
[If that constant is only a minute off, it ought to be good for a few centuries more, and I'm not planning on being around longer than decades, so probably good enough for my purposes.]
Honestly, I recommend people use the most accurate algorithm they practically can, even if it's overkill for the given application. This just avoids user confusion when different apps give different answers, even if it's not significant.
E.g., I have ported VSOP87 which gives the position of the moon and planets to sub-arcsecond accuracy to a couple of dozen languages. They are much, much longer than the "snippets", but are not impractical, and users won't notice much difference in application size or computation time. https://github.com/gmiller123456/vsop87-multilang
Thursday, June 6, 2024: New Moon 8:40am (EDT)
Friday, June 14, 2024: First Quarter Moon 1:20am (EDT)
.. I removed a few lines ...
Monday, August 19, 2024: Full Moon 2:24pm (EDT)
Monday, August 26, 2024: Last Quarter Moon 5:34am (EDT
Also, M-x sunrise-sunset computes sunrise and sunset after you give it lat/long.