My new template idea for Calendar

I just came up with a great idea and am making a template like this one.

I’m sorry if something similar has already been done before. But,
If you think it’s a good idea, please read on.

It’s all in a monthly block calendar type table, and every block has a link that adapts to the date.

The links have a simple notation for previewing, and the actual file title side-by-side, and after clicking on the link, the title can be changed at will.

Pasted image 20210828221934

After Click day 1 and change the title…

The link text in the table has been changed.
Dummy - Obsidian v0.12.14 2021-08-28 21-50-46

(If the wiki link is still active, the relationship is not lost when the title is changed.)

The editing screen becomes more complex, but the preview screen remains simple.

Dummy - Obsidian v0.12.14 2021-08-28 21-51-04

Then, after the subsequent journals have been collected, for example, the following variations are possible when creating content for weekly reflections.

|                  |                  | [[21Sep_01  I was so excited!\|1]]  | [[21Sep_02  Not so bad.\|2]]  | [[21Sep_03 Pretty good\|3]]  | [[21Sep_04 Oh my god!\|4]]  | [[21Sep_05  This weekend...\|5]]  |

If you select one week of the table, or one row of the Markdown table, and type the following in Find and Replace, the titles will appear instead of the date number.

1st-Line to Find 2nd-line to Replace (Regular expressions)

\\.+?\]]
]]

((((AFTER THAT)))))
If you want to make a list of them, use Find and replace below.

1st-Line to Find 2nd-line to Replace (Regular expressions)

\|
\r\n

If not, and you want to use bullet points, go here.

1st-Line to Find 2nd-line to Replace (Regular expressions)

\|
\r\n-

I’m sure you can think of others, but the above three types would produce a list like this.

And even in the Graph View, the notes with calendars and their respective dates maintain a relationship.


And if you’re using ZettelKasten, the following date rules should also allow for automatic linking.

YYMMM_DD

PS,
When I used the calendar plug-in, I was concerned that the title could not be changed and that the groups of notes for each date would not be linked to each other as if they were in the same month.

But that’s just because I don’t know how to use this plugin, and actually, if that plugin can do that, then this information of mine will be useless.

If this is useful information, I would like to share the Markdown file. :grinning:

2 Likes

I was looking for exactly this!! Very much interested in the markdown file :slight_smile:

Something I find useful on occasion is to have a full-year calendar to refer to, so I worked out how to generate an Obsidian note with that information using Templater.

First, I wrote the following javascript file and added it to my Templater scripts folder:

mdCalendar.js

function mdCalendar(year,month,mondayFirst)
{
   if (mondayFirst === undefined) { mondayFirst = false; }
   //Step 1: build an array of the abbreviated weekday names
   let dd = new Date(2022,1,27);  //a Sunday
   let wnames = [];
   for (let i = 0; i < 8; i++)
   {
      wnames.push(dd.toLocaleString('default', { weekday: 'short' }));
      dd.setDate(dd.getDate() + 1);
   }

   if (mondayFirst)
   {
      wnames = wnames.slice(1,8);  //gives [Mon,Tue,Wed,Thu,Fri,Sat,Sun]
   }
   else
   {
      wnames = wnames.slice(0,7);  //gives [Sun,Mon,Tue,Wed,Thu,Fri,Sat]
   }

   //Step 2: Get first day of the month
   // (Note: in the javascript Date object, the month has values from 0[Jan] to 11[Dec].)
   let day = new Date(year,month - 1,1);

   //Step 3: Establish the calendar header which includes the month, year, and abbreviated weekday names
   let cal = `${day.toLocaleString('default', { month: 'long' })} ${year}\n\n`
           + `| ${wnames.join(" | ")} |\n`
           + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n"
           ;

   //Step 4: Populate the calendar with the days of the month
   let week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
   while (day.getMonth() == month - 1)
   {
      let wday = day.getDay();  //day of the week (0[Sun] - 6[Sat])
      if (mondayFirst) { wday = (wday - 1 < 0) ? 6 : wday - 1; }
      let d = `${day.getDate()}`;
      week[wday] = '  ' + d.padStart(2,'0') + ' ';
      if (wday == 6)
      {
         cal += '|' + week.join('|') + '|\n';
         week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
      }
      day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1);
   }
   if (week[0] != '  .. ') { cal += '|' + week.join('|') + '|\n'; }

   return cal;
}

module.exports = mdCalendar;

The mdCalendar function requires two pieces of information: the 4-digit year and the month number (1-12). An optional third parameter determines whether the week starts on Monday (true) or on Sunday (false). If this parameter is omitted, the calendar starts on Sunday. From these parameters, it generates a Markdown table for that month’s calendar. For example, calling mdCalendar(2022,6,true) will return:

June 2022

| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|  .. |  .. |  01 |  02 |  03 |  04 |  05 |
|  06 |  07 |  08 |  09 |  10 |  11 |  12 |
|  13 |  14 |  15 |  16 |  17 |  18 |  19 |
|  20 |  21 |  22 |  23 |  24 |  25 |  26 |
|  27 |  28 |  29 |  30 |  .. |  .. |  .. |

Note 1:
The month name and the weekday abbreviations come from the machine’s localization settings. Presumably, therefore, if you ran this on a system other than an English one, it should output content in the local language. I don’t have a way to verify this.

Note 2:
To turn the days into links, you’d need to modify the line week[wday] = ' ' + d.padStart(2,'0') + ' '; to match the format you use to name day notes. For example, if you use the format YYYY-MM-DD to name your notes, try something like:

week[wday] = '  [[' + day.toISOString().slice(0, 10) + '|' + d.padStart(2,'0') + ']] ';

Second, I put together the following template and added it to my Templater templates folder:

insert-year-calendar.md

<%*
   let year = await tp.system.prompt("Year of Calendar",tp.date.now("YYYY"));
   let firstd = await tp.system.suggester(["Sunday","Monday"],["Sunday","Monday"],false,"First Day of the Week");

   let monFirst = false;
   if (firstd == "Monday") { monFirst = true; }

   if (tp.file.title.startsWith("Untitled"))
   {
      await tp.file.rename(`${year}.calendar`);
   }

   tR += `# ${year} Calendar\n\n`
   for (let i = 1; i <= 12; i++)
   {
      tR += `## ${tp.user.mdCalendar(year,i,monFirst)}\n\n`;
   }
%>

Opening a new note and then running this template through Templater will prompt you to enter the desired year (defaulting to the current one) and to pick which day starts the week. From that it will populate the note with the month calendars for the entire year. On my Obsidian instance it looks like this (in edit mode) when it’s done:

I hope this helps get you to what you need.

4 Likes

Hi @ninjineer ,

I am very happy with your code & template, thanks!

When I stick to just that, all works beautifully. But my reason to want a calendar like this is for making it link to my daily notes. So I tried your suggestion of replacing

      week[wday] = '  ' + d.padStart(2,'0') + ' ';

with

week[wday] = '  [[' + day.toISOString().slice(0,10) + '|' + d.padStart(2,'0') + ']] ';

but that doesn’t quite work in my situation.

It parses this:

## januari 2022

| ma | di | wo | do | vr | za | zo |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|  .. |  .. |  .. |  .. |  .. |  [[2021-12-31|01]] |  [[2022-01-01|02]] |
|  [[2022-01-02|03]] |  [[2022-01-03|04]] |  [[2022-01-04|05]] |  [[2022-01-05|06]] |  [[2022-01-06|07]] |  [[2022-01-07|08]] |  [[2022-01-08|09]] |
|  [[2022-01-09|10]] |  [[2022-01-10|11]] |  [[2022-01-11|12]] |  [[2022-01-12|13]] |  [[2022-01-13|14]] |  [[2022-01-14|15]] |  [[2022-01-15|16]] |
|  [[2022-01-16|17]] |  [[2022-01-17|18]] |  [[2022-01-18|19]] |  [[2022-01-19|20]] |  [[2022-01-20|21]] |  [[2022-01-21|22]] |  [[2022-01-22|23]] |
|  [[2022-01-23|24]] |  [[2022-01-24|25]] |  [[2022-01-25|26]] |  [[2022-01-26|27]] |  [[2022-01-27|28]] |  [[2022-01-28|29]] |  [[2022-01-29|30]] |
|  [[2022-01-30|31]] |  .. |  .. |  .. |  .. |  .. |  .. |

which looks like this in preview mode

januari 2022

ma di wo do vr za zo
[[2021-12-31 01]]
[[2022-01-02 03]] [[2022-01-03 04]] [[2022-01-04 05]] [[2022-01-05
[[2022-01-09 10]] [[2022-01-10 11]] [[2022-01-11 12]] [[2022-01-12
[[2022-01-16 17]] [[2022-01-17 18]] [[2022-01-18 19]] [[2022-01-19
[[2022-01-23 24]] [[2022-01-24 25]] [[2022-01-25 26]] [[2022-01-26
[[2022-01-30 31]]

I had a different naming format for my dailynotes, so I figured it was that, and delete all my daily notes, changed the naming back to YYYY-MM-DD as per your example, created some daily notes and tried again. But it doesn’t make a difference. It still gets parsed like I’ve shown you above.

Then I remembered you need to escape a | inside an Obsidian Markup table with a \ - and found out that in javascript, I even need to escape it with double \. So I replaced your line with this:

      week[wday] = '  [[' + day.toISOString().slice(0,10) + '\\|' + d.padStart(2,'0') + ']] ';

Now the tables do parse right…linked and all…BUT every single date links to the day before!!

What do I need to change to make the days link to the right Daily Notes?

1 Like

@ninjineer I have had some help over at the Discord channel, and

      week[wday] = `[[${momentDay.format("YYYY-MM-DD")}\\|${momentDay.format("DD")}]]`;

makes the days link to the right daily note and it parses beautifully.

I will include the working script just in case you want to look at it.

mdCalendar.js

function mdCalendar(year,month,mondayFirst)
{
   if (mondayFirst === undefined) { mondayFirst = false; }
   //Step 1: build an array of the abbreviated weekday names
   let dd = new Date(2022,1,27);  //a Sunday
   let wnames = [];
   for (let i = 0; i < 8; i++)
   {
      wnames.push(dd.toLocaleString('default', { weekday: 'short' }));
      dd.setDate(dd.getDate() + 1);
   }

   if (mondayFirst)
   {
      wnames = wnames.slice(1,8);  //gives [Mon,Tue,Wed,Thu,Fri,Sat,Sun]
   }
   else
   {
      wnames = wnames.slice(0,7);  //gives [Sun,Mon,Tue,Wed,Thu,Fri,Sat]
   }

   //Step 2: Get first day of the month
   // (Note: in the javascript Date object, the month has values from 0[Jan] to 11[Dec].)
   let day = new Date(year,month - 1,1);

   //Step 3: Establish the calendar header which includes the month, year, and abbreviated weekday names
   let cal = `${day.toLocaleString('default', { month: 'long' })} ${year}\n\n`
           + `| ${wnames.join(" | ")} |\n`
           + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n"
           ;

   //Step 4: Populate the calendar with the days of the month
   let week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
   while (day.getMonth() == month - 1)
   {
      let wday = day.getDay();  //day of the week (0[Sun] - 6[Sat])
      if (mondayFirst) { wday = (wday - 1 < 0) ? 6 : wday - 1; }
      let d = `${day.getDate()}`;
      //week[wday] = '  ' + d.padStart(2,'0') + ' ';
      //week[wday] = '  [[' + day.toISOString().slice(0,10) + '\\|' + d.padStart(2,'0') + ']] ';
      momentDay = moment(day);
      week[wday] = `[[${momentDay.format("YYYY-MM-DD")}\\|${momentDay.format("DD")}]]`;

      if (wday == 6)
      {
         cal += '|' + week.join('|') + '|\n';
         week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
      }
      day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1);
   }
   if (week[0] != '  .. ') { cal += '|' + week.join('|') + '|\n'; }

   return cal;
}

module.exports = mdCalendar;

and the template insert-year-calendar.md

   let year = await tp.system.prompt("Year of Calendar",tp.date.now("YYYY"));
   let firstd = await tp.system.suggester(["Sunday","Monday"],["Sunday","Monday"],false,"First Day of the Week");

   let monFirst = false;
   if (firstd == "Monday") { monFirst = true; }

   if (tp.file.title.startsWith("Untitled"))
   {
      await tp.file.rename(`${year}.calendar`);
   }

   tR += `# ${year} Calendar\n\n`
   for (let i = 1; i <= 12; i++)
   {
      tR += `## ${tp.user.mdCalendar(year,i,monFirst)}\n\n`;
   }
%>
3 Likes

Sorry to have caused difficulty with that line of back-of-the-envelop code. I really should have tested it before tossing it out there. For what it’s worth, I suspect the problem where your dates and day values were showing up off by one (ex: [[2022-01-02|03]]) arose from the toISOString function. Depending on what time zone you’re in, it can shift the date value into the prior day. Using moment to format the dates makes things much easier and straight forward.

2 Likes

Wow, this is phenomenal. I wonder whether it could be easily amended to include the weekly notes (like the Calendar plugin) at the beginning of each week.

1 Like

It probably can be adapted, but I am just someone who comes up with an idea and then try myself first, and usually need help (a lot!) to make it happen what I want :wink: - and I second your idea, would love the weeknumbers to be included and linked to the weekly notes. Perhaps even the months and the year, too, to their respective counterparts.

Back in the early days of Obsidian people have made a script like that, they got very far but it was abandoned. I haven’t checked it yet, it might just need a couple of edits for it to work in the present version of Obsidian. The discussion about it, with examples of the script, is here: Calendar and tasks for daily notes - #41 by afv

One that is a bit more recent and might, if further developed, eventually replace everything I have come up with myself. Looks very promising! You can find it here: GitHub - Quorafind/Obsidian-Big-Calendar: Big Calendar in Obsidian, for manage your events in a day/week/month and see agenda too!

@ninjineer and @ScholarInTraining - this is FYI too in case you want to take a look, collaborate, or anything :wink:

1 Like

Still very pleased with your work, @ninjineer ! And it’s working now, so t doesn’t matter who made that happen. Obsidian is all about community and helping each other, isn’t it:)

1 Like

Ok, I’ve nearly got it. This code seems to produce the desired layout for me, but it gets formatted incorrectly in the last week of each month (and the first week of the year). Would love some help from anyone who could fix that part.

function mdCalendar(year,month,mondayFirst)
{
   if (mondayFirst === undefined) { mondayFirst = false; }
   //Step 1: build an array of the abbreviated weekday names
   let dd = new Date(2022,1,27);  //a Sunday
   let wnames = [];
   for (let i = 0; i < 8; i++)
   {
      wnames.push(dd.toLocaleString('default', { weekday: 'short' }));
      dd.setDate(dd.getDate() + 1);
   }

   if (mondayFirst)
   {
      wnames = wnames.slice(1,8);  //gives [Mon,Tue,Wed,Thu,Fri,Sat,Sun]
   }
   else
   {
      wnames = wnames.slice(0,7);  //gives [Sun,Mon,Tue,Wed,Thu,Fri,Sat]
   }

   //Step 2: Get first day of the month
   // (Note: in the javascript Date object, the month has values from 0[Jan] to 11[Dec].)
   let day = new Date(year,month - 1,1);

   //Step 3: Establish the calendar header which includes the month, year, and abbreviated weekday names
   let cal = `${day.toLocaleString('default', { month: 'long' })} ${year}\n\n`
           + `| Week | ${wnames.join(" | ")} |\n`
           + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n"
           ;

   //Step 4: Populate the calendar with the days of the month
   let week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
   while (day.getMonth() == month - 1)
   {
      let wday = day.getDay();  //day of the week (0[Sun] - 6[Sat])
      momentDay = moment(day);
      if (mondayFirst) { wday = (wday - 1 < 0) ? 6 : wday - 1; }
      if (wday == 1){
            cal+= '|' + `[[${momentDay.format("gggg-[W]WW")}\\|${momentDay.format("WW")}]]` ;
      }
      let d = `${day.getDate()}`;
      //week[wday] = '  ' + d.padStart(2,'0') + ' ';
      //week[wday] = '  [[' + day.toISOString().slice(0,10) + '\\|' + d.padStart(2,'0') + ']] ';
      
      week[wday] = `[[${momentDay.format("YYYY-MM-DD")}\\|${momentDay.format("DD")}]]`;

      if (wday == 6)
      {
         cal += '|' + week.join('|') + '|\n';
         week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
      }
      day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1);
   }
   if (week[0] != '  .. ') { cal += '|' + week.join('|') + '|\n'; }

   return cal;
}

module.exports = mdCalendar;

1 Like

Closer still, but still messes up in the first week of most months (leaving off the last day of the month if it’s a Monday, shifting the days of the week if the first days of the month aren’t a Monday)

function mdCalendar(year,month,mondayFirst)
{
   if (mondayFirst === undefined) { mondayFirst = false; }
   //Step 1: build an array of the abbreviated weekday names
   let dd = new Date(2022,1,27);  //a Sunday
   let wnames = [];
   for (let i = 0; i < 8; i++)
   {
      wnames.push(dd.toLocaleString('default', { weekday: 'short' }));
      dd.setDate(dd.getDate() + 1);
   }

   if (mondayFirst)
   {
      wnames = wnames.slice(1,8);  //gives [Mon,Tue,Wed,Thu,Fri,Sat,Sun]
   }
   else
   {
      wnames = wnames.slice(0,7);  //gives [Sun,Mon,Tue,Wed,Thu,Fri,Sat]
   }

   //Step 2: Get first day of the month
   // (Note: in the javascript Date object, the month has values from 0[Jan] to 11[Dec].)
   let day = new Date(year,month - 1,1);

   //Step 3: Establish the calendar header which includes the month, year, and abbreviated weekday names
   let cal = `${day.toLocaleString('default', { month: 'long' })} ${year}\n\n`
           + `| Week | ${wnames.join(" | ")} |\n`
           + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n"
           ;

   //Step 4: Populate the calendar with the days of the month
   let week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
   while (day.getMonth() == month - 1)
   {
      let wday = day.getDay();  //day of the week (0[Sun] - 6[Sat])
      momentDay = moment(day);
      if (mondayFirst) { wday = (wday - 1 < 0) ? 6 : wday - 1; }
      if (wday == 1){
            cal+= '|' + `[[${momentDay.format("gggg-[W]WW")}\\|${momentDay.format("WW")}]]` ;
      }
      let d = `${day.getDate()}`;
      //week[wday] = '  ' + d.padStart(2,'0') + ' ';
      //week[wday] = '  [[' + day.toISOString().slice(0,10) + '\\|' + d.padStart(2,'0') + ']] ';
      
      week[wday] = `[[${momentDay.format("YYYY-MM-DD")}\\|${momentDay.format("DD")}]]`;

      if (wday == 6)
      {
         cal += '|' + week.join('|') + '|\n';
         week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
      }
      day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1);
   }
   if (week[1] != '  .. ') { cal += '|' + week.join('|') + '|\n'; }

   return cal;
}

module.exports = mdCalendar;

@scholarInTraining can you shine your brainlight on this for @derekvan ? And also Derek, the Obsidianers at the Discord channels are usually very helpful too!

@FiekeB Ow, datetime math makes my brain hurt! Can you answer for me what the week number should point to 2022-01-01, which was a Saturday? Does week 1 start on 2022-01-03 for you?

Things that aren’t wrong hidden, thanks for the correction @ninjineer !

Hidden

Never mind, this seems fine. @derekvan Are you sure this works at the end of months? Is January 32nd re-interpreted as Feb 1?

@derekvan uhh… typo here? 2022-01-27 is a Thursday?

I would have expected this to be on 0, since you just rotated your weekdays in the line above? I think that would fix your end of the month output above. Similarly, should the if statement right before the return really be checking for 1?
EDIT: Also I don’t see how this produces a week number for the first week of a month, unless the first day of the month is the first day of a week. I think you may need to special-case the first day of the month?

With JavaScript Date objects, the months are zero-based (i.e. 0=January, 1=February, etc.). Thus new Date(2022,1,27) corresponds to February 27, 2022.

1 Like

@ninjaneer @derekvan @FiekeB
It looks like moment’s Moment.weekday field respects locale and might help get rid of some of the extra logic?

Yeah, I’m not really sure how this is working so I’m just sort of trial and erroring my way through. The code without the week column works perfectly well, so I wasn’t really trying to monkey with any of the date logic, just figure out a way to bolt on an extra column without disrupting anything else. Haven’t been able to achieve that quite yet. At any rate, changing it to wday==0 doesn’t work either–it produces a different result but the dates in the first and last weeks are still shifted weird.

Trial-and-error indeed! Can you see if:

        if (day.getDate() === 1 || wday === 0) {
            cal += "|" + `[[${momentDay.format("gggg-[W]WW")}\\|${momentDay.format("WW")}]]`;
        }

works as a replacement for the insertion of the week link? Since we always want to make sure a week link has been placed for the first day of the month? (Javascript seems to believe in triple equals instead of double equals for equality checks, still understanding why.)

EDIT: This SHOULD fix the first week of the month. I have no idea still about the issue for the last week of the month.

2 Likes

hot dog! that did it. Thanks a million. (somehow it fixed both the first and last weeks (also I reset another change I had made at the end of the script which I think helped).

Final script for those following along at home:

function mdCalendar(year,month,mondayFirst)
{
   if (mondayFirst === undefined) { mondayFirst = false; }
   //Step 1: build an array of the abbreviated weekday names
   let dd = new Date(2022,1,27);  //a Sunday
   let wnames = [];
   for (let i = 0; i < 8; i++)
   {
      wnames.push(dd.toLocaleString('default', { weekday: 'short' }));
      dd.setDate(dd.getDate() + 1);
   }

   if (mondayFirst)
   {
      wnames = wnames.slice(1,8);  //gives [Mon,Tue,Wed,Thu,Fri,Sat,Sun]
   }
   else
   {
      wnames = wnames.slice(0,7);  //gives [Sun,Mon,Tue,Wed,Thu,Fri,Sat]
   }

   //Step 2: Get first day of the month
   // (Note: in the javascript Date object, the month has values from 0[Jan] to 11[Dec].)
   let day = new Date(year,month - 1,1);

   //Step 3: Establish the calendar header which includes the month, year, and abbreviated weekday names
   let cal = `${day.toLocaleString('default', { month: 'long' })} ${year}\n\n`
           + `| Week | ${wnames.join(" | ")} |\n`
           + "|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n"
           ;

   //Step 4: Populate the calendar with the days of the month
   let week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
   while (day.getMonth() == month - 1)
   {
      let wday = day.getDay();  //day of the week (0[Sun] - 6[Sat])
      momentDay = moment(day);
      if (mondayFirst) { wday = (wday - 1 < 0) ? 6 : wday - 1; }
      if (day.getDate() === 1 || wday === 0) {
         cal += "|" + `[[${momentDay.format("gggg-[W]WW")}\\|${momentDay.format("WW")}]]`;
     }
      let d = `${day.getDate()}`;
      //week[wday] = '  ' + d.padStart(2,'0') + ' ';
      //week[wday] = '  [[' + day.toISOString().slice(0,10) + '\\|' + d.padStart(2,'0') + ']] ';
      
      week[wday] = `[[${momentDay.format("YYYY-MM-DD")}\\|${momentDay.format("DD")}]]`;

      if (wday == 6)
      {
         cal += '|' + week.join('|') + '|\n';
         week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
      }
      day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1);
   }
   if (week[0] != '  .. ') { cal += '|' + week.join('|') + '|\n'; }

   return cal;
}

module.exports = mdCalendar;
3 Likes

For what it’s worth, I created a second function, based on my original calendar generator, that prepends each calendar line with the week number of the year. It produces output like this:

January 2022

| [W#] | Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|:----:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| [52] |  .. |  .. |  .. |  .. |  .. |  .. |  01 |
| [01] |  02 |  03 |  04 |  05 |  06 |  07 |  08 |
| [02] |  09 |  10 |  11 |  12 |  13 |  14 |  15 |
| [03] |  16 |  17 |  18 |  19 |  20 |  21 |  22 |
| [04] |  23 |  24 |  25 |  26 |  27 |  28 |  29 |
| [05] |  30 |  31 |  .. |  .. |  .. |  .. |  .. |

Note 1: I enclosed the week numbers in square brackets to visually distinguish them from the day numbers in the calendar.

Note 2: I titled the week number column [W#]. I did not use [Week] because that would not mesh with the localization of the month and day names. I did not use [#] because it did not seem obvious to me what just a raw number was supposed to represent. [W#] seemed like a reasonable compromise, but I’m still not entirely thrilled with it.

Note 3: I’ll leave it as an exercise to the reader to determine how best to turn those week numbers into links, as that will depend entirely on how you define such notes.

Here’s the new function:

 function mdCalendarWithWeekNum(year,month,mondayFirst)
 {
    if (mondayFirst === undefined) { mondayFirst = false; }

    //Step 1: build an array of the abbreviated weekday names
    let dd = new Date(2022,1,27);  //a Sunday
    let wnames = [];
    for (let i = 0; i < 8; i++)
    {
       wnames.push(dd.toLocaleString('default', { weekday: 'short' }));
       dd.setDate(dd.getDate() + 1);
    }

    if (mondayFirst)
    {
       wnames = wnames.slice(1,8);  //gives [Mon,Tue,Wed,Thu,Fri,Sat,Sun]
    }
    else
    {
       wnames = wnames.slice(0,7);  //gives [Sun,Mon,Tue,Wed,Thu,Fri,Sat]
    }

    //Step 2: Get first day of the month
    // (Note: in the javascript Date object, the month has values from 0[Jan] to 11[Dec].)
    let day = new Date(year,month - 1,1);

    //Step 3: Establish the calendar header which includes the month, year, and abbreviated weekday names

    let cal = `${day.toLocaleString('default', { month: 'long' })} ${year}\n\n`
            + `| [W#] | ${wnames.join(" | ")} |\n`
            + "|:----:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n"
            ;

    //Step 4: Define a function to calculate the week number of the year
    const weekOfYear = (adate,mondayFirst) => {
      let firstWeekStart = new Date(adate.getFullYear(),0,1);
      let offset = 0;
      if (mondayFirst) { offset = 1};
      let day = firstWeekStart.getDay() - offset;
      if (day < 0) day = 6;
      let daysUntilWeekStart = day !== 0 ? 7 - day : 0;
      firstWeekStart.setDate(firstWeekStart.getDate() + daysUntilWeekStart);
      return (Math.floor((adate - firstWeekStart)/(24 * 60 * 60 * 1000) / 7) + 1);
   }

    //Step 5: Populate the calendar with the days of the month
    let week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
    let wnum = '0';
    while (day.getMonth() == month - 1)
    {
       wnum = `${weekOfYear(day,mondayFirst)}`;
       if (wnum == '0') { wnum = '52'; }
       let wday = day.getDay();  //day of the week (0[Sun] - 6[Sat])
       if (mondayFirst) { wday = (wday - 1 < 0) ? 6 : wday - 1; }
       let d = `${day.getDate()}`;
       week[wday] = '  ' + d.padStart(2,'0') + ' ';
       if (wday == 6)
       {
          cal += '| [' + wnum.padStart(2,'0') + '] |' + week.join('|') + '|\n';
          week = ['  .. ','  .. ','  .. ','  .. ','  .. ','  .. ','  .. '];
       }
       day = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1);
    }

    if (week[0] != '  .. ') { cal += '| [' + wnum.padStart(2,'0') + '] |' + week.join('|') + '|\n'; }

    return cal;
 }
2 Likes

Overhere, we just use WK or wk as abbr form of week…is that an idea instead of W# (I HATE hastags haha)

And I wonder, is there a possibility to distinguish the weekdays and the weeknumbers in bold? That would set them both apart from the wholefield with the day numbers.