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