Never write your own Date Parsing Library

- Popularity
- npm35k
- Versions
8 versions in
2.0.6- Audit
-
0 reports
0 deps - Issues
-
0
2 - PRs
-
0
4
Never write your own date parsing library.
Never. No exceptions.
Never have I ever…
So… I’ve written my own date parsing library.
Why? Our story begins seven years ago in the year 2018. I made the very sensible choice to adopt luxon as the Date Parsing library for Eleventy. This parsing behavior is used when Eleventy finds a String for the date value in the Data Cascade (though YAML front matter will bypass this behavior when encountering a YAML-compatible date).
This choice was good for Eleventy’s Node.js-only requirements at the time: accurate and not too big (relatively speaking). Eleventy has used luxon since @0.2.12 and has grown with the dependency all the way through @3.7.1. Now that’s what I call a high quality dependency!
As we move Eleventy to run in more JavaScript environments and runtimes (including on the client) we’ve had to take a hard look at our use of Luxon, currently our largest dependency:
- 4.7 MB of 21.3 MB (22%) of
@11ty/eleventynode_modules - 229 kB of 806 kB (28%) of
@11ty/client(not yet released!) bundle size (unminified)
Given that our use of Luxon is strictly limited to the DateTime.fromISO function for ISO 8601 date parsing (not formatting or display), it would have been nice to enable tree-shaking on the Luxon library to reduce its size in the bundle (though that wouldn’t have helped the node_modules size, I might have settled for that trade-off). Unfortunately, Luxon does not yet support tree-shaking so it’s an all or nothing for the bundle.
The Search Begins
I did the next sensible thing and looked at a few alternatives:
| Package | Type | Disk Size | Bundle Size |
|---|---|---|---|
luxon@3.7.1 |
Dual | 4.59 MB |
81.6 kB (min) |
moment@2.30.1 |
CJS | 4.35 MB |
294.9 kB (min) |
dayjs@1.11.13 |
CJS | 670 kB |
6.9 kB (min) |
date-fns@4.1.0 |
Dual | 22.6 MB |
77.2 kB (min) |
The next in line to the throne was clearly dayjs, which is small on disk and in bundle size. Unfortunately I found it to be inaccurate: dayjs fails about 80 of the 228 tests in the test suite I’m using moving forward.
As an aside, this search has made me tempted to ask: do we need to keep Dual publishing packages? I prefer ESM over CJS but maybe just pick one?
Breaking Changes
Most date parsing woes (in my opinion) come from ambiguity: from supporting too many formats or attempting maximum flexibility in parsing. And guess what: ISO 8601 is a big ’ol standard with a lot of subformats. There is a maintenance freedom and simplicity in strict parsing requirements (don’t let XHTML hear me say that).
Consider "200". Is this the year 200? Is this the 200th day of the current year? Surprise, in ISO 8601 it’s neither — it’s a decade, spanning from the year 2000 to the year 2010. And "20" is the century from the year 2000 to the year 2100.
Moving forward, we’re tightening up the default date parsing in Eleventy (this is configurable — keep using Luxon if you want!).
Luckily we have a north star date format: RFC 9557, billed as “an extension to the ISO 8601 / RFC 3339” formats and already in use by the upcoming Temporal web standard APIs for date and time parsing coming to a JavaScript runtime near you.
There are a few notable differences:
| Format | ISO 8601 | Date.parse* |
luxon |
RFC 9557 |
|---|---|---|---|---|
YYYY |
||||
YYYY-MM |
||||
YYYY-MM-DD |
||||
±YYYYYY-MM-DD |
||||
Optional - delimiters in dates |
||||
YYYY-MM-DDTHH |
||||
YYYY-MM-DD HH (space delimiter) |
||||
YYYY-MM-DDtHH (lowercase delimiter) |
||||
YYYY-MM-DDTHH:II |
||||
YYYY-MM-DDTHH:II:SS |
||||
Optional : delimiters in time |
||||
YYYY-MM-DDTHH:II:SS.SSS |
||||
YYYY-MM-DDTHH:II:SS,SSS |
||||
| Microseconds (6 digit precision) | ||||
| Nanoseconds (9 digit precision) | ||||
YYYY-MM-DDTHH.H Fractional hours |
||||
YYYY-MM-DDTHH:II.I Fractional minutes |
||||
YYYY-W01 ISO Week Date |
||||
YYYY-DDD Year Day |
||||
HH:II |
||||
YYYY-MM-DDTHH:II:SSZ |
||||
YYYY-MM-DDTHH:II:SS±00 |
||||
YYYY-MM-DDTHH:II:SS±00:00 |
||||
YYYY-MM-DDTHH:II:SS±0000 |
- Unsupported
- Inaccurate parsing
- Face looking surprised
- Surprising (to me)
* Note that Date.parse results may be browser/runtime dependent. The results above were generated from Node.js.
A new challenger appears
It is with a little trepidation that I have shipped @11ty/parse-date-strings, a new RFC 9557 compatible date parsing library that Eleventy will use moving forward.
The support table of this library matches the RFC 9557 column documented above. It’s focused on parsing only and our full test suite compares outputs with both the upcoming Temporal API and existing Luxon output.
While there are a few breaking changes when compared with Luxon output (noted above), this swap will ultimately prepare us for native Temporal support without breaking changes later!
| Package | Type | Disk Size | Bundle Size |
|---|---|---|---|
@11ty/parse-date-strings@2.0.4 |
ESM | 6.69 kB |
2.3 kB (min) |
This library saves ~230 kB in the upcoming @11ty/client bundle. It should also allow @11ty/eleventy node_modules install weight to drop from 21.3 MB to 16.6 MB. (Some folks might remember when @11ty/eleventy@1 weighed in at 155 MB!)
Late Additions
For posterity, here are a few other alternative date libraries / Temporal polyfills that I think are worth mentioning (and might help you in different ways on your own date parsing journey):
| Package | Type | Disk Size | Bundle Size |
|---|---|---|---|
@js-temporal/polyfill@0.3.0 |
Dual | 2.98 MB |
186.5 kB (min) |
temporal-polyfill@0.3.0 |
Dual | 551 kB |
56.3 kB (min) |
@formkit/tempo@0.1.2 |
Dual | 501 kB |
17.3 kB (min) |



29 Comments
Replying to zachleatsuch delightful madness.
Replying to zachleatahahahahahahah
Replying to tixie✨ I’m glad you enjoyed it ✨
Oh, this is amazing!
Replying to zachleatawfully accurate :')
Replying to zachleatbUt yOu lEarNed tHiNgs frOm oThEr pEoPle so yOu'Re jUst lIKe tHe LLMs
Replying to jaredwhiteon my worst days I feel like a human autocomplete (with a smidge of pesky morality thrown in)
this is using a <squirm-inal> Web Component from 2021: github.com/zachleat/squ... 2021-vintage Web Components are React 17-era. No big refactors or rewrites required.
Stop, will you? Stop, Dave. Will you stop, Dave? Just what do you think you're doing, Dave?
this is using a <squirm-inal> Web Component from 2021: https://github.com/zachleat/squirminal/ 2021-vintage Web Components are React 17-era. No big refactors or rewrites required. GitHub - zachleat/squirminal: The squirminal is a fake antique terminal web component
Oh no how do i turn it off
Replying to zachleatSo you count how often it gets clicked vs. visits to the page?
Replying to lennybaconthe toggle is for me and only me
there’s no going back
Replying to zachleatBut I can see it! And I could toggle it. But I do not want to! Really not.
Replying to lennybaconfor the record, it doesn’t persist anywhere or do anything other than an animation
Thought process: 1. why would you add the mode in the first place 2. Hang on this is Zach 3. I wonder what it actually does 4. Heh, nice
4. Oh no you can’t turn it off once you turn it on!! 5. Knowing Zach (and AI features), I bet that’s intentional…
truly a pandora’s box model problem
Replying to zachleatBrilliant. Went on a short rollercoaster ride from WTF to laughter when I clicked through and saw this.
Replying to damianwalsh:D yessssss
Replying to zachleatProbably not fully exact anymore, but I did that this year. https://codepen.io/webcompat/pen/QwWOYjY test cases for new Date() in JavaScript
Replying to karlcownext Halloween I will go as Date.parse, which is very scary
For extra realism, enable it at random every few weeks and give the user 3 "opt-out credits" per year
I dont know how much time you put into this Zach but it was absolutely worth it.
not much time at all! I had an old web component that I repurposed that progressively renders text nodes in HTML github.com/zachleat/squ... similar to work here www.zachleat.com/web/queue-co... and used on your-year-on.netlify.com
What a day to not be in the "dislike" beta.
I don’t know what this is but I can hallucinate a probable outcome in my brain