mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2025-03-09 12:50:23 -05:00
[core] Calculate date by strftime_or_none
; fix seconds in datetime_round
- Negative datetime is not acceptable on Windows. - Scale the timestamp up 1000000 times to calculate milliseconds.
This commit is contained in:
parent
3905f64920
commit
bcda6e49b0
3 changed files with 35 additions and 13 deletions
|
@ -98,11 +98,13 @@
|
||||||
remove_start,
|
remove_start,
|
||||||
render_table,
|
render_table,
|
||||||
replace_extension,
|
replace_extension,
|
||||||
|
datetime_round,
|
||||||
rot47,
|
rot47,
|
||||||
sanitize_filename,
|
sanitize_filename,
|
||||||
sanitize_path,
|
sanitize_path,
|
||||||
sanitize_url,
|
sanitize_url,
|
||||||
shell_quote,
|
shell_quote,
|
||||||
|
strftime_or_none,
|
||||||
smuggle_url,
|
smuggle_url,
|
||||||
str_to_int,
|
str_to_int,
|
||||||
strip_jsonp,
|
strip_jsonp,
|
||||||
|
@ -392,6 +394,23 @@ def test_datetime_from_str(self):
|
||||||
self.assertEqual(datetime_from_str('now+1day', precision='hour'), datetime_from_str('now+24hours', precision='auto'))
|
self.assertEqual(datetime_from_str('now+1day', precision='hour'), datetime_from_str('now+24hours', precision='auto'))
|
||||||
self.assertEqual(datetime_from_str('now+23hours', precision='hour'), datetime_from_str('now+23hours', precision='auto'))
|
self.assertEqual(datetime_from_str('now+23hours', precision='hour'), datetime_from_str('now+23hours', precision='auto'))
|
||||||
|
|
||||||
|
def test_datetime_round(self):
|
||||||
|
self.assertEqual(datetime_round(dt.datetime.strptime('1820-05-12T01:23:45Z', '%Y-%m-%dT%H:%M:%SZ')),
|
||||||
|
dt.datetime(1820, 5, 12, tzinfo=dt.timezone.utc))
|
||||||
|
self.assertEqual(datetime_round(dt.datetime.strptime('1969-12-31T23:34:45Z', '%Y-%m-%dT%H:%M:%SZ'), 'hour'),
|
||||||
|
dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc))
|
||||||
|
self.assertEqual(datetime_round(dt.datetime.strptime('2024-12-25T01:23:45Z', '%Y-%m-%dT%H:%M:%SZ'), 'minute'),
|
||||||
|
dt.datetime(2024, 12, 25, 1, 24, tzinfo=dt.timezone.utc))
|
||||||
|
self.assertEqual(datetime_round(dt.datetime.strptime('2024-12-25T01:23:45.123Z', '%Y-%m-%dT%H:%M:%S.%fZ'), 'second'),
|
||||||
|
dt.datetime(2024, 12, 25, 1, 23, 45, tzinfo=dt.timezone.utc))
|
||||||
|
self.assertEqual(datetime_round(dt.datetime.strptime('2024-12-25T01:23:45.678Z', '%Y-%m-%dT%H:%M:%S.%fZ'), 'second'),
|
||||||
|
dt.datetime(2024, 12, 25, 1, 23, 46, tzinfo=dt.timezone.utc))
|
||||||
|
|
||||||
|
def test_strftime_or_none(self):
|
||||||
|
self.assertEqual(strftime_or_none(-4722192000), '18200512')
|
||||||
|
self.assertEqual(strftime_or_none(0), '19700101')
|
||||||
|
self.assertEqual(strftime_or_none(1735084800), '20241225')
|
||||||
|
|
||||||
def test_daterange(self):
|
def test_daterange(self):
|
||||||
_20century = DateRange('19000101', '20000101')
|
_20century = DateRange('19000101', '20000101')
|
||||||
self.assertFalse('17890714' in _20century)
|
self.assertFalse('17890714' in _20century)
|
||||||
|
|
|
@ -2673,11 +2673,7 @@ def _fill_common_fields(self, info_dict, final=True):
|
||||||
('modified_timestamp', 'modified_date'),
|
('modified_timestamp', 'modified_date'),
|
||||||
):
|
):
|
||||||
if info_dict.get(date_key) is None and info_dict.get(ts_key) is not None:
|
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,
|
info_dict[date_key] = strftime_or_none(info_dict[ts_key])
|
||||||
# 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')
|
|
||||||
|
|
||||||
if not info_dict.get('release_year'):
|
if not info_dict.get('release_year'):
|
||||||
info_dict['release_year'] = traverse_obj(info_dict, ('release_date', {lambda x: int(x[:4])}))
|
info_dict['release_year'] = traverse_obj(info_dict, ('release_date', {lambda x: int(x[:4])}))
|
||||||
|
|
|
@ -1364,6 +1364,17 @@ def datetime_add_months(dt_, months):
|
||||||
return dt_.replace(year, month, day)
|
return dt_.replace(year, month, day)
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_from_timestamp(timestamp):
|
||||||
|
# Working around out-of-range timestamp values (e.g. negative ones on Windows,
|
||||||
|
# see http://bugs.python.org/issue1646728)
|
||||||
|
# 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
|
||||||
|
return (dt.datetime.fromtimestamp(0, dt.timezone.utc)
|
||||||
|
+ dt.timedelta(seconds=timestamp))
|
||||||
|
|
||||||
|
|
||||||
def datetime_round(dt_, precision='day'):
|
def datetime_round(dt_, precision='day'):
|
||||||
"""
|
"""
|
||||||
Round a datetime object's time to a specific precision
|
Round a datetime object's time to a specific precision
|
||||||
|
@ -1371,6 +1382,7 @@ def datetime_round(dt_, precision='day'):
|
||||||
if precision == 'microsecond':
|
if precision == 'microsecond':
|
||||||
return dt_
|
return dt_
|
||||||
|
|
||||||
|
time_scale = 1000000
|
||||||
unit_seconds = {
|
unit_seconds = {
|
||||||
'day': 86400,
|
'day': 86400,
|
||||||
'hour': 3600,
|
'hour': 3600,
|
||||||
|
@ -1378,8 +1390,8 @@ def datetime_round(dt_, precision='day'):
|
||||||
'second': 1,
|
'second': 1,
|
||||||
}
|
}
|
||||||
roundto = lambda x, n: ((x + n / 2) // n) * n
|
roundto = lambda x, n: ((x + n / 2) // n) * n
|
||||||
timestamp = roundto(calendar.timegm(dt_.timetuple()), unit_seconds[precision])
|
timestamp = roundto(calendar.timegm(dt_.timetuple()) * time_scale + dt_.microsecond, unit_seconds[precision] * time_scale) / time_scale
|
||||||
return dt.datetime.fromtimestamp(timestamp, dt.timezone.utc)
|
return datetime_from_timestamp(timestamp)
|
||||||
|
|
||||||
|
|
||||||
def hyphenate_date(date_str):
|
def hyphenate_date(date_str):
|
||||||
|
@ -2047,12 +2059,7 @@ def strftime_or_none(timestamp, date_format='%Y%m%d', default=None):
|
||||||
datetime_object = None
|
datetime_object = None
|
||||||
try:
|
try:
|
||||||
if isinstance(timestamp, (int, float)): # unix timestamp
|
if isinstance(timestamp, (int, float)): # unix timestamp
|
||||||
# Using naive datetime here can break timestamp() in Windows
|
datetime_object = datetime_from_timestamp(timestamp)
|
||||||
# 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))
|
|
||||||
elif isinstance(timestamp, str): # assume YYYYMMDD
|
elif isinstance(timestamp, str): # assume YYYYMMDD
|
||||||
datetime_object = dt.datetime.strptime(timestamp, '%Y%m%d')
|
datetime_object = dt.datetime.strptime(timestamp, '%Y%m%d')
|
||||||
date_format = re.sub( # Support %s on windows
|
date_format = re.sub( # Support %s on windows
|
||||||
|
|
Loading…
Reference in a new issue