Home | Send Feedback

A closer look at date-fns

Published: January 08, 2018  •  Updated: January 09, 2018  •  javascript

A year ago I wrote a blog post about Moment.js. A very popular date/time JavaScript library.

The problem with Moment.js is that it imports a monolithic object with all the methods. Even if you only need one or two methods the application bundle contains everything. The library is not that big and when your application uses a lot of Moment.js functionality it may not matter.

But when you only need one method it would be nice if there is way to only import this specific method. Here comes date-fns to the rescue. A collection of date/time functions that you can individually import into your application. In the documentation they compare it with lodash but for date objects.

See also this discussion about the difference between date-fns and Moment.js

Size comparison

In this section we create a simple application with both libraries and compare the bundle size of the packaged application. I use Parcel for the bundling process.

First we create a new directory and a package.json file with npm init -y. Then we install Parcel and the two libraries. For this test I use Moment.js 2.22.2 and date-fns v2.0.0-alpha.7

npm install parcel-bundler -D
npm install moment
npm install date-fns@next

Next we create a directory src and add index.html, index-datefns.js and index-moment.js. The examples make a few date calculations and print the result on the screen. You find the complete source code of the project on GitHub: https://github.com/ralscha/blog/tree/master/datefns

To build the two versions set the script tag in index.html to either <script src="./index-datefns.js"></script> or <script src="./index-moment.js"></script> and then call the bundler.

npx parcel build src/index.html --no-cache --public-url .

Bundle size

The smaller size of the date-fns bundle is a result of the fact that the application only imported the required functions, with Moment.js the application imported the whole library.

Examples

In this section we are taking a closer look at some functions date-fns provides. This is not a complete overview of all methods that are part of the date-fns library. See the official documentation for more details and a complete list of all provided methods.

As mentioned before date-fns is a collection of independent date and time functions that you have to individually import into your application. date-fns does not provide a date/time wrapper and it does not add any methods to the built in Date object, but it works with the native Date object seeming less together.


Parameters

Almost all methods in the library take either a Date object, a string or a number as parameter. Internally date-fns converts these parameters to a Date object with the toDate function

const today = new Date();
const d1 = toDate(today);
const d2 = toDate(1515421707921);
const d3 = toDate('2018-01-11T03:30:03');
// d1 = Mon Jan 08 2018 15:30:09 GMT+0100 (W. Europe Standard Time)
// d2 = Mon Jan 08 2018 15:28:27 GMT+0100 (W. Europe Standard Time)
// d3 = Thu Jan 11 2018 03:30:03 GMT+0100 (W. Europe Standard Time)

The toDate function returns a clone when a Date object is passed to the function. All the functions in date-fns are immutable and don't change the parameters.
When a number is given to the function it is treated as number of milliseconds elapsed since January 1, 1970 00:00:00 UTC.
When a string is give, the function tries to parse it. The parser supports complete and partial implementations of ISO8610.

When you have functions that expect multiple parameters you can either provide arguments of the same type or mix the different types.

isAfter(new Date(2016, 11, 31), new Date(2017, 11, 31));
isAfter(1515422016315, 1515422016316);
isAfter(1515422016315, new Date(2017, 11, 31));
isAfter(new Date(2018, 0, 10), '2018-01-11T03:30:03');
isAfter(1515422016315, '2018-01-11T03:30:03');

Time Units

date-fns does not have functions that support every possible time unit, instead it provides functions that work only with one unit. Units are Millisecond, Second, Minute, Hour, Day, Weekday, Week, ISOWeek, Month, Quarter, Year and ISOYear. Not every function exists for every unit. For example the add* function exists in these variants: addMilliseconds, addSeconds, addMinutes, addHours, addDays, addWeeks, addMonths, addQuarters, addYear and addISOYears.


Parsing and Formatting

Converting a String to a Date object and vice versa is a common task in applications. For converting a String into a Date object date-fns provides the parse function.

parse requires three parameters. The input string to be parsed, the description of the format as string and the baseDate. The function returns a Date object when it was able to successfully parse the string.

const result = parse('31.01.2018', 'DD.MM.YYYY', new Date());
// Wed Jan 31 2018 00:00:00 GMT+0100 (W. Europe Standard Time)

parse supports a wide variety of format tokens. See the official documentation for a complete overview.

The third parameter (baseDate) is used when you parse partial dates like in this example

const result = parse('January 2nd', 'MMMM Do', new Date());
// Tue Jan 02 2018 00:00:00 GMT+0100 (W. Europe Standard Time)

The year is taken from the third parameter.


For the opposite way, from Date to string, we can use the format function. Unlike parse, format supports all three parameter types (Date, number and string). The format string (2nd parameter) supports the same tokens as parse.

const result = format(new Date(2018, 0, 15), 'MM/DD/YYYY');
// 01/15/2018

const result = format(1515424147742, 'MMMM Do, YYYY');
// January 8th, 2018

const result = format('2018-06-15T11:30:00', 'DDDo [day of] YYYY');
// 166th day of 2018

Set and Get

The set* and get* functions set a time unit to a specific value. As mentioned before date-fns does not change parameters so the set* functions always returns a new Date object with the changed value.

const result = getMinutes('2018-01-15T11:33:44.987'); 
// 33
const result = getMilliseconds('2018-01-15T11:33:44.987'); 
// 987
const result = getYear('2018-01-15T11:33:44.987'); 
// 2018
const result = setSeconds(1515424816229, 55);
// Mon Jan 08 2018 16:20:55 GMT+0100 (W. Europe Standard Time)
const result = setQuarter(new Date(2018, 0, 15), 3);
// Sun Jul 15 2018 00:00:00 GMT+0200 (W. Europe Summer Time)
const result = setMonth('2018-01-15T11:33:44.987', 6);
// Sun Jul 15 2018 11:33:44 GMT+0200 (W. Europe Summer Time)

Note that months are 0 based, quarters start with 1.


Comparing

date-fns provides comparing functions like isAfter, isBefore and isEqual that check if the first date ist after, before or equal the second date.

const result = isAfter(new Date(2018, 0, 15), '2018-01-16');
// false
const result = isAfter('2018-01-16', new Date(2018, 0, 15));
// true
const result = isBefore(1515424147742, 1515424147744);
// true
const result = isBefore('2018-01-16', '2018-01-17');
// true
const result = isEqual(1515424147742, 1515424147742);
// true
const result = isEqual(new Date(2018, 0, 15), '2018-01-15');
// true
const result = isEqual(new Date(2018, 0, 15), new Date(2018, 0, 16));
// false

For certain units a isSame* function exists that checks if the specific unit is the same in the two provided dates.

const result = isSameWeek(new Date(2018, 0, 15), '2018-01-16');
// true

const result = isSameHour(1515424147742, 1515414137199);
// false

const result = isSameMonth('2018-01-01', '2018-01-31');
// true

Arithmetic

The add* and sub* functions add and subtract a certain amount of a specific unit. Like the set* functions these functions don't mutate the provided parameter instead they return a new Date object.

const result = addWeeks(new Date(2018, 0, 15), 2);
// Mon Jan 29 2018 00:00:00 GMT+0100 (W. Europe Standard Time)

const result = addMinutes(1515424147742, 60);
// Mon Jan 08 2018 17:09:07 GMT+0100 (W. Europe Standard Time)

const result = addYears('2018-01-01', 5);
// Sun Jan 01 2023 00:00:00 GMT+0100 (W. Europe Standard Time)
const result = subSeconds('2018-01-31T11:55:44.987', 6);
// Wed Jan 31 2018 11:55:38 GMT+0100 (W. Europe Standard Time)

const result = subQuarters('2018-01-31T11:55:44.987', 1);
// Tue Oct 31 2017 11:55:44 GMT+0100 (W. Europe Standard Time)

const result = subMilliseconds('2018-01-31T11:55:44.987', 187);
const ms = getMilliseconds(result);
// 800

Weekdays

For weekdays there are some special functions that return the weekday and check if a date falls on a specific weekday. The getDay function returns a number between 0 = Sunday and 6 = Saturday. For each weekday a is* function exists and isWeekend to check if a date falls on a weekend (Saturday or Sunday)

const result = getDay(new Date(2018, 0, 8));
// 1

const result = isMonday(new Date(2018, 0, 8));
// true

const result = isWeekend(new Date(2018, 0, 8));
// false

Intervals

date-fns supports a special interval type that combines two dates. An interval object has two properties: start and end. Both properties can hold a Date, string or a number.

const interval1 = {start: '2018-01-01', end: '2018-01-15'};
const interval2 = {start: new Date(2018, 0, 14), end: 1517353200000};

date-fns provides four function that take intervals as parameters

areIntervalsOverlapping: checks if two intervals overlap

const result = areIntervalsOverlapping(interval1, interval2);
// true

eachDayOfInterval: returns an array of dates with each day of the interval

const result = eachDayOfInterval(interval1);
/*
[
   Mon Jan 01 2018 00:00:00 GMT+0100 (W. Europe Standard Time),
   Tue Jan 02 2018 00:00:00 GMT+0100 (W. Europe Standard Time)],
   ....
   Mon Jan 15 2018 00:00:00 GMT+0100 (W. Europe Standard Time)
]
*/

getOverlappingDaysInIntervals: returns the number of days that two intervals overlap

const result = getOverlappingDaysInIntervals(interval1, interval2);
// 1

isWithinInterval: checks if a date is within an interval

const result = isWithinInterval('2018-01-02', interval1);
// true

Difference

The difference* functions return the difference between two dates in a specific unit.

const result = differenceInSeconds(1515429928956, 1515429922956);
// 6

For days, weeks, months, quarters and years there are two functions that calculate the difference. The differenceInCalendar* functions only looks at the specific unit and returns the difference. The other differenceIn* function returns the difference in full days, weeks, months, quarters and years. For instance differenceInDays calculates how many full days (24 hours) are between the two dates.

const result = differenceInMonths('2018-03-15', '2018-01-17');
// 1

const result1 = differenceInCalendarMonths('2018-03-15', '2018-01-17');
// 2
const result2 = differenceInDays('2018-01-03T09:45:00', '2018-01-01T11:30:00');
// 1

const result3 = differenceInCalendarDays('2018-01-03T09:45:00', '2018-01-01T11:30:00');
// 2

Another common use case is representing the difference between two dates in words, like "3 days" or "in 20 minutes". For this use case date-fns provides three functions

formatDistance

const result = formatDistance('2018-01-15', '2018-01-01');
// 14 days

const result = formatDistance('2018-01-01', '2019-04-01');
// over 1 year

const result = formatDistance('2018-02-01', '2018-01-01');
// about 1 month

The function adds the word 'ago' and 'in' when addSuffix is set to true

const result = formatDistance(new Date(2018, 7, 1), new Date(2018, 0, 1), {addSuffix: true});
// in 7 months

const result = formatDistance(new Date(2018, 0, 1), new Date(2018, 0, 15), {addSuffix: true});
// 14 days ago

When includeSeconds is true the string contains a more detailed message when the difference is less than a minute.

const result = formatDistance(1515424149742, 1515424147742);
// less than a minute

const result = formatDistance(1515424149742, 1515424147742, {includeSeconds: true});
// less than 5 seconds

formatDistanceStrict: Like formatDistance but does not add words like 'almost', 'over' or 'less than'.

const result = formatDistanceStrict('2018-01-15', '2018-01-01');
// 14 days

const result = formatDistanceStrict('2018-01-01', '2019-04-01');
// 1 year

const result = formatDistanceStrict('2018-02-01', '2018-01-01');
// 1 month

formatRelative: This function returns a string for the last and next 6 days like 'last Sunday', 'yesterday', 'Tuesday'. For all the other dates it returns MM/DD/YYYY

const result = formatRelative(new Date(2018, 0, 4), new Date(2018, 0, 8));
// last Thursday at 12:00 a.m.

const result = formatRelative(new Date(2018, 0, 7), new Date(2018, 0, 8));
// yesterday at 12:00 a.m.

const result = formatRelative(new Date(2018, 0, 9), new Date(2018, 0, 8));
// tomorrow at 12:00 a.m.

const result = formatRelative(new Date(2018, 0, 10), new Date(2018, 0, 8));
// Wednesday at 12:00 a.m.

const result = formatRelative(new Date(2018, 0, 20), new Date(2018, 0, 8));
// 01/20/2018

Internationalization

The library contains translations for several languages. See the documentation for a list of all currently supported language.

To use a translation you need to import the locale object

import fr from 'date-fns/locale/fr';

and then pass it as an option to a function:

const result = formatDistance(
                  new Date(2018, 0, 15), 
                  new Date(2018, 0, 1), 
                  {locale: fr, addSuffix: true}
               );
// dans 14 jours

Only format, parse, formatDistance, formatDistanceStrict and formatRelative support internationalization.