Unapedra
Unapedra

Reputation: 2383

Javascript - Storing Date as another timezone and parsing it back

The context

I've got an application that lets an admin configure a timezone for their company. Other users, then, can modify the schedule of the company, but it should be always saved with the company's timezone.

This data needs to be stored in UTC in the server, and whenever someone gets it back it needs to be displayed with the company's timezone time.

For example:

  1. Admin sets timezone to "Europe/Madrid" (GMT +01:00).
  2. User A, in timezone "America/Sao Paulo" (GMT -03:00), sets the Schedule to 09:00 - 15:00 (two dates).
  3. The schedule needs to be sent to service in UTC as if the user was in 'Europe/Madrid'.
  4. Service stores the date in UTC

After that, someone in timezone "Europe/Athens" (GMT +02:00) visits the page and they have to see the time as if they were in "Europe/Madrid". So, in this case, 09:00 selected in "America/Sao Paulo" should be stored as "08:00 UTC" (Madrid timezone).

What I need

Basically, I need the time, no matter what is the timezone, to be displayed as if the user was always in the company's timezone, and whenever someone saves the schedule time, I need it to be saved as if they also were in "Europe/Madrid".

Also, the displayed time shouldn't change in DST, as the schedule will be always the same for all the year. So, the user should see all year the "09:00 - 15:00" schedule.

Keep in mind that the server uses SSR, and this means that in the first render we do not know the local timezone of the user who's going to see the data.

What I've tried

I've tried libraries such as "date-fns-tz" and "moment-tz" to convert the dates, but they do not work for the intended, as whenever I parse it to UTC they do it ok but when I try to parse it again to the desired timezone they do not work as expected.

I've tried "countries-and-timezones" library to get the timezone offset and try to manually adjust the dates, but I keep getting messed up by the server's local timezone (which I suspect is the reason why the other libraries didn't work as expected either).

Is there some way I can achieve that?

Thank you.

Upvotes: 0

Views: 75

Answers (1)

deceze
deceze

Reputation: 522081

Basically you'll want to forget about the user's local timezone. America/Sao Paulo should play absolutely zero role in any of this. When the user enters "9:00" in the browser, you just want that to be "9:00" and nothing else. Since the company's timezone is Madrid, you want the input "9:00" to be interpreted as "9:00 Madrid". You can do that either client-side or server-side. But at no point do you want any conversion going on from Sao Paulo time to Madrid time.

Secondarily, you'll want to be very clear what you're storing in the database and what you'll do with this information later. To be clear, what does not work and what you need to avoid is this:

  • user enters "9:00"
  • you store 2022-12-01T08:00:00Z in the database
  • you automatically generate future dates like 2023-05-01T08:00:00Z from it

That generated date in May will be 10:00 Madrid, not 9:00.

You pretty much have two choices:

  • store just "9:00 Madrid" in the database, i.e. a description of a rule that you can later use to generate times
  • generate concrete timestamps in the Madrid timezone, convert them to UTC, then store them

Which one is more appropriate depends on how you intent to use them. If you want concrete timestamps in your database to query by, then you need to follow these steps:

  • get input "9:00 Madrid"
  • create a concrete date 2022-12-01T09:00:00+01:00
    • convert it to UTC 2022-12-01T08:00:00Z to store it
  • generate a future date by adding days/weeks/months to it, keeping the time value the same, e.g. 2023-05-01T09:00:00+02:00
    • convert it to UTC 2023-05-01T07:00:00Z to store it
  • for display, convert the UTC date to the company's timezone, and display it in that timezone; i.e. the user's local timezone plays absolutely no role (right?)

To do any time arithmetic, you basically want to construct new dates by combining parts of the existing date, in order to avoid changes to the time-of-day. The details will depend on the exact library being used, but in pseudocode it's something like:

var a = new Date(2022, 12, 1, 9, 0, 0, 'Europe/Madrid')
var b = Date.combine(a.date.add(6, 'months'), a.time, a.timezone)
// 2023-05-01 9:00:00 Europe/Madrid

Hope this helps to clarify things a bit.

Upvotes: 2

Related Questions