TimeZone Conversion

timetimezone

We have a simple intake form with the following attributes. So you can think of the object records would be as follows:

ID Military Time Time Zone Add Days (from today's date)
1 0700 America/Chicago 3
2 0600 America/Chicago 4
3 0600 America/New York 1

As you probably already know, a lot of the Date and DateTime methods in salesforce work's off of the User Context's Locale Time Zone or GMT.

Now we have user's around the country with different locale settings.

For example, when a user who's locale is in (GMT-04:00) Eastern Daylight Time (America/New_York) is executing the method, we would want the return to be specific to the record's time zone.

User's Local – (GMT-04:00) Eastern Daylight Time (America/New_York)

Today's Date is 3/24/2022

ID Display Output
1 3/27/2022 7:00 AM CDT
2 3/28/2022 6:00 AM CDT
3 3/25/2022 6:00 AM EDT

I tried different approaches like for example the Record ID 1

String militaryTime= '0700';
Time timeValue= Time.newInstance(Integer.valueOf(militaryTime.substring(0, 2)), Integer.valueOf(militaryTime.substring(2, 4)), 0, 0);


DateTime dateTimeVal = DateTime.newInstance(Date.Today().addDays(3), timeValue);


System.Debug(dateTimeVal); //Returns 2022-03-27 11:00:00
System.Debug(dateTimeVal.format('MM/dd/yyyy HH:ss a z')); //Returns 03/27/2022 07:00 AM EDT
System.Debug(dateTimeVal.format('MM/dd/yyyy HH:ss a z','America/Chicago')); //Returns 03/27/2022 06:00 AM CDT

Somehow it is not returning 3/27/2022 7:00 AM CDT, is there anything I am doing wrong here?

Best Answer

You can handle creating a Datetime in a target Time Zone from "local date and time" details using the following code. Here I have explicitly selected a problematic date/time in the target Time Zone to show why we need to adjust when traversing a DST transition between UTC and the target Time Zone:

// Define the local date and local time
Date d = Date.newInstance(2022, 03, 13);
Time t = Time.newInstance(6, 0, 0, 0);

// Note this renders in the user's locale format (I'm in the UK, dd/MM/yyyy)
System.debug('Input local: ' + d.format().left(10) + ' ' +
    ('0' + t.hour().format()).left(2) + ':' +
    ('0' + t.minute().format()).left(2));

// Convert this into a UTC date/time value
Datetime dt = Datetime.newInstanceGmt(d, t);

// Identify the target time zone
TimeZone target = TimeZone.getTimeZone('America/Chicago');

System.debug('In Time Zone ' + target.getID());

// Figure out the offset at this UTC "moment"
Integer offsetAtUTC = target.getOffset(dt);

// Adjust the date/time value to be in the
// target time zone
Datetime targetDatetime = dt.addSeconds(-offsetAtUTC / 1000);

// Now it is in the target time zone, we have
// a new "moment". If the UTC "moment" was
// before a DST transition but the "moment" in
// the target time zone is after that
// transition the calculation will be adrift
Integer offsetAtLocal = target.getOffset(targetDatetime);

System.debug('UTC: ' + offsetAtUTC);
System.debug('Local: ' + offsetAtLocal);

if (offsetAtLocal != offsetAtUTC) {
    // There's a drift because of a DST
    // transition. Correct it
    System.debug('Adjusting for DST transition');
    targetDatetime = targetDatetime.addSeconds((offsetAtUTC - offsetAtLocal) / 1000);
}

System.debug(targetDatetime);
System.debug(targetDatetime.format('MM/dd/yyyy HH:ss a z', target.getID()));

This outputs:

08:40:54.20 (23543330)|USER_DEBUG|[5]|DEBUG|Input local: 13/03/2022 06:00
08:40:54.20 (24306962)|USER_DEBUG|[13]|DEBUG|In Time Zone America/Chicago
08:40:54.20 (24765664)|USER_DEBUG|[29]|DEBUG|UTC: -21600000
08:40:54.20 (24843783)|USER_DEBUG|[30]|DEBUG|Local: -18000000
08:40:54.20 (24905115)|USER_DEBUG|[35]|DEBUG|Adjusting for DST transition
08:40:54.20 (25019450)|USER_DEBUG|[39]|DEBUG|2022-03-13 11:00:00
08:40:54.20 (25184662)|USER_DEBUG|[40]|DEBUG|03/13/2022 06:00 AM CDT