šŸ› ļø Countdown timer: Counting Years

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 Years

How many years are there between these two dates?

  1. 1 January 2020
  2. 1 January 2019

This is quite easy. 2020 - 2019 gives 1 year.

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

const yearDiff = date1.getFullYear() - date2.getFullYear()
console.log(yearDiff) // 1

But how many years are there between these two dates?

  1. 1 January 2020
  2. 1 February 2019

You guessed it. There are zero years and 11 months between 1 January 2020 and 1 February 2019. Like how we checked for full months when we count months, we need to check for full years when we count years.

let yearDiff = endDate.getFullYear() - startDate.getFullYear()

// Check if there's full years of difference
const d = new Date(endDate)
d.setFullYear(endDate.getFullYear() - yearDiff)
if (d < startDate) yearDiff = yearDiff - 1

Letā€™s put what we know into a function first.

const getYearDiff = (endDate, startDate) => {
  let yearDiff = endDate.getFullYear() - startDate.getFullYear()

  // Check if there's full years of difference
  const d = new Date(endDate)
  d.setFullYear(endDate.getFullYear() - yearDiff)
  if (d < startDate) yearDiff = yearDiff - 1

  return yearDiff
}

Leap years

There are 365 days in a year, but not when a leap year occurs. When thereā€™s a leap year, there are 366 days in the year. We need to get the right number of days in the year to get a correct countdown.

Checking for a leap year

When thereā€™s a leap year, thereā€™s a February 29 in the year. We can use this information to check if itā€™s a leap year.

const isLeapYear = (year) => {
  const feb29 = new Date(year, 1 , 29)
  return feb29.getDate() === 29
}

console.log(isLeapYear(2019)) // false
console.log(isLeapYear(2020)) // true

If there is a leap year, there are 366 days in the year. We can create a function to get the number of days in the year.

const getDaysInYear = (year) => {
  return isLeapYear(year)
    ? 366
    : 365
}

console.log(getDaysInYear(2019)) // 365
console.log(getDaysInYear(2020)) // 366

Counting years and days

This is where it gets complicated. I want to bring you through an exercise to build your problem-solving skills. So follow along.

How many days are there from:

  1. 1 Feb 2019 to 1 Feb 2020?
  2. 1 March 2019 to 1 March 2020?
  3. 1 Feb 2020 to 1 Feb 2021?
  4. 1 March 2020 to 1 March 2021?

Hereā€™s the answers:

  1. Feb 2019 to Feb 2020: 365 days. (Feb 2019 has 28 days)
  2. March 2019 to March 2020: 366 days. (Feb 2020 has 29 days)
  3. Feb 2020 to Feb 2021: 366 days. (Feb 2020 has 29 days)
  4. March 2020 to March 2021: 365 days. (Feb 2021 has 28 days)

There are either 365 days or 366 days in a year, but the number of days depend on the year and month we count from. Itā€™s easier to see a pattern if we reorder the above into this:

  1. 365 days:
    1. Feb 2019 to Feb 2020
    2. March 2020 to March 2021
  2. 366 days:
    1. March 2019 to March 2020
    2. Feb 2020 to Feb 2021

We can bold the leap year to make the information easier to digest.

  1. 365 days:
    1. Feb 2019 to Feb 2020
    2. March 2020 to March 2021
  2. 366 days:
    1. March 2019 to March 2020
    2. Feb 2020 to Feb 2021

Letā€™s expand this a bit more.

  1. 365 days:
    1. Jan 2019 to Jan 2020
    2. Feb 2019 to Feb 2020
    3. March 2020 to March 2021
    4. April 2020 to April 2021
  2. 366 days:
    1. March 2019 to March 2020
    2. April 2019 to April 2020
    3. Jan 2020 to Jan 2021
    4. Feb 2020 to Feb 2021

Can you spot a pattern?

  1. 365 days:
    1. Jan 2019 to Jan 2020 (2019 has no Feb 29)
    2. Feb 2019 to Feb 2020 (2019 has no Feb 29)
    3. March 2020 to March 2021 (2021 has no Feb 29)
    4. April 2020 to April 2021 (2021 has no Feb 29)
  2. 366 days:
    1. March 2019 to March 2020 (2020 has Feb 29)
    2. April 2019 to April 2020 (2020 has Feb 29)
    3. Jan 2020 to Jan 2021 (2020 has Feb 29)
    4. Feb 2020 to Feb 2021 (2020 has Feb 29)

Hereā€™s the pattern:

  • If we count from March onwards, we need to check for a Feb 29 in the next year
  • If we count from Jan or Feb of the year, we need to check for Feb 29 this year
const date1 = new Date(2020, 1, 1)
const date2 = new Date(2019, 1, 1)

const startingMonth = date2.getMonth()
const isMarchOnwards = startingMonth > 1
console.log(isMarchOnwards) // false

If we count from March onwards, we check for Feb 29 in the next year.

let dayDiff
const currentYear = march2019.getFullYear()

if (isMarchOnwards) {
  dayDiff = getDaysInYear(currentYear + 1)
}

If we count before March, we check for Feb 29 in the current year.

if (isMarchOnwards) {
  dayDiff = getDaysInYear(currentYear + 1)
} else {
  dayDiff = getDaysInYear(currentYear)
}

This works if thereā€™s one year of difference. If there are many years between your dates, you need to know the number of days in each year.

An easy way is to use Array.from and map.

const getYearDiff = (endDate, startDate) => {
  // ...
  const dayDiff = Array.from({ length: yearDiff })
    .map((v, index) => {
      const d = new Date(startDate)
      d.setFullYear(d.getFullYear() + index)

      const year = d.getFullYear()
      const month = d.getMonth()
      const isMarchOnwards = month > 1
      return isMarchOnwards
        ? getDaysInYear(year + 1)
        : getDaysInYear(year)
    })

  console.log(dayDiff)
}
const date1 = new Date(2021, 0, 1)
const date2 = new Date(2019, 0, 1)

getYearDiff(date1, date2)
The number of days in 2019 and 2020

Once you get the number of days in a year, you can add them up with reduce.

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

Like getMonthDiff, we want to return the difference in days and milliseconds from getYearDiff.

const getYearDiff = (endDate, startDate) => {
  // ...
  return {
    years: yearDiff,
    days: dayDiff,
    ms: dayDiff * toMilliseconds('days')
  }
}

Calculating years and months

Letā€™s say you have two dates that are 1 year and 1 month apart.

  1. 1 March 2020
  2. 1 February 2019
const date1 = new Date(2020, 2, 1)
const date2 = new Date(2019, 1, 1)

We know there are one year between these two dates. We can get this information from getYearDiff.

const yearDiff = getYearDiff(date1, date2)
console.log(yearDiff) // 1

To get the the difference in months, we need to adjust date values passed into getMonthDiff. Here, we need to increase startDate by the number of years. (If you subtracted yearDiff from endDate, youā€™ll get a wrong value. See if you can figure out why šŸ˜‰).

const getCountdown = (endDate, startDate) => {
  const yearDiff = getYearDiff(endDate, startDate)

  const d = new Date(startDate)
  d.setFullYear(d.getFullYear() + yearDiff.years)

  const monthDiff = getMonthDiff(endDate, d)

  // ...
}

Populating the DOM

To populate the DOM, we need to add the ā€œYearā€ .timer__box to the HTML.

<div class="countdown__timer">
  <div class="timer__box" data-unit="years">
    <span class="timer__number">20</span>
  </div>
</div>

We also need to return years from getCountdown.

const getCountdown = (endDate, startDate) => {
  const yearDiff = getYearDiff(endDate, startDate)

  const d = new Date(startDate)
  d.setFullYear(d.getFullYear() + yearDiff.years)

  const monthDiff = getMonthDiff(endDate, d)
  const difference = endDate - startDate - yearDiff.ms - monthDiff.ms

  // ...

  return {
    years: yearDiff.years,
    // ...
  }
}
Counting down in years