Hacker News new | past | comments | ask | show | jobs | submit login
Is It Time for the JavaScript Temporal API? (openreplay.com)
156 points by ceeb on Dec 30, 2021 | hide | past | favorite | 100 comments



Anyone know why all new Ecmascript APIs are implemented to be used as "newable classes" rather than ordinary functions?

The community seems to be moving towards functional programming/away from classes.. and pretty much every mainstream JS lib is function rather than class oriented (yes, I know classes are simply special functions)

Also adds unnecessary verbosity/more syntax noise.

Is there some internal performance reason for why ordinary functions couldn't be optimized to perform the same as "new"? (Seems like it would be easy to wire the prototype internally to the function, resulting in same semantics).

At this point better to be consistent, but I'd still use a date library just to avoid added verbosity of this native solution. Luxon API reads a lot better than this... Though I'm sure there are reasons a standard implementation needs added complexity (has to cover every edge case)


Because most original API (DOM, browser, date, etc) are OO oriented and you stick your standard library in one pattern.

And speaking of everyone moving to functional programming: i work with several hundred developers in four continents in a very dynamic envirnment.... And I have not seen a FP advocate. Not that FP is not good (I prefer it where applicable) but mindset/winning/moving towards are words I would not choose when it comes to describing the state of FP and OO in the world. There is a strong influence but not to a degree where an established language changes its core paradigm: see Java, C#, JS, ...


Really? I have seen — across many jobs and company sizes and frameworks, over about a decade — a slow but inexorable trend of moving toward FP.

Be it in React, where hooks are pushing everything to be functional; to the fact that Monads have started showing up in unexpected places in codebases I’ve worked on; to the fundamentally functional nature of frameworks I have to use every day working at a FAANG.

Along the way, until the FAANG, I never met any functional advocates. What I did meet are people who reach for the appropriate functional tools when building solutions; others see the much better solutions they arrive at, and copy.


React hooks are procedural programming constructs in a thin veneer of FP. They're about as functional as java was object oriented.


I would be more prone to say that the original API are something that came up by using buzzwords from growing languages or something else, for sure they're a thing, but like it's not that `new` makes an OOP approach if then you miss inheritance, abstraction, encapsulation and polymorphism, I'm not sure if I am being picky but like if there is just a keyword and nothing else more important to the paradigm then I think it might be confusing for some reader to see something like that statement


The Temporal API supports a `from` method/function on most of the options - that's very much the functional approach to avoid `new`.

Often objects that are class based will expose a `of` or `from` method that abstracts over the `new` keyword `Array.from()` is one example instead of `new Array()` (of course the most common way is `x = [1, 2, 3]` but these other options exist too).

This seems fairly functional to me.


> The community seems to be moving towards functional programming/away from classes..

Anecdotal observation follows:

In the span of my career so far it looks like the community moved from imperative programming with a few classes/prototypes sprinkled in there, to imperative programming with a few lambdas and HOF sprinkled in there.

I've yet to come across anything significantly functional or OO in the wild, it's still mostly plain old data mutable structures and for loops.


For one: core types like Temporal and Promise are nominally-identified. i.e. they have an identity that goes beyond just plain objects with a particular set of properties. Oftentimes when people advocate for "plain functions" over constructors in JS, what they're really advocating for is using "plain data"(objects, arrays, primitives, etc). There are lots of good reasons to use plain data in JS - transparency, serializability/deserializability, duck-behavior, etc - but all of that is irrelevant for these special objects.

Now of course you could provide a plain function that returns a special, nominally-identified object. But at that point there are basically no advantages over using a constructor (unless you need multiple such functions that take different kinds of arguments); the difference is almost entirely superficial, and constructors do a slightly better job of advertising that their return values are special, and also have precedent and therefore familiarity


> Is there some internal performance reason for why ordinary functions couldn't be optimized to perform the same as "new"? (Seems like it would be easy to wire the prototype internally to the function, resulting in same semantics).

Not particularly -- JIT vms should inline `(() => new Thingy)()` as `new Thingy`. The temporal API provides a lot of nice factory functions that don't need any `new` keyword. Here are the first several examples of the Temporal API all concatenated together with comments removed:

    Temporal.Now.instant().epochSeconds;
    Temporal.Now.instant().epochMilliseconds;
    Temporal.Now.zonedDateTimeISO();
    Temporal.Now.timeZone();
    Temporal.Now.zonedDateTimeISO('Europe/London');
    Temporal.Instant.from('2022-03-04T05:56:78.999999999+02:00[Europe/Berlin]');
    Temporal.Instant.from('2022-03-04T05:06+07:00');
    Temporal.Instant.fromEpochSeconds(1.0e8);
    Temporal.ZonedDateTime.from('2025-09-05T02:55:00+02:00[Africa/Cairo]');
    Temporal.Instant('2022-08-05T20:06:13+05:45').toZonedDateTime('+05:45');
    Temporal.ZonedDateTime.from({
      timeZone: 'America/New_York'
      year: 2025,
      month: 2,
      day: 28,
      hour: 10,
      minute: 15,
      second: 0,
      millisecond: 0,
      microsecond: 0,
      nanosecond: 0
    });
It seems like the style is to provide these factory functions for almost every use-case except "build me a specific object with these exact values", which you need to use `new Constructor(...)` for. Where is the noise?

As a side note, there are concrete performance reasons to use class/prototypes over plain objects or closure-over-hidden state in your own code:

- Class and/or constructor functions helps ensure your long-lived objects instantiate their properties in a regular order. V8 has trouble unifying objects with the same properties but different property orders into the same hidden class. Each hidden class costs JIT time and memory space, so minimizing both is nice. You can fix this yourself by forcing instantiation of a value to go through a factory function to normalize property order, but at that point, why not use a class to take advantage of `instanceof`?

- Closures over hidden state, eg long-lived closures you would use to implement your own object system over enclosed local variables, cost more than plain objects or class instances. It's more straight-forward to define classes at the top level and wrap them in sugared factory functions than to code-review factories that manually build objects with function properties.


You've covered a lot here, but just want to comment on a few things.

Factory functions can return objects that have a prototype just the same as "new", there is no inherent reason object constructors should require "new". Given that most of modern, idiomatic JS relies on factory functions, it seems very inconsistent to me to then randomly require invoking "new" to construct an object.

Why not simply Set([1, 2, 3])? The noise comes when you have a nested expression with random "new" statements sprinkled in which disrupts the flow. Though I agree that overly complex expressions are an anti-pattern

My underlying point is that "new" does not provide any clarity, given that objects are created and returned all the time without "new", so represents an inconsistency in design to me. The presence or absence of "new" basically tells you nothing, except implying that the object likely has prototype functions. But whether or not they are prototyped is more of an implementation detail anyway.

I agree with you that most of Temporal has factory functions that obviate "new".

To your point on performance of closures, this isn't going to materially matter for majority of use cases. Yes, prototype is more efficient than closures functions, but benefits of prototypes can also be achieved through separation of data and function (singleton utility functions that operate on data).

Also syntax sugar for prototype could have been done in an (IMO) more consistent way through some syntax addition to functions rather than creating class. Generally modern userland code shouldn't really be using prototype and "this" anyway, as "this" has tons of footguns in JS, and I would guess majority of developers don't understand the intricacies of "this" when passing functions around, or when using arrows vs classical function declarations.


The problem with relying on JITs inlining things is that you're hoping that an opaque optimizer will come to the same conclusion as you, and historically there have been all sorts of things that spontaneously kill optimizations (I recall there being a point where one of the JS VMs made decisions based on source text size, so comments could change optimization behaviour).

There's also all sorts of optimizations you have to depend on to avoid excess object allocations, etc.

That said object allocation in JSC and V8 (not sure about spider monkey) is essentially a pointer increment in the common case, so its not exactly expensive.


One good reason is to avoid polluting the global namespace.

Otherwise you end up like Mathematica, which has ten thousand or so functions in a single namespace.

Or PHP which had (has?) a similar problem (but on a smaller scale).


You can easily split stuff into modules without getting classes involved. For example the `Math` module which has been in JS basically forever.


But they are on the global namespace. E.g. new Set() is same as new window.Set()


Right, but just the constructor. Everything else is a method of Set/Temporal.


> Anyone know why all new Ecmascript APIs are implemented to be used as "newable classes" rather than ordinary functions?

My guess, and it is only and just that, is because classes, being a newer part of the language, allow the developers and designers to work with a cleaner environment, if you will, avoiding some of the pitfalls that have lingered since ES1.

Classes are strict, by default, e.g. I can imagine other benefits, but I am too little caffeinated to articulate them right now.

Basically, if one sticks with “the old way”, one runs the risk of one’s users using poor idioms that could cause support grief, but by using the new way one avoids these.

Note also that JS classes aren’t pure OO, at least not yet, but are really a different and arguably better approach to function/object (in the JS sense) design and implementation, and that JS is slowly pulling in FP concepts as well.


First, how do you tell a date from a string or a number easily in the code. Second, the functional thing happens for many reasons, and biggest ones are practical. You can’t DCE (treeshake in hipsterish) classes in js due to highly dynamic semantics, but builtin code doesn’t suffer from that. If it was a non-special type, then maybe it was worth just Foo(), but usually there are some methods attached to it as well as introspection capabilities like toJSON(), toString(), etc.

The community seems to be moving towards functional programming/away from classes

The community moves to where React is, no real CV choice. Other parts of the community are non-functional, e.g. new WebpackPlugin, etc.


The community was in the functional space before react. Angular 1 was very function oriented too, and relied heavily on ordinary functions and closure scope rather than class. And then everybody latched onto class in the phase of time between angular 1 and next gen frameworks.

Class was something pushed by all the next gen framework authors at the time. Then most next gen frameworks abandoned class, because it proved to be worse in many ways than ordinary functions did. Which was the position I held at the time, and seem to have been borne out.

The easy way to tell types apart in ordinary JS is to have consistent naming conventions. If you always name things such that the name implies the type (not Hungarian style, but more natural language oriented), you can infer the type from the name correctly 90% of the time. E.g. always write arrays as plurals, map-like objects as keyToValue, and so on.

But the easier answer is to just use Typescript.

Inferring type from instantiation point is pretty meaningless anyway. The type needs to be inferrable at the consumption site to be readable, not the production site. Understanding a type only matters in the context where it's used


> (not Hungarian style, but more natural language oriented)

Apps Hungarian wasn't actually a stupid idea. It just got bastardized into Sys Hungarian, which was. Apps Hungarian could tell you whether the string was unsafe (and needed to be sanitized), or safe to use. Or tell you if the integer type you were using was an offset, or a row number, or whatever. It was supposed to give you semantic info that wasn't necessarily obvious from the given type.

Sys Hungarian told you that you were using a long pointer to a zero terminated string. But not what the string actually meant. It just regurgitated the type of the object again.


The easy way to tell types apart in ordinary JS is to have consistent naming conventions. If you always name things such that the name implies the type (not Hungarian style, but more natural language oriented), you can infer the type from the name correctly 90% of the time. E.g. always write arrays as plurals, map-like objects as keyToValue, and so on.

No thanks, not an easy way. instanceof and typing are the tools, not some funny notation that I have to rely on everyone to follow and parse, including me, under a pressure, after a year. Nobody can infer/assert types three modules away from a scope of their commit when it’s in the name.

But the easier answer is to just use Typescript.

Typescript is duck typed, you can’t save your day by “type Date = string” in it in a sense of a type safety. Nothing is wrong with object classes, apart from personal affinities accumulated from scary blog posts about classes in non-js languages and how that was bad. Objects have different types and different operarions on them. There is no clear reason to erase this difference and to evict methods only to group them into a scope and match by name manually, losing literally all benefits just for a functional swag.


Types are implied by the interface of the object.

Having a whole layer of abstractions and keywords built around class literally just adds complexity. Not to mention how obtusely "this" works in JS. The whole layering of special syntax around class just serves as a crutch for those coming from other languages.

Nowhere does it simplify anything really, aside from those who are already familiar with that abstraction. It's objectively simpler to have fewer abstractions, given that you can use object literals and regular functions to achieve the same results in similar amount of code. If class saved a lot of verbosity from alternative approaches, there may be a compelling argument for it. 99% of userland code does not need or use prototypes in modern JS

Typescript largely obviates need to do runtime type checking, except at API boundaries. But networked apis can't return classes by definition (not supported in JSON spec).

If I were to create shapes, I would interface the data and have a simple type field like "rectangle" "circle", etc. Or even simply duck typing it. Very easy and consistent way to check without having special prototype specific syntax of instanceof.

Using fields to imply the type is json compatible too, and works sent across the network. With Class you would need to have hooks at Json receipt point to instantiate the instances of the class. Class is a whole layer of unnecessary abstractions that don't even reduce verbosity.


I think this answers a question in my other comment. Not sure if I agree, but at least it feels like I start to get your point. Need to get some sleep, cause this mood makes me sound unpleasing sometimes, sorry if that manifested too much.


>The easy way to tell types apart in ordinary JS is to have consistent naming conventions....you can infer the type from the name correctly 90% of the time.

is any process with a 10% margin of error really an easy one?


When you're operating in a non typed language, you don't have a choice. That's the best option.

Of course you could literally spell out the type. But verbosity not worth it. Also no guarantee the name was written correctly.

But I've found most JS codebases don't have rigorous standards around naming to begin with, so even doing that is a big win


What do you mean by ordinary functions? There has to be some value to represent a date or time that you pass around, so something somewhere has to return it.

If your concern is `whatever(date)` vs `date.whatever()` then the issue you run into is pollution of the global object, which is bad, and frequently not possible due to naming collisions with existing sites (fun story, `let` required some exciting rules to deal with existing content using variables named `let`). Given an inability to add a million new functions to the global object your concise `whatever(date)` becomes `Temporal.whatever(date)`


Could be with typescript in mind. TS works well with classes.


Because they are obsessed in converting JS into Java.


It's been a bit of a headscratcher for me.

I was quite against adding the class keyword to begin with to be honest.

Think a lot of that came at the tail end of the "JavaScript is terrible" generational mentality, and the idea was to copy other languages rather than improve what they had independently.

To be clear, having additional functionality is fine in and of itself, but now the waters are mudied between function oriented and class oriented approaches, and by and large community seems to be settling on function oriented.

In the end, class just created unnecessary fragmentation.


I was also happy with prototypes, now when I use them, and I use them rarely, other developers look at my code like black magic and my "others should be able to understand my code" circuit breaker kicks in and I convert it to a class.

Is it more readable? Yes.

Do I feel like we are dumbing JS down? Yes.

Is it worth it when we consider that it helps onboarding hundreds of thousands of developers who want to choose JS? Oh f* yes!


Creating prototypes in general is not necessary for 99% of JS code. Using completely cloned/closured functions will perform just fine for the majority of use cases... And the code there reads very simply... Just a function that returns an object that has functions.

Or you can simply separate out functions into utility objects rather than attaching to the specific data object, and performance should be the same as using prototype, without any of the special syntax

This model of programming is also much simpler, I'd argue. There is no use of "this", everything is explicit. Especially in JS, "this" sucks because the semantics change depending on whether you use function declarations or arrow functions. And "this" will also reference the caller, so changes if the function gets passed around. Much more obvious to pass an explicit argument to a function than to rely on "this"

I do agree there are use cases where prototypes are more useful, performance sensitive games/server code etc. But pretty rare that you can't take one of the approaches described above. And people writing performance sensitive code aren't generally also going to be the newbies. Additionally, could have had syntax sugar for functions that wired prototype without the new/class paradigm, which I argue just adds unnecessary verbosity to the language.


I was just replying to the class vs prototypes part. There usually is a once-in-a-year situation that is solved better with classes, so I somewhat agree with your 99% code doesn't need it estimation.

Also I should mention that in the rare case when I did use the prototypes, I always used the adapter pattern (an adapter function that takes a slice of "this", calls an independent function, and merges it back to "this"), it was easier to test.

But people are so unused to declaring dependencies as parameters (even function dependencies aka services), they cry untestable whenever they see independent (static) functions. I know this from the PHP, Java and .NET world. To this day, it didn't help to explain what they call DI is just passing parameters (but rather more bureaucratically).


What you propose will likely drive the code to functional equivalent of FacadeAdapterArrayController, but written not only once but in every line of code. Because think of what even a simple .filter().map().filter() would expand into. Of course we can just stream() or | everything, but the original “simple functions” idea will quickly get lost in the woods.


Not clear how you're reaching that conclusion. Native constructs can still implement prototype methods transparently to the developer without the class keyword. Just as devs could in userspace.

But contrary to what you're saying, most popular userland libs are indeed function oriented, and they don't end up like you're suggesting. E.g. RxJS, lodash

By function oriented in this context, I mean that they don't rely on the new keyword for ordinary code. Which reads cleaner

Anyway, functional stdlib can be pretty clean, such as something like Stream(persons).filter(person => person.age > 18).map(person => person.firstName).

Also there's the Pipes spec in the works that would allow for chaining distinct functions that are not on same obj. Not sure of current status


I’m just expanding the example in my mind, nothing arcane. And maybe it’_.s just me, but I _.fail to _.see how lodash _.is “cleaner”, or why “new” makes things dirty. Are we discussing The High Art of Source Code here? If yes, it ain’t my thing.

functional stdlib can be pretty clean, such as something like Stream(persons).filter(person => person.age > 18).map(person => person.firstName)

I see a stream object with at least a couple of methods, how is it functional? What does functional mean here, callbacks? I mean, array.filter(predicate) also takes one. If that’s the thing, nobody takes it away, it’s already natural to js, since it’s multiparadigm. And we already have piping, it looks like foo().bar().baz() or map(foo).filter(bar) or Object.keys().reduce(quux). If you want a class deconstructor for FP-101 code, here it is:

  const fize = (name, …args) => (obj) => obj[name].apply(obj, args)
  array.map(fize("toString", "hex"))
You can turn it into “fize.toString("hex")” with a little Proxy magic (still no chance for a type safety, sadly).

I understand the different approaches you propose, and I could even write some decent Haskell in my experimenting ages, but my main question is what problem do we solve here and how this solution would be functional and not “move methods to same-named pojos, write them explicitly and pretend it’s FP”.


For sure, do also see cases where class can be good working with a larger team. However, since js's biggest advantage is function as a first class citizen, I do feel like they ought to make the API to be functional., however OO design may attract more developers coming from OO programming world.


Wahoo, it doesn't use 0-indexed months! Probably the biggest cause of bugs with js Date objects.


That does remind me: why that even is a thing? Ordering days of a week from zero can make sense since there is Monday/Sunday as a week start divide, and this can be seen as a bypass (0/7 as Sunday and 1 as a Monday), but with months?


Zero indexing for arrays/collections generally makes sense. If you had the months in a normal array they would obviously be zero-indexed (unless you deliberately put a placeholder value in the first position in the array, which would be pretty weird since all the normal ways of getting the first thing in the array would break).

The reason it apparently feels weird to many people to have months be zero indexed is that months are often referred to by their ordinal number, and ordinal numbers obviously start at 1.


> months are often referred to by their ordinal number

What do you mean by “often”? Humans always use ordinal numbers for counting things like months, days, weeks, etc. There is no concept of 0 to be applied here, except for programming array indexing purposes.


Lay people indicate the month with an ordinal all the time since it's how dates are standardized (12/30/2021). People don't often indicate the day of the week with a number, they use "Monday" or "Tuesday". So there's no strongly held convention in people's minds about how to number days like there is for months.


It is lifted from C's struct tm. If you're maintaining tables of month names you're going to 0-index them so tm_mon is 0-based to facilitate that.


What I mean is that people use the ordinal number as the name of the month (as well as the day of the month). If you ask someone their birthday they’ll often say something like “five twenty, nineteen eighty-four.”

This is a potential cause for confusion when writing computer code, because it’s very common for code to refer to items in a collection starting at zero.


Side note regarding humans: I was in a hotel in France that referred to the ground floor as "floor zero" in their elevator. The only time I've ever seen zero used to count something IRL.


This is actually a decent way to demonstrate why zero-indexing for arrays makes sense. If your friend gives you instructions to their apartment “go to the elevator in the lobby and go up 3 floors,” which button do you press in the elevator?


I don’t follow? Even in your specific example, it would be +3 to whatever floor the elevator says you’re currently on. Though, usually people refer to the exact floor number per the elevator signs.


Even if this is true, days in JS still start at 1. It is inconsistent.


Zero indexing of arrays only makes sense in C because array variable is a pointer to first element of the array. In all languages that have actual array type with length and bounds checking, 1 would be a better way to index arrays most of the time and having specified valid range would be the best way.


The week starts on Monday in most of the world.

Date & time APIs must match local reality.


When reality differs by locales, at least one "local reality" won't match the API.



> it was copied from Java's JDK1.0 (1995) java.util.Date, which also had this quirk



Just because the issue predated Java doesn’t preclude the fact that the initial JavaScript Date implementation was lifted from java.util.Date. Brandon Eich has explicitly stated it was so.

https://maggiepint.com/2017/04/09/fixing-javascript-date-get...



Twitter is such a bad platform for this. Code in images means it's completely inaccessible to the blind and very inaccessible to people with poor vision, and you can't copy and paste it into an editor for syntax highlighting or better examination.


Another one is JS parsing numbers with leading zeroes as octal (unless specifying base, which is why linters are so strict on it). Often surfaces when parsing date strings.


That hasn't been the case in JS for almost a decade.

parseInt('0700') returns 700. But parseInt('0x700') will be parsed as octal, so now it's just a more subtle gotcha.

Loose parsers (such as parseInt(), and Date.parse() in JS) are one of the worst ideas ever. Nothing like tracing a bug, and finding that a junior dev is trying to parse DD/MM/YYYY strings using Date.parse(), except that instead of throwing on trying to parse an incorrect [non-RFC] format, the parser makes a best-guess and propagates an incorrect date because it was parsed as MM/DD/YYYY.


Not sure why any api ever had functions like float.parse(s) or date.parse(s). Even more modern ones like .NET (circa 2000) had it. And it's literally never a good idea. Not even for a one off script is it convenient or ergonomic to omit the information about what format you expect (or try to suck it out of thin air such as a static locale on the thread). It's just a bad.idea. If it does accept date parse(s) it has to be with a fixed format (e.g. iso) unless specified. Not "system local format" or something. The same applies to number formatting. I haven't met one single person who prefers automatic string formatting to switch decimal point and decimal comma due to the system language.


I wonder how much of that has to do with the fact that ISO8601 was relatively newer back then (published 1988), and date formats were much less standardised 20+ years ago.

For instance, RFC 1945 (HTTP 1.0) specifies three acceptable date formats. Add ISO dates, and now you're writing software that needs to handle 4 different formats. Easier to just use a heuristic parser that's capable of accepting HTTP, email, Usenet, and ISO timestamps.

Fortunately, these days it's generally considered a bad idea to use anything but ISO timestamps, with the exception of a former colleague who tried arguing that DD/MM/YYYY was an acceptable format to be transmitting and storing dates in.


Why would you not have a parseFloat/float.parse/whatever?

Parsing floating point values correctly is not a trivial amount of work - to the extent that there have any been security vulnerabilities in implementations.


Of course it should be a api but it should be either explicitly requiring a format OR (more likely) default to an invariant format.

That is, if I do this

    float f = 123.456;
    Console.WriteLine(“value is “+f);
The output shouldn’t vary depending on whether it’s run in France or the US (which it does by default).

For parsing, the argument should even be explicitly required I think. So

    float f = Float.Parse(“1,23”,  frenchLocale); 
So that it’s not ever forgotten or the reader has to wonder if the author forgot it.


JavaScript’s numeric parsing is locale independent. People write too much code blindly to make any kind of locale inference anywhere.

Otherwise I agree with you whole heartedly - I have been stung by absurd locale dependent behaviour in the past (windows’ font loading APIs inexplicably translate the font variant in the programmatic api)


They are not arguing about the stdlib including them, but the api they choose. Instead of parseFloat(str) that tries with a system dependant comma or period, have the decimal type be a parameter or have two explicitly named functions.

Same with dates. Don't try lots of formats on an arbitrary string, force the user to specify the expected format somehow.


Years aren't 0-indexed either. Nobody has a problem with that.


How do you know they aren't? What happened in 1 CE? What happened in 1 BCE? What happened during the year in between?


There isn't a year between them.


In most calendar systems there is. Perhaps there isn't in your favorite calendar, or perhaps you just don't know about it. For instance, the subject of TFA is based on the "Proleptic Gregorian", which does have a year zero.


Temporal is great, and the sooner it lands and is usable the better (AFAIK, there isn't a production-grade polyfill yet.)

In the interim, JS-Joda [0] seems pretty decent, and doesn't use the wrapper approach.

[0] https://js-joda.github.io/js-joda/


My favorite has always been Day.js [0] - it is really small, has a very clean API, great documentation and does pretty much everything I want.

[0] https://day.js.org/


Used that as a replacement for momentjs until we had to make things seriously timezone-aware, and some quite crippling tz bugs (granted, probably 2 years ago now) made us move to Luxon. That worked well, but I'm glad to have something JodaTime-ish coming now that doesn't conflate instants with zoned datetimes with durations.


It’s not really the same thing though, right? It doesn’t handle instants vs zonedtimes etc.

Even the simplest of future cases is handled poorly by these other libraries. If you want to say that something happens on a certain date in the future (say, a due date), your only option is to use an instant as an approximation (often midnight utc on that day). It works ok, but you’re better to have a proper datatype that represents a day without a time and timezone component (it’s effectively a 50hr ish window). Temporal gives you all that and more.


I’ve been keeping an eye on it myself. The implementation in Chrome looks pretty complete already, which is promising.

I don’t quite get the point of the other libraries like moment etc. They have a bunch of convenience stuff, but unless you’ve modelled the different types of times that crop up in real life, it’s all a bit pointless. When I first read through the Temporal api, I was physically nodding along in agreement. It’s dates and times done right in js. Can’t wait to be able to use it.


Nice, it looks to be quite similar to the Java 8 date/time APIs (java.time.*), which are excellent. Looking forward to this!


Ironic because Javascript's Date behavior was also modeled after the original java.Util.Date (which was immediately deprecated, whoops).


Why is that ironic?


Because copying Java got them into this mess? ;-)


Yeah, but the Java 8 date/time API has been around for ~7-8 years, and is basically universally loved, while the original java.util.Date was hated for ages. Plus Java 8 date/time is based extremely strongly on joda-time, which hit 1.0 about a decade before that, and was also pretty universally loved.

So we're dealing with an API that has been considered great, and an excellent way to deal with dates/times, for nearly 2 decades. Seems like a safe bet to copy :)


There’s a direct JS port of Java time as well - https://js-joda.github.io/js-joda/


Both the builtin lib, and the popular moment lib are missing separate date and time types, which leads to subtle errors. (ie when using their workaround of using datetime types for either). From the article, it appears Temporal has the same problem. There's nothing wrong with mutable types, which is common in JS discussions; the problem is when the API mutates in place where this isn't obvious.

I hand-wrote date and dt types with basic operations instead. Limited functionality, eg timezones, formatting etc, but behaves as you'd expect.

Another option might be to wrap Rust's Chrono lib in WASM, then call from JS; it's the best DT lib I've seen in any language.



Awesome. It looks like it has a PlainDate type as well.


> Modern alternatives such as Day.js and date-fns may be better but should a library necessary when your app has minimal date-handling requirements?

Yes? Or at least, I don't think this is the, "obvious question that leads to the rest of the article" that the author may have assumed.

As another commenter mentioned, Day.js is 2kb. That's the answer: use Day.js.

But I guess that doesn't get you a blog post you can reference when applying to jobs, so here we are.


From someone who used MomentJS back in the day, Day.js is such a nicer alternative! Moment was like 59k or something!


> 74Kb payload

Is 74Kb really a cause for concern on modern internet connections? I realize death by 1,000 cuts, etc...but for such a core set of functionality that seems pretty reasonable.

DayJs is 2 (two) Kb. Perfectly reasonable for any app with minimal date handling needs.

(BTW I'm 100% in support of a native temporal API also, although have some gripes with the Dev Exp of what's shown in this post)


Is 74Kb really a cause for concern on modern internet connections?

That depends on how you define what a modern Internet connection is. If you mean a fast, low latency, unmetered connection then 74kb is obviously fine.

If you mean a 4G connection on a train, or a metered rural satellite connection, or a typically-low-income-user pay-for-data connection, or even a fast cheap Internet connection but with a low-end device that will waste a lot of battery power parsing and running all that JS code, then it's a lot less obvious.

Most of the time you don't know who the user is, so it's best to assume the worst case. If your site works well when things aren't ideal then it'll be awesome when they are good too. Only thinking about the happy path of users with M1 Macbooks with unlimited gigabit ethernet will mean everyone else has a sucky time on your site.

Also, if someone posts your site on HN, most of the comments will be about how bad the code is and you'll miss out on feedback that's actually useful. If you're deploying a 74kb date library, think of your karma.


Even in locales with high speed internet, I find myself sharing a wifi router with a dozen or more devices in classrooms or coffee shops. It is a bit disheartening seeing google docs and slack struggle to stay responsive, and in a college town with fiber internet. I’ve also tried collaborating on satellite internet on a farm and at the end of a microwave-tower-daisy-chain, where youtube works great if you’re the only one in town using the connection.

I think people assume Starlink will bring high-speed internet to every corner of the globe, but that being the case, there’s more situations where groups of people will share a single downlink, and still every kilobyte counts.


From my personal experience in Poland:

You can have LTE+ or even 5G in cities, but traveling by train means that in some areas only a select few extra-lightweight pages like Hacker News are able to load before a timeout or connection loss occurs.

So yes, in some situations even that amount of payload could have a meaningful impact.

“Modern” connections are irrelevant if we are talking about regions, which are not viable for a high-speed infrastructure. And such places are almost everywhere. Mountainous parts of Japan, many American regions, or sparsely populated Polish regions.

Of course, how much it is important varies greatly.


My take: ABSOLUTELY! 74kb or image data? Probably not. But unzipping, parsing/JITting/running/whatever that large of a payload.... on crappy 3 year old low-memory Android devices... yes. That is going to lag.

Not too long ago, Google tried to push AMP on all of us because devs were getting way too careless with their bundle sizes. We're still not to a decent place just yet. Internet speeds in The United States, Europe and populated Asia may be growing but there's an entire world that are still at "slow-3G" speeds.


I view it as more of a conceptual thing than a size issue. The 74kb is a symptom of the issue, which is that bad things can’t be deprecated because of backward compatibility.

If there is solution that removes the necessity of a 74kb package then I think we should embrace it.

I’m still fairly new to TypeScript but it’s quickly replacing Python as my daily driver for most things that aren’t back-end APIs, and Date is certainly one of those things I would love to see changed.


There are a lot of places in the world and even the US where people have slow connections. It’s also not just download speed that’s the problem. Slower phones need to process all that JS.

I’d say that time-related API being core functionality makes it more of a reason why this should be baked into the standard JS API. Why make users download something that most sites and web apps use?


It’s also 74 additional kb to parse and evaluate on the main thread before it can continue doing more interesting things like rendering the main content you came to the page for. On many devices (a huge number of users globally are on dirt cheap underpowered phones) this adds up to significant user experience problems.


I view it as more of a conceptual thing than a size issue. The 74kb is a symptom of the issue, which is that bad things can’t be deprecated because of backward compatibility.

If there is solution that removes the necessity of a 74kb package then I think we should embrace it.

I’m still fairly new to TypeScript but it’s quickly replacing Python as my daily driver for most things that aren’t back-end APIs.


I weep when I think of how many millions of times libraries like jQuery, lodash, moment and so many others have been downloaded and re-downloaded and re-downloaded again over the decades.

A considerable fraction of all the data ever sent on the internet is JS libraries that could have been bundled into browsers instead of every web page using slightly different versions.


I see it more as issues with fragmentation, wasted effort, and security. An anemic standard library forces every developer to either evaluate, or author, an alternative. This leads to an explosion of alternatives, which in turn makes it harder to agree on best practices and harder to secure the software supply chain.


Douglas Crockford has a sketch of a datetime library in his book, How JavaScript Works.

I like it. Here's a summary.

A datetime has three representations:

- (real) number of milliseconds since the epoch,

- string in some standard format (e.g. ISO 8601),

- plain old object with the following properties: year, month (one-based), day, hour, minute, second (real), zone, week, weekday.

Then there are four functions for generating datetimes and converting among the representations:

- Date.now() -> Number

- Date.object(Number | String, zone info? ...) -> Object

- Date.string(Number | Object, format and/or zone info? ...) -> String

- Date.number(Object | String, format and/or zone info? ...) -> Number

You'd need the tz database under the hood and maybe exceptions for impossible or ambiguous datetimes.

Aside from bundling the tz database, I don't see this library needing to be very large. And if it's small, it's not a big deal if it isn't standard.


I'm happy to see that it includes primitives of varying precision, specifically, that it includes PlainDate [1], which represents a calendar date, with no timezone. This year, I've spent far too many days, probably weeks, fixing tedious bugs due to incorrect handling and parsing/formatting/reparsing of dates.

[1] https://tc39.es/proposal-temporal/docs/plaindate.html


Awesome, hoping to see this in Cloudflare Workers as soon as V8 has it! (Currently using day.js)


Excited to get to use this. One area of concern is i18n. The article touches on this briefly at the end, but I hope a zoned value can be used with Intl as well; having to use only Plain values would be frustrating.


Betteridge's law does not apply here!


I kinda agreed




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

Search: