Implementation notes My calendar broke, so I made another one. ---------------------------------------------------------------------- 0. Concept Seriously, the "cal" command on our Linux workstations at work used to highlight the current date, but recently it stopped doing that. I figured I didn't need the full accuracy of "cal" going all the way back to September 1752, I just to know the days of the current month with today's date highlighted. Thus, instead of filing a bug report, I wrote this utility. As an added incentive to have another calendar, this one also shows when the next full moon will be. ---------------------------------------------------------------------- 1. Implementation This one was quite straightforward, I knew exactly what I wanted to make, and went ahead and made it. There were a few design considerations: + How to mark the full moons: initially I thought about using the Unicode moon phase characters, but they were not portable (not visible under text mode console, for example). But even if the terminal were to degrade those characters gracefully, Unicode is problematic in that the full moon character might look like new moons depending on whether your terminal colors are reversed and what font you are using. In the end I decided to mark full moons with colors. + How to draw the calendars: that is, whether this utility is going to look more or less like "cal" or something else. In the end I decided to space out the numbers more, which seems more pleasant to me. Perhaps my eyes have aged poorly. + How to actually implement it: mostly, which programming language to use. I instinctively chose Perl this time, although I did thoroughly enjoyed reading Ruby's DateTime documentation with the bits about Shakespeare and Cervantes. ---------------------------------------------------------------------- 2. Moon phase As a side note, there is one specific bit of implementation detail regarding how the moon phase is calculated. How to compute moon phase is well documented, for example around here: https://en.wikipedia.org/wiki/Lunar_phase#Calculating_phase This is roughly the third time I have had to compute moon phase, the moon hasn't changed much since when first did it ~17 years ago, so in theory I should be able to copy&paste from here: https://uguu.org/src_kiku_pl.html Kiku computes number of days from a particular fixed point and calculated moon age from that with a bit of modulus. The fixed point was meant to be 1900-01-01, which was a new moon. But actually, Kiku computed that offset from *some* fixed point, and included an extra offset of +23.686 days compensate. Clearly I calculated the number of days wrong, but I didn't gave it much thought back then and just patched it up with an offset. The bug might not have lingered on my mind much but the unsettling feeling probably did, which might be why when I did it the second time ~8 years ago, I went for a different approach: https://uguu.org/src_hazuki_cc.html Hazuki calculated moon phase directly from epoch seconds, so the number of days is most likely correct. But actually the epoch doesn't always start from 1970, and on systems where that's not true, the moon phase was most definitely wrong. The error isn't obvious for Hazuki because the computed moon phase isn't directly user-visible, but if you try to encrypt a file on one machine and decrypt it later on a different machine that is on a different epoch, decryption will most likely fail. Also, this one came with a different unsettling offset of -434928 seconds. So this time, third time around, I went back to calculating moon phase using number of days since 1900: # number of days before beginning of this year. # 0th year is 1900. $year * 365 # Add number of leap days before this year. + int(($year - 1) / 4) # Compensate for non-leap days every century. - int(($year - 1) / 100) # Add non-non-leap days every 400 years. + int(($year - 1 + 1900) / 400) - 4 This should have been obvious in retrospect, but I just wasn't that diligent 17 years ago in testing my implementation. I am pretty sure I got it right this time, especially since I didn't have to add extra unexpected offsets. But because my utility uses local time for everything, it's possible to be off by a day due to timezone differences. Also, other people seem to have their own ways of computing moon phase, for example here: https://github.com/NetHack/NetHack/blob/76090dd648dcff3cfa7503f1257e0499a381b1c1/src/hacklib.c#L1092 Basically if you expected to be extra lucky because my calendar said so, and you got killed by a swarm of soldier ants, it's not my fault. ---------------------------------------------------------------------- 3. ASCII art I made this utility into an ASCII art, as is my usual habit of making things. This is part of the reason why I chose Perl. Because I didn't record the previous ASCII art, I recorded the full process for this one. I don't usually do this for Perl since it's possible to automate this process for Perl. I also don't usually go for ASCII art designs with thin lines (extra whitespace means extra work). Still, the manual layout was done in under an hour, which is relatively fast. Compared to, for example, over 3 hours taken to do all the halftone bits in http://uguu.org/src_violet_c.html Template chosen was Suzukaze Aoba from "New Game!" manga series. Hopefully people can recognize this scene from volume 1. ---------------------------------------------------------------------- 4. Testing This program has been verified to work in these environments: - Perl 5.30.3 on cygwin. - Perl 5.20.2 on linux. Also, the colors should be mostly right in most variants of xterm, whether you have white-text-on-black-background or the reverse. Running this in text console depends on a bit of luck, most consoles seem to highlight the current date just fine, but some of them don't seem to highlight the full moons. ---------------------------------------------------------------------- 5. Finally... It's always a pleasure to write Perl, especially if you don't plan on debugging it 17 years later.