I am working with a Django app Pyroven and am doing some major re-working of its functionality. As part of this I’ve begun writing a testing framework for it, as at present it has no unit tests. By way of background it uses the University of Cambridge Raven service for authentication, and offers an authentication backend for Django to leverage this service.
The issue I’m running into is testing the WLS-Response token from Raven. This forms a string of ! separated values, which include a time field in the format:
%Y%m%dT%H%M%SZ
In my testing code, I’ve created this as:
raven_issue = datetime.now().strftime('%Y%m%dT%H%M%SZ')
and then returned that to the testing URL for processing by the view. The issue comes with the validation that this time is not too far in the past for validation of the response. The code which validates this uses:
def parse_time(t):
"""Converts a time of the form '20110729T123456Z' to a number of seconds
since the epoch.
@exception ValueError if the time is not a valid Raven time"""
time_struct = time.strptime(t, "%Y%m%dT%H%M%SZ")
return calendar.timegm(time_struct)
Now, when it is passed the string above from the datetime.now() constructor, the validation on this parsed value fails:
# Check that the issue time is not in the future or too far in the past:
if self.issue > time.time() + PYROVEN_MAX_CLOCK_SKEW:
raise InvalidResponseError("The timestamp on the response is in the future")
if self.issue < time.time() - PYROVEN_MAX_CLOCK_SKEW - PYROVEN_TIMEOUT:
raise InvalidResponseError("The response has timed out")
This code fails with my test, claiming that the response has timed out. The values for PYROVEN_MAX_CLOCK_SKEW and PYROVEN_TIMEOUT are 2s and 10s respectively.
This begs the question is there some variability in handling of time I don’t understand? If I put a datetime.now() generated value, with a datetime.timedelta of 2 hours in the future, convert it to the string and pass it to the validation, that doesn’t fail despite the timestamp being in the future, when it should. Why is this, as the logical reading of the code suggests it should?
You are getting confused between local time (as returned by
datetime.now()and GMT (as parsed bycalendar.timegm()):Conclusion: use
time.mktimeinstead ofcalendar.timegmto turn your values to timestamps.