Date manipulation in JS made easy with date-fns

Unpackaged Reviews
JavaScript in Plain English
9 min readJul 28, 2020

--

Overview

Ever written a method to get the time of day in GMT? or have wondered if I need to convert the time to UTC? Most applications have some common date-time manipulations which are often repeated and after some time become very difficult to maintain. To add upon this when internationalizing an application Date and Time manipulations are one of the key challenges you will face. So much so that you will lose track of time amending it all 😉.

Tick Tock, Tick Tock! Fear not for we have you covered. date-fns is one such package that provides simple helper functions for date-time manipulation. With internationalization options available you can always leverage it and go international quickly.

Highlights

Getting started with date-fns is pretty easy. It can be installed by running the command npm install date-fns. All helper methods can either be imported through the main package or you can install only the required submodule. One such is fp which has two copies of methods with and without format options.

/* Babel or ES6 */
import {methodName} from "date-fns";
/* Node or requireJs */
const methodName = require("date-fns").methodName;

It deserves extra brownie points as it provides typescript support without any additional packages and helps developers in not having to maintain another dev dependency. Its package structure promotes tree-shaking which helps in overall application size and hence we suggest installing the main module as only the required methods would get bundled in. The developer has to handle one of three types namely:

  • Interval: It is a simple representation of the time interval between two date instances. It is purely used in methods that operate on time spans.
  • Locale: Represents data of the desired country which is used in formatting date.
  • Duration: The main difference between a standard Date object and the Duration object is that the latter is devoid of any Locale information. A Duration can consist partially any of the following:
{    
years, months, weeks, days, hours, minutes, seconds
}

Most helpers provided can be categorized into the following four categories based on the dimension of date-time they help simplify.

Time helpers

These helpers mainly focus on manipulating and validating predicates on aspects of time such as seconds, minutes, hours. It provides useful utilities on time spans through the Interval type. The following table gives a nice overview of a few methods:

Some examples to help familiarise you with the above functions are:

const interval1 = {
start: new Date(2014, 0, 10), // 10th Jan 2014
end: new Date(2014, 11, 21) // 21st Dec 2014
};
const interval2 = {
start: new Date(2014, 6, 10), // 10th July 2014
end: new Date(2015, 0, 10) // 10th Jan 2015
};
const interval3 = {
start: new Date(2015, 6, 10), // 10th July 2015
end: new Date(2020, 11, 10) // 10th Dec 2020
};
const interval4 = {
start: new Date(2015, 11, 10), // 10th Dec 2015
end: new Date(2015, 11, 10) // 10th Dec 2015
};
  • areIntervalsOverlapping:
console.log(areIntervalsOverlapping(interval1, interval2)); 
// => true
console.log(areIntervalsOverlapping(interval1, interval3));
// => false
  • eachDayOfInterval:
console.log(eachDayOfInterval(interval4));
// => [Sun Jan 10 2016 00:00:00 GMT+0530 (India Standard Time)]
  • isWithinInterval:
console.log(isWithinInterval(Date.now(), interval1)); // => false
console.log(isWithinInterval(Date.now(), interval3)); // => true
  • fromUnixTime:
console.log(fromUnixTime(1595157314)); 
// => Sun Jul 19 2020 16:45:14 GMT+0530 (India Standard Time)
  • getTime:
console.log(getTime(Date.now())); // => 1595157440507
  • getUnixTime:
console.log(getUnixTime(Date.now())); // => 1595157492
  • startOfMinute:
console.log(startOfMinute(Date.now())); // => Sun Jul 19 2020 17:02:00 GMT+0530 (India Standard Time)
  • isThisMinute:
console.log(isThisMinute(Date.now())); // => true
console.log(isThisMinute(new Date(2020, 6, 10, 17, 4))); // => false
  • roundToNearestMinute: rounds off the time to the start of the minute that the given date is closest to. We can change this by providing the nearestTo option which can be between 1 - 30. If it is equally distant to both the previous and next interval, then it rounds up.
console.log(roundToNearestMinutes(Date.now())); 
// => Sun Jul 19 2020 17:12:00 GMT+0530 (India Standard Time)
console.log(roundToNearestMinutes(Date.now(), {nearestTo: 15}));
// => // => Sun Jul 19 2020 17:15:00 GMT+0530 (India Standard Time)
console.log(roundToNearestMinutes(new Date(2020, 6, 10, 17, 13), {nearestTo: 8}));
// => Fri Jul 10 2020 17:16:00 GMT+0530 (India Standard Time)
console.log(roundToNearestMinutes(new Date(2020, 6, 10, 17, 11), {nearestTo: 8}));
// => Fri Jul 10 2020 17:08:00 GMT+0530 (India Standard Time)
/* here 17:12 is equidistant from 17:8 and 17:16, the method rounds it off to 16 */console.log(roundToNearestMinutes(new Date(2020, 6, 10, 17, 12), {nearestTo: 8}));
// => Sun Jul 19 2020 17:16:00

Let’s see how we can apply our time helpers to some real-life use cases:

  • Calculate if a booking has come within the time of registration:
const checkIfBookingIsValid = timeOfBooking => isWithinInterval(timeOfBooking, {
start: startTimeOfRegistration,
end: endTimeOfRegistration
});
  • Specify when next the scheduled job is to run:
const getNextStartTimeOfJob = (lastExecutionTime, {days, hours, minutes, seconds}) => add(lastExecutionTime, {days, hours, minutes, seconds})

Date helpers

While the time helpers are useful in handling the micro-aspects, the Day helpers are used to manipulate the macro-aspects such as Days, Weeks, Quarters, Years, Decades. These helpers simplify our applications where we might need to calculate if the user is above an age threshold, group data, and perform a bulk operation. More such highlighted functions are as shown below:

Most methods explained for weeks above are extended to months, quarters, and years. such as differenceInMonths, differenceInCalendarMonths, isSameMonth, isSameYear, setMonth, setYear, eachWeekendOfMonth,etc.

Some examples to help familiarise you with the above functions are:

  • differenceInWeeks: The left date must be the latest, else the method returns a -1.
console.log(differenceInWeeks(new Date(2015, 11, 22), new Date(2015, 11, 10))); // => 1
  • differenceInCalendarWeeks:
console.log(differenceInCalendarWeeks(new Date(2015, 11, 25), new Date(2015, 11, 10))); // => 2
  • getWeek: We can also specify what day the week starts on through the weekStartsOn flag. It ranges from 0-6 with 0 as Sunday. The week number starts from 1.
console.log(getWeek(Date.now())); // => 30
console.log(getWeek(Date.now(), {weekStartsOn: 1})); // => 29
  • isThisWeek:
console.log(isThisWeek(Date.now())); // => true
  • eachWeekendOfYear
console.log(eachWeekendOfYear(Date.now())); 
// => [Sat Jan 04 2020 00:00:00 GMT+0530 (India Standard Time)...]

Common Helpers and use cases

Simple operations

Apart from the above we can add, subtract and get the difference on two Date objects and their naming is extremely intuitive as illustrated below:

Some examples detailing the above methods are:

addQuarters(new Date("2020-01-01"), 3); 
// => Thu Oct 01 2020 05:30:00 GMT+0530 (India Standard Time)
max([new Date(2020, 2, 22), new Date(2020, 2, 2)]);
// => Sun Mar 22 2020 00:00:00 GMT+0530 (India Standard Time)

Formatting date time

One of the most common ask is to format date and time in a particular format that suits the application requirement. This also means that it is the most self-managed method which may go out of hand if the format were to change. The date-fns format util has a wide variety of formatting options and the signature of the method is so easy that we can change the format with the update of a string.

format(date, formatOptions)

While the formatOptions list is extensive some examples to cover a few of the most commonly used are:

const date = new Date("2020-07-19 18:32:00");format(date, "yy-MM-dd") => 20-07-19
format(date, "dd/MM/yyyy") => 19/07/2020
format(date, "do LLL yyyy") => 19th Jul 2020
format(date, "HH:mm:ss") => 18:32:00
format(date, "dd/MM/yyy hh:mm:ss x") => 19/07/2020 12:32:00 +0530
format(date, "dd/MM/yyy hh:mm:ss O) => 19/07/2020 06:32:00 GMT+5:30
format(new Date("2020-07-19 00:32:00"), "hh:mm:ss a") => 12:32:00 PM
format(new Date("2020-07-19 12:00:00"), "hh:mm:ss b") => 12:00:00 noon
format(new Date("2020-07-19 00:32:00"), "kk:mm:ss") => 24:32:00
format(new Date("2020-07-19 00:32:00"), "KK:mm:ss") => 00:32:00
//second timestamp
format(date, "t") => 1595163720
//millisecond timestamp
format(date, "T") => 1595163720000
/* some really interesting built-ins *///Localized date
format(date, "P") => 07/19/2020
format(date, "PPPP") => Sunday, July 19th, 2020
format(date, "PPPP G") => Sunday, July 19th, 2020 AD
//Localized time
format(date, "p") => 6:32 PM
format(date, "pppp") => 6:32:00 PM GMT+05:30
// Localized date and time
format(date, "PPPPpppp") => Sunday, July 19th, 2020 at 6:32:00 PM GMT+05:30

If you do not require such minute locale options while formatting then you can use lightFormat. Its called the same way of format and does not consider certain options which relate to Era, Quarter, Extended year.

lightFormat(new Date("2020-07-19 18:32:00"), "dd/MM/yy") => 19/07/20
lightFormat(new Date("2020-07-19 18:32:00"), "p") => throws RangeError

date-fns allows us to format intervals and durations through its formatDistance and formatDuration methods. The main difference between them is that the former formats the date in reference to a given date and the latter simply represents the duration in words. formatDistanceToNow is the same as formatDistance with the reference set to now()

formatDistance(Date.now(), sub(Date.now(), {hours: 4})); 
// => About 4 hours
formatDistance(Date.now(), sub(Date.now(), {hours: 4}), {addSuffix: true});
// => in about 4 hours
formatDistance(Date.now(), sub(Date.now(), {seconds: 4}), { addSuffix: true, includeSeconds: true});
// => in less than 5 seconds
formatDistanceStrict(Date.now(), sub(Date.now(), {seconds: 4}), { addSuffix: true, includeSeconds: true });
// => in 4 seconds
formatDuration({days: 4, hours: 3}); // => 4 days 3 hoursformatDuration({days: 4, hours: 3}, {delimiter: "|"});
// => 4 days|3 hours
formatDuration({days: 4, hours: 3, months: 44}, {delimiter: "|", format: [ "months", "hours"]})
// => 44 months|3 hours

ISO format

The ISO date format is a standard way of representing date defined as YYYY-MM-DDTHH:mm:ss.sssZ whereas UTC is a primary time standard. The ISO format always has the time standard as UTC and is signified by the Z at the end. date-fns has methods to achieve all the above functionalities in the ISO format. Some are illustrated below:

Internationalization

Most developers and apps face the issue of internationalization as it is not at the top of the TODO list. date-fns has an accompanying module called date-fns-tz for handling timezone formatting and conversions. This helper module can be installed by running npm install date-fns-tz and also provides typescript support.

Converting zoned time to UTC and ISO

If your system has the date-time stored in a particular timezone and have to convert it to UTC then the zonedTimeToUTC method is the way to go. It takes a date and the timezone in the IANA format of the input date time.

zonedTimeToUTC(date, originTimeZone)examples:zonedTimeToUtc(new Date("Sun Jul 19 2020 14:41:43"), "America/Los_Angeles"); 
// => Mon Jul 20 2020 03:11:43 GMT+0530 (India Standard Time)
zonedTimeToUtc(new Date("Sun Jul 19 2020 14:41:43"), "Asia/Bangkok");
// => Sun Jul 19 2020 13:11:43 GMT+0530 (India Standard Time)

Converting UTC to zoned time

Consider the use case where you are to send notifications or emails to your users, It is a hassle for them if you send the time and date details in UTC and they have to convert it to their local time. Hence another common use case is to convert the UTC time stored in the database to any timezone. This is achieved by utcToZonedTime. This too takes a date and timezone in IANA format, but the timezone represents the zone to which you want to convert it.

utcToZonedTime(date, destinationTimeZone)examples:console.log(utcToZonedTime(Date.now(), "Asia/Kolkata")); 
// => Mon Jul 20 2020 02:06:59 GMT+0530 (India Standard Time)
console.log(utcToZonedTime(Date.now(), "America/Los_Angeles"));
// Sun Jul 19 2020 13:36:59 GMT+0530 (India Standard Time)

Evaluation Metrics

Check out the package and some reading materials

Video review of the package

Video review of the package with interesting use cases and in-depth exploration of the features coming soon! For more related content, check out Unpackaged Reviews

Disclosures

The content and evaluation scores mentioned in this article/review is subjective and is the personal opinion of authors at Unpackaged Reviews based on everyday usage and research on popular developer forums. They do not represent any company’s views and are not impacted by any sponsorships/collaboration.

--

--

We’re a small team of devs reviewing code packages to help choose the one for your next project!