Zach’s ugly mug (his face) Zach Leat­herman
Recent Activity
  1. Florian Bögner Timo Tijhof Shib Willoughby Seth Falco Cthos Lukas The True Source of Value Matthias Zöchling
    “AI, make the website good”

    25 reposts 9 replies 2 mentions

  2. Christopher Kirk-Nielsen
  3. Doug Parker 🕸️
    Table of Contents Web Component

  4. PDFops Joe Dakroub Tobias Fedder katherine Rob Anneke Christian Alder Clayton Errington 🖥️
    Table of Contents Web Component

    1 repost

  5. William Allyn Nova (he/they) Charity Gamble Brian David Germany Font Awesome Cumulonimbus Font Awesome
    Icon Fall Web Component

    2 reposts

Never write your own Date Parsing Library

Dresser top with mini calendar of September and potted plant
#515k 629 July 23, 2025
@11ty/parse-date-strings

Parsing content date strings in Eleventy core.

0%
Popularity
npm35k Monthly downloads sparkline for @11ty/parse-date-strings, ranging 1k to 6k
Versions
Monthly publishes sparkline for @11ty/parse-date-strings, ranging 0 to 7 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/eleventy node_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 Supported Supported Supported Unsupported
YYYY-MM Supported Supported Supported Unsupported
YYYY-MM-DD Supported Supported Supported Supported
±YYYYYY-MM-DD Unsupported Supported Supported Supported
Optional - delimiters in dates Supported Unsupported Supported Supported
YYYY-MM-DDTHH Supported Unsupported Supported Supported
YYYY-MM-DD HH (space delimiter) Unsupported Supported Unsupported Supported
YYYY-MM-DDtHH (lowercase delimiter) Unsupported SupportedFace looking surprised SupportedFace looking surprised Supported
YYYY-MM-DDTHH:II Supported Unsupported Supported Supported
YYYY-MM-DDTHH:II:SS Supported Unsupported Supported Supported
Optional : delimiters in time Supported Unsupported Supported Supported
YYYY-MM-DDTHH:II:SS.SSS Supported SupportedFace looking surprised Supported Supported
YYYY-MM-DDTHH:II:SS,SSS Supported Unsupported Supported Supported
Microseconds (6 digit precision) Supported Unsupported Supported Supported
Nanoseconds (9 digit precision) Supported Unsupported Supported Supported
YYYY-MM-DDTHH.H Fractional hours Supported Unsupported Unsupported Unsupported
YYYY-MM-DDTHH:II.I Fractional minutes Supported Unsupported Unsupported Unsupported
YYYY-W01 ISO Week Date Supported Unsupported Supported Unsupported
YYYY-DDD Year Day Supported Unsupported Supported Unsupported
HH:II Supported Unsupported Supported Unsupported
YYYY-MM-DDTHH:II:SSZ Supported Unsupported Supported Supported
YYYY-MM-DDTHH:II:SS±00 Supported Unsupported Supported Supported
YYYY-MM-DDTHH:II:SS±00:00 Supported Unsupported Supported Supported
YYYY-MM-DDTHH:II:SS±0000 UnsupportedFace looking surprised Unsupported Supported Supported
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)

IndieWeb Avatar for https://unsplash.com/Poster image by Blessing Ri

< Older
How to import() a JavaScript String
Newer >
One weird trick to reduce Eleventy Image Build Times by 60%

Zach Leatherman IndieWeb Avatar for https://zachleat.com/is a builder for the web at Font Awesome and the creator of Build Awesome (née IndieWeb Avatar for https://www.11ty.devEleventy/11ty), an award-winning open source website generator. He measures website performance with speedlify and at one point became too fixated on web fonts. He has given 89 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of CloudCannon, Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »

6 Reposts
Stéphane DeschampsKrzysztof HankiewiczCory Dransfeldt :demi:Xavier B.Wheelsbot7Jak2k ???????? ????39c3
32 Likes
Nathan KnowlerThierry ????jcletouseyEric McCormickCthosJono YeongDaniel SaundersSylvain FaurebingelingJeremias MenichelliluciCory Dransfeldt :demi:Deni Mintsaev ????????Magical CatmaybeanerdNoah LiebmanSimon Cox :SEO:???? Lascapi ⁂SergiomikeMarc LittlemoreChristian AldersteveGauteGryphonDavid Bushell ☕LennyNilesh PrajapatiGreg WolanskiMichaelRobert LeeperGeri Reid
29 Comments
  1. Eric McCormick
    Eric McCormick

    Replying to zachleatsuch delightful madness.

  2. Tixie Salander
    Tixie Salander

    Replying to zachleatahahahahahahah

    1. Zach Leatherman :11ty:
      Zach Leatherman
      OP

      Replying to tixie✨ I’m glad you enjoyed it ✨

  3. Jeremias Menichelli
    Jeremias Menichelli

    Oh, this is amazing!

  4. Tixie Salander
    Tixie Salander

    Replying to zachleatawfully accurate :')

  5. Jared White (ResistanceNet ✊)
    Jared White (ResistanceNet ✊)

    Replying to zachleatbUt yOu lEarNed tHiNgs frOm oThEr pEoPle so yOu'Re jUst lIKe tHe LLMs

    1. Zach Leatherman :11ty:
      Zach Leatherman
      OP

      Replying to jaredwhiteon my worst days I feel like a human autocomplete (with a smidge of pesky morality thrown in)

  6. Zach Leatherman
    Zach Leatherman
    OP

    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.

  7. Dan Szabo
    Dan Szabo

    Stop, will you? Stop, Dave. Will you stop, Dave? Just what do you think you're doing, Dave?

  8. Zach Leatherman :11ty:
    Zach Leatherman
    OP

    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

  9. Jono Yeong
    Jono Yeong

    Oh no how do i turn it off

  10. Daniel Fisher(lennybacon)
    Daniel Fisher(lennybacon)

    Replying to zachleatSo you count how often it gets clicked vs. visits to the page?

    1. Zach Leatherman :11ty:
      Zach Leatherman
      OP

      Replying to lennybaconthe toggle is for me and only me

  11. Zach Leatherman
    Zach Leatherman
    OP

    there’s no going back

  12. Daniel Fisher(lennybacon)
    Daniel Fisher(lennybacon)

    Replying to zachleatBut I can see it! And I could toggle it. But I do not want to! Really not.

    1. Zach Leatherman :11ty:
      Zach Leatherman
      OP

      Replying to lennybaconfor the record, it doesn’t persist anywhere or do anything other than an animation

  13. Stuart Langridge
    Stuart Langridge

    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

  14. Barry Pollard
    Barry Pollard

    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…

  15. Zach Leatherman
    Zach Leatherman
    OP

    truly a pandora’s box model problem

  16. damianwalsh
    damianwalsh

    Replying to zachleatBrilliant. Went on a short rollercoaster ride from WTF to laughter when I clicked through and saw this.

    1. Zach Leatherman :11ty:
      Zach Leatherman
      OP

      Replying to damianwalsh:D yessssss

  17. Karl Dubost
    Karl Dubost

    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

    1. Zach Leatherman :11ty:
      Zach Leatherman
      OP

      Replying to karlcownext Halloween I will go as Date.parse, which is very scary

  18. Max Böck
    Max Böck

    For extra realism, enable it at random every few weeks and give the user 3 "opt-out credits" per year

  19. Kilian Valkhof
    Kilian Valkhof

    I dont know how much time you put into this Zach but it was absolutely worth it.

  20. Zach Leatherman
    Zach Leatherman
    OP

    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

  21. Michael
    Michael

    What a day to not be in the "dislike" beta.

  22. Zach Leatherman
    Zach Leatherman
    OP

    I don’t know what this is but I can hallucinate a probable outcome in my brain

Shamelessly plug your related post

These are webmentions via the IndieWeb and webmention.io.

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)