Converting datetime to local time error

Enonic version: v7.10.3
OS: Linux

Hi Team,
we have a test server, single node setup, running v7.10.3. I don’t get the proper error, when I try to convert the published datetime to local time from ISO string. Please see the following image for reference from server and in browser (It works fine in local server!)
Test Server
image

Browser
image

Local Server
image

The code for the conversion is done using a npm package called moment,
image

Could you please help with this issue,
Thanks,
Arun

Please be more specific in describing what you think the problem is, as I don’t see any error messages. What exactly the outcome you are trying to achieve? If you are trying to convert an ISO string to a local date/time string client-side then it’s as simple as doing something like new Date('2012-09-13T19:12:23.826Z').toLocaleString(), no need to use moment for this.

Sorry that I couldn’t explain clearly, We are using the publish.from of the content in our page to display the published date and time,

Please find the below images,
The first datetime is the actual ISO datetime string from the server as is.
The second and third are the local time using moment and toLocaleString().

So as you see in test server, when we try to convert ISO string to local time we get the wrong time, it is missing +1.00 GMT, but works fine in the local server.
We are using moment for other features, to achieve custom display formatting of date and time etc. But the issue is not just with moment, as you see even new Date('2022-12-09T11:48:00Z').toLocaleString() in test server it is missing +1.00 hr

Screenshot 2022-12-09 at 12.48.54

Screenshot 2022-12-09 at 12.49.01

And we would like to have this done from server-side as there are many parts and contentTypes that are already using this to display the publish date and modified date.
This worked fine in XP6 and we face this issue in XP7 cloud servers.

Hi, Arun

Please ignore the fact that your current code works locally - the only reason that it appears to work, is because your personal computer happens to be running on the same timezone that you expect to see. Your code should never assume that the server is in the same timezone as the user, it needs to declare the proper timezone.

You’re already using Moment.js for dateTime conversion, which means that you’re halfway to solving this problem the proper way. Your current code:

moment(publishFrom)

I’m using this code as an example in my 4 alternative solutions below. These solutions are policies, which means that this decision is something that should be decided by the site editors, and once implemented, the code should be used across all your XP sites.

SOLUTION 1: Client-side timezone resolution

new Date("2012-09-13T19:12:23.826Z").toLocaleString();

The only way to make sure that the timestamp is in the same timezone as the user’s local device, is to deliver the timestamp as UTC and then use client-side JavaScript to convert to the user’s timezone. The user device local timezone is never shared with the server (even with location services enabled) so this logic must be on the client-side.

Typical use case: Sites that have a lot of their users on mobile agents, or where timestamps are shown in a relative format, e.g. “5 minutes ago”. Examples: Twitter, Facebook, single-page apps, mobile apps…

SOLUTION 2: Enforce GMT timezone

moment(publishFrom).utcOffset('+00:00');

Pros:

  • Just one extra function call after Moment
  • No special cases to account for, in other words: no bugs
  • Works equally well for all users all around the globe
  • Because GMT is the timezone equivalent of UTC, you can omit using Moment.js in some cases for better performance

Cons:

  • Users need to see the “GMT” together with the timestamp to understand its timezone. If you strip away the “GMT” either using format() in moment or using other types of string manipulation, users will interpret it wrong
  • No regional adaptation, so if this timestamp is shown on a site where other timestamp values in the same context are shown in a regional timezone, users will be confused

Typical use case: If your main user base is international and users are located in several timezones
Example: enonic.com

SOLUTION 3: Set a specific timezone using a dedicated timezone conversion library

const momentTimezone = require('/lib/moment-timezone-with-data.js');
momentTimezone(publishFrom).tz('Europe/Oslo');

Pros:

  • The most reliable solution given the server-side constraints
  • Easily set a timezone dynamically by passing a single string such as 'Europe/Oslo' into a single function call

Cons:

  • Need to import another library “Moment Timezone” which is big and has a significant performance cost!
  • Even with this library, there are still edge cases compared to client-side code, especially related to the hour during which DST changes
  • My code example above is using the require() statement, which assumes that the correct library file has been manually put into the /lib folder. But if you’re using webpack or similar methods to import packages, your syntax will be different from mine.
  • JavaScript objects created with Moment instead of Moment Timezone don’t have the necessary tz() function, so developers need to remain vigilant and only import and use the correct library, or else they’ll get confused why their code isn’t working.

Typical use case: Multiple international sites representing multiple timezones with users also spread across multiple timezones
Examples: bring.co.uk, bring.no, bring.fi, bring.com
Documentation: Moment Timezone | Docs

SOLUTION 4: Set a hard-coded UTC offset that matches a desired timezone, and resolve DST using an educated guess

moment(publishFrom).utcOffset(moment(publishFrom).isDST() ? "+02:00" : "+01:00");

Pros:

  • Lightweight and fast, yet accurate enough for most people not to notice any inaccuracies
  • If you need to support multiple timezones for different sites, you could write a function that resolves the timezone based on the current site (which you likely already have similar code for, if the timestamps are localized into different languages based on the site)

Cons:

  • isDST() has many edge cases, so there will be many times when DST hasn’t “switched” properly yet. Provided that the timezone hasn’t been stripped away, the time isn’t wrong - it’s just shown as e.g. GMT+1 instead of GMT+2 or vice versa.
  • If your code has to deal with an unknown amount of timezones, this hard-coding will not cover all cases, and you need to use a dedicated library instead.

Typical use case: Multiple international sites, but not so many different timezones that the hard-coding grows too large to maintain.
Examples: bring.co.uk, bring.no, bring.fi, bring.com

MY RECOMMENDATION
Solution 3 is easier and more reliable, Solution 4 has better performance.

  • If your app is already bundling Moment Timezone and isn’t struggling with performance, use that library convert from UTC to timezone using the tz() function.
  • If your app is not already bundling Moment Timezone, try Solution 4 to see if it adequately meets your needs. If Solution 4 is not enough, you still have the option of trying Solution 3.

I have tested all these solutions in Enonic Cloud using Moment.js version 2.27.0, and they all work as intended.

Please try one of these solutions, and if you’re stuck, don’t hesitate to share some of your code examples here!

Regards,
Bjørnar

4 Likes

Thank you for suggesting various potential solutions. Much appreciated.

Solution 3 with a lighter lib like Luxon was our choice too.

Thanks and regards,
Arun

1 Like