0

Locally, everything is accurate by the minute. Once deployed to Heroku, the difference between the times are off by about 6 hours. I am not looking to convert Heroku time zone (would like it to remain UTC). I've tried everything from getTimezoneOffset() conversions to different date formats and I still end up with the same result. How can I have these 2 date times match each other and not be offset by hours when deployed? Why are they different, when formatted the exact same way?

// Used to calculate current date time

const currentDate = new Date();
// ^ Production - (2021-10-12T19:12:41.081Z)
const time = `${currentDate.getHours()}:${currentDate.getMinutes()}`;
const fullDate = `${currentDate.getMonth()}/${currentDate.getDate()}/${currentDate.getFullYear()}`;
const currentDateFormatted = new Date(`${fullDate} ${time}`);
// ^ Production - (2021-10-12T19:12:00.000Z)

const currentParsedDateToUTC = Date.parse(currentDateFormatted.toUTCString());

// Used to calculate an event date time

const eventDate = new Date(`${event.date} ${event.endTime}`); // same exact format as above
// ^ Production - (2021-10-12T13:12:00.000Z)
const eventParsedDateToUTC = Date.parse(eventDate.toUTCString());

const isExpired = (currentParsedDateToUTC > eventParsedDateToUTC); // works locally, but not in production

In this example, the event date and start time is identical to the current date time. How can I prevent them from being vastly different?

Jayg713
  • 327
  • 3
  • 14
  • Is this code running client-side (In the browser)? Or server-side (in the node.js process)? – Declan McKelvey-Hembree Oct 12 '21 at 19:19
  • All running server side, Node.js @DeclanMcKelvey-Hembree – Jayg713 Oct 12 '21 at 19:20
  • What's the source of event.date and event.endTime? where's the event object coming from? – Declan McKelvey-Hembree Oct 12 '21 at 19:22
  • It's coming straight from the database. event.date = 10/12/2021 and event.endTime = 13:12 . It's like it's converting the current new date object to UTC, but not the date object created with the event. @DeclanMcKelvey-Hembree – Jayg713 Oct 12 '21 at 19:25
  • I've updated my post to show what the initial new Date looks like in production. Could it have something to do with the end time zone? (081Z) @DeclanMcKelvey-Hembree – Jayg713 Oct 12 '21 at 19:33
  • `currentParsedDateToUTC = Date.parse(currentDateFormatted.toUTCString());` is awful. Why create a date to create a string to then parse it ([unreliably](https://stackoverflow.com/questions/2587345/why-does-date-parse-give-incorrect-results)) to another date? Consider `let currentParsedDateToUTC = new Date().setSeconds(0,0)`, noting that *currentParsedDateToUTC* will be a time value, not a Date object. Also, in *fullDate*, the month is wrong and *currentDateFormatted* is not a formatted date, it's a Date object. – RobG Oct 13 '21 at 00:47

3 Answers3

1

That is because the Heroku server is in a different timezone than yours, you can handle it by converting the time format from your frontend, I recommend you use moment.js for example in your frontend you can convert like this:

npm install moment --save

And then you can create a function just to change the format to display:

const formatDatetime = (
  datetime = "N/A",
  format = 'LLL' // here is your format
) => {
  return moment(datetime).isValid()
    ? moment(datetime).format(format)
    : datetime;
};
Jose Lora
  • 1,392
  • 4
  • 12
  • 18
  • `datetime = "N/A"` makes no sense. If no value is passed, it will be undefined and `moment(datetime).isValid()` will fail. Returning an invalid *datetime* value also doesn't make sense, how does the caller detect an error? Parsing strings without supplying the format should be strongly discouraged. Parsing the same string twice is also inefficient, consider `let d = moment(datetime, 'M/D/Y H:m'); return d.isValid()? d.format(...) : undefined`. – RobG Oct 13 '21 at 02:03
  • The "N/A" is just a label, if the value is null the function will return "N/A", is your decision if you want to remove or add any other default value. – Jose Lora Oct 13 '21 at 12:41
0

So -- Heroku is returning the correct UTC local time, as of this writing it is 2021-10-12T19:36:00.000Z.

You're asking Heroku to interpret 2012-10-12 13:12 as a date, but you're not specifying what timezone it should use, so it defaults to its own local time of UTC.

Everything here is working as expected.

What you I think are implicitly asking is that you want it to interpret 13:12 as being in your local time. However, Heroku has no way of knowing what your local time is, so you'll need to track the timezone of events in your database.

The only reason this is working locally is because your local server happens to be in the same timezone as you -- if I were to connect to your local server from my timezone, I'd experience the same problem.

0

The first four lines of code seem to be an attempt to create a Date and set the seconds and milliseconds to zero. That can be done as:

let d = new Date();
d.setSeconds(0,0);

which will set the seconds and milliseconds to zero.

I don't know what you think the following does:

const currentParsedDateToUTC = Date.parse(currentDateFormatted.toUTCString());

but an identical result is given by:

d.getTime();

which is actually the value returned in the previous call to setSeconds. So the first 5 lines of code reduce to:

let currentParsedDateToUTC = new Date().setSeconds(0,0);

Then in:

const eventDate = new Date(`${event.date} ${event.endTime}`);

A timestamp in the format d/m/y H:m is parsed using the built–in parser, which is a bad idea, see Why does Date.parse give incorrect results?. You can use a library instead or just write a 2 line function to do the job.

Then again there is:

const eventParsedDateToUTC = Date.parse(eventDate.toUTCString());

which is simpley:

const eventParsedDateToUTC = eventDate.getTime();

Finally there is:

const isExpired = (currentParsedDateToUTC > eventParsedDateToUTC);

comparison operators will coerce Dates to number for you, so you can leave the values as Dates.

A function to do the job is:

// eventDate is UTC timestamp in m/d/y H:m format
function isExpired(eventDate) {
  // Parse eventDate as UTC
  let [M,D,Y,H,m] = eventDate.split(/\W/);
  let eventD = new Date(Date.UTC(Y, M-1, D, H, m));
  // return true if has passed (minute precision)
  return eventD < new Date().setSeconds(0,0);
}

// Event dates (UTC)
['10/12/2021 12:00', // 12 Oct 2021 12:00
 '10/13/2021 12:00', // 13 Oct 2021 12:00
 '10/13/2022 12:00', // 13 Oct 2022 12:00
 ].forEach(d => 
   console.log(d + ' has' + (isExpired(d)? '':' not') + ' Expired')   
);

Where you could use the value returned by Date.UTC(Y, M-1, D, H, m) without conversion to Date so the last two lines could be:

  return Date.UTC(Y, M-1, D, H, m) < new Date().setSeconds(0,0);

but it's a bit more semantic (if unnecessary) to use a Date. :-)

RobG
  • 142,382
  • 31
  • 172
  • 209