13 comments

  • adityapurwa 340 days ago
    If granular accuracy is not an important factor, you can also use Islamic calendar to calculate the moon phase. Muslims uses lunar cycles for some religious events (e.g. the Ayyamul Bidh or 3 days fasting during full moon). So when its 15 of an Islamic calendar, it’ll be a full moon.

    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!

    • throwup238 340 days ago
      I wonder, how is Allah's tolerance towards technotheological errors?

      What if there's a bug in the calendar for Ramadan or Salah?

      • adityapurwa 340 days ago
        In Islam, err is forgiven. If you’ve done your best and there’s a bug you missed, then its forgiven, and when you discover it, just fix it.

        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

        • httgp 339 days ago
          Really cool, thank you!
  • rubiquity 341 days ago
    I know a lot of people into fishing that would love this as an app to quickly check the moon phase on a given date when planning a multi-day fishing trip or deciding when to go out. Apex predators are typically less hungry around and during a full moon due to the extra light making hunting at night easy.
    • nektro 340 days ago
      the ios weather app has moon info for the current, previous, and next month
  • ChrisMarshallNY 341 days ago
    I don't really have use for this, but I must compliment the author on a job well-done. The code is well-structured, well-documented, well-tested, and well-designed.
    • mannylopez 341 days ago
      Thank you! I was a technical writer before learning how to program, so it's important for me to write well-documented and well-tested code.

      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).

  • shainvs 341 days ago
    Is there a plan to add this for linux / windows? Either way, super cool project
    • robsh 341 days ago
      The following formula will return the closest moon phase emoji in Excel:

      =LET(phase,MOD(ROUND(MOD(NOW(),29.5275)/3.691,0)-2,8)+1,UNICHAR(127760+phase)))

    • mannylopez 341 days ago
      Good news! According to Swift Package Index [0], Tiny Moon is already compatible with Linux environments.

      0. https://swiftpackageindex.com/mannylopez/TinyMoon/

    • ginko 340 days ago

        $ curl wttr.in/Moon
  • jheriko 340 days ago
    this looks incredibly bad from the implementation. there is a much simpler algorithm that works for hundreds of years in either direction of the present...

    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.

    • zokier 340 days ago
      > but note that it is only so accurate for each quarter phase

      So it's not actually comparable at all?

      • jheriko 340 days ago
        thats a fair cop

        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.

      • jheriko 340 days ago
        i did a quick code review. this mountain of code has the same error and the same precision with identifying phases.
    • saltcured 340 days ago
      Yeah these things are really piles of code and feature integration and can obscure the underlying logic somewhat.

      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!

    • Terretta 340 days ago
      > 0001337

      Should I be suspicious of 'LEET code showing up in here?

      https://en.wikipedia.org/wiki/Leet

    • loufe 340 days ago
      You almost earned a downvote from me for the unnecessarily negative tone. You have a great post and the idea that there's a more elegant solution is awesome, but I'm puzzled why you chose to phrase your response that way.

      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?

      • jheriko 340 days ago
        downvote me. im tired of masses of bloat and excessive code
  • jes5199 341 days ago
    nice! I have needed this before. I think you're using the Meeus algorithm from Astronomical Algorithms? it's a classic, great choice
  • 8mobile 340 days ago
    Hi,

    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

  • ks2048 341 days ago
    Nice work. Just FYI for anybody else - it adds an icon to status bar (if that's what it's called? - top right icons). I thought it was going to open an app window and didn't notice the icon, so thought it wasn't working. (In hindsight, it's clear from screenshots in app store).
    • asrael_io 341 days ago
      I think it's called the menu bar, and apps that don't open windows (with an icon in the menu bar) are known as "menu bar apps". Until recently, these were a PITA to implement with Apple's SDK. Thankfully they provided API support sometime I think starting with macOS 13... before that it was pretty kludgy.
  • whartung 341 days ago
    And then, there was an anecdote!

    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.

  • gmiller123456 341 days ago
    I have a site with a lot of these stand alone "snippets" so that you don't have to include/port an entire astronomy library just to get the Sun rise/set times, etc. https://www.celestialprogramming.com/

    Most of them are written in JavaScript only, but specifically written to be easy to port to other languages.

  • 082349872349872 341 days ago
    Here's a tiny calculator for moon phase that I kludged up ca. 2018 but (at least as of this month) is still tracking the full moon (which makes sense given that the algo came out of a dead tree source from over half a century ago?):

      _=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)]] )
    • madcaptenor 341 days ago
      So this is just saying that the phase of the moon is the day mod 29.53, starting from a new moon on 2000-01-06. That's about a minute off the actual mean length of a lunation - I guess the error from that approximation is less than the approximation in your "varies significantly"?

      (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.)

      • 082349872349872 341 days ago
        Yes. (any cultures that have a purely lunar calendar could do away with all the date fiddling? but then again, I guess they'd just know when the next full moon would be...)

        [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.]

        • gmiller123456 341 days ago
          I think the point is that the lunations vary by a couple of days within a year, so you might confuse users by stating a phase is on the wrong day when they are unaware a low accuracy algorithm is being used. But that algorithm probably would be good enough for just drawing an icon, since the differences would be imperceptable.

          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

  • kickingvegas 341 days ago
    Side note, if you use Emacs you can get the phases of the Moon by pressing “M” in either calendar or Org Agenda.
    • eesmith 341 days ago
      With M-x lunar-phases you get the next three lunar months; something like:

        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.
  • onetokeoverthe 340 days ago
    [dead]