🛠️ Countdown timer: Counting Months

Hey ,

I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.

Please upgrade (use this link) access this content.

I'm super eager to help you learn more!

🛠️ Countdown timer: Counting Months

It’s easy to count days, hours, minutes, and seconds. There are:

  • 60 seconds in one minute
  • 60 minutes in one hour
  • 24 hours in one day

It’s harder to count months, because there are different number of days in different months.

  • March has 31 days
  • April has 30 days
  • May has 31 days
  • And so on…

You need to take care of this difference if you want an accurate count.

Month Math

Let’s say you have two dates:

  1. 5 February 2019
  2. 3 January 2019

How many months are there between these two dates? This is quite easy. Feb - Jan gives one month.

const date1 = new Date(2019, 1, 5)
const date2 = new Date(2019, 0, 3)

const months = date1.getMonth() - date2.getMonth()
console.log(months) // 1

Let’s do this again. Say you have these two dates:

  1. 1 June 2020
  2. 1 September 2019

How many months do you have between these dates? Here, we can calculate the number of months by adding 12 (the number of months in a year) to 6 (for June). This totals 18. Then, we use 18 minus 9 (for September). And we know there are 9 months between 1 June 2020 and 1 September 2020.

const date1 = new Date(2020, 5, 1)
const date2 = new Date(2019, 8, 1)

const monthDiff = date1.getMonth() + 12 - date2.getMonth()
console.log(monthDiff) // 9

What if we have these two dates?

  1. 1 June 2021
  2. 1 September 2019

This time, we need to add 24 (2 years worth) to 6. This adds up to 30. Then, we subtract 30 by 9 (for September). And we know there are 21 months between 1 June 2021 and 1 September 2019.

const date1 = new Date(2021, 5, 1)
const date2 = new Date(2019, 8, 1)

const yearDiff = date1.getFullYear() - date2.getFullYear()
const monthDiff = date1.getMonth() + yearDiff * 12 - date2.getMonth()
console.log(monthDiff) // 21

One more time. Let’s say you have these two dates. What’s the number of months between them?

  1. 1 April 2019
  2. 11 March 2019

The answer is zero.

There are zero months between the 11 March 2019 and 1 April 2019. But there are 21 days between the two dates.

If there’s one full month between your two dates (like 1 Jan to 1 Feb, or 15 Feb to 15 March), you count the month as one month. If there’s no full month, you don’t count the month. You count the number of days in the month instead.

The easiest way to check for full months is:

  1. Create a throwaway date with the date1.
  2. Subtract monthDiff from the throwaway date.
  3. Check if the throwaway date is later compared to date2.
  4. If the throwaway date is larger, there’s a full month.
  5. Otherwise, there are no full months.
const date1 = new Date(2019, 1, 5)
const date2 = new Date(2019, 0, 3)

let monthDiff =  date1.getMonth() - date2.getMonth()

// Check if there's a full month of difference
const d = new Date(date1)
d.setMonth(date1.getMonth() - 1)
if (d < date2) monthDiff = monthDiff - 1

Let’s put what we know into a function.

const getMonthDiff = (endDate, startDate) => {
  const yearDiff = endDate.getFullYear() - startDate.getFullYear()
  let monthDiff = endDate.getMonth() + yearDiff * 12 - startDate.getMonth()

  // Check if there's a full month of difference
  const d = new Date(endDate)
  d.setMonth(endDate.getMonth() - monthDiff)
  if (d < startDate) monthDiff = monthDiff - 1

  return monthDiff
}

Calculating Months and Days

Let’s say you have these two dates:

  1. 5 February 2019
  2. 3 January 2019

We know there are 1 month and 2 days between the dates. The timestamp between these two dates is: 2851200000

const date1 = new Date(2019, 1, 5)
const date2 = new Date(2019, 0, 3)

const difference = date1 - date2
console.log(difference) // 2851200000

To calculate the difference in months, we can use getMonthDiff.

const monthDiff = getMonthDiff(date1, date2)
console.log(monthDiff) // 1

To calculate the difference in days, we need to find the remaining difference in milliseconds. To find the remaining difference in milliseconds, we need to subtract the original difference by the number of days in the month.

In this case, there are 31 days from 3 January to 3 February.

const remainingDiff = difference - monthDiff * 31 * toMilliseconds('days'))
console.log(remainingDiff) // 172800000

Then, to get the number of days, we divide the remaining difference by the number of milliseconds in a day.

const days = Math.floor(remainingDiff / toMilliseconds('days'))
console.log(days) // 2

Let’s pause for a moment here because we used a magic number as we calculated remainingDiff. We used 31. How do we get 31 programmatically?

Getting the number of days in a month

First, we know there are 31 days in January. This means there are 31 days from 1 January to 1 February. It also means there are 31 days from 3 January to 3 February.

To get 31, we get the number of days in January, like this:

const getDaysInMonth = (date) => {
  const year = date.getFullYear()
  const month = date.getMonth()
  const lastDayOfMonth = new Date(year, month + 1, 0)
  return lastDayOfMonth.getDate()
}

Once we get the number of days in January, the rest becomes easy.

2 Months and more

Let’s say you have these two dates:

  1. 1 June 2020
  2. 1 September 2019

We know there are 9 months and 0 days between them.

We can get the number of months with getMonthDiff.

const date1 = new Date(2020, 5, 1)
const date2 = new Date(2019, 8, 1)

const monthDiff = getMonthDiff(date1, date2)
console.log(monthDiff) // 9

To get the remaining difference, we need to calculate the number of days that have passed during these 9 months.

  • September 2019: 30 days
  • October 2019: 31 days
  • November 2019: 30 days
  • December 2019: 31 days
  • January 2019: 31 days
  • February 2019: 29 days
  • March 2019: 31 days
  • April 2019: 30 days
  • May 2019: 31 days

So there are 274 days between 1 September 2019 and 1 June 2020.

In code, we can create a loop with 9 months of differences. You can use a for loop if you want to, but I’m going to use Array.from to create an array, and map to loop through it.

const dayDiff = Array.from({ length: monthDiff })
  .map((value, index) => {
    // ...
  })

We can create a new Date object in each iteration of the loop to get the number of days in that month.

const dayDiff = Array.from({ length: monthDiff })
  .map((v, index) => {
    const d = new Date(startDate)
    // ...
  })

We’ll get the number of days from each month, starting from September. To do this, we change the date’s Month value according to the index.

  • September: + 0 months
  • October: + 1 month
  • November: + 2 months
  • … and so on
const dayDiff = Array.from({ length: monthDiff })
  .map((v, index) => {
    const d = new Date(startDate)
    d.setMonth(date2.getMonth() + index)
    return getDaysInMonth(d)
  })

console.log(dayDiff)
Number of days in each month from September 2019 to May 2020

To get the total number of days that passed during these 9 months, we sum the array up.

const dayDiff = Array.from({ length: monthDiff })
  .map(/* ... */)
  .reduce((sum, v) => sum + v, 0)

console.log(dayDiff) // 274

To count the remaining difference, we subtract the difference with the number of days that have passed.

const difference = date1 - date2
const remaining = difference - dayDiff * toMilliseconds('days')
console.log(remaining) // 0

It makes sense for us to calculate this difference (for days and milliseconds passed for Month calculations) directly in getMonthDiff. This makes the remaining calculation much easier.

const getMonthDiff = (endDate, startDate) => {
  let monthDiff = /* ... */
  const dayDiff = /* ... */

  return {
    months: monthDiff,
    days: dayDiff,
    ms: dayDiff * toMilliseconds('days')
  }
}

And you can use getMonthDiff this way:

const date1 = /* ... */
const date2 = /* ... */
const difference = date1 - date2

const monthDiff = getMonthDiff(date1, date2)
const months = monthDiff.months
const remaining = difference - monthDiff.ms

console.log(months) // 9
console.log(remaining) // 0

Updating getCountdown

It shouldn’t be hard to update getCountdown. You can put the calculations we’ve done in this lesson directly into the function.

const getCountdown = (endDate, startDate) => {
  const monthDiff = getMonthDiff(endDate, startDate)
  const difference = endDate - startDate - monthDiff.ms

  // ...

  return {
    months: monthDiff.months,
    days,
    minutes,
    hours,
    seconds
  }
}

Updating the DOM

First, you want to include the months timer box in the HTML.

<div class="countdown__timer">
  <div class="timer__box" data-unit="months">
    <span class="timer__number">20</span>
  </div>
  <!-- ... -->
</div>

Now, let’s say you want to countdown to the end of this year. To count to the end of this year, you can increase the year value by 1, set month to 0, and day to 1.

const now = new Date()
const nextYear = new Date(now.getFullYear() + 1, 0, 1)

The rest is as before.

// Update countdown
updateBoxes(nextYear)
setInterval(updateBoxes, 1000, nextYear)

// Update Countdown target
setCountdownTarget(nextYear)
Counting down to the end of the year.