DEV Community

loading...
Cover image for You might not need date-fns

You might not need date-fns

dmtrkovalenko profile image Dmitriy Kovalenko ・7 min read

Hola! Lazy dev here and today we are going to discuss date-fns. People often choose date libraries before they really need it. "How we will format the date?", "Are there any alternatives?"

But really, are there?

Am I a hater?

Sorry, this question was required. No, I'm not. Moreover, I was a super-active user and evangelist of date-fns. I am the creator of date-io and @material-ui/pickers which have been proposing to choose date-fns over the other date libraries.

But one day I said that date-fns is not a panacea
After the twitter thread, that was transformed in this blog post, date-fns' maintainer blocked me everywhere because it says that you might not need it. So probably this thread contains some useful information for people that choosing date lib – so I decided to share it in the blog as well! Hope you will have some fun reading it :)

That's it for prerequisites so let's start the discussion

You might not need date library at all

First of all, when you need to only show date values in the user-readable format – you can avoid date libraries at all. Today all the modern browsers (even IE11) and node.js perfectly support Intl.DateTimeFormat!

can I use Intl.DateTimeFormat

This means that if your task is only to show the date/time value in a user-readable format you can do the following:

const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0))
// Ouput will depend on user locale and timezone 
console.log(new Intl.DateTimeFormat().format(date));

It perfectly supports native IANA timezone and locale formatting. It means that you can not include the locales in the bundle at all.

const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
// Results below assume UTC timezone - your results may vary

console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2012"

console.log(new Intl.DateTimeFormat('fr').format(date));
// expected output: "20/12/2012"

If you need more

But there is a problem. When you need more than formatting – for example, parsing or you are working with dates too often so the native (not really best) Date API is not enough. You probably will start looking for some helpful date-management library.

🌈 One wonderful day we will probably be able to get rid of all libraries in favor of native API. Here is the proposal for Temporal, which provides a nice functional API for working with Date.

But today you probably will pick up date-fns. Nothing personal – only statistics. Date-fns it is the most popular date-library as for now. What about moment.js? It is dead.

Today date-fns is much more often used for new projects. Here are downloads stats from date-io.

date-io stats

Statistically, you will pick date-fns. But do you really need it?

Let's discuss some criterias that are commonly used to choose the date library, and see does date-fns is the best one or not?

Bundlesize

Date-fns is solving only 1 problem much better than any other date library. And that's not a bundlesize. πŸŽ‰ Surprise πŸŽ‰ date-fns takes mostly 18kb gzip without locales. Dayjs takes 6 (yes six kb).

Alt Text

But if compare not gzipped, but parsed size – date-fns is the biggest one

Alt Text


Ok. Ok. The problem date-fns solving REALLY nice is tree-shaking. Because each function has its own entry point and exported as esm, unused code will be removed from the bundle, right?

Treeshaking

Let's create a more "real-world" example, that uses the hardest to implement manually functions:

  • Formatting
  • Parsing
  • Display time from X to Y
  • 3 locales

Result:

Alt Text

As you can see date-fns takes 13.88kb gzip when only importing the most important functionality. It is a lot.

Here is a pretty fun example of a react datepicker that has a peer dependency on date-fns. Date-fns takes 3 times more space than the datepicker itself and mostly 1/3 size of react. And it's only to make a single date-picker work.

react-nice dates

Also as you saw in the bundlesize stats luxon did not change its size at all. This because luxon npm package provides only commonjs output which is not tree shakeable. So maybe one day it will become smaller.

But do not forget the most wonderful thing about Luxon – it is built over native Intl – so it doesn't bundle locales at all. You can support even 50 locales without any additional bundlesize for date formatting!

P.S. All date-fns' 75 locales bundle takes 80kb gzip
Alt Text

Bundlesize conclusion

Date-fns is not lightweight library for date/time management. There are underrated alternatives – e.g. Dayjs is much smaller when using around the same functionality.

API

The next criteria for choosing a library would be the API. API must be clear, well-typed, and comprehensive. And here the most unclear for me personally – why everybody is choosing date-fns?

Date-fns design is pretty straightforward – you have a separate function for everything. And this is totally perfect, but unfortunately, not for all javascript developers. The problem is that javascript doesn't have native function composition utils.

I mean that some complex code with date-fns is completely unreadable:

function checkIsBeforeDateFns(time: Date, maxTime: Date) {
  return isBefore(
    setMilliseconds(setSeconds(setMinutes(time, 0), 0), 0),
    maxTime
  );
}

Function executions need to be read like from the inside out. The first function call will be setMinutes and the last will be isBefore.

Let's compare the same functions in dayjs and luxon. They are using the old good chaining API. Most developers/editors/linters/static analyzers work like a charm with such APIs.

function checkIsBeforeDayjs(time: Dayjs, maxTime: Dayjs) {
  return time.minute(0).second(0).millisecond(0).isBefore(maxTime);
}

function checkIsBeforeLuxon(time: DateTime, maxTime: DateTime) {
  return time.set({ second: 0, minute: 0, millisecond: 0 }) < maxTime;
}

Much readable right? This is actually overall a common problem in functional programming. And it can be easily fixed by using some of the function composition techniques. For example here are the same functions with date-fns/fp submodule and ReasonML (now Rescript) – native functional language compiling to javascript. And this is awesome πŸ’œ

let checkIsBeforeDateFns = (time, maxTime) =>
  time
  |> DateFns.setMilliseconds(0)
  |> DateFns.setSeconds(0.)
  |> DateFns.setMinutes(0.)
  |> DateFns.isBefore(maxTime);

This is still just 4 function calls. But much much more readable. Beautiful!

By the way I am the maintainer of date-fns binding for ReasonML. Bring us a ⭐️

But ask yourself – do you and more important your team are ready for functional programming? And do you have all the required tools for it like pipe or compose?

If yes – take date-fns and be happy with functional programming πŸ‘¨β€πŸ’»πŸ‘©β€πŸ’».

Performance

You should not think about performance before the problem was encouraged.

Premature optimization is the root of all evil Β© Donald Knuth

The performance difference will be visible only on the thousand of function calls per second. But if you still interesting in performance differences between date-fns and the other libraries:

Short results from our date-io benchmark:

  • Date-fns is fastest for date calculations (add, subtract, before, etc)
  • Date-fns is fastest for date parsing
  • Moment is fastest for formatting (ha-ha didn't expect moment here)

Yes, date-fns is really fast because it works directly with a native date without creating any additional wrapper. Dayjs is focused on size instead of speed and Luxon is using Intl which is super slow 🐌.

So yes date-fns is the best option if you have performance issues with other libs. But do you really have?

Conclusion

Make sure that the author of this post is incompetent, subjective, stupid, awful, and lazy. So you must reach your own conclusions for your particular based on many factors.

BTW here is the repo with all date-fns comparison stuff from this post. And I will be happy if you will think about the requirement of date-fns after this reading πŸ€“

In the author's humble opinion there are no reasons to choose date-fns if you are not cooking functional programming. But as far I can see literally nobody using their really good functional approach 😿.

If this lazy author one day will start a new project in javascript he will probably do the following:

  • Try to start with native Intl formatting
  • When lib will become required choose dayjs – because its
    • a) ~Harder, Better, Faster, Stronger~
    • a) smaller
    • b) tree shakable by plugins
    • c) have a nice API

Thank you

For this loooong read and by the tradition:

No date-fns maintainers were harmed in the making of this article πŸ˜‰

Discussion

pic
Editor guide
Collapse
ninjin profile image
Jin

Some comparison of time-libs:

So be carefull Intl is very very slow.

Collapse
ninjin profile image
Collapse
dmtrkovalenko profile image
Dmitriy Kovalenko Author

Yea thank you. Our benchmark is comparing libs in node.js and it is slow enough because of node-icu.

But you probably won’t meet performance problems. So do not think about them prematurely

Thread Thread
ninjin profile image
Jin

I've already meet it with Angular, which likes to recalculate template expressions frequently to detect changes.