The JavaScript Date Type

Dates and times are less than straightforward to deal with, partly because of the varying sizes of the units which don't often fit neatly into the larger units, and partly because of the almost infinite number of ways they can be expressed in natural language.

Programming languages usually provide robust ways of handling dates and in this article I will put together a solution providing a reference to JavaScript's core date handling functionality.

The files for this project can be downloaded as a zip from the Downloads page, or you can clone/download the Github repository if you prefer.

Source Code Links

ZIP File
GitHub

If you download the source code and open date.htm in your browser you'll see something like this:

On the left are three methods of setting a date: setting it to the current date, using the HTML date input type, and parsing a string.

On the right the selected date, or parts of it, is shown in a number of ways using built-in methods and also four which I have added.

Let's look at the source code.

date.js

window.onload = function()
{
    createDateMethods();

    setEventHandlers();
}

function setEventHandlers()
{
    document.getElementById("btnSetToNow").onclick = function(){setToNow();};
    document.getElementById("dateinput").onchange = function(){setFromDateInput();};
    document.getElementById("datetext").onchange = function(){parseFromText();};
}

function setToNow()
{
    const now = new Date();

    outputDate(now);
}

function setFromDateInput()
{
    const datevalue = document.getElementById("dateinput").value;

    const date = new Date(datevalue);

    outputDate(date);
}

function parseFromText()
{
    const datevalue = document.getElementById("datetext").value;

    const date = new Date(datevalue);

    outputDate(date);
}

function outputDate(date)
{
    if(isNaN(date.valueOf()))
    {
        alert("Invalid date");
    }

    clearConsole("console");

    writeToConsole(`${padWithNBSP("Raw object:", 20)}${date}<br/><br/>`, "console");
    writeToConsole(`${padWithNBSP("toLocaleString:", 20)}${date.toLocaleString()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("toString:", 20)}${date.toString()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("valueOf:", 20)}${date.valueOf()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("toDateString:", 20)}${date.toDateString()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("toTimeString:", 20)}${date.toTimeString()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("toLocaleDateString:", 20)}${date.toLocaleDateString()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("toLocaleTimeString:", 20)}${date.toLocaleTimeString()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("toUTCString:", 20)}${date.toUTCString()}<br/>`, "console");

    writeToConsole("<br/>", "console");

    writeToConsole(`${padWithNBSP("getFullYear:", 20)}${date.getFullYear()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("getMonth:", 20)}${date.getMonth()} (0 = January)<br/>`, "console");
    writeToConsole(`${padWithNBSP("getDay:", 20)}${date.getDay()} (0 = Sunday)<br/>`, "console");
    writeToConsole(`${padWithNBSP("getDate:", 20)}${date.getDate()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("getHours:", 20)}${date.getHours()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("getMinutes:", 20)}${date.getMinutes()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("getSeconds:", 20)}${date.getSeconds()}<br/>`, "console");

    writeToConsole("<br/>", "console");

    writeToConsole(`${padWithNBSP("getDayNameFull:", 20)}${date.getDayNameFull()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("getDayNameShort:", 20)}${date.getDayNameShort()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("getMonthNameFull:", 20)}${date.getMonthNameFull()}<br/>`, "console");
    writeToConsole(`${padWithNBSP("getMonthNameShort:", 20)}${date.getMonthNameShort()}<br/>`, "console");

    writeToConsole("<br/>", "console");
}

function createDateMethods()
{
    Date.prototype.getDayNameFull = function()
    {
        return this.toLocaleDateString(undefined, { weekday: 'long' });
    }

    Date.prototype.getDayNameShort = function()
    {
        return this.toLocaleDateString(undefined, { weekday: 'short' });
    }

    Date.prototype.getMonthNameFull = function()
    {
        return this.toLocaleDateString(undefined, { month: 'long' });
    }

    Date.prototype.getMonthNameShort = function()
    {
        return this.toLocaleDateString(undefined, { month: 'short' });
    }
}

function padWithNBSP(string, length)
{
    return string + " ".repeat(length - string.length);
}

window.onload

Here we simply call createDateMethods which I will get to later, and also a function to set event handlers on the three controls.

setEventHandlers

The page contains a button and two input elements. For the button we need to handle the click event and for the inputs we need to handle change events.

setToNow

Creating a new Date object with no arguments populates the object with the current date and time. This is then passed to outputDate which I'll discuss in a moment.

setFromDateInput

This is a bit more involved as we need to get the value from the date input element which is then used to construct a new date.

Major browsers now support the date input element and show some form of dialog which enforces a valid date. However, it can still be left blank and older browsers may fall back to a text input which dos not enforce valid date entry. For these reasons any value of a date input should be validated as I'll demonstrate.

parseFromText

You can also throw a string at the Date constructor and it will attempt to parse it. Here I have provided a text box which, when the value changes, will have its text parsed by the Date constructor. Obviously you wouldn't do this in a real-life situation but here it provides an opportunity to check whether dates obtained in some format elsewhere can be parsed. However, bear in mind that the "parsability" of a format might differ across browsers.

outputDate

Firstly we check if the date argument is valid. Unfortunately and perhaps surprisingly there is no inbuilt way of doing this but the method I have used is to check if the value (number of milliseconds after or before midnight on 1 January 1970) is NaN.

After that I have output the results of various Date methods, including four home-made ones which I will look at next.

Remember that some of the results are locale-specific.

createDateMethods

You can get the month and day of the week from a Date object as 0-based numbers but not their names. This is not a major shortcoming as they can be obtained using toLocaleDateString with a custom options object.

Here I have done this in four methods added to Date.prototype for ease of use, as seen in outputDate. You might like to spend a bit of time playing with these options as they provide a way of getting a date in the exact format you want, subject to locale variations.

Note that the first argument is a locale string (eg. "ja-JP") which defaults to the browser setting, which of course is probably what you want most of the time.

Looking for a VPN?

StrongVPN is running a promotion where customers can get a permanent discount on the monthly plan for just $5 for life! Ends June 30, 2020.