How to outsmart time: Building futuristic JavaScript applications using Temporal

Hello and welcome to my presentation.

I'm Ujjwal and I am one of the champions of the Temporal proposal.

And today I'm going to talk about Temporal.

So let's start.

First of all, just to give a quick recap about the stuff that I've already talked about, about Temporal in the wild.

What is Temporal?

Why do we need it?

Well, as you might know, the Date object in JavaScript is very severely outdated.

There's serious issues.

And people don't really like writing code using that.

There are popular third party libraries, like moment JS or Luxon for handling these things, but they have quite a few problems.

And something needs to be done and something needed to be done.

So we decided to create Temporal.

Temporal is a state of the art date/time handling library in JavaScript.

It is special to me because it tries to combine these two things.

It has an ergonomic API with a special focus on common use cases.

So some simple things like adding a month to a date becomes super easy.

And then it also combines that with a powerful feature set that can accommodate very complex use cases with the likes of local calendar support or custom time zones and calendars.

So you might have a custom calendar and time zone for Mars and you can run JavaScript on Mars.

I have a talk about this, so.

I suppose you can watch this talk on YouTube.

And this goes into much greater detail about these small points that I mentioned.

But without further ado, let's talk about where we are now.

So now Temporal is stage 3.

What does this mean?

It means that all the tiny details have been discussed.

We in TC 39, realized that we had done all the discussing we could in our cushy chairs.

And now it's time to actually push this and it's time to actually implement it in a browser and see what people think and have them use it.

The specification text has been approved by the committee.

So the committee is now satisfied with the design of the proposal that I'm going to introduce you to.

Now it's time for us to start implementing and using Temporal, which is why I'm here.

I want you to start using it and start thinking about it.

They, there have been polyfill implementations.

So when I'll talk to you about one of the sort of canonical polyfills that me and my colleagues have developed and you can use it immediately and there have been ongoing browser implementations.

So we're going to talk about that as well.

This is a quick chart that gives you a quick overview of all the different classes that we have in Temporal.

Actually, it's one of the biggest proposals that one of the biggest proposals that we've had in TC 39, it just look at the API surface.

That's so many classes the classes are more or less divided into two categories.

There's exact time types, which is instant.

And there are calendar or "wall clock" time types, which are fuzzy time types, so to say.

And there's PlainDateTime, PlainDate, PlaintTime, and so on.

There are specific cases.

There's three helper classes, TimeZone, Calendars, and Durations.

Durations for actually doing all sorts of arithmetic on, on these different classes.

And then there's ZoneDateTime, which is just sort of the Superman, which, which sort of combines both of these together.

And you can use that.

Just to give a quick summary of the API.

There is instant, instant represents an absolute point in time.

It's an exact point in time.

It is represented via nanoseconds that have passed since the Epoch There are plain types.

So plain foo so to say, which deal with regular wall clock time and calendar date.

So you can say, okay, well, At 8:00 AM.

And it doesn't matter if you're in St.

Petersburg or in Victoria.

And that's why they're great.

There are calendars which refer to human calendars, calendars, like the Gregorian, the, the Hijri Islamic calendar, or the Persian calendar, for example.

And there are times zones that refer to an offset.

Which could be, you know, plus five plus six 30, so on or a human time zone, like your Moscow or, or, you know, Australia, Sydney.

ZoneDateTime is a combination of instant and time zone.

So it takes an instant, it takes a time zone and then it can project that instant in that time zone to give you actually a ... something that resembles a date time, but then you know, it does refer to an instant in time.

All arithmetic operations are done using durations as I've mentioned.

So there is a serialization format for Temporal basically when we started out with Temporal, we were using ISO 8601, which if you might, if you know, it is the sort of canonical timestamp format, it's very prevalent.

There's RFC 3339, which is it's IETF counterpart.

And it's severely old and limited.

Just to give you an idea.

It was created before I was more so a bunch of things need to be done.

There's a number of ad hoc formats with additional time zone that are just floating in the wild.

If you write Java, you might've noticed one of them and we needed to build on top of that.

So we needed to also add calendars to the mix and, this, this made me realize this made us realize that there is the need for a standardized generalize extension format.

Something that can have time-zones, which is the past, calendars, which are all present and anything which can come up in the future.

There's there should be a generalized way of doing these things and not sort of ad hoc, just pushing things into each other.

And so we came up with a draft.

I've written a draft and actually this draft has now been adopted.

So there's a new title, but you can find this it's the title of the repository, which it's on.

And it's a new draft that adds things to the timestamp to make it a more modern.

So to say, so we're working through the standards process and hopefully by the end of this year, maybe it would be published or definitely in the next year.

So just to give you a quick overview, this is what the what this looks like.

It is the standard sort of, if you see the green box, there is the standard timestamp, then there's the time zone extension that I was talking about, which is used in the Java ecosystem, for example, but it is not completely standardized.

And then there's the calendar extension that we added to that.

So, and these are the different types that can accept sort of subsets.

So now that boring stuff is done, let's make an invoice calculator.

Let's see how you could practically use this API and judge for yourselves.

Let's like if, if you know, if it even makes your life better in any way.

So step one is pick a date-time picker.

We need to pick a picker.

You need to pick a date, time picker that fits your rendering strategy.

I'm not a person who does front end, but If I I've been told by friends who do front end, that people usually pick libraries that, that work with their sort of rendering strategy with whatever framework they're working on.

The only important part when it comes to Temporal is that it should return an ISO-8601 string, which are accepted by Temporal.

Should it return a Temporal type?

Well, these are JavaScript libraries and Temporal is supposed to be the sort of canonical way of doing dates and times in JavaScript.

So it makes sense and because Temporal is fairly new this doesn't happen right now, but it should, and maybe you should fork some of these libraries to make that happen.

But talking about date-time pickers that return ISO-8601 strings there are already many you can pick from.

So there's react-picker.

I just went on NPM and found a few for you.

There's react-picker if you like React and datetimepicker, if you like jQuery or just nothing at all.

So once you had this ISO-8601 string, you can call Temporal.

PlainDateTime.from().

And so from here being a static method, that returns, it's a static factory function so to say, and this patttern must be familiar if you are familiar with the Rust ecosystem, for example, which uses it extensively.

And I think Ruby as well, but I'm not sure about that.

So you can throw this string in there, and that would construct a PlainDateTime from the string that was returned by the picker.

And that would just work.

So now you can have a start date, time and end date time.

And now you have two date times.

Now you find you need to find the difference between them, right?

Because you're, you're building an invoicing application.

And then you have the start and the end.

So when you have a start point and end point, you can find the difference.

Durations can be both positive and negative.

So, the direction is important.

Keep that in mind.

You need to keep that in mind, especially when you're adding durations and especially when you're dealing with money.

So we have, we're going to have a list of durations here and you're going to add them all up.

If one or, or some of them are negative, then it's just going to completely change your, your results.

So keep that in mind.

You can check the sign of any duration by calling the duration.sign accessor on, on the duration object, you can also find the absolute value of a duration by calling duration.abs.

It's a function like math.abs for absolute value.

So that would make your life easier.

Let's say.

So we have earlier the, the early date, the sort of date time instance.

So this as you can see from the, from the string there it is midnight.

And then there's later, which is the same day, but at 4:00 AM.

So it's earlier and later, and then you can get the result as, and this is the fun part.

There's two methods to do this for difference, right?

There's since.

And, and for since, keep that in mind later comes before earlier, so later since, right.

Because it's since later.

And, and until comes with earlier first.

So if you keep these two in the correct order, you're just getting positive durations as a result, as you might see.

If you're not you're going to get a negative duration and you can check that as I said, by signing over, or just find out the absolute value.

What is this output of this?

It's the string serialization of durations that we are going to talk about in just a second, but let's find out how much you worked.

So you have all these durations, you have an array of durations, so to say, and you can add them all together.

If you're a happy functional programmer, you might like something like that.

So you can call reduce on the.

Durations array.

And if you're like me you might just do forEach and I, I find that much more convenient.

Remember to call absolute here if, if any of the durations might be negative, it might be the better way to do this, actually.

Talking about the duration serialization format that I was telling you about.

So as you might see, you can create a duration from one years, two months, three weeks and so on.

And the format is P one Y so P for period.

And that's the sort of it denotes that it's the duration one, Y means one year, two M two months, three W so on until days.

And then after days you have T so that it's now time minutes and then there's five H for five hours and so on.

You can use fractions here, so you can use 1.5 Y 1.5 years, but be careful about that.

So, so first of all, fractions only make sense within the context they're in.

So the value of 2M two months and the 3d three days is, is kind of fixed.

It's mostly fixed, right.

But 2.5.

M the, the number of days in this now depend on which month you're adding to.

So, so half a month, like, what does that mean?

Is that is it February or is it July?

So be careful also because in the polyfill, the polyfill is implemented in JavaScript and I implemented this in the polyfill actually, and the JavaScript implementation has a lot of issues because it it's doing all this math and it cannot handle some factions carefully.

So if you're doing fractions, maybe don't use this serialization format, maybe use the object format that we're showing here.

So now all the boring stuff is done.

Let's charge money by the hour.

You have depending on the contract, you might want to charge per day or per hour or.

Whatever works for you.

The math is easy.

In fact, the math for doing this is so easy.

It's built into temporal.

It's actually, not easy, but it's something that we wanted to support for big bringing things.

So you have a big duration.

It might have a lot of units, right?

And, and for bringing things to just a single unit, use the total method.

So the total method, super simple, you can have a duration let'ssay here I, I worked a million seconds.

And so how many 24 hour days did I work?

Because I'm charging per day.

And, and if you do total with unit hours, you may see that I have worked 2277 point many sevens hours actually, oh, no.

24 hour days, but I did hours.

Okay.

Nevermind.

But yeah, these many hours of work, so I'm charging per hour.

Step five B it says relativity is important.

Now what does this mean?

Is that when you are adding two durations and you're finding all these things relativity is important.

If you add a one month, as I was saying to, to today's date the final result depends on which month it is today.

Right?

I remember this anecdote from, from a friend who was saying that.

Well, they earn the same amount of money every month, but then when they take a PTO the a,ount that is sort of no, not a PTO unpaid day off, right?

The, the amount that is sort of reduced from their salary, is a fraction of like how many days they're already in a month.

So a unpaid vacation in February would cost you much larger than in August, let's say, which sounds fun.

Probably not.

So anyway, in this case we have a duration, it has 200, 2,756 hours.

And if you find the total, relative to this thing, right?

Midnight at 1st of January, but with the time zone.

So here you have Europe slash Rome and you find the number of months here, then the number of months, which it's 3.79 something is more than if you do not take the timezone into account.

And why does this happen?

This happens because there is a DSD shift during this time.

So, so do keep that in mind when, when you're doing anything at the boundaries of months and DST shifts and all these things can impact what the result is going to be.

Now, once that's done, sometimes total is not what you can use.

So you might need rounding.

A final value can be the final value that you get can be rounded down or up depending on the contract.

Sometimes you don't charge by it.

So the problem with duration is that not everybody charges by like a single unit, right?

Yeah.

And I was telling you about charging per day.

But when, even if you do charge for a day, that doesn't mean 24 hour days, right.

Each day must be, say eight hours or seven hours.

And so you don't charge by hour or days, but by eight hours and so on.

And so for this round comes to the rescue.

So round is a sort of lower level function that can help you do some of these fun math things.

So for example, let's say you're visiting an immigration lawyer and they charge per five minutes.

If you visit them for six minutes, you can round that to the nearest five minutes and ceiling because they always charge extra.

Anyhoo.

That's that's our invoice app.

And I, I hope you found that interesting and engaging.

There is an assignment here.

So if you can scan this QR code you'll find an assignment in this link, and there's a bunch of examples there, which are written.

In using the date object, using moment, using Luxon.

So, and you can rewrite all of those using Temporal there's links to their Temporal API docs, and we are pushing Temporal to MDN and the polyfill is up.

So I'm going to talk about that in just a bit, but try that out.

Tweet to me I'm at @ryzokuken, as I said, the beginning And, and let us know what you think and hopefully all of those examples you can see when you write code using temporal, that is hopefully simpler to write and for about links to the future and to the present.

We have a link here to the polyfill.

I will, you can check my slides and just click through these links.

There is a link to the V8 tracking issue where you can track.

How long it's going to take for this to land in chromium in, in Chrome V8 also used in Edge for example.

And I recommend refreshing this page, like every five minutes.

There is a spider monkey tracking issue for Firefox.

There is JavaScriptCore which some of my colleagues are working on.

So I hope fingers crossed that that's Safari might be the first browser to get Temporal.

There is temporal V2, which is a repository that we set up for discussing ideas that weren't included in this version of the proposal, but might be interesting to explore in the future.

And I just have to give special thanks to a few people.

So we have the Temporal champions.

We have the moment JS maintainers.

Thank you so much for your support.

We have the organizers and program committee of the conference.

Thank you for inviting me.

It's so nice to be able to present at this conference.

There's Olga Kovets by the way, helped me.

Set up the playground thing I'm a grandfather when it comes to some of these things.

So thank you.

And thank you all.

How to outsmart time πŸ§ πŸ•’

Building futuristic JavaScript applications using Temporal

Ujjwal Sharma (@ryzokuken), WD Code πŸ‡¦πŸ‡Ί

Recap πŸ”™

  • Date severely outdated, has serious issues.
  • Popular third party libraries for date/time handling.
  • Quite a few problems exist, need to do something.
  • Temporal: state-of-the-art date/time handling in JS.
  • Ergonomic API with special focus on common use-cases.
  • Powerful feature set accommodating complex use-cases.
    • Local Calendar Support
    • Custom Time Zones and Calendars

embed of Ujjwal's presentation on Temporal that can be found on YouTube.

Temporal is now Stage 3! πŸŽ‰

What does that mean? πŸ€”

  • All the tiny details have been discussed.
  • The specification text has been approved.
  • The committee is satisfied with the design.
  • Time to start implementing and using Temporal.
  • Polyfill implementations.
  • Browser implementations.

Slide shows two groups, and a space between them.

On the left is a group labelled Exact Time Types.

It contains ZonedDateTime which spans both groups and the space between. Under ZonedDateTime is Instant, and below that the text "These types know time since Epoch".

In the gap between the two groups are

From top to bottom: ZonedDateTime which spans the gap and both groups then TimeZone, Calendar and Duration.

A line from the right edge of Instant branches and joins to the left edges of TimeZone and Calendar.

In the group on the right, labelled Calendar Date Wall-Clock Time Types in addition to ZonedDateTime there are 5 types, arranged in a hierarchy that is displayed left to right.

Lines emerging from the right hand edges of TimeZone and Calendar join and then connect to the left hand edge of PlainDateTime. A line emerges from the right of PlainDateTime an branches and connects above and to the right to PlainDate on its left hand edge. A line emerges from the right edge of PlainDate, branches and them connects withPlainMonthDay and below it the second branch connects to PlainYearMonth.

The second branch that emerged from the right edge of PlainDateTime connects with the left edge of PlainTime

At the bottom of this group is the text "These have a calendar (except PlainTime) and know wall-clock time"

Summary

  • Instant represents an absolute time.
  • Plain* types deal with regular wall-clock time and calendar date.
  • Calendars refer to a human calendar.
  • TimeZones refer to an offset or a human time zone.
  • ZonedDateTime is the combination of an Instant and a TimeZone.
  • All arithmetic operations are done using Durations.

Serialization Format πŸ“œ

  • ISO8601/RFC3339 old and limited.
  • Ad-hoc formats with additional time zone.
  • Need to also add calendar into the mix.
  • The need for a standardized, generalized extension format.
  • draft-ryzokuken-datetime-extended
  • Working through the standards process.

Persistence Model

Let’s make an invoice calculator! πŸ–©

Step 1: Pick a date-time picker πŸ“†

  • Pick a date-time picker component that fits rendering strategy.
  • Should return an ISO-8601 string.
    • Should return a Temporal type?
  • There are already many you can pick from!
    • react-picker (React)
    • datetimepicker (jQuery)

Temporal.PlainDateTime.from(myString)

Step 3: Two date-times? Find the difference!

  • When you have a start point and an end point, you can find the difference.
  • Durations can be both positive and negative, direction is important!
    • Note when adding durations especially.
    • Also especially when dealing with money!
  • You can check the sign with duration.sign.
  • You can find the absolute value by duration.abs().
const earlier = Temporal.PlainDateTime.from('2020-01-09T00:00');
const later = Temporal.PlainDateTime.from('2020-01-09T04:00');
 
const result = later.since(earlier, {
  largestUnit: 'hours'
}); // 'PT4H'
 
const result2 = earlier.until(later, {
  largestUnit: 'minutes'
}); // 'PT240M'

Step 4: Find out how much you worked!

  • Once you have an array of durations, you can add all of them together.
  • durations.reduce(
      (total, current) => total.add(current),
      new Temporal.Duration()
    );
    const total = Temporal.Duration.from(β€˜PT0S’);
    durations.forEach(duration => total.add(duration));
    
  • Remember to call abs() if you need to!

Duration Serialization Format πŸ“œ

  • Temporal.Duration.from({
    	years: 1, months: 2, weeks: 3, days: 4,
    	hours: 5, minutes: 6, seconds: 7})
    .toString(); // 'P1Y2M3W4DT5H6M7S'
    
  • Can use fractions! (careful)

Step 5a: πŸ’Έ by the hour

  • Depending on the contract, you might want to charge per day or per hour.
  • The math is easy! In fact, it’s built into Temporal.
  • For bringing things to a single unit, just use total(...).
// How many 24-hour days is 1,000,000 seconds?
d = Temporal.Duration.from(β€˜PT1000000S’);
d.total({ unit: β€˜hours’ }); // 277.77777777777777

Step 5b: Relativity is important! πŸ”Ž

// Find totals in months, w/o taking DST into account
d = Temporal.Duration.from({ hours: 2756 });
d.total({
   relativeTo: '2020-01-01T00:00+01:00[Europe/Rome]',
   unit: 'months'
}); // => 3.7958333333333334
d.total({
  unit: 'months',
  relativeTo: '2020-01-01'
}); // => 3.7944444444444443

Step 5c: Rounding for the win! πŸ†

  • The final value can be rounded up or down, depending on the contract.
  • Sometimes you don’t charge by a β€œX”, but rather β€œn Xs”.
  • round(...) to the rescue!
d = Temporal.Duration.from({ minutes: 6 });
d.round({
  smallestUnit: 'minutes',
  roundingIncrement: 5,
  roundingMode: 'ceil' }); // => PT10M

Assignment Time πŸ“‹

Links to the future (and present) πŸ”—

Special Thanks πŸ™

  • Temporal Champions
  • Moment.js Maintainers
  • Organizers and PC
  • Olga Kobets

Ta! β€πŸ•’