def http_date( timestamp: t.Optional[t.Union[datetime, date, int, float, struct_time]] = None ) -> str: """Format a datetime object or timestamp into an :rfc:`2822` date string. This is a wrapper for :func:`email.utils.format_datetime`. It assumes naive datetime objects are in UTC instead of raising an exception. :param timestamp: The datetime or timestamp to format. Defaults to the current time. .. versionchanged:: 2.0 Use ``email.utils.format_datetime``. Accept ``date`` objects. """ if isinstance(timestamp, date): if not isinstance(timestamp, datetime): # Assume plain date is midnight UTC. timestamp = datetime.combine(timestamp, time(), tzinfo=timezone.utc) else: # Ensure datetime is timezone-aware. timestamp = _dt_as_utc(timestamp) return email.utils.format_datetime(timestamp, usegmt=True) if isinstance(timestamp, struct_time): timestamp = mktime(timestamp) return email.utils.formatdate(timestamp, usegmt=True)
def http_date( timestamp: t.Optional[t.Union[datetime, int, float]] = None) -> str: """Format a datetime object or timestamp into an :rfc:`2822` date string. This is a wrapper for :func:`email.utils.format_datetime` and ``.formatdate``. It assumes naive datetime objects are in UTC instead of raising an exception. :param timestamp: The datetime or timestamp to format. Defaults to the current time. .. versionchanged:: 2.0.0 Use ``email.utils.format_datetime``. """ if isinstance(timestamp, datetime): timestamp = _dt_as_utc(timestamp) return email.utils.format_datetime(timestamp, usegmt=True) return email.utils.formatdate(timestamp, usegmt=True)
def is_resource_modified( environ: "WSGIEnvironment", etag: t.Optional[str] = None, data: t.Optional[bytes] = None, last_modified: t.Optional[t.Union[datetime, str]] = None, ignore_if_range: bool = True, ) -> bool: """Convenience method for conditional requests. :param environ: the WSGI environment of the request to be checked. :param etag: the etag for the response for comparison. :param data: or alternatively the data of the response to automatically generate an etag using :func:`generate_etag`. :param last_modified: an optional date of the last modification. :param ignore_if_range: If `False`, `If-Range` header will be taken into account. :return: `True` if the resource was modified, otherwise `False`. .. versionchanged:: 2.0 SHA-1 is used to generate an etag value for the data. MD5 may not be available in some environments. .. versionchanged:: 1.0.0 The check is run for methods other than ``GET`` and ``HEAD``. """ if etag is None and data is not None: etag = generate_etag(data) elif data is not None: raise TypeError("both data and etag given") unmodified = False if isinstance(last_modified, str): last_modified = parse_date(last_modified) # HTTP doesn't use microsecond, remove it to avoid false positive # comparisons. Mark naive datetimes as UTC. if last_modified is not None: last_modified = _dt_as_utc(last_modified.replace(microsecond=0)) if_range = None if not ignore_if_range and "HTTP_RANGE" in environ: # https://tools.ietf.org/html/rfc7233#section-3.2 # A server MUST ignore an If-Range header field received in a request # that does not contain a Range header field. if_range = parse_if_range_header(environ.get("HTTP_IF_RANGE")) if if_range is not None and if_range.date is not None: modified_since: t.Optional[datetime] = if_range.date else: modified_since = parse_date(environ.get("HTTP_IF_MODIFIED_SINCE")) if modified_since and last_modified and last_modified <= modified_since: unmodified = True if etag: etag, _ = unquote_etag(etag) etag = t.cast(str, etag) if if_range is not None and if_range.etag is not None: unmodified = parse_etags(if_range.etag).contains(etag) else: if_none_match = parse_etags(environ.get("HTTP_IF_NONE_MATCH")) if if_none_match: # https://tools.ietf.org/html/rfc7232#section-3.2 # "A recipient MUST use the weak comparison function when comparing # entity-tags for If-None-Match" unmodified = if_none_match.contains_weak(etag) # https://tools.ietf.org/html/rfc7232#section-3.1 # "Origin server MUST use the strong comparison function when # comparing entity-tags for If-Match" if_match = parse_etags(environ.get("HTTP_IF_MATCH")) if if_match: unmodified = not if_match.is_strong(etag) return not unmodified