DEV Community

Cover image for Say Goodbye to JavaScript’s DST Date Confusion
Hayato Takenaka
Hayato Takenaka

Posted on

Say Goodbye to JavaScript’s DST Date Confusion

JavaScript's Date object can behave in non-intuitive ways when handling daylight saving time transitions.

For example, see the following scenario in the Central Standard Time (CST) zone of the USA.

const date = new Date('2021-03-14T03:00:00.000')
date.setMilliseconds(-1) // results 2021-03-14 03:59:59.999 CST
Enter fullscreen mode Exit fullscreen mode

On March 14, 2021, daylight saving time begins. The time jumps directly from 2021-03-14 01:59:59 CST to 2021-03-14 03:00:00 CST.
In this example, subtracting 1 millisecond from 2021-03-14 03:00:00.000 CST results in 2021-03-14 03:59:59.999 CST. This appears to be a simple subtraction of 1 millisecond, but it actually advances the time by 1 hour.

This behavior is not a bug but a result of strictly following the ECMAScript specification (https://262.ecma-international.org/11.0/#sec-local-time-zone-adjustment).

How to handle duplicated time

A Date object created from a duplicated time during daylight saving time (DST) transition always refers to the time before DST ends. In other words, there is no simple way to obtain a Date object that refers to the UTC time after the end of DST from a duplicated time.

Let's consider a method to obtain the correct UTC time by specifying a local time string that represents a duplicated time during the daylight saving time transition and indicating whether it is before or after the end of DST.

function utc(localtime, after) {
  const result = new Date(localtime)
  if (after) {
    const clock = date => date.getUTCHours() * 60 + date.getUTCMinutes()
    const nextday = new Date(localtime)
    nextday.setDate(localtime.getDate() + 1)
    const adjust = clock(nextday) - clock(localtime)
    if (adjust > 0) {
      const advanced = new Date(localtime).setMinutes(localtime.getMinutes() + adjust)
      const advancedUTC = new Date(localtime).setUTCMinutes(localtime.getUTCMinutes() + adjust)
      if (advanced !== advancedUTC) {
        result.setUTCMinutes(localtime.getUTCMinutes() + adjust)
      }
    }
  }
  return result
}
Enter fullscreen mode Exit fullscreen mode

By taking the difference in UTC hours and minutes between the same time on the next day in local time and the original time, you can find the time adjusted for daylight saving time within 24 hours (the overlapping period, usually 1 hour).

If you advance the original time by the overlapping period in both local time and UTC, and there is a difference, you can determine that the original time is the overlapping time when daylight saving time ends.

Solutions

These issues can also occur in date-time libraries such as moment.js, date-fns, Day.js, and luxon.

qrono addresses these issue. This library is designed with a very simple approach by not handling multiple time zones simultaneously beyond the local environment, thereby resolving the aforementioned issue.

Top comments (0)