How to Sort Date and/or Time in JavaScript

Robbie Brandrick | 7/23/2013 7:10:22 PM

Introduction

I have recently been working on a JavaScript project where I needed to sort Dates by descending order and Time by ascending order. For this project, I am already using Moment.js as my Date Time JavaScript Library. The aggregate I am sorting contains moment objects and not native Date JavaScript objects.

Sorting by the JavaScript Date object is easy enough, but I have not found many methods on sorting by Date and Time independently. Therefore, the goal of this post is to come up with a solution for sorting by descending Date and ascending Time.

Sort by Date

In JavaScript, sorting by Date and Time in either ascending or descending order is pretty much built in through the Date’s greater then ( > ) and less then ( < ) operators.

Example 1:

var 
	//MomentJs Objects to Sort
	momentDates = […],
	//Sort Date By Ascending Order Algorithm            
	sortByDateAsc = function (lhs, rhs)  { return lhs > rhs ? 1 : lhs < rhs ? -1 : 0; },
	//Sort Date By Descending Order Algorithm
	sortByDateDesc = function (lhs, rhs) { return lhs < rhs ? 1 : lhs > rhs ? -1 : 0; },
	
//Sort Date By Ascending Order 
momentDates.sort(sortByDateAsc);
//Sort Date By Descending Order
momentDates.sort(sortByDateDesc);

Sort by Time

With JavaScript alone, I have not found a quick and easy solution to sort specifically by time; therefore, we need to create one.

I am going to breakdown the hours, minutes and seconds then compare them individually. I am going to start with comparing hours first and only if the hours are identically I will compare minutes. If minutes are identical, I will finally compare seconds. One could easily go as precise as milliseconds, but that level of accuracy is not necessary for my needs.

Example 2:

var
            sortByTimeAsc = function (lhs, rhs)  {
                var results;
 
                results = lhs.hours() > rhs.hours() ? 1 : lhs.hours() < rhs.hours() ? -1 : 0;
 
                if (results === 0)
                    results = lhs.minutes() > rhs.minutes() ? 1 : lhs.minutes() < rhs.minutes() ? -1 : 0;
 
                if (results === 0)
                    results = lhs.seconds() > rhs.seconds() ? 1 : lhs.seconds() < rhs.seconds() ? -1 : 0;
 
                return results;
            },
            sortByTimeDesc = function (lhs, rhs) {
                var results;
results = lhs.hours() < rhs.hours() ? 1 : lhs.hours() > rhs.hours() ? -1 : 0; if (results === 0) results = lhs.minutes() < rhs.minutes() ? 1 : lhs.minutes() > rhs.minutes() ? -1 : 0; if (results === 0) results = lhs.seconds() < rhs.seconds() ? 1 : lhs.seconds() > rhs.seconds() ? -1 : 0; return results; }; //Sort Only Time by Ascending Order momentDates.sort(sortByTimeAsc); //Sort Only Time by Descending Order momentDates.sort(sortByTimeDesc);

Sort by Descending Date and Ascending Time

It would be nice to simply combine both of these algorithms into a nice easy to use function:

Example 3

            sortByDateDescAndTimeAsc = function (lhs, rhs) {
                var results = sortByDateDesc(lhs,rhs);
 
                if (results === 0)
                    results = sortByTimeDesc(lhs, rhs);
                
                return results;
            }, 

Unfortunately, as I previously stated the greater then ( > ) and less then ( < ) operators sort by Date and Time; Therefore, this approach would not work because sortByDateDesc would only ever return “0” if both of the Date and Time are identical, effectively bypassing or uselessly executing the Time sorting portion of the code. 

 

I have decided we could segregate the Date portion into Years, Months and Days, similarly to how we sorted Times (e.g., If the Years, Months and Days are identical then compare the Time portion as we previously did).

Example 4

            sortByDateDescAndTimeAsc = function (lhs, rhs) {
                var results;
 
                results = lhs.years() < rhs.years() ? 1 : lhs.years() > rhs.years() ? -1 : 0;
 
                if (results === 0)
                    results = lhs.months() < rhs.months() ? 1 : lhs.months() > rhs.months() ? -1 : 0;
 
                if (results === 0)
                    results = lhs.date() < rhs.date() ? 1 : lhs.date() > rhs.date() ? -1 : 0;
 
                if (results === 0)
                    results = lhs.hours() > rhs.hours() ? 1 : lhs.hours() < rhs.hours() ? -1 : 0;
 
                if (results === 0)
                    results = lhs.minutes() > rhs.minutes() ? 1 : lhs.minutes() < rhs.minutes() ? -1 : 0;
 
                if (results === 0)
                    results = lhs.seconds() > rhs.seconds() ? 1 : lhs.seconds() < rhs.seconds() ? -1 : 0;
 
                return results;
            }, 

Conclusion

By breaking down this problem, we were easily able to create a solution that works. But, I know I know you are not using Moment.js and you don’t want to use Moment.js just to sort Dates. Well, Moment.js is simply a wrapper, with amazing features, around a native Date object. You can easily alter my provided examples and use the Date object to accomplish the exact same thing. The only difference, for example,  is instead of invoking the method seconds on a moment.js object, you would invoke getSeconds on a Date object.

To quickly generate the native JavaScript code, skillfully write a macro to find and replace the Date object’s getters (I.E. Find: hs. Replace: hs.get), capitalize the method names and make a couple other small tweaks.

Example 5:

                sortByDateDescAndTimeAscDateObj = function (lhs, rhs) {
                    var results;
 
                    results = lhs.getYear() < rhs.getYear() ? 1 : lhs.getYear() > rhs.getYear() ? -1 : 0;
 
                    if (results === 0)
                        results = lhs.getMonth() < rhs.getMonth() ? 1 : lhs.getMonth() > rhs.getMonth() ? -1 : 0;
 
                    if (results === 0)
                        results = lhs.getDate() < rhs.getDate() ? 1 : lhs.getDate() > rhs.getDate() ? -1 : 0;
 
                    if (results === 0)
                        results = lhs.getHours() > rhs.getHours() ? 1 : lhs.getHours() < rhs.getHours() ? -1 : 0;
 
                    if (results === 0)
                        results = lhs.getMinutes() > rhs.getMinutes() ? 1 : lhs.getMinutes() < rhs.getMinutes() ? -1 : 0;
 
                    if (results === 0)
                        results = lhs.getSeconds() > rhs.getSeconds() ? 1 : lhs.getSeconds() < rhs.getSeconds() ? -1 : 0;
 
                    return results;
                },

I hope this can be of value to someone. You can find another example of the code here:

Utilizing Firefox’s Scratchpad

Robbie Brandrick | 3/6/2015 11:33:47 AM

Firefox’s Scratchpad is a useful feature within Firefox’s developer tools that allows one to run JavaScript on any web page. I have been using this feature lately to test JavaScript on webpages, and run automation scripts. I will go over how I have been able to accomplish this in the following sections.

Our Customers’ Needs

For demonstration purposes, let us say we have a client who wants a webapp widget that asks visitors for the type of content they would like to see.

Let us create a prototype widget to ensure that this is what the customer wants:

Raspberry Pi

Requirement Change, Surprise

The client has said that the form is good thus far. However, the client has asked that the forms fields change to different colors every five seconds to attract visitor’s attention…

Since this new requirement will require dynamic changes to the widget, we will need to utilize JavaScript. There are many different options one could use to quickly write and test JavaScript against a webpage:

  1. One could create a JavaScript file
  2. One could create inline JavaScript using a script tag
  3. One could utilize Firefox's Scratchpad

I am sure there are other options as well. Since in this post I am describing Scratchpad let us try that avenue.

Creating and Running JavaScript with Scratchpad

To open Scratchpad, launch Firefox, and press Shift+F4. To become familiar with the tool let us simply try to change the value within the email text box. To facilitate this we will need the form's HTML and CSS. You can download it here.

Open the HTML document with the Firefox browser, launch Scratchpad, add the following piece of JavaScript, and then press run within Scratchpad:

document.getElementById("email").value = "test@scratchpad.ca";

The email field then changes to test@scratchpad.ca

Now that we are familiar with how the tool works let us create the flashy functionality for our client.

In Scratchpad, test the following JavaScript against our HTML document:

(function () {
  var
  CSSHelper = function () {
    var
    getRandomNumberForColor = function () {
      var
      maxColor = 256
      ;
      return (Math.floor(Math.random() * maxColor));
    },
    getFormattedRandomRGB = function () {
      var
      red = getRandomNumberForColor(),
      green = getRandomNumberForColor(),
      blue = getRandomNumberForColor()
      ;
      return 'RGB(' + red + ',' + green + ',' + blue + ')';
    }
    ;
    return {
      GetRandomRGB: getFormattedRandomRGB
    };
  }(),
  updateFieldsBackgoundColor = function () {
    document.getElementById('name').style['background-color'] = CSSHelper.GetRandomRGB();
    document.getElementById('email').style['background-color'] = CSSHelper.GetRandomRGB();
    document.getElementById('contentType').style['background-color'] = CSSHelper.GetRandomRGB();
    document.getElementById('Comments').style['background-color'] = CSSHelper.GetRandomRGB();
  }
  ;
  setInterval(function () {
    updateFieldsBackgoundColor();
  }, 5000);
}())

Once we run this code in Scratchpad against the widget’s HTML we can see that the form’s fields background colors are changing randomly every five seconds. Awesome! I showed this to our customer and they said this is exactly what they are looking for. Excellent!

Automation Script

Our customer approves what we are showing them, and says that no changes are necessary. Now we have to start testing the form. We could manually input data into the form; however, let us not repeat ourselves. Launch Scratchpad, and run the following code against our widget.

(function () {
  var
  getRandomValue = function () {
    var
    randomValue = Math.floor(Math.random() * (Math.pow(2, 16)))
    ;
    return randomValue
  },
  setFieldsToRandomValue = function () {
    document.getElementById('name').value = 'Name ' + (getRandomValue());
    document.getElementById('email').value = 'email' + getRandomValue() + '@fake.ca';
    document.getElementById('Comments').value = 'Comments ' + (getRandomValue());
  }()
  ;
}())

Look at that, this piece of JavaScript is automatically filling out the form's fields with random data by simply leveraging Scratchpad. What a time saver! Way to not repeat ourselves while testing the form.

Conclusion

While this was a frivolous example, I have demonstrated how one could utilize Firefox’s Scratchpad by quickly mocking up some JavaScript to add functionality to a form, and creating an automation script that helped us not repeat ourselves while testing.

There are a myriad of other examples I can cover here, but I would like to offer a challenge to you! Come up with other techniques on how to leverage Scratchpad, and share them. That is it. That is all.