DateTime values in Architect flows and Daylight Savings Time (DST) calculations


Architect deals natively with Coordinated Universal Time (UTC) for DateTime values. With regards to converting a UTC DateTime to a local DateTime for a specific timezone, flow authors must build logic inside the flow to perform this conversion as Architect does not currently include a timezone data type that could be used to supply an offset and Daylight Savings Time (DST) bias.

For example, notice that the Flow.StartDateTimeUtc DateTime variable and the GetCurrentDateTimeUtc() function return DateTime values in UTC. Architect offers various expression functions and operators to help a flow author manipulate DateTime values to suit a flow author’s needs. A common question from flow authors is “How can I convert a DateTime value so that it reflects the UTC DateTime in a specific timezone?” because they want to change flow execution based on that local time. This question may come up when, in a call flow, you only want to route a call to a queue between the hours of 9:00 a.m. and 5:00 p.m. local time.

Functions commonly used to apply an offset to a DateTime and return a resulting DateTime value include:

  • AddHours
  • AddMinutes

Some operators can also be used to change a DateTime value. For example, use the + to add a Duration to a DateTime and get a resulting DateTime as well.

The following examples show ways to calculate a local DateTime value from a UTC DateTime and can be modified according to the best fit for your organization.


In this basic approach, a flow author can store an offset in a flow level variable and apply that to UTC DateTime values used in flow calculations. In the following example, the offset is stored in Flow.HourOffset:

Flow.LocalDateTime = AddHours(GetCurrentDateTimeUtc(), Flow.HourOffset)

When a time zone springs forward or falls back, change the value in Flow.HourOffset and republish the flow. This simple logic may be sufficient for your organization. Some timezones; for example, Arizona and Hawaii, do not spring forward or fall back, so a hard-coded offset is sufficient.

Additionally, some time zones spring forward or fall back by 30 minutes, rather than one hour. In those instances, use AddMinutes instead of AddHours to apply the appropriate offset. If you have a local time that deals with DST, change the value stored in the variable used for the offset as appropriate when the time springs forward or falls back. In the example above, that means changing the value in Flow.HourOffset and republishing the flow when the time springs forward or falls back.


To avoid the task of republishing the flow whenever the time zone springs forward or falls back, as in the previous example, you can use collection variables to consider offsets for a time zone. While this option requires more work initially, it eliminates the need for managing offsets stored in flow-level variables and offers more simplicity than using expressions.

This method uses a parallel array approach and saves the desired offsets that consider the bias offset for a timezone given a UTC DateTime value. This example is a setup of integer collection variables for Eastern timezone DateTime value calculations. 

Note: The offset is -5 hours. Manually apply the daylight savings time bias to entries in the Flow.LocalTimeZoneMinuteOffsetCollection items as appropriate.
Collection Item Flow.UtcDateTimeCollection Flow.LocalTimezoneMinuteOffsetCollection
Item 0 Sunday, March 13, 2016 at 7:00 AM

-300 

Note: -300 represents -300 minutes or -5 hours.

Item 1 Sunday, November 6, 2016 at 8:00 AM

-240

Note: -240 represents -240 minutes or -4 hours. In this case, after Sunday, March 13, 2016 at 7:00 a.m. UTC (the first item in the collection) until Sunday, November 6, 2016 at 8:00 a.m. UTC, the system should offset a UTC DateTime value by -240 minutes, which ensures it is in the correct local time.

Item 2 Sunday, March 12, 2017 at 7:00 AM -300
Item 3 Sunday, November 5, 2017 at 8:00 AM -240
Item 4 Sunday, March 11, 2018 at 7:00 AM -300
Item 5 Sunday, November 4, 2018 at 8:00 AM -240
Item 6 Sunday, March 10, 2019 at 7:00 AM -300
Item 7 Sunday, November 3, 2019 at 8:00 AM -240
Item 8 Sunday, March 8, 2020 at 7:00 AM -300
Item 9* Sunday, November 1, 2020 at 8:00 AM -240

* Add more entries for call flows that run past the year 2020.

This collection is a set of UTC DateTime values for when the Eastern time zone springs forward or falls back. Now, build a task that, when a flow initiates, looks for a local time based on DST and time zone considerations:

  1. Determine the UTC DateTime value for which the flow author wishes to get a local DateTime. For example, use an Update Data action or a Flow.StartDateTimeUtc to save off GetCurrentDateTimeUtc() to Flow.UtcDateTimeToCheck, or in this case, use Flow.StartDateTimeUtc.
  2. Add and configure a Loop action:
    1. Starting at Index 0, loop through the Flow.UTCDateTimeCollection.
    2. For the first value found in Flow.UTCDateTimeCollection greater than or equal to Flow.UtcDateTimeToCheck, save the corresponding minute offset from the same item index in Flow.LocalTimezoneMinuteOffsetCollection to Flow.MinuteOffsetToApply.
  3. After the loop, if no value is assigned to Flow.MinuteOffsetToApply, assign a default minute offset; for example, 300. In this situation, the UTC DateTime value for checking time does not have an entry greater than or equal to it in the Flow.UtcDateTimeCollection variable.
  4. Next, use the following expression to obtain the local time from the algorithm above:
    Flow.MyLocalDateTimeToUse = AddMinutes(Flow.UtcDateTimeToCheck, Flow.MinuteOffsetToApply)

You now have a local time with the appropriate bias applied to it that you can use for calculations. The applied bias is based on the lookup value in the Flow.LocalTimezoneMinuteOffset collection.


Although the expression approach is more complex, this method does not require hard-coded DateTime values in to a collection along with a corresponding offset integer collection. However, expressions can prove more complicated when adapting a particular time zone, especially if springing forward or falling back causes a day change in the UTC DateTime calculations.

In this example, the time zone springs forward on the second Sunday in March at 7:00 a.m. UTC, and falls back on the first Sunday in November at 8:00 a.m., regardless of the year. Using the Eastern time zone, this means that during DST, the UTC offset is -4 hours (or -240 minutes) and when not in DST the UTC offset is -5 hours (or -300 minutes). The expression result is a “local” DateTime with the appropriate offset added to the UTC DateTime of either -4 or -5 hours. To calculate the DateTime value:

  1. Add an Update Data action.
  2. In the Update Data action, add a DateTime update statement. 
  3. Under Variable Name, add Flow.LocalDateTime.
  4. Under Value To Assign, switch to the large expression editor and add the expression:
AddMinutes(Flow.StartDateTimeUtc,
      (If(Flow.StartDateTimeUtc >= GetDayOfWeekOccurrence(1,2,Year(Flow.StartDateTimeUtc),3,7,0,0)
               and Flow.StartDateTimeUtc <= GetDayOfWeekOccurrence(1,1,Year(Flow.StartDateTimeUtc),11,8,0,0),
               -240,
               -300)
   )
)

The logic in this expression determines whether you are currently in DST, according to the second Sunday in March at 7:00 a.m. when you spring forward, and on the first Sunday of November at 8:00 a.m. when you fall back.

The first parameter submitted to the logical If in the expression above provides the functionality to determine if a given UTC DateTime falls in the DST window for the Eastern time zone. Use this to determine if the offset to apply to the DateTime should be either 240 minutes (-4 hours) or -300 minutes (-5 hours). The remainder of the expression supplies the values to apply. If you need to have a Boolean expression that evaluates to true when DST is on, or false when it is not, extract this logic out to a separate expression since it returns a Boolean:

(
      (Month(Flow.UtcDateTime) > 3)
        or
      (Month(Flow.UtcDateTime) == 3 and Month(AddDays(Flow.UtcDateTime, -7)) == 3 and Month(AddDays(Flow.UtcDateTime, -14)) == 3)
        or
      (
        (Month(Flow.UtcDateTime) == 3 and Month(AddDays(Flow.UtcDateTime, -7)) == 3 and Month(AddDays(Flow.UtcDateTime, -14)) == 2)
          and
        (DayOfWeek(Flow.UtcDateTime) > 1 or (Hour(Flow.UtcDateTime) >= 7))
      )
    )
    and
    (
      (Month(Flow.UtcDateTime) < 11)
        or
      (
        (Month(Flow.UtcDateTime) == 11 and Month(AddDays(Flow.UtcDateTime, -7)) == 10)
          and
        (
          (DayOfWeek(Flow.UtcDateTime) == 1 and Hour(Flow.UtcDateTime) < 8)
            or
          DayOfWeek(Flow.UtcDateTime)!=1
        )
      )
    )