From eb97ba3765a555ee3d6b08168166069006b7e72e Mon Sep 17 00:00:00 2001 From: John Haddon Date: Fri, 8 Dec 2023 10:12:31 +0000 Subject: [PATCH] LocalDispatcher, LocalJobs : Fix timezone handling Before, the Properties tab was showing the correct time in the local timezone, and the Start Time column was showing the wrong thing. The difference is that the Properties tab was formatted directly in Python, whereas the column went through a conversion to `IECore::DataTimeData` and then on to `QDateTime` for display. DateTimeData stores a `boost::posix_time::ptime` instance internally, which store time in UTC. The wrapper that constructs that from a Python `datetime` assumes that the Python value is also stored in UTC, but that was not the case in our code - we were instead using "naive" objects which didn't record timezone data, and which only we knew were in local time. The solution is to store time in UTC in Python, by passing a timezone to `now()`. Using UTC also has the benefit of insulating us from changes to the system timezone made during a Job's runtime, which would otherwise be causing us to compare start and end times from different timezones and return bogus results. > Note : This isn't quite the whole story. Even when given a non-naive `datetime` object which records its timezone, the DataTimeData bindings still assume that it is stored in UTC. We should make the DateTimeData bindings convert to UTC appropriately using the timezone information, but that can wait for another day. Either way, we would still be wanting to store as UTC in Python. --- python/GafferDispatch/LocalDispatcher.py | 6 +++--- python/GafferDispatchUI/LocalJobs.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/python/GafferDispatch/LocalDispatcher.py b/python/GafferDispatch/LocalDispatcher.py index bb82e151f70..62f01806978 100644 --- a/python/GafferDispatch/LocalDispatcher.py +++ b/python/GafferDispatch/LocalDispatcher.py @@ -96,7 +96,7 @@ def __init__( self, batch, dispatcher ) : ) self.__executeInBackground = dispatcher["executeInBackground"].getValue() - self.__startTime = datetime.datetime.now() + self.__startTime = datetime.datetime.now( datetime.timezone.utc ) self.__endTime = None self.__messageHandler = _MessageHandler() @@ -140,7 +140,7 @@ def runningTime( self ) : if self.__endTime is not None : return self.__endTime - self.__startTime else : - return datetime.datetime.now() - self.__startTime + return datetime.datetime.now( datetime.timezone.utc ) - self.__startTime def processID( self ) : @@ -384,7 +384,7 @@ def __updateStatus( self, status ) : self.__status = status if status in ( self.Status.Complete, self.Status.Failed, self.Status.Killed ) : - self.__endTime = datetime.datetime.now() + self.__endTime = datetime.datetime.now( datetime.timezone.utc ) if threading.current_thread() is threading.main_thread() : self.__emitStatusChanged() diff --git a/python/GafferDispatchUI/LocalJobs.py b/python/GafferDispatchUI/LocalJobs.py index b386ce9f150..5981a5b8005 100644 --- a/python/GafferDispatchUI/LocalJobs.py +++ b/python/GafferDispatchUI/LocalJobs.py @@ -341,7 +341,7 @@ def soleFormat( values ) : value = sole( values ) if value is not None : if isinstance( value, datetime.datetime ) : - return "{:%a %b %d %H:%M:%S}".format( value ) + return "{:%a %b %d %H:%M:%S}".format( value.astimezone() ) else : return str( value ) else :