Metadata-Version: 2.1
Name: httpdate
Version: 1.2.0
Summary: Parse and format HTTP dates, such as Last-Modified and If-Modified-Since headers.
Project-URL: Homepage, https://github.com/jamielinux/httpdate
Project-URL: Issues, https://github.com/jamielinux/httpdate/issues
Project-URL: Source, https://github.com/jamielinux/httpdate
Author-email: Jamie Nguyen <j@jamielinux.com>
License-Expression: MIT
License-File: LICENSE
Keywords: if-modified-since,last-modified,lastmodified,rfc9110
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.7
Requires-Dist: leapseconds>=2023.4.0
Description-Content-Type: text/markdown

# httpdate

[![PyPi Version][pypi-img]][pypi-url]
[![License][license-img]][license-url]
[![Continuous Integration][ci-img]][ci-url]
[![Code Coverage][coverage-img]][coverage-url]
[![Python Versions][python-img]][python-url]

[pypi-img]: https://img.shields.io/pypi/v/httpdate.svg
[pypi-url]: https://pypi.org/project/httpdate
[license-img]:  https://img.shields.io/github/license/jamielinux/httpdate.svg
[license-url]: https://github.com/jamielinux/httpdate/blob/main/LICENSE
[ci-img]: https://github.com/jamielinux/httpdate/actions/workflows/ci.yml/badge.svg
[ci-url]: https://github.com/jamielinux/httpdate/actions/workflows/ci.yml
[coverage-img]: https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/jamielinux/f3b70fb7174f1a8a87f2185e80cbb2ef/raw/httpdate.covbadge.json
[coverage-url]: https://github.com/jamielinux/httpdate/actions/workflows/ci.yml
[python-img]: https://img.shields.io/pypi/pyversions/httpdate.svg
[python-url]: https://pypi.org/project/httpdate

---

**httpdate** is a Python library for parsing and formatting HTTP date fields that are
used in headers like `Last-Modified` and `If-Modified-Since`. It does so in strict
accordance with [RFC 9110 Section 5.6.7][0].

It has only one dependency, which is my tiny [leapseconds][1] library that just provides
a tuple of official leap seconds.

[0]: https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.7
[1]: https://github.com/jamielinux/leapseconds

## HTTP date formats

All HTTP dates (eg, in `Last-Modified` headers) must be sent in this format:

```console
# IMF-fixdate
Sun, 06 Nov 1994 08:49:37 GMT
```

However, RFC 9110 states that recipients must also accept two other obsolete formats:

```console
# rfc850-date
Sunday, 06-Nov-94 08:49:37 GMT

# asctime-date
Sun Nov  6 08:49:37 1994
```

RFC 9110 criteria for the HTTP date field includes the following:

- It must represent time as an instance of UTC.
- It must represent weekday names and month names in English.
- It is case-sensitive.
- It must not have any additional whitespace.
- It must be semantically correct (eg, the weekday must be the correct weekday).
- It can include leap seconds (eg, `23:59:60`).
- It must represent a year of `1900` or above.

It isn't stated explicitly in the RFCs, but `httpdate` will only consider a leap second
semantically correct if it's an [official leap second][2].

[2]: https://www.ietf.org/timezones/data/leap-seconds.list

## Installation

```console
pip install httpdate
```

## Usage

```python
from httpdate import is_valid_httpdate, httpdate_to_unixtime, unixtime_to_httpdate

# Check if an HTTP date (eg, `Last-Modified` header) is valid:
is_valid_httpdate("Sun, 06 Nov 1994 08:49:37 GMT")

try:
    # Parse an HTTP date:
    httpdate_to_unixtime("Sun, 06 Nov 1994 08:49:37 GMT")
    # Parse an HTTP date (rfc850-date):
    httpdate_to_unixtime("Sunday, 06-Nov-94 08:49:37 GMT")
    # Parse an HTTP date (asctime-date):
    httpdate_to_unixtime("Sun Nov  6 08:49:37 1994")
except ValueError:
    # Not a valid HTTP date string.
    pass

# Format a Unix timestamp as an HTTP date:
try:
    unixtime_to_httpdate(784111777)
except ValueError:
    # Outside the range supported by the operating system.
    pass
```

- **`is_valid_httpdate(httpdate)`**:
  - *Args*
    - `httpdate (str)`: An HTTP date string.
  - *Returns*
    - `bool`: True if `httpdate` is a valid HTTP date string, otherwise False.
  - *Raises*
    - `TypeError` if `httpdate` is not of type `str`.
- **`httpdate_to_unixtime(httpdate)`**:
  - *Args*
    - `httpdate (str)`: An HTTP date string.
  - *Returns*
    - `int`: A Unix timestamp (`int`).
  - *Raises*
    - `TypeError` if `httpdate` is not of type `str`.
    - `ValueError` if `httpdate` is not a valid HTTP date string.
- **`unixtime_to_httpdate(unixtime)`**:
  - *Args*
    - `unixtime (int)`: A Unix timestamp.
  - *Returns*
    - `str`: An HTTP date string.
  - *Raises*
    - `TypeError` if `unixtime` is not of type `int`.
    - `ValueError` if `unixtime` represents a year before 1900, or if it is outside
      the range supported by the operating system.

## Why Unix timestamps?

Unix timestamps are unambiguous, efficient, and can easily be converted to other
date-time formats using only the standard library.

When a `Last-Modified` header is semantically correct, this conversion — from string to
Unix timestamp and back to string — is lossless. (The only exception is leap seconds;
for example `Sat, 31 Dec 2016 23:59:60 GMT` would be returned as `Sun, 01 Jan 2017
00:00:00 GMT`. Recipients should interpret these as being identical anyway.)

If you want to store the original string instead, just use `is_valid_httpdate()` to
validate before storing.

## License

`httpdate` is distributed under the terms of the [MIT][license] license.

[license]: https://spdx.org/licenses/MIT.html
