How to Outsmart Time: Building Futuristic JavaScript Apps Using Temporal

Bookmark

For close to 25 years now, JavaScript developers have suffered at the hands of time: the Date object. We have tried all sorts of solutions from using popular libraries like Moment.js all the way to handling dates and times on the backend.

Now it's time to show "time" who is boss. With the Temporal API stable at Stage 3 and the polyfill ready for production use, let me show you how to harness the power of this delightful API in order to build powerful JavaScript applications that handle dates and times like we always wished we could. We shall also discuss the orthogonal features being worked on in the JavaScript Intl API and find out how these play along with each other and come together to form a comprehensive set of APIs that allow us to build state of the art date and time components in our applications.



Transcription


Hello and welcome everyone. I'm Ujwal. You can find me on the internet at Vysokuken. If you want to write to me on Twitter or GitHub about how terrible this talk is or want to talk about Tempo or TZ39 stuff. But today we're going to talk about building futuristic javascript applications using Tempo. So let's get into it. Because I'm a self-obsessed person, let's first talk about me. So I am a Compilers actor at Igalia. For those of you who don't know, it's a free software consultancy in Spain. We work on a bunch of cool things. We work on browsers, compilers, standards, like the ones that I'm going to talk to you about today, and a bunch of cool Linux stuff. I am also a tc39 delegate, which is why I'm talking to you about these things. I am one of the editors of ECMA 402 and the champion of the proposal that we talked about today. And also happen to be one of the core collaborators of node.js. Just to give you a little recap about the history of the whole Tempo thing, what the buzz is all about. If you've used the date object in javascript ever to do any sort of date time handling, you would know that it is severely outdated. It has serious issues. It dates at least back to 1995, which is before I was born. So how's that for a metaphor? But it needs a lot of repairs, which means that if you're writing any serious date time handling application, you're probably using some popular third-party libraries. They're pretty popular these days in the javascript ecosystem. You're either using Moment or Luxon date functions. And, well, they fill a really important role in the javascript language. But there's still quite a few problems. There's still deficiencies. If you follow javascript drama, not so long ago, Lighthouse, which is a tool that sort of gives you performance tips regarding your javascript code, started flagging applications that use Moment, asking them to use something that would use less bundle size. So that's, for example, one of the problems that cannot be solved in the user space. I have a bunch of others that I'm going to talk to you about. But because of all these problems, we realized that something needed to be done on the programming language level itself. Thus, I bring in Temporal. So Temporal is a state-of-the-art date time handling proposal in javascript. It brings you not only to the present, but as you will see in the next few slides, quite into the future. One of the most interesting challenges that we had when designing the Temporal api was certainly about going to conflicting things. There's javascript proposals that you see that are more specifically geared towards advanced users. You know them when you see them. There's others that are more sort of geared towards beginners. They make your life easier, but not as much if you have been writing this language for like 10 years. And in Temporal, the beauty of the Temporal proposal is that it tries to pair an ergonomic api with a special focus on some of the most common use cases alongside some really powerful features that accommodate some of the most complex use cases. Some of the use cases that have never been accommodated so far. Things including local calendar support. So including calendars like the Hebrew or Islamic calendar, or custom time zones of calendars. These are truly features that have been so far overlooked. So Temporal, the thing that I talked about just now, is now phase three, which is great news, right? Except we don't really know what that means. So just to sum it up real quick, in the interest of time, it means that all the tiny details have been discussed. We've been sitting around in our chairs for a long time and realized that we've basically exhausted everything that we could have done while sitting in our chairs. So the specification text has been approved. The committee is now satisfied with the general design. And now the idea is to start implementing and using Temporal. So for implementers like us, that means that we will start implementing things in different polyfills. We will start implementing Temporal in different browsers. And for you, amazing javascript developers, it means that you can start using Temporal in your applications, including production. Just to give you a quick idea of the huge, honestly, api surface of Temporal, you have a number of these classes that we're going to talk about in the next few slides. Just to quickly go over them, there's Instant, which happens to exist in the exact time space. So it knows the exact time that it happens since the epoch. And then there's a bunch of fuzzy time types. So there's plain date time, plain time, plain date. They happen to be all subsets of plain date time. So they talk about more sort of fuzzy notions of time and dates, right? So, hey, 8 a.m. in the morning doesn't really matter how many exact nanoseconds have passed, right? And then there's a zone date time type, which is sort of the Ubermensch of all these types. It encompasses all the different use cases that we so far offer, and it can be completely replaced. It can replace totally date objects, but hopefully you would realize that you need something much less powerful. There's time zones and calendars, which sort of play between these. And then there's durations, which are used mostly for math. To quickly summarize this, because this is going fast, but hopefully I'd be able to at least draw your attention in the span of 20 minutes, Instants represent absolute points in time. So something as granular and as boring, you may say, as a number of nanoseconds that have passed since the epoch, which in this case is the Unix epoch, 1970. And then there's the plain types, plain date time and friends who deal with the regular wall clock time and calendar dates. So, for example, if you're building a fitness tracker, it doesn't really matter if I'm in Moscow or in Amsterdam. All that matters is that I wake up at 8 a.m. and eat a lot of bagels. Don't go for a run. Calendars refer to human calendars. So, you know, in common understanding, it could refer to the Gregorian calendar, which is pretty popular. It may refer to more obscure calendars, but still used throughout the world, like the Buddhist calendar or the Hebrew calendar, the Julian calendar. And then there's time zones that refer to an exact offset. So something like plus one hour from UTC or more sort of fuzzy human time zone, right? So something like Europe slash Amsterdam, which may represent different points of time, depending on what time you're talking about and if it's summer or not. Zone date time, as I said, is a combination of an instant and a time zone. So it takes the boring instant and it pairs it with the time zone to project a date time from it, right? So if you know which time zone you're in and you know the exact number of nanoseconds that have passed, then you can give any information. You can tell me what time of the day it is and so on. It's the most powerful thing you have. And then, of course, all the arithmetic operations that are done in this space are done in durations. So what is the difference between two dates or two times or two date times? Well, they're all durations. So enough talking, right? Let's get down to business. Let's make an invoice calculator, which happens to be not so common application of dates and times, but it does happen to be sort of a power user in terms of computation. And let's see how simple it is for Tempo. So step one, if I was building a calculator like this, would be to pick a date time picker. So all the cool kids have told me that these days you pick a date time picker and top it based on the rent book strategy. So whatever friend's library you're using, you pick something based on that, right? The key point is that it should return an ISO A0601 string. So this is the string persistence format that is accepted all over the Tempo api and it's standardized by ISO. And as long as it returns this format, you should be able to construct a temporal type from this. Should it directly return a temporal type? Probably. I mean, these libraries were written in javascript for javascript applications, right? So given that temporal is the sort of future of dates and times in javascript, it makes sense that they could return temporal types. I guess I could fork all these libraries and make versions of them that do this and make that become really popular. Or some of you could do it. That would be fun as well. There are already many that you can pick from. I went to npm and quickly found out too. So there's react type picker. If you're using react, there's date time picker. If you're using jQuery or nothing at all, which I mean, it's just less so jQuery. And yeah, you can drop any of these in. It would return an ISO 601 type and you can use this string to construct a plain date time object in temporal. So temporal is the namespace on the global. You can say temporal.plaindatetime.from, which is a static constructor method. It's a factory method. It takes in a convenient string format and returns a plain date time object. So once you have two plain date time objects right here, you're making an invoicing application. So you at least need two, right? A starting point and an ending point. So now that you have two date times, you can find the difference between the two date times. You can find out how much you worked. When you have a starting point and an ending point, you can find the difference by difference methods. And as we talked about, it's arithmetic. So the answer would be a duration. Now one key caveat that I want to talk about, and one thing that you should note here is that durations can be both positive and negative. And the direction is important. Especially when you're adding a number of durations. If you're adding 50 durations and one of them is accidentally negative, then you're not going to notice that the answer is a little off. And you end up charging less money. Which you don't want to lose money, especially when you're dealing with money. You should make sure. You can check the sign with duration.sign. That sign is a getter. And you can also find just the absolute value. So whatever the sign is, it will return a positive result. So let's see here. In this code example, we have two play date times. We have earlier, which is, well, earlier. It's at midnight. And we have later, which is the same day, but 4 o'clock. So it's later. And just to notice the directionality here, there's two ways to find the difference. There is later.since, earlier. So here later is the receiver. And earlier is the parameter. Because it's been x hours since earlier. And that would give you, you can put in what is the largest you want in the output. And it would give you a duration with four hours. Or you can say until. So because it's until, it means it's until later. And so the sign reverses. This time earlier is the receiver. And this time we just said minutes. So it would give me 240 minutes, which is also four hours. If you change the objects, if you say later until earlier, it would be a negative result. We don't want a negative result because we did not work negative four hours. And then once you have all these durations, you can find out how much it worked. Because you have an array of durations, you can add them all up. My friends who like functional programming, clean code, might try a reduced method. I'm more sloppy. I tend to write code that looks like this. So you can just have a for each loop or sort of a reduced method. One thing to see here is that we created zero durations using two different ways. So in one, we just constructed a new temporal duration object. And in the second, we used the from method that we talked about from last time. If you notice here, I've used a spring format for the duration, PT0S. And that was also sort of referred to in the last slides. Let's talk about it in just a bit. But remember to call absolute if you need to. If you say total.add duration.absolute, that just ignores the sign, which is something we probably need to do, right? To talk about this duration serialization format that I just talked about, durations can be a number of units, right? A duration could be one year, two months, three weeks. And all this duration can be represented in a string by this persistence format. So it's P1Y2M. So if you see the pattern here, it's P, which is constant. It stands for period if you're curious. So period, one year, two months, 1Y2M, 3W for three weeks, four days. Then there's a T for time, and then five hours, six minutes, seven seconds, so on. One thing to note here is that this format can use fractions. So you can say something, you can totally say something like 2.5 months. But do keep in mind that it's probably a bit problematic because, so if you use the template polyfill that I'm going to talk about in just a bit, it's written in javascript. javascript does not handle these numbers really well because it is IEEE floats. Some of these fractions cannot be actually represented in javascript. So if you're using fraction, probably stick to these objects. But if you want, you can use the string format. So now that you have the duration, you can charge money by the hour. Depending on the contract, actually, you might want to charge per day, per hour, per month, whatever the contract is really. The math is easy. The math is so easy, in fact, that it's both intergeneral. For all these related mathematical operations where you have a big duration and you want to bring it down to a single unit, you can just use the total method. So let's see here. I'm a weirdo who calculates the time I've worked in one second durations. So if I work a total of a million seconds, it actually just means that I, you can see I used the total method here to say that I want all this in unit hours, and it would give me 277.7 seconds. So that's how much I work. That's not a lot, but I like to calculate that in seconds. So once you have that, one last thing that I want you to remember is that relativity is important. We talked about things like 2.5 months. Now, 2.5, what's the meaning of 2.5 months or even one month? It's really relative to what you're talking about. Right now, one month might mean 31 days. In February, one month might mean 28 days. So an example would be that we can have a duration that has 2,756 hours. And if you take that total relative to two different times, right, it's actually the same point in time. In this case, it is 1st of January, 2020 midnight. It's also 1st of January, 2020 midnight here. But in this one, it is in the time zone Europe slash Rome, which as you know, is a real time zone that does the daylight savings. So in this case, the number of months that have passed is slightly less. So because they didn't have the daylight savings here, they didn't have the daylight savings here. So some of these things do make sense given relative to what we're talking about. A quick example could be that in many places, if you take an unpaid vacation day off, you get paid a constant amount of money for every month irrespective of how many days in the month that work. But if you take a single unpaid day off, let's say the money that they pay you less is actually dependent on the number of days that they're working that. So, you know, taking the unpaid day off in July is technically much cheaper than taking one in February. So all these things, of course, need to be taken into account. That's why you can use relative to to provide exactly what you're relative to. And next off you have rounding. So now that we have all these things, the final value can just be rounded up or down depending on what contract we're talking about. But sometimes you don't charge by a single unit, you don't charge by every hour or every day, but you charge by a number of X's, a number of days, a number of hours. And if you think about it, charging by the day is technically that. When I say I charge per day, that means that I charge per eight hours. That doesn't mean that I'm talking about charging for 24 hour checks. So this gives you a sort of more powerful method called round. And unlike total, it's you can do more fun things with it. So let's say that I had a six minute appointment with an immigration lawyer and the immigration lawyer actually charges per every five minutes. And of course, they round up. So if I spend six minutes with them, actually, I need to pay them double the amount if I just talked a little faster, which is why I should talk faster now. But that's all fun stuff. We talked a lot and had a lot of fun. But now it's time to do something more sort of practical. So I'm pretty sure a few of you had your phones out. But if you scan that QR code, I can show you that for a bit more, you will find this code sandbox that I made for you. And this is the fun stuff, right? The code sandbox is actually about a job application form. So you can fill stuff out like the date of birth and where you work from and work to. And it calculates a bunch of stuff. It's just an example. But the fun stuff is the calculate age method on this one is written using the moment api. The calculate time to interview is written using the Luxon api. And calculate job experiences is written using plain on date. And your job now is to actually rewrite all these methods. There's links to the docs all over the place. And you need to follow the comments and you need to fork this code sandbox and write the code using Tempo. And you can see maybe you can just comment it out and see the new code. But you'd see a bigger improvement. You'd see less code. And you'd see code that makes more sense to you, hopefully. But, yeah, I'd love to see what solutions you come up with for these. So let's now let's quickly go back to my slides. Okay. My slides. Let's quickly go back to this. Okay. So you can scan that. And if I'm there, I'll probably just paste the link to the chat. And quickly links to the future and present. And send you links to these slides as well. So you can click around these. Here's a link to the polyfill. We're using this polyfill also in the code sandbox. So this is a polyfill that we came up with. The Tempo champions. It's sort of a reference implementation. And it works. It should be like it should work in production as well. So you can follow that. There's an issue on V8 for when Tempo would land in Chromium and Chrome and Edge and all your favorite browsers. And also Spiderman 3 for Firefox, which is my favorite browser. You can keep refreshing these javascript core if you like Safari. And I try to refresh them every five minutes to see if there's some progress. But there's a repository for Tempo v2 for all the new ideas that we're coming up with for sort of a future extension to this. And just quickly need to thank a bunch of people. I want to thank the Tempo champions for working on all these special things. The Moment JS maintainers for kickstarting the original proposal. And the organizers in PC for inviting me and for facilitating this entire event. And Olga for helping me build the sandbox. Thank you, everyone. But last but not least, thank you. And that's it.
25 min
11 Jun, 2021

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic