Skip to content

Commit

Permalink
Finish with the blogpost
Browse files Browse the repository at this point in the history
  • Loading branch information
kossnocorp committed Dec 18, 2023
1 parent 29bd721 commit 8d82d40
Showing 1 changed file with 110 additions and 29 deletions.
139 changes: 110 additions & 29 deletions src/content/blog/2023-12-18-v3-is-out.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,47 @@
---
title: "date-fns v3 is out!"
description: ""
pubDate: "18 Dec 2023"
---

🎉 After many months of development, v3 is out!

For most developers, upgrading won’t require any changes. For a few, it will be pretty trivial.
For most developers, upgrading won’t require any changes. For some, it will be pretty trivial.

However, the release brings tons of good stuff, so for the starter, here’s a quick overview of the most notable changes:

- The library is now 100% TypeScript with brand-new handcrafted types.
- ESM support for Node.js.
- Support for Date class extensions like [UTCDate](https://github.com/date-fns/utc).
- Removed arguments checking and conversion code from all functions, except for formatting and parsing, which resulted in a smaller minimal build size.
- String date arguments are not back!
- No more IE support.
- New flat library structure improving DX for ESM/Deno (`node_modules/date-fns/add.mjs`).
- String date arguments are now back!
- Support for Date class extensions like [UTCDate](https://github.com/date-fns/utc).
- ESM support for Node.js.
- All functions now export via named exports, improving compatibility with different setups.
- New flat library structure improving DX for ESM/Deno (`node_modules/date-fns/add.mjs`).
- No more IE support.

If you want to jump right into it, [see the v3.0.0 change log](https://date-fns.org/v3.0.0/docs/Change-Log#3.0.0-2023-12-18).

In this blog post I'll explain the changes in a bit more detail and also give you some context for the choices we made working on v3.
In this blog post, I'll explain the changes in more detail and give context for the choices we made while working on v3.

## TypeScript support

This release started as a rewrite to TypeScript. When I started working on the library in 2014, TypeScript seemed a niche project.
This release started as a rewrite to TypeScript. When I started working on the library in 2014, TypeScript seemed like a niche project.

Since then the world has changed and most of the developers realized the power of types and largely adopted the language.
Since then, the world has changed, and most developers have realized the power of types and largely adopted the language.

In date-fns types were an afterthough generated from JSDoc. I didn't really understand how it works and over the years, the generated types become 23K LOC monster with no hope for an easy fix.
In date-fns, types were an afterthought generated from JSDoc. I didn't really understand how it worked, and over the years, the generated types became a 23K LOC monster with no hope for an easy fix.

To make sure date-fns provides first-class TypeScript support we decided to rewrite hundreds of functions to TypeScript. We carefully handcrafted every type and interface. We exported everything including options interfaces for every function.
To ensure date-fns provides first-class TypeScript support, we rewrote hundreds of functions to TypeScript. We carefully handcrafted every type and interface. We exported everything, including options interfaces for every function, so you can easily access those.

It took a while, but with the help of the community it happened and I'm very proud of the result.
It took a while, but it happened with the community's help, and I'm very proud of the result.

## Asd
## Removed arguments checking

The big theme of major date-fns releases is fixing the mistakes we made in the previous ones.
While migrating to TypeScript, I reevaluated the value of the argument checks we introduced in v2.

When working on the v1 API, we drew inspiration from many places, from Moment.js to Ruby on Rails. It resulted in an inconsistent API and approach to dealing with edge cases. So, when the time came, we decided to rework the library from the ground up, reviewing every bit of a hundred functions. I decided to take the most conservative route, between developer experience and making the API error-proof, always choosing the latter. We would use an existing standard, replacing familiar JS patterns where we could.
The idea behind them was to provide as much type safety to the runtime as possible. Checking the number of arguments and their types seemed like a good idea.

Back in the time, JavaScript fatigue was a big topic. New library versions, often incompatible, would be released every few months, making users endlessly refactor their work to keep their dependencies working. Since we had to make significant breaking changes, we packed changes worth a few major versions in a single release.

All that, of course, backfired. All started in long development cycles and endless “any updates,” “what’s the ETA” (and other less “polite”) comments. Many breaking changes, especially the ones that degraded the developer experience in favor of the “best” approach, caused an overwhelming amount of (justified) critique. Another side effect is that because of many guardrails, the library build size increased.
Now, with first-class TypeScript support, it seems redundant.

While it was worth it, as in the end, we got extremely consistent API, in v3, I decided to take a step back and make the API more developer-friendly. Having time to reflect, I now see which trade-offs were the least efficient.
So, we removed as many checks as possible, leaving only the format and parse functions mostly intact.

Also, while having many ideas for improving the library that require breaking changes, I decided to focus on a few and make smaller, iterative steps.
Delegating type safety to TypeScript made the source code cleaner and improved the minimal build size.

The biggest (but invisible for the most) change is that we, with the help of the community, rewrote the library to TypeScript.
Before, I was happy to bring the minimal build size down to [300 bytes](/300-bytes/). Now it's just 200!

## Strings as arguments

Expand All @@ -61,6 +53,95 @@ However, it did not solve the issue for users. They would wrap strings in `new D

I admit my mistake, and now the strings are back. It might cause an influx of new issues, but I’ll deal with them separately.

## Exceptions
## Support for Date extensions

Timezones support is one of the oldest feature requests I have masterfully avoided for years. I didn't want to reimplement Moment.js IANA functionality as shipping a huge timezones database contradicts the goal of making the most lightweight date library for JavaScript.

However, I decided to do something about it finally and released [@date-fns/utc](https://github.com/date-fns/utc) that provides the `UTCDate` class which is an extension that maps regular functions like `getHours` to the UTC versions like `getUTCHours`.

That enabled date-fns to perform all calculations in UTC without changing its code. However, making it happen required overhauling how arguments are handled, so I postponed it until v3.

Also, thanks to TypeScript generics, if you pass the `UTCDate` date as an argument and expect a date back, you'll get the `UTCDate`.

This change opens the door for more date extensions in the future.

## ESM support for Node.js

Another big thing that has changed since the v2 release is that Node.js got ESM support. A simple problem on the surface became an enormous task requiring a build system overhaul.

In v3, I had to rewrite the build system almost entirely, so I tackled Node.js ESM as well.

date-fns is still a dual package with both CommonJS and ESM, but given how much effort is required to make it right, v4 will be ESM-only.

## No more default exports

All functions used to export default, but while working on ESM support for Node.js and testing the library against [Are the types wrong?](https://github.com/arethetypeswrong/arethetypeswrong.github.io) I found it impossible to make it work in all possible setups due to the nature of how `export default` behaves.

So I decided to compromise and switch to named exports, so now you import functions from their submodules, you'll need to change your code:

```ts
import addDays from "date-fns/addDays";
// ->
import { addDays } from "date-fns/addDays";
```

[To my surprise, it broke Next.js](https://twitter.com/kossnocorp/status/1731181274579325260). So, after some wrestling with it, I decided to add a fallback so that Next.js users don't come to me complaining. It sucks, but there's nothing I can do.

By the way, the Next.js team never did get back to me. Hello?!

## Flat structure

Since I was reworking the build system, I decided to address the annoyance that browser ESM and Deno users had to deal with. The index files!

Before, the import looked like that: `https://unpkg.com/date-fns/addDays/index.mjs`

Now, the library structure is flat, so there are no more ugly index files: `https://unpkg.com/date-fns/addDays.mjs`

Neat, right?

## Bye-bye IE

I shipped v2 four years ago, and IE still had its legs. Now it's time to say goodbye!

IE was never a good boy.

## Some history

The big theme of major date-fns releases is fixing the mistakes we made in the previous ones.

When working on the v1 API, we drew inspiration from many places, from Moment.js to Ruby on Rails. It resulted in an inconsistent API and approach to dealing with edge cases. So, when the time came, we decided to rework the library from the ground up, reviewing every bit of a hundred functions. I decided to take the most conservative route, between developer experience and making the API error-proof, always choosing the latter. We would use an existing standard, replacing familiar JS patterns where possible.

Back in the time, JavaScript fatigue was a big topic. New library versions, often incompatible, would be released every few months, making users endlessly refactor their work to keep their dependencies working. Since we had to make significant breaking changes, we packed changes worth a few major versions in a single release.

All that, of course, backfired. All started in long development cycles and endless “any updates,” “what’s the ETA” (and other less polite) comments. Many breaking changes, especially the ones that degraded the developer experience in favor of the “best” approach, caused an overwhelming amount of (justified) critique. Another side effect is that the library's build size increased because of many guardrails.

While it was worth it, as in the end, we got highly consistent API, in v3, I decided to take a step back and make the API more developer-friendly. Having time to reflect, I now see which trade-offs were the least efficient.

Also, while having many ideas for improving the library that requires breaking changes, I decided to focus on a few and make smaller, iterative steps.

And it has still taken three years since I announced migration to TypeScript.

## v4 and beyond

To stay relevant, date-fns must evolve and adapt to the ever-changing world of JavaScript.

While it's a bit early to start working with [`Temporal`](https://tc39.es/proposal-temporal/docs/), it's time to embrace [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) fully.

`format` stays the most popular yet most heavy function in date-fns, which power most developers don't need. Often, people use it to format an ISO date instead of using built-in functionality:

```ts
new Date().toISOString().slice(0, 10);
//=> '2023-12-18'
```

The rest can solve their problems with `Intl`.

So my goal for v4 is to extract I18n to separate packages like `@date-fns/es` and `@date-fns/de` and replace `format`, `formatDistance`, etc, with Intl alternatives `intlFormat`, `intlFormatDistance`.

I hope this will make people migrate to much lighter functions and reduce the build and package size significantly.

It's famous last words, but this time, I promise it won't take three years to finish v4.

---

To mitigate missing validation I added new class `Interval` that reimplements the validation logic that previously was
I hope you'll like what we did with v3. If you want to support the project, you can [become my sponsor on GitHub](https://github.com/sponsors/kossnocorp).

0 comments on commit 8d82d40

Please sign in to comment.