Alarm Notification Timezones

In our system, we will be keeping track of each user’s preferred timezone in the user DB, and would like to send them alarm messages with the event times formatted to that timezone. (Our gateway timezone is UTC.) Unfortunately, there is a chance that there could be multiple recipients for a single alarm with differing timezone preferences. To add complexity to this, we’re using calculated rosters and don’t have the list of recipients until we run the script in the notification block of the alarm pipeline.

I’m looking for the best way to adjust the eventTime as displayed in each user’s notification.

I haven’t seen any hint of how to change the timezone used by the “{eventTime}” expression, but even if I could force that it probably wouldn’t work for me since I need to support multiple timezones in one notification pass.

I dug around the forum a bit, and the closest match I found was this which seems to resolve it’s issue with Java scripting in a Vision module, and won’t work for me.

Is there any sane way to achieve this?

I have this suspicion that I might need to generate the roster in a separate scripting block of the pipeline, then switch between multiple output paths based on timezone and format the message in each one separately. I’m very comfortable with Ignition scripting and expressions, but haven’t played with alarm pipelines very much, so I’m not sure if I’m thinking in the right direction.

In case anyone ever comes upon this and wonders, here is the (slightly scary) solution I am using:

The bulk of the work is done in the left-most script block. It builds the list of all recipients, figures out how many different versions of the alarm message need to be generated, and stores all of this information as event properties. As it’s final step, it picks one version of the alarm message, stores an indicator of that format in event[‘next_format’], and stores indicators for all the other formats that will be needed as a list in event[‘remaining_formats’]. The remaining_formats property may be an empty list if only one format is needed.

The right-most script block updates the custom alarm message properties based on the event[‘next_format’] it receives, and the notification block ensures that version of the message is sent to the proper people.

The middle script block simply removes the next entry out of event[‘remaining_formats’] and puts it into event[‘next_format’], or cancels the alarm event if event[‘remaining_formats’] is empty.

I describe this as “scary” since every time an alarm event passes through the splitter it spawns an additional alarm event which runs in a multi-threaded sort of manner, and they will all be sharing data via any alarm event properties that are pass-by-reference. My initial prototype modified dictionaries on the alarm event in the middle and right script blocks, and the behavior was definitely random depending on which of the multiple alarm events got to run first. The solution was to minimize the alarm event properties that are modified in the middle or right blocks, and ensure that none of the ones it did modify were pass-by-reference.

Oh, the timezone formatting is done in scripting as well:

def date_format_with_timezone(datetime, timeformat, tzstring):
	# tzstring should preferably be a canonical TZ database name from
	# tzstring should preferably be a canonical TZ database name from
	# e.g. 'America/New_York' or 'America/Denver'
	# The full supported list will vary based on the Java version and libraries on your host.
	from java.util import TimeZone
	from java.text import SimpleDateFormat
	target_tz = TimeZone.getTimeZone(tzstring)
	date_formatter = SimpleDateFormat(timeformat)
	return date_formatter.format(datetime)

(EDIT: 2020-10-23 - Safer and simpler implementation that doesn’t use TimeZone.setDefault())