Centralize formatting of HTTP-style dates.
Use time.strftime, which turns out to be a bit faster than either datetime.strftime or email.utils.formatdate.
This commit is contained in:
parent
e5f4763938
commit
2baf3c02ab
|
@ -31,8 +31,6 @@ supported version is 7.18.2, and the recommended version is 7.21.1 or newer.
|
|||
|
||||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
|
||||
import calendar
|
||||
import email.utils
|
||||
import time
|
||||
import weakref
|
||||
|
||||
|
@ -279,9 +277,8 @@ class HTTPRequest(object):
|
|||
if headers is None:
|
||||
headers = httputil.HTTPHeaders()
|
||||
if if_modified_since:
|
||||
timestamp = calendar.timegm(if_modified_since.utctimetuple())
|
||||
headers["If-Modified-Since"] = email.utils.formatdate(
|
||||
timestamp, localtime=False, usegmt=True)
|
||||
headers["If-Modified-Since"] = httputil.format_timestamp(
|
||||
if_modified_since)
|
||||
self.proxy_host = proxy_host
|
||||
self.proxy_port = proxy_port
|
||||
self.proxy_username = proxy_username
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
|
||||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
|
||||
import datetime
|
||||
import numbers
|
||||
import re
|
||||
import time
|
||||
|
||||
from tornado.escape import native_str, parse_qs_bytes, utf8
|
||||
from tornado.log import gen_log
|
||||
|
@ -288,6 +291,26 @@ def parse_multipart_form_data(boundary, data, arguments, files):
|
|||
arguments.setdefault(name, []).append(value)
|
||||
|
||||
|
||||
def format_timestamp(ts):
|
||||
"""Formats a timestamp in the format used by HTTP.
|
||||
|
||||
The argument may be a numeric timestamp as returned by `time.time()`,
|
||||
a time tuple as returned by `time.gmtime()`, or a `datetime.datetime`
|
||||
object.
|
||||
|
||||
>>> format_timestamp(1359312200)
|
||||
'Sun, 27 Jan 2013 18:43:20 GMT'
|
||||
"""
|
||||
if isinstance(ts, (tuple, time.struct_time)):
|
||||
pass
|
||||
elif isinstance(ts, datetime.datetime):
|
||||
ts = ts.utctimetuple()
|
||||
elif isinstance(ts, numbers.Real):
|
||||
ts = time.gmtime(ts)
|
||||
else:
|
||||
raise TypeError("unknown timestamp type: %r" % ts)
|
||||
return time.strftime("%a, %d %b %Y %H:%M:%S GMT", ts)
|
||||
|
||||
# _parseparam and _parse_header are copied and modified from python2.7's cgi.py
|
||||
# The original 2.7 version of this code did not correctly support some
|
||||
# combinations of semicolons and double quotes.
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
|
||||
|
||||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
from tornado.httputil import url_concat, parse_multipart_form_data, HTTPHeaders
|
||||
from tornado.httputil import url_concat, parse_multipart_form_data, HTTPHeaders, format_timestamp
|
||||
from tornado.escape import utf8
|
||||
from tornado.log import gen_log
|
||||
from tornado.testing import ExpectLog
|
||||
from tornado.test.util import unittest
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
|
||||
|
||||
class TestUrlConcat(unittest.TestCase):
|
||||
|
@ -224,3 +227,29 @@ Foo: even
|
|||
[("Asdf", "qwer zxcv"),
|
||||
("Foo", "bar baz"),
|
||||
("Foo", "even more lines")])
|
||||
|
||||
|
||||
class FormatTimestampTest(unittest.TestCase):
|
||||
# Make sure that all the input types are supported.
|
||||
TIMESTAMP = 1359312200.503611
|
||||
EXPECTED = 'Sun, 27 Jan 2013 18:43:20 GMT'
|
||||
|
||||
def check(self, value):
|
||||
self.assertEqual(format_timestamp(value), self.EXPECTED)
|
||||
|
||||
def test_unix_time_float(self):
|
||||
self.check(self.TIMESTAMP)
|
||||
|
||||
def test_unix_time_int(self):
|
||||
self.check(int(self.TIMESTAMP))
|
||||
|
||||
def test_struct_time(self):
|
||||
self.check(time.gmtime(self.TIMESTAMP))
|
||||
|
||||
def test_time_tuple(self):
|
||||
tup = tuple(time.gmtime(self.TIMESTAMP))
|
||||
self.assertEqual(9, len(tup))
|
||||
self.check(tup)
|
||||
|
||||
def test_datetime(self):
|
||||
self.check(datetime.datetime.utcfromtimestamp(self.TIMESTAMP))
|
||||
|
|
|
@ -53,14 +53,12 @@ from __future__ import absolute_import, division, print_function, with_statement
|
|||
|
||||
import base64
|
||||
import binascii
|
||||
import calendar
|
||||
import datetime
|
||||
import email.utils
|
||||
import functools
|
||||
import gzip
|
||||
import hashlib
|
||||
import hmac
|
||||
import itertools
|
||||
import mimetypes
|
||||
import numbers
|
||||
import os.path
|
||||
|
@ -231,8 +229,7 @@ class RequestHandler(object):
|
|||
self._headers = httputil.HTTPHeaders({
|
||||
"Server": "TornadoServer/%s" % tornado.version,
|
||||
"Content-Type": "text/html; charset=UTF-8",
|
||||
"Date": datetime.datetime.utcnow().strftime(
|
||||
"%a, %d %b %Y %H:%M:%S GMT"),
|
||||
"Date": httputil.format_timestamp(time.gmtime()),
|
||||
})
|
||||
self.set_default_headers()
|
||||
if not self.request.supports_http_1_1():
|
||||
|
@ -308,8 +305,7 @@ class RequestHandler(object):
|
|||
# return immediately since we know the converted value will be safe
|
||||
return str(value)
|
||||
elif isinstance(value, datetime.datetime):
|
||||
t = calendar.timegm(value.utctimetuple())
|
||||
return email.utils.formatdate(t, localtime=False, usegmt=True)
|
||||
return httputil.format_timestamp(value)
|
||||
else:
|
||||
raise TypeError("Unsupported header value %r" % value)
|
||||
# If \n is allowed into the header, it is possible to inject
|
||||
|
@ -410,9 +406,7 @@ class RequestHandler(object):
|
|||
expires = datetime.datetime.utcnow() + datetime.timedelta(
|
||||
days=expires_days)
|
||||
if expires:
|
||||
timestamp = calendar.timegm(expires.utctimetuple())
|
||||
morsel["expires"] = email.utils.formatdate(
|
||||
timestamp, localtime=False, usegmt=True)
|
||||
morsel["expires"] = httputil.format_timestamp(expires)
|
||||
if path:
|
||||
morsel["path"] = path
|
||||
for k, v in kwargs.items():
|
||||
|
|
Loading…
Reference in New Issue