I’m currently working on a local event website for a U.K. city that will show tourists and residents what’s happening in the city today (and today ONLY).
I’m building it in NodeJS with Express, using MongoDB via Mongoose.
I ran into a problem when building an ‘add event’ route that takes various details, including (obviously) dates. However, I currently spend a lot of my time in the CET (Central Europe) timezone and when I was entering dates and times they were being converted into UTC time – which is normal for when we use new Date()
in JavaScript.
But…
This means the time is now an hour too early when viewed in local U.K. time.
For example;
There is an event taking place at 09:30 on the 5th May 2024. I enter the details, and the time is then stored in the database as 2024-05-05T07:30:00.
A visitor in the U.K. will be shown the time 08:30 (BST is currently UTC + 1 hour).
One solution is to just take an hour off when I enter the time. But, that means I’d have to remember to do this when I’m not in the U.K. and also, we have to take into account daylight savings times for dates entered in the future. It quickly becomes obvious that we need a better solution.
I tried searching for one in the Date section of the MDN, but to no avail. But there is a solution and I found it on a Stack Overflow question… Thanks Copper Boy for solving the issue I was having.
To save a time in a different timezone from the one that you are currently in, we need to use the Intl.DateTimeFormat()
object to work out what the GMT offset is in the target country for any given date (this is what solves the issue of daylight savings time).
If we pass in the date (which in my code I will be getting from a form entry – which you can see in primitive version below, without any error checking), we then use Intl.DateTimeFormat()
to format the time to the required timezone (in my case, "Europe/London"
).
To see a full list of supported timezones, run this code in a modern browser or in NodeJS: console.log( Intl.supportedValuesOf("timeZone") )
.
If I pass my datetime to const longOffsetFormatter = new Intl.DateTimeFormat("en-GB", { timeZone: "Europe/London", timeZoneName: "longOffset" }).format(myTime)
, the output will be 06/05/2024, GMT+01:00
.
Once we get this data, we just need to split it so that we only have the +01:00
so that we can append it to our date before saving to the database, where it converts to UTC time.
We get the split by using const gmtOffset = longOffsetFormatter.split("GMT")[1]
. This will split the string at GMT and return the second part (after the split).
The final step is to create a new date with the get offset appended to the original date.
The result is for a datetime of 2024-05-06T09:30 the date with the gmt offset is 2024-05-06T09:30+01:00. This is then saved in the database as 2024-05-06T08:30:00. But for a date in, say, November, the date saved could be 2024-11-06T09:30:00+00:00. Both of these times are for 09:30, but taking into account daylight savings. Boom!
Basic Code (No error checking)
const myTime = new Date(`${req.body.date}T${req.body.time}`)
const longOffsetFormatter = new Intl.DateTimeFormat("en-GB", { timeZone: "Europe/London", timeZoneName: "longOffset" }).format(myTime)
const gmtOffset = longOffsetFormatter.split('GMT')[1]
// THIS FUNCTION QUICKLY CHECKS FOR BST OR GMT AND ADDS RELEVANT INFO TO THE TIME
const getDate = () => {
return gmtOffset !== "" ? myTime + gmtOffset : myTime + "+00:00"
}
const newDate = new Date(getDate())
Note: I have an extra step in my code as I’m dealing specifically with U.K. time, which for 6 months of the year is actually GMT+00:00. In this scenario the gmtOffset returns an empty string, so my dates are saved as per CET time instead. Therefore I added the getDate()
function to append +00:00
if this is the case. For other time zones, this would not be required.
Leave a Reply