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 much Moment.js functionality, it may not matter.
But when you only need one method, it would be nice if there is a way only to 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.24.0 and date-fns 2.0.0
npm install parcel-bundler -D
npm install moment
npm install date-fns
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
- Moment.js: 162 KB (Gzip: 42.2 KB, Brotli: 32 KB)
- date-fns: 37.3 KB (Gzip: 11.4 KB, Brotli: 9.41 KB)
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 import into your application individually. 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 or a number as a parameter.
Internally date-fns converts these parameters to a Date object with the toDate
functions.
If you have a date string, you need to convert that manually with the parseISO
function.
import toDate from 'date-fns/toDate';
const today = new Date();
const d1 = toDate(today);
const d2 = toDate(1515421707921);
import parseISO from 'date-fns/parseISO';
const d3 = parseISO('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 passed to the function, it is treated as a number of milliseconds elapsed since January 1, 1970, 00:00:00 UTC.
When you have functions that expect multiple parameters, you can either provide arguments of the same type or mix the different types.
import isAfter from 'date-fns/isAfter';
import parseISO from 'date-fns/parseISO';
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), parseISO('2018-01-11T03:30:03'));
isAfter(1515422016315, parseISO('2018-01-11T03:30:03'));
Time Units ¶
Instead of providing one function that supports multiple time units, date-fns provides multiple variants of a function.
For example the add* function exists in these variants:
addMilliseconds
, addSeconds
, addMinutes
, addHours
, addDays
, addWeeks
, addMonths
, addQuarters
, addYear
and addISOYears
.
Not every function supports every time unit.
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 is able to parse the string successfully.
import parse from 'date-fns/parse';
const result = parse('31.01.2018', 'dd.MM.yyyy', new Date());
// Wed Jan 31 2018 00:00:00 GMT+0100
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.
Reversely, from Date to string, we can use the format
function. The format string (2nd parameter) supports the same tokens as parse
.
import format from 'date-fns/format';
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(parseISO('2018-06-15T11:30:00'), 'Do \'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 return a new Date object with the changed value.
import getMinutes from 'date-fns/getMinutes';
const result = getMinutes(parseISO('2018-01-15T11:33:44.987'));
// 33
import getMilliseconds from 'date-fns/getMilliseconds';
const result = getMilliseconds(parseISO('2018-01-15T11:33:44.987'));
// 987
import getYear from 'date-fns/getYear';
const result = getYear(parseISO('2018-01-15T11:33:44.987'));
// 2018
import setSeconds from 'date-fns/setSeconds';
const result = setSeconds(1515424816229, 55);
// Mon Jan 08 2018 16:20:55 GMT+0100 (W. Europe Standard Time)
import setQuarter from 'date-fns/setQuarter';
const result = setQuarter(new Date(2018, 0, 15), 3);
// Sun Jul 15 2018 00:00:00 GMT+0200 (W. Europe Summer Time)
import setMonth from 'date-fns/setMonth';
const result = setMonth(parseISO('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 is after, before, or equal the second date.
import isAfter from 'date-fns/isAfter';
const result = isAfter(new Date(2018, 0, 15), parseISO('2018-01-16'));
// false
const result = isAfter(parseISO('2018-01-16'), new Date(2018, 0, 15));
// true
import isBefore from 'date-fns/isBefore';
const result = isBefore(1515424147742, 1515424147744);
// true
const result = isBefore(parseISO('2018-01-16'), parseISO('2018-01-17'));
// true
import isEqual from 'date-fns/isEqual';
const result = isEqual(1515424147742, 1515424147742);
// true
const result = isEqual(new Date(2018, 0, 15), parseISO('2018-01-15'));
// true
const result = isEqual(new Date(2018, 0, 15), new Date(2018, 0, 16));
// false
For specific units, a isSame*
function exists that checks if the specific unit is the same in the two provided dates.
import isSameWeek from 'date-fns/isSameWeek';
const result = isSameWeek(new Date(2018, 0, 15), parseISO('2018-01-16'));
//true
import isSameHour from 'date-fns/isSameHour';
const result = isSameHour(1515424147742, 1515414137199);
//false
import isSameMonth from 'date-fns/isSameMonth';
const result = isSameMonth(parseISO('2018-01-01'), parseISO('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.
import addWeeks from 'date-fns/addWeeks';
const result = addWeeks(new Date(2018, 0, 15), 2);
//Mon Jan 29 2018 00:00:00 GMT+0100 (W. Europe Standard Time)
import addMinutes from 'date-fns/addMinutes';
const result = addMinutes(1515424147742, 60);
//Mon Jan 08 2018 17:09:07 GMT+0100 (W. Europe Standard Time)
import addYears from 'date-fns/addYears';
const result = addYears(parseISO('2018-01-01'), 5);
//Sun Jan 01 2023 00:00:00 GMT+0100 (W. Europe Standard Time)
import subSeconds from 'date-fns/subSeconds';
const result = subSeconds(parseISO('2018-01-31T11:55:44.987'), 6);
//Wed Jan 31 2018 11:55:38 GMT+0100 (W. Europe Standard Time)
import subQuarters from 'date-fns/subQuarters';
const result = subQuarters(parseISO('2018-01-31T11:55:44.987'), 1);
//Tue Oct 31 2017 11:55:44 GMT+0100 (W. Europe Standard Time)
import subMilliseconds from 'date-fns/subMilliseconds';
import getMilliseconds from 'date-fns/getMilliseconds';
const result = subMilliseconds(parseISO('2018-01-31T11:55:44.987'), 187);
const ms = getMilliseconds(result);
//800
Weekdays ¶
For weekdays some special functions 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)
import getDay from 'date-fns/getDay';
const result = getDay(new Date(2018, 0, 8));
//1
import isMonday from 'date-fns/isMonday';
const result = isMonday(new Date(2018, 0, 8));
//true
import isWeekend from 'date-fns/isWeekend';
const result = isWeekend(new Date(2018, 0, 8));
//false
Intervals ¶
date-fns supports a particular interval type that combines two dates. An interval object has two properties: start
and end
. Both properties can hold a Date or a number.
const interval1 = {start: parseISO('2018-01-01'), end: parseISO('2018-01-15')};
const interval2 = {start: new Date(2018, 0, 14), end: 1517353200000};
date-fns provides four functions that take intervals as parameters
areIntervalsOverlapping
: checks if two intervals overlap
import areIntervalsOverlapping from 'date-fns/areIntervalsOverlapping';
const result = areIntervalsOverlapping(interval1, interval2);
// true
eachDayOfInterval
: returns an array of dates with each day of the interval.
import eachDayOfInterval from 'date-fns/eachDayOfInterval';
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.
import getOverlappingDaysInIntervals from 'date-fns/getOverlappingDaysInIntervals';
const result = getOverlappingDaysInIntervals(interval1, interval2);
// 1
isWithinInterval
: checks if a date is within an interval
import isWithinInterval from 'date-fns/isWithinInterval';
const result = isWithinInterval(parseISO('2018-01-02'), interval1);
// true
Difference ¶
The difference*
functions return the difference between two dates in a specific unit.
import differenceInSeconds from 'date-fns/differenceInSeconds';
const result = differenceInSeconds(1515429928956, 1515429922956);
// 6
For days, weeks, months, quarters, and years two functions calculate the difference. The differenceInCalendar*
functions only look at the specific unit and return the difference. The other differenceIn*
functions return 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.
import differenceInMonths from 'date-fns/differenceInMonths';
const result = differenceInMonths(parseISO('2018-03-15'), parseISO('2018-01-17'));
//1
import differenceInCalendarMonths from 'date-fns/differenceInCalendarMonths';
const result = differenceInCalendarMonths(parseISO('2018-03-15'), parseISO('2018-01-17'));
//2
import differenceInDays from 'date-fns/differenceInDays';
const result = differenceInDays(parseISO('2018-01-03T09:45:00'), parseISO('2018-01-01T11:30:00'));
// 1
import differenceInCalendarDays from 'date-fns/differenceInCalendarDays';
const result = differenceInCalendarDays(parseISO('2018-01-03T09:45:00'), parseISO('2018-01-01T11:30:00'));
// 2
Another everyday 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
import formatDistance from 'date-fns/formatDistance';
const result = formatDistance(parseISO('2018-01-15'), parseISO('2018-01-01'));
//14 days
const result = formatDistance(parseISO('2018-01-01'), parseISO('2019-04-01'));
//over 1 year
const result = formatDistance(parseISO('2018-02-01'), parseISO('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'.
import formatDistanceStrict from 'date-fns/formatDistanceStrict';
const result = formatDistanceStrict(parseISO('2018-01-15'), parseISO('2018-01-01'));
//14 days
const result = formatDistanceStrict(parseISO('2018-01-01'), parseISO('2019-04-01'));
//1 year
const result = formatDistanceStrict(parseISO('2018-02-01'), parseISO('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
import formatRelative from 'date-fns/formatRelative';
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:
import formatDistance from 'date-fns/formatDistance';
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.