It’s time for a new Date API in JavaScript
Have you ever felt that the Javascript date 0.1.2023, representing the New Year, doesn’t make any sense? If so, let’s look at how future Javascript will fix that plus a couple of other DateJS issues.
But first things first: why does January equal zero? The answer lies in the distant past. The author of Javascript, Brendan Eich, lacked the time to invent Javascript’s own Date format. Thus he copied Java’s Date, which had the months starting with zero. So then why did Java represent January as month zero? Because the same behavior was in the language named POSIX C. Why did POSIX C have this behavior? Let’s stop here — continuing would just be going down the rabbit hole.
Unfortunately, a month’s quirky numbering is just the tip of the iceberg. The problems with JS Date are numerous. To name a few:
- You can’t work with a date without a time part
- JS Date supports only the user’s time zone and UTC
- JS Date constructor doesn’t check the boundaries
new Date(2021,1,31).toDateString() equals Wed Mar 03 2021
Plus, JS Date’s mutability is another big pain. Look at the following code and notice that tomorrow equals today:
It’s definitely not programmer-friendly behavior.
Working with date and time is tough
Besides all the aforementioned JS Date issues (a.ka. accidental complexity), measuring time and date alone contains an immense intrinsic complexity.
I’ve always wondered why working with dates feels more challenging than working with strings, numbers, or booleans. In many languages, the type date
is just a primitive type, like the string
or number
types. However, unlike numbers, I have always had a difficult time (pun intended) with the date and time arithmetic, no matter what language I use.
My suspicion that something is off with dates has been confirmed by the famous Computerphile’s video about time zones. So if anyone believes that dates are a somewhat simpler topic, please watch his explanation. You will see what I am talking about :).
MomentJS, Luxon, date-fns
To mitigate the problems mentioned above, the JS community has started creating new libraries. Probably the most recognized was MomentJS, a handy tool dealing with multiple glitches in JS Date type. MomentJS is a great library, but I would recommend using Luxon today. Luxon, which succeeds MomentJS, offers immutability and native operators. Luxon’s immutability is especially useful as you can see in the following example:
Another popular library, Date-fns, provides a set of utility functions trying to fix the main pain points of the standard JS Date object. However, you still work with JS Dates which means that even the official example has the same JS Date weird vibes:
Will Temporal be our new savior?
MomentJs, Luxon, and date-fns are great but don’t we deserve something more native? Yes, we definitely do, and thus Temporal API was born.
Temporal JS aims to replace the old JS Date object by bringing a modern date and time functionality. It’s already in stage 3 which means it’s pretty stable and it will eventually land in our browsers.
I see Temporal API as a huge improvement, bringing a lot of functionality. Nevertheless, I don’t want to enumerate what you can read in its documentation. Instead of that, let’s focus on some essential points.
One type for every problem
The main improvement seems to be that Temporal embraces the concept of one specialized type for one use case. To better understand its value, we must look at how we work with time in our daily lives.
People use different representations of time for different occasions.
You don’t say:
I have a birthday on 5.5.2023 and then 5.5.2024 and then 5.5.2025.
Instead, you say:
I’ll throw a party on Friday 5.5, bring your own cake.
Correspondingly, very rarely do you declare:
I will be there at 10:30 pm today UTC time plus 5 hours.
You say:
give me an hour to get there.
Temporal API respects these real-life scenarios and offers a set of types for numerous model situations. Whereas the JS Date class offers one single object for every situation, the Temporal has a couple of concrete types for our use cases. The following are the most useful ones:
PlainDateTime represents a date and wall-clock time with no time zone information. If you do not want to deal with the time zones, PlainDateTime
is the way to go.
ZonedDateTime represents an exact date and time with time zone information. It’s a direct substitute for the old JS Date object.
PlainDate represents the calendar date without a time zone or time.
PlainTime represents only time. Independent of any time zone or date.
Instant represents a point in time with precision in nanoseconds — no time zone or calendar. It represents the count of integer nanoseconds since the Unix epoch.
Duration expresses a length of time. You will use it for date and time arithmetic.
I haven’t included less essential Temporal types so if you want to learn more, feel free to look at this excellent API summary.
Explicitness
The following point relates to the previous one in which we learned that Temporal offers you more than one type. It means that you should be precise (a.k.a. explicit) with choosing the proper type for your use case. So when you need to work with time, you should use PlainTime
. Whereas, when your use case requires a time zone, use ZonedDateTime
class.
Moreover, Temporal’s author made several smart decisions to stretch its explicitness more. For example, type Instant
(which represents the current time) doesn’t have the property day
, month
, or year
. At first sight, it doesn’t make sense. As you probably know you can write the following code with an old JS Date:
const month = new Date().getMonth()
On the other hand, when using Temporal.Instant
you must first provide a time zone to get these dates:
After a bit of thought, you will realize that this approach makes sense. Although you can have a precise point in time, you can’t tell what hour or day it is without a specified time zone. That type of explicitness means no surprises like hidden time zones in JSON serialization:
Immutability by default
Immutability proved its value many years ago and Temporal embraces the concept as you can see here:
Temporal offers a method with
, which is a useful way for changing Temporal instances:
The variable date
has not changed and newDate
has the same properties as the date
except for the day
. It’s reminiscent of F# language, which has the same function. However, this function works with all instances, not only DateTime
instances.
Huge set of functionality
Temporal supports the whole range of functions for working with date and time. For example, methods since
, until
, and equals
cover the simple comparison, and subtract and add cover manipulation with dates. Moreover, Temporal API also supports transforming the PlainDate
to ZonedDateTime
and vice versa. Finally, converting between Temporal types and legacy JS dates is also supported.
To get an overview of Temporal functionality, check TC39 Cookbook to learn more canonical examples.
Comparing
I am a bit disappointed with the missing comparison operators for Temporals. As a result, you can’t compare the instances of Temporal as easily as you can with old JS Date. You need to use the static method to compare. However, there is a catch! It’s easy to make a mistake by using different types for comparison. In the following example I perform comparisons by using two types: PlainDate
and PlainDateTime
. Using different types gives you different results:
The methods sadly lack explicitness. You can compare the PlainDateTime
with PlainDate
without any error, which might lead to bugs. Operators <
, >
would’ve solved this problem.
Support for time zone arithmetic
Have you seen the aforementioned video about time zones? Then you see why zone arithmetic is a complex topic. Temporal API provides a pretty robust solution to deal with all weirdnesses, like daylight saving time, odd time zones, and ambiguity of time. By ambiguity of time, I mean the difference between Clock Time and Exact Time. For example, in the USA 1:30 am on 13th March 2022 happened twice due to daylight saving time. That might be good for people but bad for software developers. You can read more in the Temporal cookbook.
Formatting
Here I have bad news for you: the formatting still seems limited. There has been a bit of heated discussion about that, yet Temporal API supports only Intl.DateTimeFormat
so far. See the following example:
It seems the potential mini-formatting language like yyyy-MM-dd HH:mm:ss
has to be provided by a 3D party library in the future.
Support of calendars
Temporal API supports calendars other than only ISO. It might seem a bit unnecessary from the perspective of our Western world. However, in this day and age of emerging markets like China and India, it’s become an essential feature of every mature library. For more information, read the article about writing cross-calendar code.
Balancing
Now let’s talk about the duration of time. As you can probably imagine, we can represent the duration of time in many forms. For example, two hours equals 180 minutes or 7 200 seconds — in various use cases, you need various formats. The good news is that Temporal API supports converting to different formats through balancing:
You can use a handy method round
to transform duration into different representations. Take a look at the official documentation to learn more.
Support
Now comes the saddest part of my text. The support of Temporal API is still terrible. No browsers have implemented it yet and no browser has even signaled that there will be any milestones soon.
However, on the bright side, there is a polyfill, which is gaining traction, as the graph of downloads shows:
Thus, I feel that you can start thinking about adopting Temporal. I’ve tested the polyfill and its quality looks good. Nevertheless, some optimization still needs to be done according to the authors.
Conclusion
Even though I can imagine some improvements, like using Luxon-type operators (< > ==), I look forward to seeing Temporal API incorporated into our browsers. It’s a better API than JS Date on many levels. In addition, the documentation is well-written, with support materials like a handy cookbook or quick reference.
For now, I recommend trying the polyfill in the not critical part of your application and waiting for the native implementation.
We are ACTUM Digital and this piece was written by Marek, Senior Front End Developer of Apollo Division.
If you seek help with your project or initiative, just drop us a line. 🚀