Hacker News new | comments | ask | show | jobs | submit login
You Don't Need Moment.js (github.com)
241 points by ggregoire 5 months ago | hide | past | web | favorite | 85 comments

If you are dealing with timestamps from the past (e.g. birthdays via a datepicker that constructs Date objects), then you should watch out for different browsers' inconsistent handling of Daylight Savings Time. Moment is really indispensable here, since it includes a database of past years' DST transition dates. I wrote a blog post about it here: https://illuminatedcomputing.com/posts/2017/11/javascript-ti...

I agree, it's similar to Joda for Java. Sure, Joda does some things that exist in the standard library, but you can be sure that by using Joda that you're getting it right in all kinds of twisted scenarios you never considered.

actually joda is built into java8+ called java.time

We can only hope moment or at least its API gets similar treatment.

Thanks for the tip, I had no idea and was still including the dependency.

It's not API compatible, so if you use Joda and you're thinking of using java.time, it's going to be a lot of refactoring.

It's pretty straight forward to migrate from Joda to Java 8's builtin classes though. Just remove the dependency on Joda Time and let the compiler tell you which files to fix.

This blog post lists which Java 8 classes replace their Joda Time counterparts:


The worst is when you’re stuck on older libraries that still use Joda transitively, so you gotta transition to and from joda at the barriers.

Past time zones should only be a fairly specialist need: our front end code (SPA that is especially in the time domain) doesn't need to deal with past time zones. Then again, although our SPA is 100% JSON, we do all date and time processing at the server.

Putting UTC times into our front end would require the front end to know too many business rules.

> e.g. birthdays via a datepicker that constructs Date objects

> [From your comment in link]: The birthday will be wrong from the very start, because datepickers din't care about people can be born in some TZ and fill forms different TZs. Normally if you're only interested in date only, it doesn't matter if time part is off by hour or more hours. But if you live in Africa, then it could be whole day difference (because of how date will parsed).

I have trouble understanding your example - I am guessing you are sending UTC date including time of birth? Seems a little contrived. Maybe pick a better example (calendars or alerts and timezones?)

That said, I agree that trusting the browser implementation is mad. We still have clients accessing using IE8 (we just dropped support), Safari 7 (iPhone 4), and a fair number use Chrome 30/33 because that is the version the WebView is stuck on for Android 4.4.

Some of our users have the date on their computer set a week out (or more)!

Edit: just saw comment at end of your article "If your users enter birthdays with some kind of date picker, you probably suffer from this bug!". I think you just have a problem with your code: you would not use UTC date/time from a date picker, and you should either zero the time component or don't use Date internally when you just need a date - e.g. an input type=date naturally returns "yyyy-mm-dd".

The problem is that if you use a zeroed time component, but at some point it's assumed 00:00:00 (midnight), shifting timezones might mean that it shifts to the previous day. Even if you then truncate the hour portion before display, the date is wrong.

For example, you use a datepicker at some point, and it stores 2000-01-01 00:00:00 in the US/Eastern time zone (because that's where you and the computer you're on are when you do it). If you happen to store that as UTC, you might find some interesting outputs for people on the West coast if you convert back to a local (US/Pacific) timestamp. Specifically, you'll get 1999-12-31 21:00:00, and if you do this blindly and throw away the hour portion, you still get the wrong date.

When you want dates, you often really want to store just the date potion. Any time you want a date and time, you probably also want a timezone along with it, or at a minimum, you want to know what type of output your use case requires and handle it appropriately. Dates and times are one of those things that seems simple until you've had to deal with the details and encountered the problems enough that you always treat it with a bit of respect.

Exactly: it isn't difficult to safely use Date() to hold and compare dates, but you need enough experience to avoid the gotchas (which are not unique to JavaScript).

You can roll your own solution using Number for date, since Number can safely hold a 52 bit integer (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...) and if you avoid using 32 bit Unix epoch dates as you get a falsy value on 1/1/1970.

Even just using dates can be a mess. At midday UTC everyone experiences the same date. The other 23 hours of the day, a varying proportion of people are experiencing a different date.

"using dates" therefore works out to "not caring about the time of day" and that reduces to "not caring about the time of day in the same timezone" since the same time of day in two timezones have a ~50% chance of being in different dates.

It really comes down to "does your application need to worry about time zones?" If so, then you probably need to treat this seriously, and getting help with that is good. If not, not.

> At midday UTC everyone experiences the same date.

Afraid not. Kiribati, Tonga, Samoa, and New Zealand might be in the following day - they all experience UTC+13. Kiribati and Samoa even experience UTC+14.



> At midday UTC everyone experiences the same date.

Would that this be true! There are time zones as far ahead as UTC+14: https://en.wikipedia.org/wiki/UTC%2B14:00

Sure, but I was referring more to the times you actually do just want a date. It's perfect for a birthday, or "this day in history", or any number of other things. Nobody really cares that while it was Tuesday where you were born, it was Wednesday for them.

In some instances, if you really only care about the day something happened, adding a time is just extra details to screw up. But if you do need need a a time, you very well may also need a timezone.

well, yes, 99% of the time you're right... but... an astrology application (for example) will care about your timezone for your birthday.

It comes down to the use case, not the data.

This is why Javascript needs an equivalent to Java 8's LocalDate and LocalDateTime. It copied Java's terrible, fundamentally broken class for dealing with dates, if it could finally get around to copying the good date library that Java added later, that would be great.

Just this summer I saw the Angular Material team struggle with misunderstandings around this.

Some German (I suppose, based on the GitHub profile) had to write a UTC aware implementation and then others had to push them to realize the issue.

Seems time and other regional issues are really hard even for the geniuses at Google. (Yep, I honestly believe they're smarter than me, which makes this really confusing.)

The lack of timezone support in the two suggested alternatives (date-fns, dayjs) means they're not real alternatives for any use-case as soon as you're operating in a country with any DST, https://en.wikipedia.org/wiki/Daylight_saving_time_by_countr....

I wrote a tiny library, tzjs, for exactly this reason.

Modern browsers have built-in timezone support via window.Intl. It's actually pretty nice! One glaring omission in the spec: given (UTC instant, tzname), there is no clean way to get that offset for that timezone at that moment.

tzjs works around this omission. This saves a lot of bundle space. moment-timezone includes the whole tzdata dataset: https://bundlephobia.com/result?p=moment-timezone@0.5.21


Seriously... It's not like timezone support is some esoteric interest in a Date/Time library.

No, but it does add quite a bit of complexity, and if it's done correctly usually requires not just a database of timezones, but also when and how they've changed over the years.

Not supporting timezones is a valid design choice, but is so important and integral it should be the first item both mentioned by a library and used as criteria to decide whether it's a valid choice for those surveying libraries.

It's sort of like choosing a storage solution. Do you need persistent storage that can survive a reboot, or is something ephemeral that works only while powered good enough? Both have their places, but choosing the wrong one for your specific use case is usually very problematic.

And as date-fns doesn't support all locales, and moment.js without locale support is roughly the same size as date-fns, what was the point of this?

It's not like the author can't have known this, moment.js without locales is listed right at the top on the moment.js home page (but left out of this list).

At a previous client, we only imported the locales they actually support, you don't need to import the whole lot of locales if you don't want to. When you don't need the whole lot, they're actually tiny.

Side-note, Chrome recently changed how it parsed dates breaking existing code (if you left the "z" off a ISO date), yes, that meant different browsers native date parsing wasn't consistent until recently. I forget exactly when it happened but I think it's was about a year or two ago. I think we ran into it because we were using .Net MVC which by default the serializers would spit out DateTime's without the Z and DateTimeOffsets with them. They changed it for newer versions.

Personally, working with future dates in the restaurant trade, the recent dogmatic approach to treating everything with ISO is frustrating. When humans say "meet at a restaurant on 27th Sept at 7pm" they don't, at all, mean it by timezone. A person in France doesn't want to see the booking show 27th Sept at 8pm. And storing it ISO is dangerous as timezones actually change.

Date-fns supports 46 locales currently, so it's not nothing.

Regarding local times, if it's always local, why worry about it? Just store it in "UTC" without conversion, with the understanding that it will never be converted. That's what the Postgres "timestamp without time zone" type essentially is.

Because if you send it up to the client it's very easy to accidentally get it converted unless you constantly guard against it.

I've sent a PR to date-fns to add time zone support, but the maintainer doesn't seem to have much bandwidth to review it. In the meantime, I'm going with Luxon

There's also Luxon from (one of?) the maintainers of Moment: https://moment.github.io/luxon/.

It abuses the i18n api (by parsing the date out of a formatted string) to provide timezone support without shipping timezone data, which is pretty clever.

Oh, that's a beautiful hack, I love it.

Most of moment's size is in the localizations. You can reduce it from 300kb to 100kb by removing them or only including the ones you care about: https://stackoverflow.com/questions/25384360/how-to-prevent-...

I think the bigger lesson, if moment's size is news to you (for those of us using webpack), is to install webpack-bundle-analyzer. It's loads of fun, if you like visualizations.

You can further reduce that to 0kb by using window.Intl . You get locale and timezone aware date formatting, built-in on all modern browsers.

The API is pretty nice!


Whenever a "you might not need this heavy, popular lib" gets posted to HN, the responses are always pretty divided. I always try and write my own functions and import things like lodash, underscore, jQuery, and moment as a last resort. Bundle size may not be that important to the people working on prototypes and side projects, but it certainly is to many other people.

If a library is heavy, it is in their interest to organize it instead as a collection of submodules that can be depended on individually (IIRC, lodash does this) otherwise they risk people just not using their stuff.

I’ve picked up node and js more seriously recently, coming from .Net and C# and I’ve been wondering why people in my learning material are relying so much on third party libraries for very basic things.

I’m probably a little old fashioned, but I didn’t even know moment existed and I’ve been fooling around quite a bit with JS dates recently. It didn’t take a long time to write the .ToString(dd-MM-yyyy) module, that was the only thing I really needed aside from the standard library, but I guess it would’ve been faster to import moment.js.

One unique thing to js that the backend usually doesn't have to deal with is a lot of edge-cases with different browsers. That's (at least for me) the main motivation to use 3rd party libs when ever possible, hoping that they know more than me, and have invested more time in testing and thinking about all the possibilities... and usually it's true

Coming from .NET you should know Noda Time (https://nodatime.org) and the problem it solves. Consider moment the JS alternative, however, with JS native Date being more limited / broken than .NET's DateTime.

TL;DR: Goes way beyond standard formatting.

The C# DateTime has more useful methods than JS Date, but DateTimeKind and the way it counts ticks makes it an absolute trap for anyone to use, so I wouldn't say it's less broken.

I've definitely made my peace with it. Think of it this way: how much better is a bunch of imports than the slew of stackexchange copy-pasta you'd find in an old PHP app?

Think of it this way: how much safer is the slew of stackexchange copy-pasta, which might have at least been fully read. And it doesn't add another dependency that might steal your credentials or your customer's credentials in the future.

(moment.js supports a huge number of localizations, which is impressive and good for i18n. so in this case maybe it isn't directly comparable to copy&paste for anything but trivial use-cases.)

Let's be honest though, if someone's copy-pasting from Stack Exchange, they're probably not reading or understanding the whole thing they're copying either.

My rule in code reviews is if you're basing/copying something from the internet, you better be able to explain everything it's doing, and if you're pulling a dependency you better be able to explain why we need it and why you think it's trustworthy enough to put in our application.

You are not accounting for adding proper unit tests...

A good lightweight alternative is js-joda. I recently switched from moment.js and I've been very happy with it.


At 43kb gzipped, it's hardly lightweight - that's 70% of moment.js' size, 4x larger than date-fns

js-joda offers a lot more than lightweight native Date type wrappers like date-fns. It offers separate time and date implementations which allows you to more precisely model your problem. The native Date object always consists of a time, date, and timezone.

For example, a js-joda LocalDate is just a date without a time or timezone. This allows you to cleanly represent things like birthdays, anniversaries or holidays in an unambiguous way.

js-joda is also immutable which in my experience is more predictable and less error prone. With js-joda, "d.plusDays(366);" leaves d unchanged.

js-joda also has very good duration support.

Because it does not use the native Date implementation it is potentially more consistent across platforms. It is also has lots of tests.

If all you are doing is formatting a few dates, date-fns is probably sufficient and it is certainly smaller, but for more intensive manipulations of dates and times I think js-joda is well worth the size.

Just like the OG Joda, I'm assuming it doesn't support nanosecond time resolution?

For all the problems with Moment.js this article really sells it as the better solution for many use cases.

The problem is that moment didn’t include locales as a default at one point, and then suddenly it did. And it’s the kind of thing you don’t want to do module splitting on, date functions can be used in all sorts of places so it’s really a core lib

Yes, I’m using date-fns and bits of this pull request [1]. It works very well and uses the Intl browser API to support time zones.

Also worth noting that the mutable nature of moment can make things very confusing where as date fns is immutable and consistent.

Remember that unless building a calendaring system, all dates should be stored in UTC and only converted into a specific Timezone for presentation.

[1] https://github.com/date-fns/date-fns/pull/707

While you might not need moment.js and can remove it from your project. The problem is that every other project is using it, so you're already picking up the dependency. Many months ago I went digging into if we could remove it and while I successfully pulled it out, found that three of the packages we install all needed it -- thus having one date library was preferable to extra code size.

But react-dates has moment as dependency, so…

Welcome to the wonderful world of code re-use. Hey, at least we're not re-inventing the wheel.

We're reimporting the wheel (which, by the way, also has moment.js as a dependency)


A devDependency is not the same as a dependency.

It's a dev dependency because they use it in unit tests but it's also a peer dependency, meaning that if you want to use the library you need to include moment.

That's my error, thanks for the correction.

I need Moment because Date.parse does not do the same thing in Safari as it does in Chrome

When will JS engines ship with timezone data to not let a third party maintain it?

Ironically, it's possible every browser includes this already internally, but doesn't expose it to Javascript [0].

Packages still have their place, but the lack of a decent standard library will hurt us for decades to come (already is arguably).

[0] https://dxr.mozilla.org/mozilla-central/source/intl/icu/sour...

This. People often have a beef with the very wild npm ecosystem with tons of libs of varying quality, abandonned projects, bloated libs, etc but mostly it comes down to the standard library being anemic.

I really don't understand this... while yes, I might at some point be interested in reducing my bundle size, for 99% of developers out there, a difference in 300kb is just completely irrelevant.

I choose my JS libraries because they are actively maintained, have well designed APIs (easy to use), are common enough to where newly hired developers already know them, and just generally make my life easier. This doesn't address any of that.

300kb is half a second to load for someone with a 5mb connection (which is still quite common in a lot of the world...and even in parts of the US). It's also a LOT of crap to parse on a lower end computer.

Repeat with a few libraries, and you're going to need a Flash-style progress part as your app loads. That's not exactly a great user experience. Doesn't really matter if you're building an MVP or if you have bigger fish to fry, but eventually it certainly matters. There's a reason the modern web is so freagin slow, and only half of it is because of slow backend APIs.

That is totally fair, but for me, I make internal company apps. They happen to use Javascript, but realistically we could ask someone to download 100mb of a binary, and it would still be an ok situation.

For other devs? I bet they say that people on slow connections are not the target market. After all, a slow line will choke on all the ads used to pay for the site.

Yeah, we definitely care about this more for our external apps than internal ones, but as we grow and our internal users expect more and more sophistication from our internal tool, it's becoming a bigger priority. It really does matter. Think the customer support agent or the sales rep who's on the phone and has to make the customer wait just a few more seconds can have an impact... but it's definitely harder to justify.

> I bet they say that people on slow connections are not the target market

That however... there's a LOT of data showing how every seconds count for things like ecommerce or news websites. And for actual B2B type apps, they likely don't have adds after you are logged in. We're gathering a lot of data on this internally, and customers care a -lot- about perf of things like CMSs, CRMs and other enterprisey apps.

Unit symbols and prefixes are (very!) case sensitive. It's 300 kB, 5 Mb.

After gzipping none of the variants are that large, and let's be real: the 3rd party ads and analytics and cat pictures are gonna strictly dominate the load time.

Just because there is even more bloat being loaded from other sources doesn't mean that adding more bloat doesn't matter. This kind of mindset has brought us to exactly this point where modern web sites are loading more slowly than one from 20 years ago would have while using dial-up.

Cat pictures don't interfere with my ability to interact with the web page.

The 2MB+ of bloated, unoptimised JS that you ship with your page that I need to parse and render before I'm allowed to see the 20kb of actual content _does_ interfere with my ability to use the page.

The library in question is nowhere near 2MB. You're tilting at windmmills.

It also won't be the only dependency.

it depends on who will be downloading the bundle. if it's a group of consistent users primarily using desktop browsers, it may not be worth worrying about since they'll probably download it once at a fast speed and reuse from the cache repeatedly afterwards.

if you have lots of new users on mobile using slow cellular connections, big libraries like moment.js add up and can really hurt load times and make for a poor user experience. for circumstances like that, a developer might look at what are the biggest libraries in their vendor bundle and look for ways to reduce them via tree-shaking or find alternatives, that is if they are even aware of how slow their current bundle might be for mobile users. i think this is for that kind of use case, not only to offer alternatives to those who might need it, but also to raise awareness for those who develop on their desktop and don't engage with their app like most of their users do

I think mobile phones have browser cache too.

sorry, i shouldn't have mentioned caching. the emphasis should have only been on connection speed

> for 99% of developers out there, a difference in 300kb is just completely irrelevant.

If this is true, which I hope it's not, then I feel very sorry for anyone who doesn't live in a major first-world c̶o̶u̶n̶t̶r̶y̶ city and wants to enjoy the internet.

I feel like this is just a very short-sighted view of the JavaScript ecosystem. A huge amount of JavaScript applications aren't used internationally, aren't used on mobile, or simply aren't websites. I've worked on multiple projects that are served locally from a pre-downloaded package (for example a Kiosk UI).

> A huge amount of JavaScript applications aren't used internationally, aren't used on mobile, or simply aren't websites.

This is the exception, not the rule. I find it hard to believe at 99/100 JavaScript programmers are not working mobile or web applications, where 300KB matters, even if you do some sort of content hashing.

True, but in your OP you said 99%. Far more than 1% of javascript developers are working on websites and all of those people should be thinking twice before adding 300kb to their bundle size.

I'm assuming you don't have anything to do with mobile web design.

Our complex single page app is approximately 180kb gzipped (JavaScript files and a single html main page) - no html from the server except for reports.

Size is very important for first visit (lower delay loading files over cellular), and important for subsequent cached visits (mobile devices are slow to parse JavaScript).

300KB is more than enough for a fully functional website including _all code assets_. This is the sort of thinking that got us here.

Devs like you is the main reason why the web is much, much slower than it could be.

Perhaps it's time to start reading about UX , browser JS parsing speed, how JS executes, etc.

Making your dev life easier is nice, but don't forget the end goal...

(Note: I'm not talking about those internal apps that have 10 users :p)

I wonder how many seconds it takes to load your site on mobile when you think 300KB is irrelevant in your product.

From users point of view, it's irrelevant what you use inside but how snappy things are.

Why and how are the suggested alternatives worse regarding the aspects you mentioned?

Yeah, I don't understand why comparisons are always made for gzipped code sizes. I mean, I could write something really small that is difficult to parse and abuses memory vs. something big but is all comments.

Why is this the first metric that is always trotted out?

Because it's a simple, universal metric. Accurately stating the the performance impact of something like Moment is extremely involved and entirely dependent on how your project works and your users.

Because every web browser and server understands gzip. The gzipped size reflects the transfer size for the file without doing anything at all exotic.

Guess you have to measure yourself then :)

Another light alternative (~2kB) is [fecha](https://github.com/taylorhakes/fecha)

Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact