Parse a date format as specified by HTTP RFC 9110 Section 5.6.7. The three formats allowed by the RFC are accepted, even if only the first one is still in widespread use. Return an integer expressed in seconds since the epoch, in UTC.
(date)
| 98 | |
| 99 | |
| 100 | def parse_http_date(date): |
| 101 | """ |
| 102 | Parse a date format as specified by HTTP RFC 9110 Section 5.6.7. |
| 103 | |
| 104 | The three formats allowed by the RFC are accepted, even if only the first |
| 105 | one is still in widespread use. |
| 106 | |
| 107 | Return an integer expressed in seconds since the epoch, in UTC. |
| 108 | """ |
| 109 | # email.utils.parsedate() does the job for RFC 1123 dates; unfortunately |
| 110 | # RFC 9110 makes it mandatory to support RFC 850 dates too. So we roll |
| 111 | # our own RFC-compliant parsing. |
| 112 | for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE: |
| 113 | m = regex.match(date) |
| 114 | if m is not None: |
| 115 | break |
| 116 | else: |
| 117 | raise ValueError("%r is not in a valid HTTP date format" % date) |
| 118 | try: |
| 119 | year = int(m["year"]) |
| 120 | if year < 100: |
| 121 | current_year = datetime.now(tz=UTC).year |
| 122 | current_century = current_year - (current_year % 100) |
| 123 | if year - (current_year % 100) > 50: |
| 124 | # year that appears to be more than 50 years in the future are |
| 125 | # interpreted as representing the past. |
| 126 | year += current_century - 100 |
| 127 | else: |
| 128 | year += current_century |
| 129 | month = MONTHS.index(m["mon"].lower()) + 1 |
| 130 | day = int(m["day"]) |
| 131 | hour = int(m["hour"]) |
| 132 | min = int(m["min"]) |
| 133 | sec = int(m["sec"]) |
| 134 | result = datetime(year, month, day, hour, min, sec, tzinfo=UTC) |
| 135 | return int(result.timestamp()) |
| 136 | except Exception as exc: |
| 137 | raise ValueError("%r is not a valid date" % date) from exc |
| 138 | |
| 139 | |
| 140 | def parse_http_date_safe(date): |