diff --git a/swift3/test/unit/test_utils.py b/swift3/test/unit/test_utils.py index 0735cd96..d2ceddf7 100644 --- a/swift3/test/unit/test_utils.py +++ b/swift3/test/unit/test_utils.py @@ -13,10 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os +import time import unittest import mock -from swift3 import utils +from swift3 import utils, request strs = [ ('Owner', 'owner'), @@ -97,6 +99,38 @@ class TestSwift3Utils(unittest.TestCase): ts = utils.S3Timestamp(1.9) self.assertEqual(expected, ts.s3xmlformat) + def test_mktime(self): + date_headers = [ + 'Thu, 01 Jan 1970 00:00:00 -0000', + 'Thu, 01 Jan 1970 00:00:00 GMT', + 'Thu, 01 Jan 1970 00:00:00 UTC', + 'Thu, 01 Jan 1970 08:00:00 +0800', + 'Wed, 31 Dec 1969 16:00:00 -0800', + 'Wed, 31 Dec 1969 16:00:00 PST', + ] + for header in date_headers: + ts = utils.mktime(header) + self.assertEqual(0, ts, 'Got %r for header %s' % (ts, header)) + + # Last-Modified response style + self.assertEqual(0, utils.mktime('1970-01-01T00:00:00')) + + # X-Amz-Date style + self.assertEqual(0, utils.mktime('19700101T000000Z', + request.SIGV4_X_AMZ_DATE_FORMAT)) + + def test_mktime_weird_tz(self): + orig_tz = os.environ.get('TZ', '') + try: + os.environ['TZ'] = 'EST+05EDT,M4.1.0,M10.5.0' + time.tzset() + os.environ['TZ'] = '+0000' + # No tzset! Simulating what Swift would do. + self.assertNotEqual(0, time.timezone) + self.test_mktime() + finally: + os.environ['TZ'] = orig_tz + time.tzset() if __name__ == '__main__': unittest.main() diff --git a/swift3/utils.py b/swift3/utils.py index 053cc5ba..9adfbb37 100644 --- a/swift3/utils.py +++ b/swift3/utils.py @@ -13,19 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import re -import uuid import base64 +import calendar +import email.utils +import re import socket import time +from urllib import unquote +import uuid from swift.common.utils import get_logger -import email.utils # Need for check_path_header from swift.common import utils from swift.common.swob import HTTPPreconditionFailed -from urllib import unquote from swift3.cfg import CONF @@ -186,14 +187,17 @@ def mktime(timestamp_str, time_format='%Y-%m-%dT%H:%M:%S'): :param time_format: a string of format to parse in (b) process :return : a float instance in epoch time """ - try: - epoch_time = email.utils.mktime_tz( - email.utils.parsedate_tz(timestamp_str)) - except TypeError: + # time_tuple is the *remote* local time + time_tuple = email.utils.parsedate_tz(timestamp_str) + if time_tuple is None: time_tuple = time.strptime(timestamp_str, time_format) - # add timezone info as utc (no time difference) time_tuple += (0, ) - epoch_time = email.utils.mktime_tz(time_tuple) + + # We prefer calendar.gmtime and a manual adjustment over + # email.utils.mktime_tz because older versions of Python (<2.7.4) may + # double-adjust for timezone in some situations (such when swift changes + # os.environ['TZ'] without calling time.tzset()). + epoch_time = calendar.timegm(time_tuple) - time_tuple[9] return epoch_time