Local Timezone based Terminations

Say my client is in “Europe/Stockholm” timezone. Org timezone for the tenant is set to “Europe/Stockholm”, which would trigger Identity refresh at 8AM/8PM “Europe/Stockholm” timezone. End date is coming in format “2025-10-24”. Their is also a IANA timezone value for user coming in format “Asia/Kolkata” or “America/Adak” etc depending on the timezone the user is in. Need to terminate the user in “Asia/Kolkata” or “America/Adak” timezone on “2025-10-24” at 10 PM EOD. If end Date is in past, terminate immediately.
Any pointers would be highly appreciated, not sure where I am going wrong.
Methods I am trying are:
A] Setting Lifecycle state to inactive.
B] Setting nextProcessingDate to users local timezone date.

CloudLifecycleState Calculation IdentityAttribute Rule:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

// Input date from Sweden
String endDateSweden = "9000-12-30";

if (identity.getAttribute("endDate") != null) {
            endDateSweden = identity.getAttribute("endDate");
}

String hireDateStr = identity.getAttribute("startDate");
String employeeStatus = identity.getAttribute("employmentstatus");
        
// Parse the input dates
LocalDate endDate = LocalDate.parse(endDateSweden, formatter);
LocalDate hireDate = LocalDate.parse(hireDateStr, formatter);

// Sweden's timezone
ZoneId swedenZone = ZoneId.of("Europe/Stockholm");

// Convert LocalDate to ZonedDateTime in Sweden's timezone at start of the day
ZonedDateTime swedenZonedDateTime = endDate.atStartOfDay(swedenZone);

// Local timezone
ZoneId localZone = ZoneId.of(identity.getAttribute("timezone")); // Replace with desired local timezone

// Convert Sweden's ZonedDateTime to local ZonedDateTime
ZonedDateTime localZonedDateTime = swedenZonedDateTime.withZoneSameInstant(localZone);

// Set the time to 10 PM in local timezone
localZonedDateTime = localZonedDateTime.withHour(22).withMinute(0).withSecond(0).withNano(0);

DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
String isoFormattedDate = localZonedDateTime.format(isoFormatter);

// Get current date and time in local timezone
ZonedDateTime currentDateTime = ZonedDateTime.now(localZone).withMinute(0).withSecond(0).withNano(0);
LocalDate todayDate = currentDateTime.toLocalDate();

// Determine the status based on the conditions
String status = "intermediate";

if (employeeStatus.equals("Active") && (hireDate.equals(todayDate) || hireDate.isBefore(todayDate)) && (!(localZonedDateTime.withMinute(0).withSecond(0).withNano(0).equals(currentDateTime)) && (localZonedDateTime.withMinute(0).withSecond(0).withNano(0).isAfter(currentDateTime)))) {
status = "active";
} else if (employeeStatus.equals("Pre Joiner") && hireDate.isAfter(todayDate) && (!(localZonedDateTime.withMinute(0).withSecond(0).withNano(0).equals(currentDateTime)) && (localZonedDateTime.withMinute(0).withSecond(0).withNano(0).isAfter(currentDateTime)))) {
status = "preJoiner";
} else if ((localZonedDateTime.withMinute(0).withSecond(0).withNano(0).equals(currentDateTime)) || (localZonedDateTime.withMinute(0).withSecond(0).withNano(0).isBefore(currentDateTime))) {
status = "inactive";
} else {
status = "intermediate";
}

return status;

NextProcessingDate IdentityAttribute Rule

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

// Input date from Sweden
String endDateSweden = "9000-12-30";

if (identity.getAttribute("endDate") != null) {
endDateSweden = identity.getAttribute("endDate");
}

String hireDateStr = identity.getAttribute("startDate");
String employeeStatus = identity.getAttribute("employmentstatus"); // Example status, change as needed

// Parse the input dates
LocalDate endDate = LocalDate.parse(endDateSweden, formatter);
LocalDate hireDate = LocalDate.parse(hireDateStr, formatter);

// Sweden's timezone
ZoneId swedenZone = ZoneId.of("Europe/Stockholm");

// Convert LocalDate to ZonedDateTime in Sweden's timezone at start of the day
ZonedDateTime swedenZonedDateTime = endDate.atStartOfDay(swedenZone);

// Local timezone
ZoneId localZone = ZoneId.of(identity.getAttribute("timezone")); // Replace with desired local timezone

// Convert Sweden's ZonedDateTime to local ZonedDateTime
ZonedDateTime localZonedDateTime = swedenZonedDateTime.withZoneSameInstant(localZone);

// Set the time to 10 PM in local timezone
localZonedDateTime = localZonedDateTime.withHour(22).withMinute(0).withSecond(0).withNano(0);

DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
String isoFormattedDate = localZonedDateTime.format(isoFormatter);

// Get current date and time in local timezone
ZonedDateTime currentDateTime = ZonedDateTime.now(localZone).withMinute(0).withSecond(0).withNano(0);
LocalDate todayDate = currentDateTime.toLocalDate();

return isoFormattedDate;

The similar requirement was addressed using a timezone transform written by @Mccarney and it is available in Colab marketplace: Timezone Transform

Hello,

I worked on something similar using the next processing date, three points to keep in mind:

  • nextProcessingDate format is ISO8601
  • adding a delay to the end date with dateMath function in the nextProcessingDate transform (to set end date to 10PM for example)
  • if the computed nextProcessingDate is in the next 24 hours, it will not be used

Regards