mirror of https://github.com/home-assistant/core
Deprecate relative_time() in favor of time_since() and time_until() (#111177)
* add time_since/time_until. add deprecation of relative_time * fix merge conflicts * Apply suggestions from code review * Update homeassistant/helpers/template.py * Update homeassistant/helpers/template.py * Update homeassistant/helpers/template.py --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
e0b58c3f45
commit
1120246194
|
@ -56,6 +56,10 @@
|
|||
"config_entry_reauth": {
|
||||
"title": "[%key:common::config_flow::title::reauth%]",
|
||||
"description": "Reauthentication is needed"
|
||||
},
|
||||
"template_function_relative_time_deprecated": {
|
||||
"title": "The {relative_time} template function is deprecated",
|
||||
"description": "The {relative_time} template function is deprecated in Home Assistant. Please use the {time_since} or {time_until} template functions instead."
|
||||
}
|
||||
},
|
||||
"system_health": {
|
||||
|
|
|
@ -59,6 +59,7 @@ from homeassistant.const import (
|
|||
UnitOfLength,
|
||||
)
|
||||
from homeassistant.core import (
|
||||
DOMAIN as HA_DOMAIN,
|
||||
Context,
|
||||
HomeAssistant,
|
||||
State,
|
||||
|
@ -2480,6 +2481,29 @@ def relative_time(hass: HomeAssistant, value: Any) -> Any:
|
|||
|
||||
If the input are not a datetime object the input will be returned unmodified.
|
||||
"""
|
||||
|
||||
def warn_relative_time_deprecated() -> None:
|
||||
ir = issue_registry.async_get(hass)
|
||||
issue_id = "template_function_relative_time_deprecated"
|
||||
if ir.async_get_issue(HA_DOMAIN, issue_id):
|
||||
return
|
||||
issue_registry.async_create_issue(
|
||||
hass,
|
||||
HA_DOMAIN,
|
||||
issue_id,
|
||||
breaks_in_ha_version="2024.11.0",
|
||||
is_fixable=False,
|
||||
severity=issue_registry.IssueSeverity.WARNING,
|
||||
translation_key=issue_id,
|
||||
translation_placeholders={
|
||||
"relative_time": "relative_time()",
|
||||
"time_since": "time_since()",
|
||||
"time_until": "time_until()",
|
||||
},
|
||||
)
|
||||
_LOGGER.warning("Template function 'relative_time' is deprecated")
|
||||
|
||||
warn_relative_time_deprecated()
|
||||
if (render_info := _render_info.get()) is not None:
|
||||
render_info.has_time = True
|
||||
|
||||
|
@ -2492,6 +2516,50 @@ def relative_time(hass: HomeAssistant, value: Any) -> Any:
|
|||
return dt_util.get_age(value)
|
||||
|
||||
|
||||
def time_since(hass: HomeAssistant, value: Any | datetime, precision: int = 1) -> Any:
|
||||
"""Take a datetime and return its "age" as a string.
|
||||
|
||||
The age can be in seconds, minutes, hours, days, months and year.
|
||||
|
||||
precision is the number of units to return, with the last unit rounded.
|
||||
|
||||
If the value not a datetime object the input will be returned unmodified.
|
||||
"""
|
||||
if (render_info := _render_info.get()) is not None:
|
||||
render_info.has_time = True
|
||||
|
||||
if not isinstance(value, datetime):
|
||||
return value
|
||||
if not value.tzinfo:
|
||||
value = dt_util.as_local(value)
|
||||
if dt_util.now() < value:
|
||||
return value
|
||||
|
||||
return dt_util.get_age(value, precision)
|
||||
|
||||
|
||||
def time_until(hass: HomeAssistant, value: Any | datetime, precision: int = 1) -> Any:
|
||||
"""Take a datetime and return the amount of time until that time as a string.
|
||||
|
||||
The time until can be in seconds, minutes, hours, days, months and years.
|
||||
|
||||
precision is the number of units to return, with the last unit rounded.
|
||||
|
||||
If the value not a datetime object the input will be returned unmodified.
|
||||
"""
|
||||
if (render_info := _render_info.get()) is not None:
|
||||
render_info.has_time = True
|
||||
|
||||
if not isinstance(value, datetime):
|
||||
return value
|
||||
if not value.tzinfo:
|
||||
value = dt_util.as_local(value)
|
||||
if dt_util.now() > value:
|
||||
return value
|
||||
|
||||
return dt_util.get_time_remaining(value, precision)
|
||||
|
||||
|
||||
def urlencode(value):
|
||||
"""Urlencode dictionary and return as UTF-8 string."""
|
||||
return urllib_urlencode(value).encode("utf-8")
|
||||
|
@ -2890,6 +2958,8 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||
"floor_id",
|
||||
"floor_name",
|
||||
"relative_time",
|
||||
"time_since",
|
||||
"time_until",
|
||||
"today_at",
|
||||
"label_id",
|
||||
"label_name",
|
||||
|
@ -2946,6 +3016,10 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||
self.globals["now"] = hassfunction(now)
|
||||
self.globals["relative_time"] = hassfunction(relative_time)
|
||||
self.filters["relative_time"] = self.globals["relative_time"]
|
||||
self.globals["time_since"] = hassfunction(time_since)
|
||||
self.filters["time_since"] = self.globals["time_since"]
|
||||
self.globals["time_until"] = hassfunction(time_until)
|
||||
self.filters["time_until"] = self.globals["time_until"]
|
||||
self.globals["today_at"] = hassfunction(today_at)
|
||||
self.filters["today_at"] = self.globals["today_at"]
|
||||
|
||||
|
|
|
@ -286,36 +286,78 @@ def parse_time(time_str: str) -> dt.time | None:
|
|||
return None
|
||||
|
||||
|
||||
def get_age(date: dt.datetime) -> str:
|
||||
"""Take a datetime and return its "age" as a string.
|
||||
|
||||
The age can be in second, minute, hour, day, month or year. Only the
|
||||
biggest unit is considered, e.g. if it's 2 days and 3 hours, "2 days" will
|
||||
be returned.
|
||||
Make sure date is not in the future, or else it won't work.
|
||||
"""
|
||||
def _get_timestring(timediff: float, precision: int = 1) -> str:
|
||||
"""Return a string representation of a time diff."""
|
||||
|
||||
def formatn(number: int, unit: str) -> str:
|
||||
"""Add "unit" if it's plural."""
|
||||
if number == 1:
|
||||
return f"1 {unit}"
|
||||
return f"{number:d} {unit}s"
|
||||
return f"1 {unit} "
|
||||
return f"{number:d} {unit}s "
|
||||
|
||||
if timediff == 0.0:
|
||||
return "0 seconds"
|
||||
|
||||
units = ("year", "month", "day", "hour", "minute", "second")
|
||||
|
||||
factors = (365 * 24 * 60 * 60, 30 * 24 * 60 * 60, 24 * 60 * 60, 60 * 60, 60, 1)
|
||||
|
||||
result_string: str = ""
|
||||
current_precision = 0
|
||||
|
||||
for i, current_factor in enumerate(factors):
|
||||
selected_unit = units[i]
|
||||
if timediff < current_factor:
|
||||
continue
|
||||
current_precision = current_precision + 1
|
||||
if current_precision == precision:
|
||||
return (
|
||||
result_string + formatn(round(timediff / current_factor), selected_unit)
|
||||
).rstrip()
|
||||
curr_diff = int(timediff // current_factor)
|
||||
result_string += formatn(curr_diff, selected_unit)
|
||||
timediff -= (curr_diff) * current_factor
|
||||
|
||||
return result_string.rstrip()
|
||||
|
||||
|
||||
def get_age(date: dt.datetime, precision: int = 1) -> str:
|
||||
"""Take a datetime and return its "age" as a string.
|
||||
|
||||
The age can be in second, minute, hour, day, month and year.
|
||||
|
||||
depth number of units will be returned, with the last unit rounded
|
||||
|
||||
The date must be in the past or a ValueException will be raised.
|
||||
"""
|
||||
|
||||
delta = (now() - date).total_seconds()
|
||||
|
||||
rounded_delta = round(delta)
|
||||
|
||||
units = ["second", "minute", "hour", "day", "month"]
|
||||
factors = [60, 60, 24, 30, 12]
|
||||
selected_unit = "year"
|
||||
if rounded_delta < 0:
|
||||
raise ValueError("Time value is in the future")
|
||||
return _get_timestring(rounded_delta, precision)
|
||||
|
||||
for i, next_factor in enumerate(factors):
|
||||
if rounded_delta < next_factor:
|
||||
selected_unit = units[i]
|
||||
break
|
||||
delta /= next_factor
|
||||
rounded_delta = round(delta)
|
||||
|
||||
return formatn(rounded_delta, selected_unit)
|
||||
def get_time_remaining(date: dt.datetime, precision: int = 1) -> str:
|
||||
"""Take a datetime and return its "age" as a string.
|
||||
|
||||
The age can be in second, minute, hour, day, month and year.
|
||||
|
||||
depth number of units will be returned, with the last unit rounded
|
||||
|
||||
The date must be in the future or a ValueException will be raised.
|
||||
"""
|
||||
|
||||
delta = (date - now()).total_seconds()
|
||||
|
||||
rounded_delta = round(delta)
|
||||
|
||||
if rounded_delta < 0:
|
||||
raise ValueError("Time value is in the past")
|
||||
|
||||
return _get_timestring(rounded_delta, precision)
|
||||
|
||||
|
||||
def parse_time_expression(parameter: Any, min_value: int, max_value: int) -> list[int]:
|
||||
|
|
|
@ -31,7 +31,7 @@ from homeassistant.const import (
|
|||
UnitOfTemperature,
|
||||
UnitOfVolume,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import DOMAIN as HA_DOMAIN, HomeAssistant
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import (
|
||||
area_registry as ar,
|
||||
|
@ -2240,6 +2240,7 @@ def test_relative_time(mock_is_safe, hass: HomeAssistant) -> None:
|
|||
"""Test relative_time method."""
|
||||
hass.config.set_time_zone("UTC")
|
||||
now = datetime.strptime("2000-01-01 10:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
issue_registry = ir.async_get(hass)
|
||||
relative_time_template = (
|
||||
'{{relative_time(strptime("2000-01-01 09:00:00", "%Y-%m-%d %H:%M:%S"))}}'
|
||||
)
|
||||
|
@ -2249,7 +2250,9 @@ def test_relative_time(mock_is_safe, hass: HomeAssistant) -> None:
|
|||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour"
|
||||
|
||||
assert issue_registry.async_get_issue(
|
||||
HA_DOMAIN, "template_function_relative_time_deprecated"
|
||||
)
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
|
@ -2308,6 +2311,333 @@ def test_relative_time(mock_is_safe, hass: HomeAssistant) -> None:
|
|||
assert info.has_time is True
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
def test_time_since(mock_is_safe, hass: HomeAssistant) -> None:
|
||||
"""Test time_since method."""
|
||||
hass.config.set_time_zone("UTC")
|
||||
now = datetime.strptime("2000-01-01 10:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
time_since_template = (
|
||||
'{{time_since(strptime("2000-01-01 09:00:00", "%Y-%m-%d %H:%M:%S"))}}'
|
||||
)
|
||||
with freeze_time(now):
|
||||
result = template.Template(
|
||||
time_since_template,
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour"
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 09:00:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "2 hours"
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 03:00:00 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour"
|
||||
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 11:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 11:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result1 == result2
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 09:05:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour 55 minutes"
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour 54 minutes 33 seconds"
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z")'
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "2 hours"
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "1999-02-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 0"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "11 months 4 days 1 hour 54 minutes 33 seconds"
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "1999-02-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z")'
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "11 months"
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 11:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 11:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result1 == result2
|
||||
|
||||
result = template.Template(
|
||||
'{{time_since("string")}}',
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "string"
|
||||
|
||||
info = template.Template(time_since_template, hass).async_render_to_info()
|
||||
assert info.has_time is True
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
def test_time_until(mock_is_safe, hass: HomeAssistant) -> None:
|
||||
"""Test time_until method."""
|
||||
hass.config.set_time_zone("UTC")
|
||||
now = datetime.strptime("2000-01-01 10:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
time_until_template = (
|
||||
'{{time_until(strptime("2000-01-01 11:00:00", "%Y-%m-%d %H:%M:%S"))}}'
|
||||
)
|
||||
with freeze_time(now):
|
||||
result = template.Template(
|
||||
time_until_template,
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour"
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 13:00:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "2 hours"
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 05:00:00 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour"
|
||||
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 09:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 09:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result1 == result2
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 12:05:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour 5 minutes"
|
||||
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 hour 54 minutes 33 seconds"
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z")'
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "2 hours"
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2001-02-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 0"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 year 1 month 2 days 1 hour 54 minutes 33 seconds"
|
||||
result = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2001-02-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 4"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "1 year 1 month 2 days 2 hours"
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 09:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = template.Template(
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 09:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
hass,
|
||||
).async_render()
|
||||
assert result1 == result2
|
||||
|
||||
result = template.Template(
|
||||
'{{time_until("string")}}',
|
||||
hass,
|
||||
).async_render()
|
||||
assert result == "string"
|
||||
|
||||
info = template.Template(time_until_template, hass).async_render_to_info()
|
||||
assert info.has_time is True
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
|
|
|
@ -178,12 +178,18 @@ def test_get_age() -> None:
|
|||
"""Test get_age."""
|
||||
diff = dt_util.now() - timedelta(seconds=0)
|
||||
assert dt_util.get_age(diff) == "0 seconds"
|
||||
assert dt_util.get_age(diff, precision=2) == "0 seconds"
|
||||
|
||||
diff = dt_util.now() - timedelta(seconds=1)
|
||||
assert dt_util.get_age(diff) == "1 second"
|
||||
assert dt_util.get_age(diff, precision=2) == "1 second"
|
||||
|
||||
diff = dt_util.now() + timedelta(seconds=1)
|
||||
pytest.raises(ValueError, dt_util.get_age, diff)
|
||||
|
||||
diff = dt_util.now() - timedelta(seconds=30)
|
||||
assert dt_util.get_age(diff) == "30 seconds"
|
||||
diff = dt_util.now() + timedelta(seconds=30)
|
||||
|
||||
diff = dt_util.now() - timedelta(minutes=5)
|
||||
assert dt_util.get_age(diff) == "5 minutes"
|
||||
|
@ -196,20 +202,81 @@ def test_get_age() -> None:
|
|||
|
||||
diff = dt_util.now() - timedelta(minutes=320)
|
||||
assert dt_util.get_age(diff) == "5 hours"
|
||||
assert dt_util.get_age(diff, precision=2) == "5 hours 20 minutes"
|
||||
assert dt_util.get_age(diff, precision=3) == "5 hours 20 minutes"
|
||||
|
||||
diff = dt_util.now() - timedelta(minutes=1.6 * 60 * 24)
|
||||
assert dt_util.get_age(diff) == "2 days"
|
||||
assert dt_util.get_age(diff, precision=2) == "1 day 14 hours"
|
||||
assert dt_util.get_age(diff, precision=3) == "1 day 14 hours 24 minutes"
|
||||
diff = dt_util.now() + timedelta(minutes=1.6 * 60 * 24)
|
||||
pytest.raises(ValueError, dt_util.get_age, diff)
|
||||
|
||||
diff = dt_util.now() - timedelta(minutes=2 * 60 * 24)
|
||||
assert dt_util.get_age(diff) == "2 days"
|
||||
|
||||
diff = dt_util.now() - timedelta(minutes=32 * 60 * 24)
|
||||
assert dt_util.get_age(diff) == "1 month"
|
||||
assert dt_util.get_age(diff, precision=10) == "1 month 2 days"
|
||||
|
||||
diff = dt_util.now() - timedelta(minutes=32 * 60 * 24 + 1)
|
||||
assert dt_util.get_age(diff, precision=3) == "1 month 2 days 1 minute"
|
||||
|
||||
diff = dt_util.now() - timedelta(minutes=365 * 60 * 24)
|
||||
assert dt_util.get_age(diff) == "1 year"
|
||||
|
||||
|
||||
def test_time_remaining() -> None:
|
||||
"""Test get_age."""
|
||||
diff = dt_util.now() + timedelta(seconds=0)
|
||||
assert dt_util.get_time_remaining(diff) == "0 seconds"
|
||||
assert dt_util.get_time_remaining(diff) == "0 seconds"
|
||||
assert dt_util.get_time_remaining(diff, precision=2) == "0 seconds"
|
||||
|
||||
diff = dt_util.now() + timedelta(seconds=1)
|
||||
assert dt_util.get_time_remaining(diff) == "1 second"
|
||||
|
||||
diff = dt_util.now() - timedelta(seconds=1)
|
||||
pytest.raises(ValueError, dt_util.get_time_remaining, diff)
|
||||
|
||||
diff = dt_util.now() + timedelta(seconds=30)
|
||||
assert dt_util.get_time_remaining(diff) == "30 seconds"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=5)
|
||||
assert dt_util.get_time_remaining(diff) == "5 minutes"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=1)
|
||||
assert dt_util.get_time_remaining(diff) == "1 minute"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=300)
|
||||
assert dt_util.get_time_remaining(diff) == "5 hours"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=320)
|
||||
assert dt_util.get_time_remaining(diff) == "5 hours"
|
||||
assert dt_util.get_time_remaining(diff, precision=2) == "5 hours 20 minutes"
|
||||
assert dt_util.get_time_remaining(diff, precision=3) == "5 hours 20 minutes"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=1.6 * 60 * 24)
|
||||
assert dt_util.get_time_remaining(diff) == "2 days"
|
||||
assert dt_util.get_time_remaining(diff, precision=2) == "1 day 14 hours"
|
||||
assert dt_util.get_time_remaining(diff, precision=3) == "1 day 14 hours 24 minutes"
|
||||
diff = dt_util.now() - timedelta(minutes=1.6 * 60 * 24)
|
||||
pytest.raises(ValueError, dt_util.get_time_remaining, diff)
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=2 * 60 * 24)
|
||||
assert dt_util.get_time_remaining(diff) == "2 days"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=32 * 60 * 24)
|
||||
assert dt_util.get_time_remaining(diff) == "1 month"
|
||||
assert dt_util.get_time_remaining(diff, precision=10) == "1 month 2 days"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=32 * 60 * 24 + 1)
|
||||
assert dt_util.get_time_remaining(diff, precision=3) == "1 month 2 days 1 minute"
|
||||
|
||||
diff = dt_util.now() + timedelta(minutes=365 * 60 * 24)
|
||||
assert dt_util.get_time_remaining(diff) == "1 year"
|
||||
|
||||
|
||||
def test_parse_time_expression() -> None:
|
||||
"""Test parse_time_expression."""
|
||||
assert list(range(60)) == dt_util.parse_time_expression("*", 0, 59)
|
||||
|
|
Loading…
Reference in New Issue