0

I am struggling to handle dates and times within Javascript environment. We are using two input fields to obtain a date and time for an event. The event needs to display and calculate all dates within it's local timezone. E.g. if event is in Melbourne Australia, then I see the local time in Melbourne.

We are using flatpickr for date entry and time entry (2 separate fields).

Date returns: 2022-11-18T00:00:00.000Z (i.e. we only care about the date portion. time is always 00:00:00. This is stored in Mongo as "date" : ISODate("2022-11-18T00:00:00.000+0000"))

Time returns: 09:00 AM

I would like to store this into our MongoDB as 2022-11-18T09:00:00.000.

Therefore whenever this is read or used in a calculation it always uses the time local to the event.

I have tried moment and date-fns: however I cannot find consistency between servers and browsers. Everything I have tried on SO always converts UTC.

Any help is greatly appreciated.

  • It is likely a much better idea to store everything in UTC along with the offset or timezone of the location that the event is occurring. That way you can display dates and times in the context of the user's current location, the event location or UTC. – RobG Nov 18 '22 at 04:09
  • Thanks @RobG For this to work I would need the timeZone of the event location. We won't know the location in some instances. We would obtain the date time from the user (whom is posting the data) location – papawheelie Nov 18 '22 at 05:26

2 Answers2

2

Date and time functionality can be confusing so best to keep things as simple as possible. It's generally recommended to keep everything in UTC and convert to local for display.

One issue is how to get UTC in the first place.

Provided a user has their device set to appropriate regional settings, construct a Date from the date and time inputs (likely it requires a manual parse, don't use the built–in parser). Then generate a timestamp using toISOString which will be the UTC equivalent.

To then get the correct offset information, you can use:

new Intl.DateTimeFormat().resolvedOptions().timeZone

which will return the IANA representative location that the system is set to, which hopefully is consistent with the date and time of the local event. If not (e.g. the user can create an event for a different timezone) you'll need library support to generate a suitable UTC timestamp (see Calculate Timezone offset only for one particular timezone).

Store the UTC timestamp (or date object) in the database along with the event timezone. That way you can generate a date and time for the location of the event and also the equivalent date and time based on the user's system settings, or any timezone you like.

E.g.

// Create a Date for New Year's Eve party starting
// at 8:00pm based on the user's current system settings
let event = new Date(2022,11,31, 20);

// Generate UTC timestamp
let ts = event.toISOString();

// Get the (assumed) event offset data
let tz = new Intl.DateTimeFormat().resolvedOptions().timeZone;

// Data to store:
console.log(
  `Store\n` +
  `UTC timestamp: ${ts}\n` +
  `IANA timezone: ${tz}`
); 

console.log(
  `Event local date and time\n` +
  new Date(ts).toLocaleString('en-ca', {
    timeZone: tz,
    timeZoneName: 'long'
  })
); 

// Equivalent for user in Somalia
console.log(
  `Mogadishu equivalent\n` +
  new Date(ts).toLocaleString('en-ca', {
    timeZone:'Africa/Mogadishu',
    timeZoneName: 'long'
  })
);

The above demonstrates that once you have the UTC timestamp and IANA timezone, you can generate a timestamp for any timezone, including the one for the event itself.

RobG
  • 142,382
  • 31
  • 172
  • 209
1

In MongoDB / JavaScript Date objects are UTC times - always and only!

If you need to preserve the input time zone from user, then you have to store it in a separate field. Then your application can display the time in time zone base on input data.

At almost any "date-to-string" function you can provide the time zone information for desired output.

Wernfried Domscheit
  • 54,457
  • 9
  • 76
  • 110
  • This makes sense however applying to my scenario I am not experiencing this. e.g. I am using the following to combine: new Date(`${savedEvent.date.toISOString().slice(0, -1).substring(0, 10)} ${savedEvent.deliveryTime}`) Locally: converts to UTC time as expected AWS (EB): uses local timezone I can pinpoint this to new Date but not sure how to resolve – papawheelie Nov 18 '22 at 12:01
  • So when I change this back to basics and store new Date() it works as expected on both servers. Therefore it's related to the string manipulation of joining 2 fields (data and time) - I will continue until I have a result – papawheelie Nov 18 '22 at 12:39
  • In order to parse a string into a `Date` object, I suggest ready-to-use libraries like [moment.js](https://momentjs.com/docs/), [Luxon](https://moment.github.io/luxon/index.html#/?id=luxon) or [Day.js](https://day.js.org/en/). Usually they are much better than the native JavaScript `Date` methods. – Wernfried Domscheit Nov 18 '22 at 13:39
  • `new Intl.DateTimeFormat().resolvedOptions().timeZone` returns the IANA representative location for the host so you can use that (provided it's consistent with the date ant time that the user is providing). – RobG Nov 19 '22 at 07:36