Hello, I have some questions about time functions and timezones. We are using Ignition version 8.1.37 and MES OEE Downtime 3.81.10.
We have our frontend and backend gateways located in the UK. They are serving a packaging line in US/Central, and will also serve equipment in other timezones eventually. I'm trying to understand how some of the functions work with regard to timezones and display of time data from the MES database using the MESAnalysisController component.
Here is a screenshot of a perspective dashboard with the results of various functions and session properties (in US/Central):
Why does the getTimezone() function always yield "Europe/London"?
Why does midnight() function give me midnight for the gateway server and not the client?
All of the MES data is timestamped UTC in the database, but I want the dashboard to display data based on central time (for example - charts for today's OEE statistics and downtime reasons should show today in US/Central regardless of where I pull them up). How can I use timezone functions to do this without hard coding -6 (current UTC offset from London)? -Especially since the daylight savings does not always match up, and may change in the future.
Thanks!
Ben
Because the expression runs in the gateway, not in the client.
Because the expression runs in the gateway, not in the client.
You will need to set the project timezone to "America/Chicago" so Perspective components that understand date/time types can handle them in your desired timezone, and use scripts to apply session.props.timeZoneId wherever you wish direct control over datetime formatting.
See this topic for suggestions on the latter (read the whole topic):
Ok thank you - this is helpful, but I think what I really need is the ability for a given day/datetime to find the Offset between "Europe/London" and "US/Central." Any suggestions for that?
Here is the code I currently use to set the startDate and endDate for the MESAnalysisController to perform an iterative query of the OEE for the last 14 days for a dashboard. The Custom property is an integer called "custom.DayCount.set" that increments for each day and gets set to -1 when finished.
The 6 hour offset set in the line for self.props.startDate works fine now and will in the winter, but when daylight savings doesn't match up, there will be a slight issue. Plus, when this is propogated to other sites, I want this calculation to work for other timezones.
Don't do arithmetic with dates to correct for time zones. java.util.Date objects are fundamentally UTC under the hood, representing a worldwide point in time, and trying to "adjust" them to correct for timezones simply makes them incorrect. If you pass incorrect timestamps to historian calls or other system functions, you will get wrong answers.
These objects are stringified in the timezone of the JVM that does the string conversion, and are created by string parsing in the timezone of the JVM that does the parsing.
If you want to stringify or parse in a timezone other than the JVM's timezone, you must use the zone-aware java data types.
Ok, thank you for your response. How should I go about iteratively producing the timestamp for midnight to midnight central time? (Keep in mind, I don't have the ability to directly query the MES database and must use the MESAnalysisController, which has pre-canned queries that I can only feed timestamps to.)
Ok great - thank you! I ended up modifying it - please let me know if I'm doing anything foolish:
from java.time import ZonedDateTime
from java.time import ZoneId
from java.util import Date
def getChicagoMidnight(x):
"""
Returns the midnight time for 'x' days ago in the Chicago timezone.
:param x: Number of days from today (negative integer).
:return: ZonedDateTime representing midnight of that day.
"""
# Define the Chicago timezone
chicago_zone = ZoneId.of("America/Chicago")
# Get the current date-time in Chicago timezone
now = ZonedDateTime.now(chicago_zone)
# Calculate the date for 'x' days ago where x is a negative number indicating days ago
date_x_days_ago = now.plusDays(x).toLocalDate()
# Create a ZonedDateTime for midnight of that date
midnight = ZonedDateTime.of(date_x_days_ago.getYear(),date_x_days_ago.getMonthValue(),date_x_days_ago.getDayOfMonth(), 0, 0, 0, 0,chicago_zone)
# Convert ZonedDateTime to java.util.Date
midnight_date = Date.from(midnight.toInstant())
return midnight_date
And I call it in the change script on my day counter:
def valueChanged(self, previousValue, currentValue, origin, missedEvents):
from java.util import Calendar, TimeZone
from java.text import SimpleDateFormat
if currentValue.value > -1 and currentValue.value < 16:
CurrentDay = currentValue.value - 14
self.props.startDate = timeStampMaker.getChicagoMidnight(CurrentDay)
self.props.endDate = timeStampMaker.getChicagoMidnight(CurrentDay+1)
else:
if currentValue.value == -1:
self.custom.DayCount.ret = -1
No, you want to do the date arithmetic with the ZonedDateTime so that it crosses daylight time changes properly (in the desired zone, not the gateway zone). I'd compute midnight once (separate function, perhaps), then do arithmetic and conversion to Date for each endpoint.