7
\$\begingroup\$

Given year, month and optionally weekday of 1st, output the calendar of the month. The first week should remain nonempty.

For empty cell, fill it with the date where it's supposed to be, in last or next month, and add # to indicate gray. If last few days can't fit in 5 lines, then they share last line with 5th week, use / to separate two days.

Sample Input: Nov 2023

Sample Output:

 #29   #30   #31     1     2     3     4
   5     6     7     8     9    10    11
  12    13    14    15    16    17    18
  19    20    21    22    23    24    25
  26    27    28    29    30    #1    #2

Sample Input: Dec 2023

Sample Output:

 #26   #27   #28   #29   #30     1     2
   3     4     5     6     7     8     9
  10    11    12    13    14    15    16
  17    18    19    20    21    22    23
24/31   25    26    27    28    29    30

Sample Input: Feb 2026

Sample Output:

   1     2     3     4     5     6     7
   8     9    10    11    12    13    14
  15    16    17    18    19    20    21
  22    23    24    25    26    27    28
  #1    #2    #3    #4    #5    #6    #7

Outputting a 2D array of string is fine. SMTWTFS and MTWTFSS are both allowed. Input is flexible.

Shortest code win.

\$\endgroup\$
5
  • \$\begingroup\$ Related. \$\endgroup\$ Commented Dec 12, 2023 at 7:49
  • \$\begingroup\$ Do we need to handle months like Feb 2100 (has 28 days)? \$\endgroup\$ Commented Dec 12, 2023 at 13:34
  • 1
    \$\begingroup\$ @NickKennedy I think so as you're given 4-digit years \$\endgroup\$
    – l4m2
    Commented Dec 12, 2023 at 20:53
  • \$\begingroup\$ Is the first sample output correct? There are 31 days in October. \$\endgroup\$ Commented Dec 13, 2023 at 12:05
  • \$\begingroup\$ @doubleunary Fixed \$\endgroup\$
    – l4m2
    Commented Dec 13, 2023 at 13:47

6 Answers 6

2
\$\begingroup\$

Charcoal, 108 93 bytes

≔⊕⪪”)∨←∧h”¹ε§≔岬﹪⊟Φ↨N¹⁰⁰ι⁴UMεI…·¹⁺²⁸ιNη≔⁺⁺#⮌…⮌§ε⊖ηN§εηυF⁻Lυ³⁵§≔υ±⁷⁺⁺§υ±⁸/⊟υE…⪪⁺υ⁺#⊟ε⁷¦⁵⭆ι◧λ

Try it online! Link is to verbose version of code. Takes input in the order year, month, weekday (0-indexed). Explanation:

≔⊕⪪”)∨←∧h”¹ε§≔岬﹪⊟Φ↨N¹⁰⁰ι⁴UMεI…·¹⁺²⁸ι

Get an array of lists of the days of the months as strings, with February calculated according to whether the year is divisible by four.

Nη≔⁺⁺#⮌…⮌§ε⊖ηN§εηυ

Input the month and concatenate any necessary trailing days from the previous month to the current month.

F⁻Lυ³⁵§≔υ±⁷⁺⁺§υ±⁸/⊟υ

If there are more than 35 days then fold the extra days into the previous week. (The assignment uses -7 because the extra day has been popped by this point.)

E…⪪⁺υ⁺#⊟ε⁷¦⁵⭆ι◧λ

Fill any remaining days with days from the next month and pretty-print the output. (…⪪⁺υ⁺#⊟ε⁷¦⁵ would just output the 2D array.)

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @NickKennedy Fair enough; I've added code to handle century leap years. \$\endgroup\$
    – Neil
    Commented Dec 12, 2023 at 22:37
1
\$\begingroup\$

Python, 165 bytes

from datetime import*
lambda y,m,w,k=timedelta:[*zip(*[(f"#{(q:=date(y,m,1)+k(t-w)).day}"[q.month==m:]+f'/{q.day+7}'*(t>27>(q+k(7)).month==m)for t in range(35))]*7)]

Attempt This Online!

MTWTFSS. Takes one-indexed year and month, and zero-indexed weekday

\$\endgroup\$
1
  • \$\begingroup\$ Python makes heavy work out of this even using a library! \$\endgroup\$
    – Simd
    Commented Dec 13, 2023 at 13:58
1
\$\begingroup\$

Jelly, 59 bytes

ọ4,25>/“ḢȷĠ’b4¤o+28ṙḣ3RUḣ€1¦⁵UṾ€€Ż€€ÐoẎs7ḣ6µṪẠƇż@Ṫj€”/ṭo”#G

Try it online!

A full program that takes three integer arguments: the 4-digit year, the 1-indexed month, and the 0-indexed day of the week for the first of the month. My example on TIO assumes Monday as the first day of the week. The output to STDOUT is a calendar in the desired format.

The first 7 bytes are taken directly from @Dennis’ clever answer to a previous question on calculating leap years.

Explanation

ọ4,25>/“ḢȷĠ’b4¤o+28ṙḣ3RUḣ€1¦⁵UṾ€€Ż€€ÐoẎs7ḣ6µṪẠƇż@Ṫj€”/ṭo”#G­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏⁠‎⁡⁠⁤⁤‏⁠‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁢⁡⁡‏⁠‎⁡⁠⁢⁡⁢‏⁠‎⁡⁠⁢⁡⁣‏⁠‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁢⁡⁤‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁢⁢⁡‏⁠‎⁡⁠⁢⁢⁢‏‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁢⁢⁣‏‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁢⁢⁤‏‏​⁡⁠⁡‌⁣⁡​‎‎⁡⁠⁢⁣⁡‏⁠‎⁡⁠⁢⁣⁢‏⁠‎⁡⁠⁢⁣⁣‏⁠‎⁡⁠⁢⁣⁤‏⁠‎⁡⁠⁢⁤⁡‏‏​⁡⁠⁡‌⁣⁢​‎‎⁡⁠⁢⁤⁢‏‏​⁡⁠⁡‌⁣⁣​‎‎⁡⁠⁢⁤⁣‏⁠‎⁡⁠⁢⁤⁤‏⁠‎⁡⁠⁣⁡⁡‏⁠‏​⁡⁠⁡‌⁣⁤​‎‎⁡⁠⁣⁡⁢‏⁠‎⁡⁠⁣⁡⁣‏⁠‎⁡⁠⁣⁡⁤‏⁠‎⁡⁠⁣⁢⁡‏⁠‎⁡⁠⁣⁢⁢‏‏​⁡⁠⁡‌⁤⁡​‎‎⁡⁠⁣⁢⁣‏‏​⁡⁠⁡‌⁤⁢​‎‎⁡⁠⁣⁢⁤‏⁠‎⁡⁠⁣⁣⁡‏‏​⁡⁠⁡‌⁤⁣​‎‎⁡⁠⁣⁣⁢‏⁠‎⁡⁠⁣⁣⁣‏‏​⁡⁠⁡‌⁤⁤​‎‎⁡⁠⁣⁣⁤‏‏​⁡⁠⁡‌⁢⁡⁡​‎‎⁡⁠⁣⁤⁡‏‏​⁡⁠⁡‌⁢⁡⁢​‎‎⁡⁠⁣⁤⁢‏⁠‎⁡⁠⁣⁤⁣‏‏​⁡⁠⁡‌⁢⁡⁣​‎‎⁡⁠⁣⁤⁤‏⁠‎⁡⁠⁤⁡⁡‏⁠‎⁡⁠⁤⁡⁢‏‏​⁡⁠⁡‌⁢⁡⁤​‎‎⁡⁠⁤⁡⁣‏⁠‎⁡⁠⁤⁡⁤‏⁠‎⁡⁠⁤⁢⁡‏⁠‎⁡⁠⁤⁢⁢‏‏​⁡⁠⁡‌⁢⁢⁡​‎‎⁡⁠⁤⁢⁣‏‏​⁡⁠⁡‌⁢⁢⁢​‎‎⁡⁠⁤⁢⁤‏⁠‎⁡⁠⁤⁣⁡‏⁠‎⁡⁠⁤⁣⁢‏‏​⁡⁠⁡‌⁢⁢⁣​‎‎⁡⁠⁤⁣⁣‏‏​⁡⁠⁡‌­
ọ4,25                                                        # ‎⁡Number of times the year can be divided by 4 and 25 (importantly the first number will be >=2 for years that are multiples of 400 and 1 for those that are other century years)
     >/                                                      # ‎⁢Reduce using greater than (will return 1 for leap years and 0 for others)
       “ḢȷĠ’b4¤o                                             # ‎⁣Or with [2,3,3,0,3,2,3,2,3,3,2,3] (This is stored as a base-250 integer than is converted to base 4)
                +28                                          # ‎⁤Add 28
                   ṙ                                         # ‎⁢⁡Rotate to the left by the 1-indexed month number
                    ḣ3                                       # ‎⁢⁢First three (will be the lengths of the months before, at and after the relevant month)
                      R                                      # ‎⁢⁣Ranges 1..each of these
                       U                                     # ‎⁢⁤Reverse each range
                        ḣ€1¦⁵                                # ‎⁣⁡For the month before, take the first x values only where x is the 0-indexed day of the first day of the main month
                             U                               # ‎⁣⁢Reverse each month’s days again
                              Ṿ€€                            # ‎⁣⁣Uneval each day (will convert to Jelly strings)
                                 Ż€€Ðo                       # ‎⁣⁤Prefix the first and last month’s days with a zero
                                      Ẏ                      # ‎⁤⁡Join outer lists
                                       s7                    # ‎⁤⁢Split into pieces length 7
                                         ḣ6                  # ‎⁤⁣Take the first six
                                           µ                 # ‎⁤⁤Start a new chain with this as the argument
                                            Ṫ                # ‎⁢⁡⁡Pop the tail (the sixth row of the calendar)
                                             ẠƇ              # ‎⁢⁡⁢Keep only those where all are truthy (will remove any days from the following month since those are prefixed by zero)
                                               ż@Ṫ           # ‎⁢⁡⁣Zip the tail of the calendar with this (I.e. the fifth row of the calendar)
                                                  j€”/       # ‎⁢⁡⁤Join each with slashes
                                                      ṭ      # ‎⁢⁢⁡Tag onto the remaining (first) four rows of the calendar
                                                       o”#   # ‎⁢⁢⁢Or with hashes (will replace the zero prefixes with hashes)
                                                          G  # ‎⁢⁢⁣Format as a grid
💎

Created with the help of Luminespire.

\$\endgroup\$
1
\$\begingroup\$

Scala 3, 239 224 bytes

Saved 15 bytes thanks to @corvus_192


MTWTFSS. Takes one-indexed year and month, and zero-indexed weekday


Golfed version. Attempt This Online!

(y,m,w)=>{val s=LocalDate.of(y,m,1);val f=s.`with`(TemporalAdjusters.previousOrSame(DayOfWeek.of(w%7)))
Seq.tabulate(35)(i=>f.plusDays(i))grouped 7 map(_.map(day=>(if(day.getMonthValue==m)""else"#")+day.getDayOfMonth))toSeq}

Ungolfed version. Attempt This Online!

import java.time._
import java.time.temporal.TemporalAdjusters

object Main {
  def main(args: Array[String]): Unit = {
    def f(year: Int, month: Int, weekday: Int): Array[Array[String]] = {
      val monthStart = LocalDate.of(year, month, 1)
      val firstDayOfWeek = monthStart.`with`(TemporalAdjusters.previousOrSame(DayOfWeek.of((weekday % 7) + 0)))

      val days = Array.tabulate(35)(i => firstDayOfWeek.plusDays(i))
      val weeks = days.grouped(7).toArray
      weeks.map(_.map { day =>
        val prefix = if (day.getMonthValue == month) "" else "#"
        s"$prefix${day.getDayOfMonth}"
      })
    }

    val result = f(2023, 12, 1) // 1 corresponds to Monday in LocalDate
    result.foreach(week => println(week.mkString("(", ", ", ")")))
  }
}
\$\endgroup\$
2
  • \$\begingroup\$ Doesn't seem to handle months that start on the last day of the week? \$\endgroup\$
    – Neil
    Commented Dec 14, 2023 at 0:32
  • \$\begingroup\$ 224 bytes: ato.pxeger.com/… \$\endgroup\$
    – corvus_192
    Commented Dec 16, 2023 at 11:29
0
\$\begingroup\$

Google Sheets, 134 bytes

=wraprows(index(regexreplace(substitute(text(sequence(35,1,A1-weekday(A1,2)*(mod(A1,6)<>4)),"MMMd"),text(A1,"MMM"),),"[A-z]+","#")),7)

Put a value like Nov 2023 in cell A1 and the formula in cell B1.

CG Calendar

The rules allow that the weekday can be specified separately. To do that , put =weekday(A1, 2) in cell B1 (123 bytes):

=wraprows(index(regexreplace(substitute(text(sequence(35,1,A1-B1*(mod(A1,6)<>4)),"MMMd"),text(A1,"MMM"),),"[A-z]+","#")),7)

(does not produce 24/31 for Dec 2023)

\$\endgroup\$
0
\$\begingroup\$

JavaScript (ES6), 123 bytes

Expects (year, month)(weekday), where weekday is 0-indexed SMTWTFS.

(y,m,n=0)=>g=w=>n++<35?(n-(h=k=>i=new Date(y,m-1,n-w+k).getDate())``&~7?"#"+i:i)+(n*h(7)>868?"/"+i:"")+`
 `[n%7&&1]+g(w):""

Try it online!

Method

We walk through the calendar slots by iterating from n=1 to n=35.

The helper function h takes a parameter k and returns the date of the month for the year y, month m and day n-w+k. The result is saved in the global variable i.

h = k =>
  i = new Date(y, m - 1, n - w + k).getDate()

If n - h(0) is negative or greater than 7, it means that the date belongs to the previous or the next month respectively. Hence the test:

n - h(0) & ~7 ? "#" + i : i

We have to add the /xx pattern if we are located on the last row of the calendar (n ≥ 29) and the date at +7 days still belongs to the current month. For this purpose, we use the test:

n * h(7) > 868

This test is true if and only if n ≥ 29 and h(7) reaches a threshold corresponding to one of the last few days of the month:

n h(7)
28 ≥32 (impossible)
29 ≥30
30 ≥29
31 ≥29
32 ≥28
33 ≥27
34 ≥26
35 ≥25
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.