Working with Time in Javascript Applications

Intro

If you are building an application at some point it’s likely you will have to deal with the concept of time. You may need to synchronize actions across various systems, you may need to fetch data in a specific time range, the list goes on.

UTC

The crux of the problem is that people all over the world use different timezones, daylight savings time, etc etc.

The first and most important concept to understand is that we need to use a synchronized clock, no matter what part of the world a user, or server is located in. That synchronized time is called Coordinated Universal Time or UTC.

Anytime you create a timestamp in your application, or schedule an event, or serve data with dates, it should always be UTC. That way the time is the time, no matter what. This time should be stored and transferred in ISO 8601 format. Ex: 2017-05-15T13:30:34Z.

Showing Local Time

If we use UTC for all dates, you can simply convert UTC into the local timezone for a user at the last moment. Local timezones are typically detected by the browser, and then can be quickly converted with the use of a library.

Time Libraries

So how do we actually do this?

It is possible to use built in Javascript time calculations and formatting.

MDN: Date()

For example if we wanted to get yesterdays date in YYYYMMDD format we could do the following

// get yesterdays date in timeformat YYYYMMDD
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
let isodate = yesterday.toISOString().split('T')[0];
let finaldate = isodate.replace(/-/g,"");
return finaldate;
 // "20220515"

It’s not very semantic is it? Fortunately there are some very good libraries that make dealing with time much easier.

Moment.js

For many years Moment.js was the defacto library for dealing with time. For a long time it was completely essential because those native Date() methods didn’t even exist. Now however Moment.js is outdated, and they will be the first ones to tell you so.

They have a list of recommended alternatives, the first of which is Luxon.

Luxon.js

This is going to be the new defacto time library in my opinion. Written by one of the Moment.js contributors to take advantage of the new nate Date() functions. Tons of features, and well supported.

Let’s take a look at a couple of examples using Luxon.

Generate UTC

Let’s generate a UTC date and then view it in four different formats.

const { DateTime } = require("luxon");

let today = DateTime.utc();
console.log(today);
console.log(today.toISO());
console.log(JSON.stringify(today));
console.log(Date.parse(today));

four different UTC formats

Date Formats

Object

The first item is just the raw object that Luxon creates. It notes the unix timestamp and other information that is useful for Luxons methods.

luxon date object

ISO

One of those methods for example is converting the time to ISO format.

2022-05-16T18:28:52.464Z

Remember, this is the preferred format for storing and transferring time data.

String

We can also use the native JSON.stringify method, which curiously does not stringify the whole object, but outputs a string of the ISO format.

"2022-05-16T18:28:52.464Z"

Timestamp

And lastly the native Date.parse() method which just returns the unix timestamp.

1652725732464

The unix timestamp is the number of seconds that have elapsed since January 1st, 1970 at UTC.

It is very handy to know the methods to convert the date, because it can only be consumed in certain formats in certain situations.

Display Format

Changing the display format is easy with Luxon. For example I have a specific application that requires the date to be in YYYYMMDD format. So I can simply do the following.

let todayYYYYMMDD = DateTime.utc().toFormat("yyyyMMdd");
console.log(todayYYYYMMDD);
// 20220516

There are many many options for display formatting.

Luxon: Formatting

Convert to Local Time

Luxon can easily convert to local time with the .toLocal() method. It uses your system settings to determine your locale and timezone.

let todayLocal = DateTime.utc().toLocal();
console.log(todayLocal);

Which then returns the Luxon date object we learned about earlier.

local luxon time object

And we now know how to convert this object to other formats.

Past Dates

What if we wanted to specify a date in the past or future? Very easy.

let fiveDaysAgo = DateTime.utc().minus({days: 5}).toISO();
console.log(fiveDaysAgo)
// 2022-05-11T19:14:59.794Z

let verySpecificFutureTime = DateTime.utc().plus({ years: 10, months: 2, days: 5, hours: 12, minutes: 13, seconds: 59}).toISO();
console.log(verySpecificFutureTime);
// 2032-07-22T07:31:38.437Z

Past Date at Specific Time

Additionally we can generate a past date at a specific time, for example midnight is a common time. Specifying a past date is pretty easy if we know the exact date. But what if we just want a function that gives us days from today, whatever today is?

function generatePastDateMidnightISOUTC(daysAgo) {
    let now = DateTime.now();
    let currentYear = now.year;
    let currentMonth = now.month;
    let currentDay = now.day;

    let currentDateMidnightUTC = DateTime.utc(
      currentYear,
      currentMonth,
      currentDay
    );
    let pastDateMidnightUTC = currentDateMidnightUTC.minus({ days: daysAgo });
    let pastDateMidnightISOUTC = pastDateMidnightUTC.toISO();
    return pastDateMidnightISOUTC;
  }

First we get the current date, then we extract the year month and day from that. We use that to generate a Datetime of UTC Midnight today. Then we subtract the number of days. Lastly we can convert that to ISO.

Conclusions

  • Dates should be in UTC
  • Preferred storage and transfer format is ISO
  • Convert to local time only for display in the client
  • Use Luxon library