1

I have a scheduling application, with a calendar that I build from scratch. As a lawyer, you should be able to configure your available times for booking, like below:

A lawyer's availabilities in Australia :

1- 10/01/2020, from 07:00am to 08:am ...

Here's what I do :

1- Get the epoch number of the entered date in Javascript :

  const dateFrom = new Date(firstOfJanSevenAm).getTime() // 1578600000000
// Fri Jan 10 2020 07:00:00 GMT+1100 (Australian Eastern Daylight Time)

  const dateTo = new Date(firstOfJanEightAm).getTime() // 1578603600000 
// Fri Jan 10 2020 08:00:00 GMT+1100 (Australian Eastern Daylight Time)

2- Send this to NodeJS server and save it MongoDB

     Mongoose.save({
      from:dateFrom, //1578600000000
      from:dateTo    //1578603600000
     })

3- Represent it inside the Calendar :

    <div>{format(from, 'HH:mm')}</div>

Everything is working as expected.

Now, this lawyer is traveling to the US and he's in a coffee shop using the US local time ( any city), he opens the Calendar, he wants to add some availability, but in Sydney time. I need to provide him with a timezone dropdown so he can tell me that he wants the new date to be based on his home, Syndey.

Question :

1- Do I save the date as I'm doing ( as a number ), and save the timeZone separately next to it, and when representing it, just apply the timeZone?

     Mongoose.save({
      from:dateFrom, //1578600000000
      from:dateFrom //1578603600000
      currentTimeZone : 'America/Costa_Rica',
      desiredTimeZone: 'Australia/Sydney'
     })

 <div>{formatWithTimeZone(from, 'HH:mm',desiredTimeZone)}</div>

Is this all I have to do? Or am I naively missing something that is going to trip me down the road?

And back to my original questions, where do I do the whole "always store time as UTC" thing?

All I've realized is, when I use the library that I'm using date-fns-tz and try to convert the user entered date to UTC, I get exactly the same output :

      const dateFrom = new Date(firstOfJanSevenAm).getTime() // 1578600000000
      const dateFromUTC = zonedTimeToUtc(dateFrom,currentTimeZone) // 1578600000000
      // currentTimeZone is America/Costa_Rica, when he is in Costa Rica's caffee shop.
    enter code here

      1578600000000 === 1578600000000 = true 

So why do I get the same output when converting the entered date, to it's UTC date?

Milad
  • 27,506
  • 11
  • 76
  • 85
  • I've closed this as duplicate of many answers that describe this problem and its solution. Though not all in JavaScript, the advice and approach are consistent: **UTC is not appropriate for future-time scheduling**. The "Always UTC" advice is shortsighted, and has been refuted many times. [Here is a good blog post on the subject](http://www.creativedeletion.com/2015/03/19/persisting_future_datetimes.html). – Matt Johnson-Pint Jan 07 '20 at 19:44
  • @MattJohnson-Pint My question is how to store a javascript date as UTC, how does it actually look like ? Is it a number or is a string? Where exactly did you find that a duplicate? My other question was why I get the same output for both UTC and none-UTC. – Milad Jan 08 '20 at 04:21
  • UTC is simply the timekeeping standard where we choose to synchronize our clocks. It's point of reference is the prime meridian, so it is similar to GMT though semantically UTC applies everywhere, and GMT only applies there. There is more good information [here](https://www.timeanddate.com/time/aboututc.html), [here](https://www.timeanddate.com/time/gmt-utc-time.html) and [here](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). – Matt Johnson-Pint Jan 08 '20 at 16:43
  • However, UTC itself does not have a particular format - that is a separate concept. [Unix time](https://en.wikipedia.org/wiki/Unix_time) is one representation. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) and [RFC 3339](https://tools.ietf.org/html/rfc3339) offer another. There are others, and the choice is yours. In JS, you could call `.getTime()` to get the Unix timestamp in milliseconds (divide by 1000 to get seconds), or you could call `.toISOString()` to get an ISO8601 string representation. – Matt Johnson-Pint Jan 08 '20 at 16:43

2 Answers2

1

I am going to give you an answer decoupled from the technical implementation. Let's think by contradiction, you have a lawyer living in Australia and another living in Switzerland, what happens if you decide to store time in their preferred location?

You then need to save two information: the time of course (11 am) but it's relative so you also need to store the timezone (11 am in Australia) or (1 pm in Switzerland)

Now what happens if your lawyer travels to France? Do you want to update all his calendar information? 11 am is not 11 am anymore.

UTC solves this problem, you just need to store 11 am or 1 pm. UTC is arbitrary absolute, universal by convention.

So your backend/database should store this kind of information in UTC, always. And you need a way to know your user's timezone, maybe it's possible to update it using a web interface and it could be stored in a user database or just cookies. Maybe you want to derive it yourself by using your user's location, IP, browser's language, whatever.

Now that you know his timezone, your server can send UTC (absolute) time to the client and the client can seamlessly display the correct (relative) time.

The client deals with relative time, the server always makes it absolute.

Swann
  • 2,413
  • 2
  • 20
  • 28
  • 1
    Hi. UTC is not appropriate for future time scheduling. The correct approach is what you describe in your second paragraph - store both the local time and the time zone identifier. Please read further in the dup posts linked at the top. Thanks. – Matt Johnson-Pint Jan 07 '20 at 19:47
  • I admit that I understand why UTC is interesting to avoid unnecessary conversions but I haven't had to handle complex cases such as repeating dates and future time. I guess I did a good job at explaining why UTC might be useful when appropriate but my answer was indeed short-sighted. I will have a look at the blog post, it got me curious, thanks! – Swann Jan 07 '20 at 20:12
  • No problem. It's a common misconception. The case illustrated in the post is that the relationship between UTC and the event-local time zone (as determined by the local government) may actually change between the time the event is originally scheduled and the actual time of the event. Thus, it's important to capture the user's intended event-local time - not the UTC equivalent you determined when they told you. (There are other scenarios as well.) – Matt Johnson-Pint Jan 07 '20 at 21:06
1

So why do I get the same output when converting the entered date, to it's utc date?

The value returned by getTime is the number of milliseconds since 1 January 1970 00:00:00 according to universal time.

Therefore, UTC and getTime both represent a moment in time using the same universal timezone.

Do I save the date as I'm doing, and save the timeZone separately next to it, and when representing it, just apply the timeZone?

Don't save the timezone with the date. Just save the date in universal time. If you need to save timezone. That should go in user settings. However, you can guess the timezone; therefore, you don't need to save this information.

Saving the date

When you do save the date to the database, it should be represented by a UTC time string or by a UNIX timestamp, both options demonstrated below.

UNIX timestamp

UNIX timestamp should be in seconds. getTime returns milliseconds. You can just divide by 1000 to get the UNIX timestamp.

 const unixTimestamp = new Date('December 17, 1995 03:24:00').getTime() / 1000;

UTC Date string

 const utcDate = new Date('December 17, 1995 03:24:00').toUTCString();

Displaying the date

When you get the date from the back-end, then, converted it to the correct timezone.

const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone

const fromUnix = utcToZonedTime(1578423483, timezone)
const fromUtc = utcToZonedTime('Fri, 02 Feb 1996 03:04:05 GMT', timezone)

Closing thoughts

Dealing with timezones can be confusing. I am not to familiar with date-fns-tz. If you have the option, I would suggest migrating to Moment.js. Moment.js is the de facto standard JavaScript library these days - I highly recommend it.

Marc Barbeau
  • 826
  • 10
  • 21
  • Hi. UTC is not appropriate for future time scheduling. The correct approach is to store both the local time and the time zone identifier. Please read further in the dup posts linked at the top. Thanks. – Matt Johnson-Pint Jan 07 '20 at 19:47
  • @MattJohnson-Pint see, now I'm even more confused. When we say save as user's local or UTC or whatever else you're proposing, what does that actually look like ? Is it like save "08:00am" in my database ? or is a full date ? or is a Mongo date? or is a number ( Epoch) ? – Milad Jan 08 '20 at 04:30
  • @Marc, thank you , your answer is the closest thing to what I was actually asking, I appreciate it. – Milad Jan 08 '20 at 04:33
  • @Marc, when I store as Unix timestamps ( aka Epoch? ) , I'm not saving it as a UTC, or in another word, the offset from UTC is not deducted from the original time, whereas when I use `toUTCString`, it actually shows the time at UTC, which is 11h behind Sydney. So these two must be very different, aren't day? Also, why do I need to divide it by 1000? if I save everything as milliseconds, do I still have to do this conversion? – Milad Jan 08 '20 at 04:43
  • Dividing by 1000 gives seconds. A "Unix timestamp" was traditionally defined in terms of seconds since the Unix epoch. Today however, computers are more precise. JavaScript (like many others) use milliseconds. Some system use microseconds or nanoseconds. Also, "epoch" is the 0 point, not the timestamp itself. [See here](https://codeofmatt.com/please-dont-call-it-epoch-time/). – Matt Johnson-Pint Jan 08 '20 at 16:46