'How to set up only time in date-fns and still keep the timezone in javascript

I currently have the following use case:

  • User receives a date in UTC from the backend
  • This date is transformed into local time for displaying purposes
  • The date is displayed in different inputs. One input for date and other for time
  • User can select time independently
  • The date should be sent back to the backend in UTC format as well

I'm not very experienced with time zones and I'm getting beaten by trying to allow the user to set up only the time (or date) in a datetime field.

My pseudo code is the following:

  • When receiving the from backend simply convert the date to show it to the user, making the orignal date stay in UTC
  • When the user picks the hour and minute (all in one action) use setHours and setMinutes from date-fns library
  • Use native toISOString() to set in models

Code so far [playground]:

import { utcToZonedTime, format } from "date-fns-tz";

import { setHours, setMinutes } from "date-fns";

const UTCStringdate = "2022-04-06T10:00:00.000000Z";
const userTimezone = "Asia/Bangkok";
const localizedTime = utcToZonedTime(UTCStringdate, userTimezone);

// Prints the correct information
// 10:00 in UTC is equal to 17:00 in Bangkok
console.log(format(localizedTime, "HH:mm"));

// Now I expext to set only the `minutes` and `hours`
// to this `localizedTime` so that I can update the backend
const [hours, minutes] = "10:30".split(":");

// Somewhere over here the `setHours` and `setMinutes`
// is turning the Date object into my own timezone
// and not using `Asia/Bangkok` timezone anymore
let newTime = setHours(localizedTime, hours);
newTime = setMinutes(newTime, minutes);

// Now I expect to print 17:30 since we only
// set up 30 minutes forward than the original one
// but it ends up printing 10:30
console.log(format(newTime, 'HH:mm'));

I understand that somewhere along the way (most likely in setHours and setMinutes) the date-fns library turns back the localizedTime back into my own timezone, completely ruining the idea of turning the Asia/Bangkok time into UTC.

Questions

First, is this the best approach to manipulate only the time part of a date when considering timezones? If not, anyone can point me to articles? I wasn't able to find anything on the topic

Second, how can I use setHours and setMinutes and still maintain the timezone?



Solution 1:[1]

There are no multiple time zones in JavaScript. There is UTC and there is your local one. What date-fns-tz does, is adjusting the time to the chosen user time zone. In your example, you can see this when printing both the original and the localized time

const utcTime = new Date(UTCStringdate);
console.log(utcTime.toISOString()); // -> 2022-04-06T10:00:00.000Z
console.log(localizedTime.toISOString()); // -> 2022-04-06T14:00:00.000Z 

To solve your issue, convert UTC time to users time and let the user to adjust hours and minutes in his local time zone. After that, convert the time object back to UTC using zonedTimeToUtc

newTime = zonedTimeToUtc(newTime, userTimezone);

and then use newTime.toISOString() to send it back to the server.

console.log(newTime.toISOString());

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ochakov