close
close

first Drop

Com TW NOw News 2024

JS dates will be announced soon
news

JS dates will be announced soon

Of all the recent changes that ECMAScript has in store, my favorite by far is the Temporal proposal. This proposal is very advanced and we can already use this API via the polyfill that the FullCalendar team provides.

This API is so incredible that I will probably dedicate multiple blog posts to highlighting its key features. In this first post, however, I will focus on explaining one of its key benefits: we finally have a native object to represent a “Zoned Date Time”.

But… What is a “Zoned Date Time”?

Well, if we’re talking about human datawe usually say something like: “I have a doctor’s appointment on August 4, 2024 at 10:30 am”, but we leave out the time zone. This omission makes sense, because in general our conversation partner knows us and understands that when I talk about dates, I usually do so in the context of my time zone, Europe/Madrid.

Unfortunately, this is not the case with computers. When we work with “Date” objects in JavaScript, we are dealing with regular numbers.

If we read the official specification it says:

“An ECMAScript time value is a number, either a finite integral number representing an instant in time with millisecond precision, or NaN representing no specific instant”

Aside from the VERY IMPORTANT fact that dates in JavaScript are not UTC but POSIX, completely ignoring leap seconds, the problem with just numbers is that the original semantics of the date are lost. This is because with a human date we can get the equivalent js date, but not the other way around.

Let’s take an example: suppose I want to capture the moment I make a payment with my card. Many people might be tempted to do something like this:

const paymentDate = new Date('2024-07-20T10:30:00');

Because my browser is on a CET timezone, when I write this the browser is just “calculates the number of milliseconds since the EPOX at this CET time”

This is what we actually store in a date:

This means that depending on how you read this information, you will get a different ‘human date’:

If we read this from the CET perspective, I get 10:30:

and if we read this from the ISO perspective we get 8:30:

Many people think they are safe by working with UTC or communicating data in ISO format. This is not correct, as information is still lost.

Even if we work with dates in ISO format, including the offset, the next time we want to display that date we only know the number of milliseconds that have elapsed since the UNIX epoch and the offset. But this is still not enough to know the human moment and time zone in which the payment was made.

Strictly speaking, given a timestamp t0we can obtain n human readable dates that display…

In other words, the function responsible for transforming a timestamp into a human-readable date is not injective, since each element of the set of timestamps matches more than one element of the set of ‘human dates’.

This happens in exactly the same way when storing ISO dates, since timestamps and ISO are two representations of the same point in time:

And this also happens when working with offsets because different time zones can have the same deviation.

If you still don’t see the problem clearly, let me illustrate it with an example. Imagine you live in Madrid and take a trip to Sydney.

A few weeks later you return to Madrid and see a charge on your transaction statement that you don’t recognize… a charge for 3.50 at 2am on the 16th? What did I do? I went to bed early that night!… I don’t get it.

After worrying for a while, you realize that the charge is for the coffee you had the next morning. After all, while reading this article, you deduced that your bank stores all transactions in UTC and the app translates them to your phone’s time zone.

This may end up as an anecdote, but what if your bank has a promotion of one free cash withdrawal per day? When does that day start and end? UTC? Australia?… Trust me, it gets complicated…

I hope you are convinced that working with only timestamps is a problem that fortunately now has a solution.

Among many other things, the new Temporal API introduces a Temporal.ZonedDateTime object specifically designed to represent dates and times with their associated time zone. They have also proposed an extension to RFC 3339 to standardize the serialization and deserialization of strings representing dates:

JS dates will be announced soon

For example:

   1996-12-19T16:39:57-08:00(America/Los_Angeles)

This string represents 39 minutes and 57 seconds past the 16th hour of December 19, 1996, with an offset of -08:00 from UTC. Additionally, the string specifies the associated human time zone (“Pacific Time”), so that time zone-aware applications can take this into account.

In addition, this API allows working with different calendars, such as:

  • buddhist
  • Chinese
  • coptic
  • dangi
  • Ethiopia
  • ethiopian
  • Gregory
  • Hebrew
  • Indian
  • Islamic
  • islamic-umalqura
  • islamic-tbla
  • islamic-civil
  • islamic-rgsa
  • Japanese
  • Persian
  • rock

Of all these, the most common will be: iso8601 (the default adjustment to the Gregorian calendar) that you will work with most often.

Basic operations

Create data

The Temporal API offers a significant advantage when creating dates, particularly with the Temporal.ZonedDateTime object. One of its standout features is its ability to effortlessly handle time zones, including those tricky situations involving Daylight Saving Time (DST). For example, when you create a Temporal.ZonedDateTime object like this:

const zonedDateTime = Temporal.ZonedDateTime.from({
year: 2024,
month: 8,
day: 16,
hour: 12,
minute: 30,
second: 0,
timeZone: 'Europe/Madrid'
});

You’re not just setting a date and time; you’re ensuring that this date is displayed accurately within the specified time zone. This precision means that regardless of DST changes or other local time adjustments, your date will always reflect the correct moment in time.

This feature is especially powerful when scheduling events or logging actions that need to be consistent across regions. By incorporating the time zone directly into the date creation process, Temporal eliminates common pitfalls of working with traditional Date objects, such as unexpected shifts in time due to DST or time zone differences. This makes Temporal not just a convenience, but a necessity for modern web development where global time consistency is crucial.

If you’re curious about why this API is so great, check out this article that explains how to handle changes in time zone definitions.

Compare data

ZonedDateTime provides a static method called compare which will return given 2 ZonedDateTimes one and two:

  • −1 if one is less than two
  • 0 if the two instances describe the same exact moment, ignoring the time zone and calendar
  • 1 if one is greater than two.

You can easily compare dates in unusual cases, such as the repeated clock hour after the end of daylight saving time. Values ​​that are later in the real world may be earlier in clock time, or vice versa:

const one = Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00(America/Los_Angeles)');
const two = Temporal.ZonedDateTime.from('2020-11-01T01:15-08:00(America/Los_Angeles)');

Temporal.ZonedDateTime.compare(one, two);


Cool built-in wardrobes

A ZonedDateTime has a number of pre-calculated characteristics that make your life easier, for example:

hoursInDay

The read-only hoursInDay property returns the number of actual hours between the start of the current day (usually midnight) in zonedDateTime.timeZone and the start of the next calendar day in the same time zone.

Temporal.ZonedDateTime.from('2020-01-01T12:00-08:00(America/Los_Angeles)').hoursInDay;


Temporal.ZonedDateTime.from('2020-03-08T12:00-07:00(America/Los_Angeles)').hoursInDay;


Temporal.ZonedDateTime.from('2020-11-01T12:00-08:00(America/Los_Angeles)').hoursInDay;


Other cool features are daysInYear, inLeapYear

Transforming time zones

ZonedDateTimes offers a .withTimeZone method by which we can modify a ZonedDateTime as desired:

zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+09:00(Asia/Tokyo)');
zdt.toString();
zdt.withTimeZone('Africa/Accra').toString();

Basic arithmetic

We can the .add method to add the date portion of a duration using calendar arithmetic. The result is automatically adjusted for daylight saving time using the rules of the timeZone field of this instance.

The GREAT SHARE The advantage of this is that it offers the possibility to play with calendar calculations or regular time durations.

  • Adding or subtracting days keeps the clock time consistent across DST transitions. For example, if you have an appointment on Saturday at 1:00 PM and you ask to reschedule it 1 day later, you expect the new appointment to still be at 1:00 PM even if there is a DST transition during the night.
  • Adding or subtracting the time portion of a duration should ignore DST transitions. For example, a friend you asked to meet in 2 hours will be annoyed if you show up 1 hour or 3 hours late.
  • There should be a consistent and relatively unsurprising order of operations. If results are at or near a DST transition, ambiguities should be handled automatically (no crashes) and deterministically.
zdt = Temporal.ZonedDateTime.from('2020-03-08T00:00-08:00(America/Los_Angeles)');

laterDay = zdt.add({ days: 1 });


laterDay.since(zdt, { largestUnit: 'hour' }).hours;



laterHours = zdt.add({ hours: 24 });



laterHours.since(zdt, { largestUnit: 'hour' }).hours;

Calculate differences between dates.

Temporal offers a method called .until which calculates the difference between the two times represented by zonedDateTime and other, optionally rounds it, and returns it as a Temporal.Duration object. If other is earlier than zonedDateTime, the resulting duration is negative. If you use the default options, appending the returned Temporal.Duration to zonedDateTime produces other.

This may seem like an obvious thing to do, but I recommend you read the full specification to understand the details.

The Temporal API represents a revolutionary shift in how time is handled in JavaScript, making it one of the few languages ​​to comprehensively address this problem. This article has only scratched the surface by discussing the difference between human-readable dates (or wall clock time) and UTC dates, and how the Temporal.ZonedDateTime object can be used to accurately represent the former.

In future articles we will explore other fascinating objects such as Instant, PlainDate, and Duration.

I hope you enjoyed this introduction.

Have fun coding! 🙂