normalizeDateFromController function

DateTime? normalizeDateFromController(
  1. DateTime? date,
  2. DateTime? controllerTime,
  3. DateTime currentTime
)

Normalizes the given date from the controller into the user's local time, based on the difference between the controllerTime and the currentTime.

The resulting date will be based on the user's timezone, instead of the controller's timezone. This must only be used for dates that are generated by the controller (and not by the server, for example). Normally this means dates coming from parameters such as the last ran date.

Implementation

DateTime? normalizeDateFromController(
  DateTime? date,
  DateTime? controllerTime,
  DateTime currentTime,
) {
  if (date == null || controllerTime == null) {
    return date;
  }

  // Backend currently stores dates based on the controller's time, but then
  // sends back the dates using the server's timezone.
  //
  // For example, an event that happens 5PM ET from the controller is stored
  // in the backend as 5PM (with no timezone value). Then, the backend service
  // sends the time as 5PM CT. See below table.
  //
  //     5PM EST               5PM         5PM CST / 6PM EST      3 PM PST
  //   Event time        Time stored in      Time sent by      Time displayed
  // from controller        database           backend            to user
  //
  // The frontend can correctly convert the time to the user's local
  // timezone, but we still need to correct for the difference between the
  // controller's time and the server's time.

  // First, get the current server time. Per Ashok, backend servers use
  // US / Central Time.
  final serverTzOffset = Duration(
    milliseconds: tz
        .getLocation('US/Central')
        .timeZone(currentTime.millisecondsSinceEpoch)
        .offset,
  );
  final tzDifference = serverTzOffset - currentTime.timeZoneOffset;
  final serverTime = currentTime.add(tzDifference);

  // Then get the difference between the controller time and the server.
  var difference = serverTime.difference(controllerTime);

  final inMinutes = difference.inMinutes;
  final inHours = (inMinutes / 60.0).round();

  final minutesRemainder = (inMinutes - (inHours * 60)).abs();
  if (minutesRemainder <= 5) {
    // If the difference is close to a multiple of an hour, round to the
    // nearest hour. This means the controller time is up-to-date, but
    // potentially in a different timezone.
    difference = Duration(hours: inHours);
  }

  // Add the difference to the event date
  return date.add(difference);
}