Programming Challenge: Mini Calendar Display
It has been a while since the last time we did something like a programming challenge, so here's one for ya.
The life story of the author before you get to the recipe
I've been working on a little "today" website, showing what day it is, if it's a significant date for holiday/independence/... reasons, and one of the things I wanted was a small calendar display that showed the full month and days in each week. Like how XFCE's Clock plugin does it.
So I got to figuring it out and after finishing it up I thought this could be a nice little programming challenge. It has one input (the date) that can be in any of the rows and columns, and it's up to you to figure out all the rest.
Here's how mine looks in about 250ish lines of TypeScript (TSX technically) and SCSS.
The Recipe
Make a mini calendar display that shows all the days of the current month and at least one day of each adjacent month. So for example for May 2023: the 31 days in May, the 30th of April and the 1st of June should at least be visible.
It can be in any language with any method of rendering; simple text, TUI/GUI toolkit, web-based, raytraced in some game engine, nixie tubes, whatever.
Bonus Points
- Highlight the current day name in the first row, if you're including day names.
- Highlight the current day number, wherever it is.
- Highlight the current week row, wherever it is.
- Differentiate the days of current month and the days of the other adjacent months, wherever they are.
Some Tips
The week number
If your programming language of choice doesn't have a built-in way to get the week number, like JavaScript doesn't, this website may have you covered.
Testing
Make sure to test multiple different input dates, I thought I was finished with my display until I tried some other dates and noticed that there were still some bugs left to squash.
Starting
If you know what the first day in the calendar should be, counting up is as easy as "one two three"!
Weeks
If you use 6 weeks in the display, you will always have enough space to fit all the current month's days and the minimum 1 day of the adjacent month's too.
Showcase
If at all possible and with at least a few entries I will try to run all the submissions myself and create a little showcase website for it.
Fun little challenge. I got a solution in BQN — it should work for any POSIX
date
.Full source code
Tildes's font has surprisingly nice BQN symbols!
This was a good exercise for boiling down the problem to its essential parts. I decided those should be:
In order to print one month you need to know how many days it has and which day of the week it starts on. I think the
Days
function (number of days in a month) is really beautiful. Take the index of the month into a list. If it's February, figure out whether it's aLeap
year (modulus 4 and 100 and 400, xor together) and add 28 to the boolean. One weird note: xor is written≠
, so "xor of a boolean list" is written≠´
.Then I figured out how to print a single month. This is string formatting and it's ugly. But the interface
2 Chart 29
for printing a 29-day month starting on Tuesday (2s-day) is nice. You can then splice those together. Each chart is just a list of strings for each day, padded on the front by the starting weekday, padded on the back by loads of spaces, cut into a 6×7 grid, and merged into an array of lines.Then it's just a matter of finding the number of days in each month and the weekday they start on. Boring math but we got
Offset
out of it:(2023‿6 Offset 3) ≡ 2023‿9
, forward 3 months;(2023‿6 Offset ¯8) ≡ 2022‿10
, backward 8 months. Finally, a little modular math to find the weekdays (using 0-based month and day indexes).I decided to print the date in native BQN array format, ⟨surrounded by angle brackets⟩, as a nod to the language! I think it looks cute.
Nicely done! I've always wondered what the development workflow looks like with this kind of programming language that uses all these different symbols. Half the characters used I didn't even know existed let alone how I would go about typing them. Do you use some kind of special keyboard layout to get access to those? Or are you copying and pasting them manually? Or something else?
I installed a keyboard layout! It's just QWERTY but with a backslash dead key. So I type
\r
for↑
and1\ 2
for1‿2
. It sounds hard but the symbols are good mnemonics for the functions and the keys are good mnemonics for the symbols.\z\Z
makes⥊⋈
for example, which both look kind of likez
. Andhjkl
ist the dot zone, so\h\j\k\K\l
makes⊸∘○⌾⟜
. (Neither of those is meaningful code.)Right now I do most of the drafting in the online interpreter because my terminal editor feels weird for it. Over there you can see all the primitives at the top, click them to insert, hover to see the key binding, and shift/control/command-click them to see the documentation.
Yesterday I was brainstorming and was like "hmm, what's the symbol for this?" So I fumbled around the keyboard trying to find it, and eventually I gave up and looked at the top bar. I was looking for a regular ASCII slash.
Honestly I'm no expert and the biggest challenge is usually figuring out what not to care about, finding the right place of abstraction. For example I needed to pad the end of each month with spaces, otherwise it would cycle back to 1. How many spaces do you need? Well, actually it doesn't matter, just pad with 42 cells and chop it back to size.
It feels like you should be able to extract 7 and make it more general. But, like, who on Earth is going to change the length of the week? (And there's only two 7s in the code anyway.)
And even if it does matter and you do need the generalization — well, it's only 11 lines so you can just go in there and do whatever you want.
Python actually includes calendar formatting in the standard library, producing output like:
I think it'd be fun to try to complete this challenge without performing any date/time arithmetic, so I'll instead use regex substitutions and string manipulation to decorate that output with ANSI escape sequences to satisfy the challenge rules.
https://i.imgur.com/gjr02vM.png
I do need a little date/time arithmetic to find the preceeding or following month; for example a date in January should include December from the previous year and vice versa. I do this via
datetime.timedelta
to at least hide that arithmetic even though it's still there.https://i.imgur.com/3SIET7v.png
For a more compact view I can insert only the last week of the previous month and the first week of the next month by string manipulation.
https://i.imgur.com/bL3amTZ.png
I want to merge those lines into the current month, but I need to do this conditionally in case the month starts on the first day of the week, or ends on the last day of the week. I could determine this by date arithmetic... but I will do it via string manipulation instead. The ANSI sequences present in the content make this tricky, but I can check
line.startswith(' ')
to tell if a line should be merged into the previous.With all that we have the last feature
Today's date: https://i.imgur.com/1ZhYPhe.png
Some edge cases: https://i.imgur.com/EGiJJDm.png
The Python Standard Library never ceases to amaze, nice find!
I wrote a terminal program, like some of the others here. I started this a couple days ago but only got around to finishing it now. It's in C and uses ANSI escapes for formatting. The logic is a little hacky, but it works and that's what counts, I guess!
calendar.c
Good work! When I compiled with gcc I did get a warning about something but the calendar still outputs correctly so I don't know, I'm not familiar enough with C to tell. :P
The warning if you're interested
Looks like I made a typo, oops!
year & 100
should beyear % 100
. It only affects the leap year test, though, so I guess that's why it still works right now.I'll go ahead and edit the original post to fix that. I didn't notice it because I didn't have all the warnings on.
Cool idea. Your calendar turned out quite nicely.
I happen to be seeking a mini project to try out Svelte, so think I'll try an entry.
I spent some hours and whipped up something: https://github.com/talklittle/mini-calendar-2023
Screenshot on the GitHub page. No polish and I didn't do all the extras like day-of-week headings and week numbers, nor a date picker.
Learning to setup a Svelte project
nvm install --lts
)npm create svelte@next mini-calendar-2023
Stumbling blocks
So after these learnings I was satisfied and decided to leave it at that. Thanks for the challenge idea @Bauke!
Great job! The way you got there is pretty much the same way I did it:
Spoilies
Set the start as the first day of the week (for the first column), calculate back to the previous month (for the first row), and finally loop de loop up 6 * 7 days.
Thanks for participating!
Oh yeah, I used your hints right away for a head start. Even so, it did take a while of staring at the screen and a couple false starts on the core logic until I got a straightforward algorithm in place.