Your time zone issue requires changes across all three layers: user time zone detection to determine the target zone, Intl.DateTimeFormat usage for client-side formatting, and Apex UTC conversion to provide zone-agnostic timestamps. Here’s the complete solution:
User Time Zone Detection:
Salesforce stores each user’s preferred time zone in User.TimeZoneSidKey. Fetch this in your Apex controller and pass it to the LWC so you can explicitly format times in the user’s configured zone rather than relying on browser detection (which may be incorrect for traveling users):
@AuraEnabled(cacheable=true)
public static EventWrapper getEvents() {
String userTimeZone = UserInfo.getTimeZone().getID();
List<Event> events = [SELECT Id, Subject, StartDateTime, EndDateTime FROM Event];
return new EventWrapper(events, userTimeZone);
}
This returns both the events and the user’s IANA time zone identifier (like ‘America/New_York’). The wrapper pattern keeps data organized and allows the LWC to know which time zone to target.
Intl.DateTimeFormat Usage:
In your LWC, receive the time zone from Apex and use it with Intl.DateTimeFormat to format times correctly. First, ensure your Apex returns ISO 8601 UTC timestamps (covered in next section). Then format client-side:
formatEventTime(isoString, timeZone) {
const date = new Date(isoString);
return new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
hour12: true,
timeZone: timeZone
}).format(date);
}
This produces ‘2:00 PM’ formatted in the user’s Salesforce time zone, not the browser’s zone. For your calendar grid positioning, work with Date objects and calculate positions based on hours-since-midnight in the target time zone:
getHourPosition(isoString, timeZone) {
const date = new Date(isoString);
const formatter = new Intl.DateTimeFormat('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: false,
timeZone: timeZone
});
const parts = formatter.formatToParts(date);
const hour = parseInt(parts.find(p => p.type === 'hour').value);
const minute = parseInt(parts.find(p => p.type === 'minute').value);
return hour + (minute / 60);
}
This returns a decimal hour value (14.5 for 2:30 PM) in the user’s time zone, which you can multiply by your pixels-per-hour constant to position events accurately in the calendar grid.
Apex UTC Conversion:
Change your Apex to return DateTime values in UTC ISO format, not formatted strings. Never format dates in Apex for display - always send raw UTC timestamps to the client:
public class EventData {
@AuraEnabled public String eventId;
@AuraEnabled public String subject;
@AuraEnabled public String startDateTime;
@AuraEnabled public String endDateTime;
public EventData(Event evt) {
this.eventId = evt.Id;
this.subject = evt.Subject;
this.startDateTime = evt.StartDateTime.formatGmt('yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'');
this.endDateTime = evt.EndDateTime.formatGmt('yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'');
}
}
The formatGmt() method converts the DateTime to UTC and formats it as ISO 8601 with the ‘Z’ suffix indicating UTC. JavaScript’s Date constructor parses this reliably across all browsers. The .SSS milliseconds ensure precision for event positioning.
For all-day events (where StartDateTime has time 00:00:00), add a flag in your wrapper:
@AuraEnabled public Boolean isAllDay;
this.isAllDay = evt.IsAllDayEvent;
In your LWC, render all-day events in a separate row above the timeline, without time-based positioning.
With these changes, your calendar will correctly display event times for all 8 time zones in your global sales team. An EST event at 2:00 PM will show as 11:00 AM for PST users, 7:00 PM for GMT users, etc. The client-side formatting respects each user’s Salesforce time zone preference, ensuring accurate scheduling across your distributed team.
This draft is based on general Salesforce knowledge. It has not been verified against your specific version and environment. Practitioners: verify the steps and share your experience below.