[compat] Add compat_datetime_from_timestamp (#11902)

Authored by: pzhlkj6612, seproDev

Co-authored-by: sepro <sepro@sepr0.com>
This commit is contained in:
Mozi
2025-09-06 23:48:24 +00:00
committed by GitHub
parent e6e6b51214
commit 6a763a55d8
5 changed files with 77 additions and 15 deletions

View File

@@ -2717,11 +2717,7 @@ class YoutubeDL:
('modified_timestamp', 'modified_date'),
):
if info_dict.get(date_key) is None and info_dict.get(ts_key) is not None:
# Working around out-of-range timestamp values (e.g. negative ones on Windows,
# see http://bugs.python.org/issue1646728)
with contextlib.suppress(ValueError, OverflowError, OSError):
upload_date = dt.datetime.fromtimestamp(info_dict[ts_key], dt.timezone.utc)
info_dict[date_key] = upload_date.strftime('%Y%m%d')
info_dict[date_key] = strftime_or_none(info_dict[ts_key])
if not info_dict.get('release_year'):
info_dict['release_year'] = traverse_obj(info_dict, ('release_date', {lambda x: int(x[:4])}))

View File

@@ -1,3 +1,4 @@
import datetime as dt
import os
import xml.etree.ElementTree as etree
@@ -27,6 +28,13 @@ def compat_ord(c):
return c if isinstance(c, int) else ord(c)
def compat_datetime_from_timestamp(timestamp):
# Calling dt.datetime.fromtimestamp with negative timestamps throws error in Windows
# Ref: https://github.com/yt-dlp/yt-dlp/issues/5185, https://github.com/python/cpython/issues/81708,
# https://github.com/yt-dlp/yt-dlp/issues/6706#issuecomment-1496842642
return (dt.datetime.fromtimestamp(0, dt.timezone.utc) + dt.timedelta(seconds=timestamp))
# Python 3.8+ does not honor %HOME% on windows, but this breaks compatibility with youtube-dl
# See https://github.com/yt-dlp/yt-dlp/issues/792
# https://docs.python.org/3/library/os.path.html#os.path.expanduser

View File

@@ -47,6 +47,7 @@ import xml.etree.ElementTree
from . import traversal
from ..compat import (
compat_datetime_from_timestamp,
compat_etree_fromstring,
compat_expanduser,
compat_HTMLParseError,
@@ -1376,6 +1377,7 @@ def datetime_round(dt_, precision='day'):
if precision == 'microsecond':
return dt_
time_scale = 1_000_000
unit_seconds = {
'day': 86400,
'hour': 3600,
@@ -1383,8 +1385,8 @@ def datetime_round(dt_, precision='day'):
'second': 1,
}
roundto = lambda x, n: ((x + n / 2) // n) * n
timestamp = roundto(calendar.timegm(dt_.timetuple()), unit_seconds[precision])
return dt.datetime.fromtimestamp(timestamp, dt.timezone.utc)
timestamp = roundto(calendar.timegm(dt_.timetuple()) + dt_.microsecond / time_scale, unit_seconds[precision])
return compat_datetime_from_timestamp(timestamp)
def hyphenate_date(date_str):
@@ -2056,18 +2058,13 @@ def strftime_or_none(timestamp, date_format='%Y%m%d', default=None):
datetime_object = None
try:
if isinstance(timestamp, (int, float)): # unix timestamp
# Using naive datetime here can break timestamp() in Windows
# Ref: https://github.com/yt-dlp/yt-dlp/issues/5185, https://github.com/python/cpython/issues/94414
# Also, dt.datetime.fromtimestamp breaks for negative timestamps
# Ref: https://github.com/yt-dlp/yt-dlp/issues/6706#issuecomment-1496842642
datetime_object = (dt.datetime.fromtimestamp(0, dt.timezone.utc)
+ dt.timedelta(seconds=timestamp))
datetime_object = compat_datetime_from_timestamp(timestamp)
elif isinstance(timestamp, str): # assume YYYYMMDD
datetime_object = dt.datetime.strptime(timestamp, '%Y%m%d')
date_format = re.sub( # Support %s on windows
r'(?<!%)(%%)*%s', rf'\g<1>{int(datetime_object.timestamp())}', date_format)
return datetime_object.strftime(date_format)
except (ValueError, TypeError, AttributeError):
except (ValueError, TypeError, AttributeError, OverflowError, OSError):
return default