<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tech Around And Find Out</title>
    <description>The latest articles on Forem by Tech Around And Find Out (@webchick).</description>
    <link>https://forem.com/webchick</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F740431%2F0de9819c-b992-4fba-8e2c-bbd81b24a233.jpg</url>
      <title>Forem: Tech Around And Find Out</title>
      <link>https://forem.com/webchick</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/webchick"/>
    <language>en</language>
    <item>
      <title>Using `git bisect` to figure out when brokenness was introduced</title>
      <dc:creator>Tech Around And Find Out</dc:creator>
      <pubDate>Mon, 03 Jul 2023 15:23:47 +0000</pubDate>
      <link>https://forem.com/webchick/using-git-bisect-to-figure-out-when-brokenness-was-introduced-1mih</link>
      <guid>https://forem.com/webchick/using-git-bisect-to-figure-out-when-brokenness-was-introduced-1mih</guid>
      <description>&lt;p&gt;One fine day, I did a fresh installation of &lt;a href="https://www.drupal.org/"&gt;Drupal&lt;/a&gt; 8, and came across a bit of ugliness: an ugly grey border on the home page, caused by an empty div being inserted into the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8tdaIJcr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m89v87457kid064j7ekc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8tdaIJcr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m89v87457kid064j7ekc.png" alt='Screenshot of Drupal 8 home page showing grey border with a "?!"' width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My last fresh install was about a week prior and didn't have this problem. I didn't relish the idea of going through &lt;a href="https://git.drupalcode.org/project/drupal/-/commits"&gt;all of the commits&lt;/a&gt; since then one-by-one to figure out where this bug was introduced.&lt;/p&gt;

&lt;p&gt;Enter the &lt;a href="https://git-scm.com/docs/git-bisect"&gt;git bisect&lt;/a&gt; command!&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;git bisect&lt;/code&gt; command works by performing a &lt;a href="http://en.wikipedia.org/wiki/Binary_search_algorithm"&gt;"binary" search&lt;/a&gt; between one state of the code and the other in order to find, by process of elimination, where a given problem was introduced. It's quick, it's easy, and by golly, it &lt;em&gt;just works&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Here's how to use &lt;code&gt;git bisect&lt;/code&gt;, step-by-step!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Find a commit where things were working.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Have a look through the git log for a commit from around the time you know things were last working, such as &lt;a href="https://git.drupalcode.org/project/drupal/-/commit/fd0a623f478e335794e09"&gt;fd0a623f478e335794e09&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Confirm that this commit actually works ok by switching your checkout to it instead:&lt;/p&gt;

&lt;p&gt;(Tip: you only need to reference the first 5-6 characters of the commit hash; you don't need the entire thing... Git is smart and will figure it out!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout fd0a623f

Note: checking out &lt;span class="s1"&gt;'fd0a623f'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

You are &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'detached HEAD'&lt;/span&gt; state. You can look around, make experimental
changes and commit them, and you can discard any commits you make &lt;span class="k"&gt;in &lt;/span&gt;this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
&lt;span class="k"&gt;do &lt;/span&gt;so &lt;span class="o"&gt;(&lt;/span&gt;now or later&lt;span class="o"&gt;)&lt;/span&gt; by using &lt;span class="nt"&gt;-b&lt;/span&gt; with the checkout &lt;span class="nb"&gt;command &lt;/span&gt;again. Example:

git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; new_branch_name

HEAD is now at fd0a623 - Patch &lt;span class="c"&gt;#1201024 by pillarsdotnet: drupal_realpath() should describe when it should be used.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now refresh your Drupal site in your browser. Yay! No more ugly border. So we know that this version of the code, at least, was working okay. Progress!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Find a commit where things are &lt;em&gt;not&lt;/em&gt; working.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Next, we need to know the &lt;em&gt;current&lt;/em&gt; commit hash, where things are not working so well. That would be &lt;a href="https://git.drupalcode.org/project/drupal/-/commit/256d85044583970a2d39e"&gt;256d85044583970a2d39e&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Confirm this by doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout 256d85
Previous HEAD position was fd0a623... - Patch &lt;span class="c"&gt;#1201024 by pillarsdotnet: drupal_realpath() should describe when it should be used.&lt;/span&gt;
HEAD is now at 256d850... Issue &lt;span class="c"&gt;#1011614 by catch: Fixed Theme registry can grow too large for MySQL max_allowed_packet() and memcache default slab size.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and refreshing the page again. That ugly border is back again. Ick!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3 - N: Use git bisect to find the problem commit.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Great. So we know where it was working, and we know where it isn't anymore. Time for some fun!&lt;/p&gt;

&lt;p&gt;To begin, we need to start &lt;code&gt;git bisect&lt;/code&gt; and then give it the "known good" place, and the "known bad" place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect start
&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect good fd0a623
&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect bad 256d850
Bisecting: 28 revisions left to &lt;span class="nb"&gt;test &lt;/span&gt;after this &lt;span class="o"&gt;(&lt;/span&gt;roughly 5 steps&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;68c0c8c6359961cbef832398c4ddab0fe2f17083] Issue &lt;span class="c"&gt;#1238258 by kiamlaluno, izmeez: Change 'Track page visits' to 'Page visits'.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git bisect looked at all of the commits between fd0a623 and 256d850, picked the middle one, and switched the current checkout to that instead.&lt;/p&gt;

&lt;p&gt;Now, we need to figure out if the code in commit 68c0c8c has the same issue or not. Reload the page. Hey, the ugly border is gone again!&lt;/p&gt;

&lt;p&gt;Tell Git about the good news, and it will start the next search, this time at the halfway point between commit 68c0c8c (&lt;em&gt;new&lt;/em&gt; known good) and 256d850 (known bad):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect good
Bisecting: 14 revisions left to &lt;span class="nb"&gt;test &lt;/span&gt;after this &lt;span class="o"&gt;(&lt;/span&gt;roughly 4 steps&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;03b443e8f5043b6e8c3a743ad01a3db9d663ad3c] Issue &lt;span class="c"&gt;#1123092 by michaellenahan, varunarora: Fixed user-profile.tpl.php doc has confusing/misleading first paragraph.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh the page. Ewww. Border's back again. That's no good. However, we did manage to find a new "known bad" state. Let's tell Git about it so it can give us a new version to try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect bad
Bisecting: 6 revisions left to &lt;span class="nb"&gt;test &lt;/span&gt;after this &lt;span class="o"&gt;(&lt;/span&gt;roughly 3 steps&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;e217c4ef1bf06ad75bf6124b9af520195f212001] Issue &lt;span class="c"&gt;#1291458 by arithmetric: Fixed Database::getConnection mentions short-lived fixed bug and shouldn't.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You know the drill now. Reload. Border gone. Bisect good!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect good
Bisecting: 3 revisions left to &lt;span class="nb"&gt;test &lt;/span&gt;after this &lt;span class="o"&gt;(&lt;/span&gt;roughly 2 steps&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;08b9bd41723a0a23687c59033c15d66c1840b4f3] Issue &lt;span class="c"&gt;#918808 by moshe weitzman, Damien Tournoud, dww: Standardize block cache as a drupal_render() #cache.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload. Border's back. BAD!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect bad
Bisecting: 0 revisions left to &lt;span class="nb"&gt;test &lt;/span&gt;after this &lt;span class="o"&gt;(&lt;/span&gt;roughly 1 step&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;9763ca906a6229125dfbe1b56956405457d55892] Issue &lt;span class="c"&gt;#1290512 by droplet: Fixed Seven: remove duplicated css.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last time, reload. Border's gone. GOOD!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect good
08b9bd41723a0a23687c59033c15d66c1840b4f3 is the first bad commit
commit 08b9bd41723a0a23687c59033c15d66c1840b4f3
Author: Nathaniel
Date: Thu Sep 29 12:29:59 2011 +0900

Issue &lt;span class="c"&gt;#918808 by moshe weitzman, Damien Tournoud, dww: Standardize block cache as a drupal_render() #cache.&lt;/span&gt;

:040000 040000 493cba4b1f84c0e907a37f5f4e40d3576bb84785 7763c1bb6f7e4e7b1296863b5df945f5b1760f67 M modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;BAM!&lt;/strong&gt; By process of elimination, we've just found our problem &lt;a href="https://git.drupalcode.org/project/drupal/-/commit/08b9bd41723a0a23687c59033c15d66c1840b4f3"&gt;commit&lt;/a&gt;! It was from pesky old issue &lt;a href="http://drupal.org/node/918808"&gt;#918808&lt;/a&gt;. Time to &lt;a href="http://drupal.org/node/918808#comment-5065722"&gt;update the issue&lt;/a&gt; with a pointer to the &lt;a href="http://drupal.org/node/1297370"&gt;bug&lt;/a&gt; it introduced (and vice-versa).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4: Get back to a working state.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once you're all finished up, run these commands to end your &lt;code&gt;git bisect&lt;/code&gt; session and get back to the normal branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git bisect reset
Previous HEAD position was 9763ca9... Issue &lt;span class="c"&gt;#1290512 by droplet: Fixed Seven: remove duplicated css.&lt;/span&gt;
HEAD is now at 256d850... Issue &lt;span class="c"&gt;#1011614 by catch: Fixed Theme registry can grow too large for MySQL max_allowed_packet() and memcache default slab size.&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout 8.0.x

Switched to branch &lt;span class="s1"&gt;'8.0.x'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you're back in business!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Awww, yeah!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We've successfully deduced where a problem was introduced in about 3 minutes. Sifting through commit logs and trying to revert patches on an ad-hoc basis can take 10 times that or more. Let's say it together now, &lt;strong&gt;Git bisect rules&lt;/strong&gt;!! :D&lt;/p&gt;

</description>
      <category>git</category>
      <category>tutorial</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Using iCalendar for semi-predictable but oddball events</title>
      <dc:creator>Tech Around And Find Out</dc:creator>
      <pubDate>Sat, 06 Nov 2021 08:49:06 +0000</pubDate>
      <link>https://forem.com/webchick/using-icalendar-for-semi-predictable-but-oddball-events-20nk</link>
      <guid>https://forem.com/webchick/using-icalendar-for-semi-predictable-but-oddball-events-20nk</guid>
      <description>&lt;p&gt;(RFC reading for fun &lt;em&gt;and&lt;/em&gt; pragmatism!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;My girlfriend is a nurse, and around these parts that means she works shift work. Her schedule is a block of shifts (sometimes day shifts, sometimes evening shifts) followed by a block of days off (which also varies as to the number of days), and the entire schedule itself repeats every so many weeks. Basically, picture something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhx39j2baaf7ynzf1xabr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhx39j2baaf7ynzf1xabr.png" alt="A grid showing Day and Evening shifts spread throughout a calendar in an unpredictable manner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;..and repeat.&lt;/p&gt;

&lt;p&gt;While there &lt;em&gt;is&lt;/em&gt; a pattern to it, it essentially means that without access to a copy of her schedule readily at hand, it is &lt;em&gt;literally impossible&lt;/em&gt; to know if we are free on some random day next month. So obviously, I want an electronic version at the ready for these types of inquiries.&lt;/p&gt;

&lt;p&gt;Unfortunately, it is also &lt;em&gt;incredibly tedious&lt;/em&gt; to enter all of these shifts one by one, by hand, into something like Google Calendar, because it requires manually creating dozens of events (“repeats every other Monday” unfortunately does not cut it here :P), and on each of them, manually selecting “Repeats,” manually selecting “Custom…”, manually selecting “every X weeks”, all without introducing any errors. &lt;em&gt;Ugh.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, here to save the day… the iCalendar specification!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the iCalendar specification?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.ietf.org/rfc/rfc2445.txt" rel="noopener noreferrer"&gt;iCalendar specification&lt;/a&gt; (as in &lt;a href="https://www.ietf.org/rfc/rfc2445.txt" rel="noopener noreferrer"&gt;RFC 5545&lt;/a&gt;, not to be confused with good ol’ &lt;a href="http://www.apple.com/ical/" rel="noopener noreferrer"&gt;iCal&lt;/a&gt;, now known as Apple’s &lt;a href="https://support.apple.com/en-ca/guide/calendar/welcome/mac" rel="noopener noreferrer"&gt;Calendar&lt;/a&gt; app) is a “data format for representing and exchanging calendaring and scheduling information such as events, to-dos, journal entries, and free/busy information, independent of any particular calendar service or protocol.”&lt;/p&gt;

&lt;p&gt;Basically, it’s a list of rules about how to describe events in computer-friendly language, and if you follow those rules, users can import your events consistently in whatever various calendaring programs they might be using. Glancing through the specification, you’ll see it talks about how to specify all kinds of features you may have seen in various calendaring apps, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specify a location&lt;/li&gt;
&lt;li&gt;Specify a time zone&lt;/li&gt;
&lt;li&gt;Repeat an event with a certain frequency&lt;/li&gt;
&lt;li&gt;Note whether someone’s an optional or required attendee&lt;/li&gt;
&lt;li&gt;Denote whether it shows up as busy or free time on someone’s calendar&lt;/li&gt;
&lt;li&gt;Send an alert a few minutes before an event happens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...and much, much more.&lt;/p&gt;

&lt;p&gt;If you’ve ever received an email with a .ics file attached, and when you click it you’re prompted to add an event to your calendar, such as below… Congratulations! You officially have experience with iCalendar! :D&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1tswavc4g6u0dmihk8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1tswavc4g6u0dmihk8j.png" alt="An image of an calendar prompt, asking whether to add a new event to your calendar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s talk about what iCalendar looks like under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basics of iCalendar
&lt;/h2&gt;

&lt;p&gt;The “simplest thing that can possibly work” in terms of iCalendar is a text file, with an .ics extension, that contains the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;BEGIN:VCALENDAR … END:VCALENDAR&lt;/code&gt;&lt;/strong&gt;: (required) Marks the beginning and end of the iCalendar object as a whole (similar to how  …  wraps around a web page).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;VERSION&lt;/code&gt;&lt;/strong&gt;: (required) The version number of the specification required (generally, “2.0” unless you’re feeling super retro).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;PRODID&lt;/code&gt;&lt;/strong&gt;: (required) A unique identifier for the product that created the iCalendar object. Some real-world examples are &lt;code&gt;“-//Google Inc//Google Calendar 70.9054//EN”&lt;/code&gt; for Google Calendar and &lt;code&gt;“-//Apple Inc.//Mac OS X 10.14.6//EN”&lt;/code&gt; for Apple Calendar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;BEGIN:VEVENT … BEGIN:VEVENT&lt;/code&gt;&lt;/strong&gt;: One or more of these pairings mark the beginning and end of an event definition.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;UID&lt;/code&gt;&lt;/strong&gt;: (required) A unique identifier for the event. &lt;code&gt;[buncha-random-chars]&lt;/code&gt; and &lt;code&gt;[buncha-random-chars]@[yourdomain.com]&lt;/code&gt; are both common patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;DTSTAMP&lt;/code&gt;&lt;/strong&gt;: (required) The date/time that the event was created (not to be confused with when it starts, which is the next one).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;DTSTART … DTEND&lt;/code&gt;&lt;/strong&gt;: When the event starts and ends.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a simple example tying all of that together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp//Example Calendar App 1.0//EN
BEGIN:VEVENT
UID:20210310T001345Z-0242ac130003@example.com
DTSTAMP:20210310T001345Z
DTSTART:19991231T235959
DTEND:20000101T000000
SUMMARY:Party like it’s 1999!
END:VEVENT
END:VCALENDAR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you save this as “y2k.ics” and import it, you should see a new event appear at the very tail end 1999 that lasts from 11:59:59PM on December 31, 1999 until midnight on January 1, 2020!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to specify a date/time?
&lt;/h2&gt;

&lt;p&gt;The RFC gets into this at length, but the basic gist is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If it’s a date only, it’s specified in the format of &lt;code&gt;YYYYMMDD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If it’s a date and time, it’s specified in the format of &lt;code&gt;YYYYMMDDTHHMMSS&lt;/code&gt; (with &lt;code&gt;HH&lt;/code&gt; in 24-hour time). If it’s a UTC time (see below) it also has a &lt;code&gt;Z&lt;/code&gt; at the end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are &lt;a href="https://tools.ietf.org/html/rfc5545#section-3.3.5" rel="noopener noreferrer"&gt;three different ways to specify a date and time&lt;/a&gt; such as 11:59:59PM on December 31, 1999:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Date with local time&lt;/strong&gt;: Used when you want an event to be at the same time, regardless of a person’s location.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    DTSTART:19991231T235959
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Date with UTC time&lt;/strong&gt;: Used when you want an event to be at an absolute time across all geographic locations, and disregarding Daylight Savings Time. (Note: &lt;code&gt;DTSTAMP&lt;/code&gt; is always in this format.)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     DTSTART:19991231T235959Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Date with local time and timezone reference&lt;/strong&gt;: A combination of both; it pegs to a specific time, but within a given time zone, so it takes into consideration things like Daylight Savings Time.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     DTSTART;TZID=America/Vancouver:19991231T235959
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, we went with the “date with local time” format so that the event would be at the same time regardless of location, since exactly when the “last minute of 1999” is differs across the globe. But for tracking shift schedules, “Date with local time and timezone reference” makes more sense, because if I fly to Europe for work, I don’t want the start times to shift by several hours.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nope, that semi-colon after &lt;code&gt;DTSTART&lt;/code&gt; in the third example is not a typo. “Property parameters” such as &lt;code&gt;TZID&lt;/code&gt; are passed in with a semicolon rather than a colon. (See List and Field Separators) This will also come into play with...&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Repeating Events
&lt;/h2&gt;

&lt;p&gt;Ok, so we already have enough information to create each shift schedule the first time, but note that it repeats every X weeks. How do we handle &lt;em&gt;that&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://tools.ietf.org/html/rfc5545#section-3.8.5.3" rel="noopener noreferrer"&gt;Recurrence Rules (RRULE)&lt;/a&gt;! This property defines a rule or repeating pattern for recurring events. Here are just a few of the &lt;a href="https://tools.ietf.org/html/rfc5545#section-3.3.10" rel="noopener noreferrer"&gt;options&lt;/a&gt; that it supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;FREQ&lt;/code&gt;: How often the event occurs. Some examples are &lt;code&gt;WEEKLY&lt;/code&gt;, &lt;code&gt;DAILY&lt;/code&gt;, and even &lt;code&gt;SECONDLY&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;INTERVAL&lt;/code&gt;: How many &lt;code&gt;FREQ&lt;/code&gt;s should there be in between events?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;COUNT&lt;/code&gt;: Repeat the event X times. (Useful if you have a class that runs for 4 weeks, for example)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;UNTIL&lt;/code&gt;: Alternatively, this will repeat the event until a certain date.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;BYDAY&lt;/code&gt;: Allows you to say something only happens on certain days of the week.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The RFC has &lt;a href="https://tools.ietf.org/html/rfc5545#section-3.8.5.3" rel="noopener noreferrer"&gt;all sorts of examples&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it All Together
&lt;/h2&gt;

&lt;p&gt;For our purposes, we need events that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use a &lt;strong&gt;Date with local time and timezone reference&lt;/strong&gt; because I travel frequently (in non-COVID times) and I want the events to be shown at the proper start/end times regardless of where I am in the world.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have a &lt;code&gt;FREQ&lt;/code&gt; of &lt;code&gt;WEEKLY&lt;/code&gt; and an &lt;code&gt;INTERVAL&lt;/code&gt; of 8 since the schedule repeats every 8 weeks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And an &lt;code&gt;UNTIL&lt;/code&gt; date of &lt;code&gt;12/31/2021&lt;/code&gt; because the schedule gets reset every calendar year, and next year will be something different! (Hance my very strong desire to write this process down. ;))&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Put it all together, and you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Webchick Inc//Webchick’s Crappy Bash Script 1.0//EN
BEGIN:VEVENT
UID:3231CB09-1E10-4910-BB24-B358629890B7
DTSTAMP:20210314T090733Z
SUMMARY:Day Shift
DTSTART;TZID=America/Vancouver:20210111T080000
DTEND;TZID=America/Vancouver:20210111T180000
RRULE:FREQ=WEEKLY;INTERVAL=8;UNTIL=20211231
END:VEVENT

... (repeat x many more events) ...

BEGIN:VEVENT
UID:F20376E5-FA0F-429D-AB1F-3F0155EE10BA
DTSTAMP:20210314T090734Z
SUMMARY:Evening Shift
DTSTART;TZID=America/Vancouver:20210208T173000
DTEND;TZID=America/Vancouver:20210208T233000
RRULE:FREQ=WEEKLY;INTERVAL=8;UNTIL=20211231
END:VEVENT

... (repeat x many more events) ...

END:VCALENDAR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when imported into Apple Calendar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0ocqkx9re064qlr6rpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0ocqkx9re064qlr6rpu.png" alt="Apple Calendar showing a variety of day/evening shifts interspersed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there you have it!&lt;/p&gt;

&lt;p&gt;So. The next time you get one of those .ics files, try popping it open in a text editor and see how much of it makes sense now!&lt;/p&gt;

&lt;p&gt;PS: If you want to see the bash script I used to generate this, it’s at &lt;a href="https://github.com/webchick/shiftycal" rel="noopener noreferrer"&gt;https://github.com/webchick/shiftycal&lt;/a&gt;. If you want to go it alone, &lt;a href="https://icalendar.org/validator.html" rel="noopener noreferrer"&gt;this handy validator&lt;/a&gt; is your friend!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Cover image: "&lt;a href="https://www.flickr.com/photos/64503524@N00/2319463826" rel="noopener noreferrer"&gt;iCal icon replacement&lt;/a&gt;" by &lt;a href="https://www.flickr.com/photos/64503524@N00" rel="noopener noreferrer"&gt;bertop&lt;/a&gt; is licensed under &lt;a href="https://creativecommons.org/licenses/by-nc-sa/2.0/?ref=ccsearch&amp;amp;atype=rich" rel="noopener noreferrer"&gt;CC BY-NC-SA 2.0&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>icalendar</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
