Thursday, December 12, 2024

Build a Birthday Countdown Calculator using Moment.js

Build a Birthday Countdown Calculator using Moment.js

In the Formatting JavaScript Date Intervals into Years, Months, Weeks, and Days article, I described how to break down JavaScript date intervals into various units using the momentJS library. You might wonder what practical applications that this exercise might have in “real life”. There are many, but one common usage is to include them within countdown calculators, for instance, how many days remain until next Christmas. In today’s tutorial, we’ll construct an HTML5 form that provides detailed information about a person’s age and birthdays based on their date of birth. In follow-up installments, we’ll exploit momentJS’s many features to effectuate the various calculations required.

Why MomentJS?

In the Formatting JavaScript Date Intervals into Years, Months, Weeks, and Days article, I warned that calculating months and years between two dates is fraught with challenges due to the variability of month lengths and leap years. That’s why I always recommend using a well-known JS (JavaScript) date library for client-side calculation of date intervals. I personally rely on momentJS for such tasks. It’s both easy to use and yields highly accurate results.

The HTML Code

Whereas the Formatting JavaScript Date Intervals into Years, Months, Weeks, and Days demo employed the HTML5 date input type for data entry purposes, our Birthday Countdown form will use three separate number inputs for each (day, month, and year) date part. Not to say that this is superior to the alternate approach, but separating each date part does make validation a little easier. I’ve also seen dropdowns employed for this purpose.

Calculated values are displayed within read-only inputs. These are rendered borderless with a light gray background using CSS to differentiate these from input fields.

Before we get into the minutiae of the markup, let’s take a look at the form without CSS or JS, just to see what types of calculations we’ll eventually have to perform:

On what day of the week were you born?

Please Enter your birthday

(then click the “Update”button)

Numeric Month (1-12):   Day of Month (1-31):   Year (e.g. 1960):   

Today You Are:

Date of Birth:    Day of Week:

Birthday This Year:    Day of Week:

Birthday Last Year:    Day of Week:

Birthday Next Year:    Day of Week:

  Your Next Birthday will be:  

Your Age » In Years:     In Months:   In Weeks:   In Days:   In Hours:

 

To get a feel for the formatted output data, here are the results for top Canadian tennis pro Milos Raonic, who was born on December 27th, 1990:

Numeric Month (1-12):   Day of Month (1-31):   Year (e.g. 1960):   

Today You Are:

Date of Birth:    Day of Week:

Birthday This Year:    Day of Week:

Birthday Last Year:    Day of Week:

Birthday Next Year:    Day of Week:

  Your Next Birthday will be:  

Your Age » In Years:     In Months:   In Weeks:   In Days:   In Hours:

The Input Fields

The first order of business is to obtain the date parts from the user. From a UI standpoint, this is always a challenge. Text input is easiest for the user to deal with, but it places some extra burden on us with regards to validation. The number input type comes with some built-in validation, but I find it to be too simplistic for dates.

<form onreset="messagesElt.innerHTML = ' ';bDayMessage.innerText = '';">
<p>Numeric Month (1-12): <input TYPE="number" NAME="Inputmonth"> 
&nbsp;&nbsp;Day of Month (1-31): <input TYPE="number" NAME="Inputday">    
&nbsp;&nbsp;Year (e.g. 1960): <input TYPE="number" NAME="Inputyear">  
&nbsp;&nbsp;<input TYPE="button" VALUE="Update" ONCLICK="compute(this.form)">
<input TYPE="reset" VALUE="Clear"> </p>
<p id="messages">&nbsp;</p>

The button’s onclick attribute hooks right into out compute() function since we only need it to execute on the button click. The next section walks through the first part of the function code.

The compute() Function

The compute() function does three things: it validates the user input, creates moment objects for today and the user’s birth date, and then proceeds to generate the values for each output field.

function compute(form) {
   messagesElt.innerHTML = ' ';
   bDayMessage.innerText = '';
 
   var today       = moment(new Date()),
       birthday    = getBirthday(form.Inputyear, form.Inputmonth, form.Inputday);
  
   if (!birthday) return;
  
   //...

The getBirthday() function above is responsible for validation; it returns the user’s birth date as a moment object if successful, or undefined otherwise. This allows us to use a JS truthy test on it. Another noteworthy point is that momentJS offers its own function for checking a date’s validity. That allows you to create your date from just about anything without worrying about exceptions. Once instantiated, you can invoke isValid() on the moment object to confirm that the internal date is a valid one. How to validate dates using momentJS will be the subject of the next article.

Generating the Formatted Age String

The “Today You Are:” field displays the user’s age, broken down into years, months, weeks, and days. It’s what the getFormattedDateDiff() function that I presented in the Formatting JavaScript Date Intervals into Years, Months, Weeks, and Days article was designed for.

form.AgeYMD.value = Date.getFormattedDateDiff(birthday, today);

I made a couple of minor modifications to the getFormattedDateDiff(), so I’m including the source code here.

  1. I added the optional intervals argument, in case someone using my function wanted to supply different intervals from the ones I’m using.
  2. I replaced the for loop with the newer Array.forEach(). It’s cleaner.
Date.getFormattedDateDiff = function(date1, date2, intervals) {
  var b = moment(date1),
      a = moment(date2),
      intervals = intervals || ['years','months','weeks','days'],
      out = [];
 
  intervals.forEach(function(interval) {
      var diff = a.diff(b, interval);
      b.add(diff, interval);
      out.push(diff + ' ' + interval.singularize(diff));
  });

The function produces the values for each of the supplied time intervals, such as “26 years, 0 months, 1 week, 3 days”.

Here‘s a demo with today’s code (and validation) for you to play with.

Conclusion

The next article will be dedicated to the validation of dates using momentJS. In it, I will present a way to validate date parts individually without having to reference the data input components. That allows us to decouple the validation code from the UI.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured