mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 04:50:05 +00:00
Extract date/time template functions into an datetime Jinja2 extension (#157042)
This commit is contained in:
@@ -8,8 +8,6 @@ import json
|
||||
import logging
|
||||
import math
|
||||
import random
|
||||
from types import MappingProxyType
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun import freeze_time
|
||||
@@ -50,7 +48,6 @@ from homeassistant.helpers.template.render_info import (
|
||||
)
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.read_only_dict import ReadOnlyDict
|
||||
from homeassistant.util.unit_system import UnitSystem
|
||||
|
||||
from .helpers import assert_result_info, render, render_to_info
|
||||
@@ -436,25 +433,6 @@ def test_converting_datetime_to_iterable(hass: HomeAssistant) -> None:
|
||||
render(hass, "{{ set(value) }}", {"value": dt_})
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
([1, 2], False),
|
||||
({1, 2}, False),
|
||||
({"a": 1, "b": 2}, False),
|
||||
(ReadOnlyDict({"a": 1, "b": 2}), False),
|
||||
(MappingProxyType({"a": 1, "b": 2}), False),
|
||||
("abc", False),
|
||||
(b"abc", False),
|
||||
((1, 2), False),
|
||||
(datetime(2024, 1, 1, 0, 0, 0), True),
|
||||
],
|
||||
)
|
||||
def test_is_datetime(hass: HomeAssistant, value, expected) -> None:
|
||||
"""Test is datetime."""
|
||||
assert render(hass, "{{ value is datetime }}", {"value": value}) == expected
|
||||
|
||||
|
||||
def test_rounding_value(hass: HomeAssistant) -> None:
|
||||
"""Test rounding value."""
|
||||
hass.states.async_set("sensor.temperature", 12.78)
|
||||
@@ -594,202 +572,6 @@ def test_as_function_no_arguments(hass: HomeAssistant) -> None:
|
||||
assert render(hass, tpl) == "Hello"
|
||||
|
||||
|
||||
def test_strptime(hass: HomeAssistant) -> None:
|
||||
"""Test the parse timestamp method."""
|
||||
tests = [
|
||||
("2016-10-19 15:22:05.588122 UTC", "%Y-%m-%d %H:%M:%S.%f %Z", None),
|
||||
("2016-10-19 15:22:05.588122+0100", "%Y-%m-%d %H:%M:%S.%f%z", None),
|
||||
("2016-10-19 15:22:05.588122", "%Y-%m-%d %H:%M:%S.%f", None),
|
||||
("2016-10-19", "%Y-%m-%d", None),
|
||||
("2016", "%Y", None),
|
||||
("15:22:05", "%H:%M:%S", None),
|
||||
]
|
||||
|
||||
for inp, fmt, expected in tests:
|
||||
if expected is None:
|
||||
expected = str(datetime.strptime(inp, fmt))
|
||||
|
||||
temp = f"{{{{ strptime('{inp}', '{fmt}') }}}}"
|
||||
|
||||
assert render(hass, temp) == expected
|
||||
|
||||
# Test handling of invalid input
|
||||
invalid_tests = [
|
||||
("1469119144", "%Y"),
|
||||
("invalid", "%Y"),
|
||||
]
|
||||
|
||||
for inp, fmt in invalid_tests:
|
||||
temp = f"{{{{ strptime('{inp}', '{fmt}') }}}}"
|
||||
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, temp)
|
||||
|
||||
# Test handling of default return value
|
||||
assert render(hass, "{{ strptime('invalid', '%Y', 1) }}") == 1
|
||||
assert render(hass, "{{ strptime('invalid', '%Y', default=1) }}") == 1
|
||||
|
||||
|
||||
async def test_timestamp_custom(hass: HomeAssistant) -> None:
|
||||
"""Test the timestamps to custom filter."""
|
||||
await hass.config.async_set_time_zone("UTC")
|
||||
now = dt_util.utcnow()
|
||||
tests = [
|
||||
(1469119144, None, True, "2016-07-21 16:39:04"),
|
||||
(1469119144, "%Y", True, 2016),
|
||||
(1469119144, "invalid", True, "invalid"),
|
||||
(dt_util.as_timestamp(now), None, False, now.strftime("%Y-%m-%d %H:%M:%S")),
|
||||
]
|
||||
|
||||
for inp, fmt, local, out in tests:
|
||||
if fmt:
|
||||
fil = f"timestamp_custom('{fmt}')"
|
||||
elif fmt and local:
|
||||
fil = f"timestamp_custom('{fmt}', {local})"
|
||||
else:
|
||||
fil = "timestamp_custom"
|
||||
|
||||
assert render(hass, f"{{{{ {inp} | {fil} }}}}") == out
|
||||
|
||||
# Test handling of invalid input
|
||||
invalid_tests = [
|
||||
(None, None, None),
|
||||
]
|
||||
|
||||
for inp, fmt, local in invalid_tests:
|
||||
if fmt:
|
||||
fil = f"timestamp_custom('{fmt}')"
|
||||
elif fmt and local:
|
||||
fil = f"timestamp_custom('{fmt}', {local})"
|
||||
else:
|
||||
fil = "timestamp_custom"
|
||||
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, f"{{{{ {inp} | {fil} }}}}")
|
||||
|
||||
# Test handling of default return value
|
||||
assert render(hass, "{{ None | timestamp_custom('invalid', True, 1) }}") == 1
|
||||
assert render(hass, "{{ None | timestamp_custom(default=1) }}") == 1
|
||||
|
||||
|
||||
async def test_timestamp_local(hass: HomeAssistant) -> None:
|
||||
"""Test the timestamps to local filter."""
|
||||
await hass.config.async_set_time_zone("UTC")
|
||||
tests = [
|
||||
(1469119144, "2016-07-21T16:39:04+00:00"),
|
||||
]
|
||||
|
||||
for inp, out in tests:
|
||||
assert render(hass, f"{{{{ {inp} | timestamp_local }}}}") == out
|
||||
|
||||
# Test handling of invalid input
|
||||
invalid_tests = [
|
||||
None,
|
||||
]
|
||||
|
||||
for inp in invalid_tests:
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, f"{{{{ {inp} | timestamp_local }}}}")
|
||||
|
||||
# Test handling of default return value
|
||||
assert render(hass, "{{ None | timestamp_local(1) }}") == 1
|
||||
assert render(hass, "{{ None | timestamp_local(default=1) }}") == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input",
|
||||
[
|
||||
"2021-06-03 13:00:00.000000+00:00",
|
||||
"1986-07-09T12:00:00Z",
|
||||
"2016-10-19 15:22:05.588122+0100",
|
||||
"2016-10-19",
|
||||
"2021-01-01 00:00:01",
|
||||
"invalid",
|
||||
],
|
||||
)
|
||||
def test_as_datetime(hass: HomeAssistant, input) -> None:
|
||||
"""Test converting a timestamp string to a date object."""
|
||||
expected = dt_util.parse_datetime(input)
|
||||
if expected is not None:
|
||||
expected = str(expected)
|
||||
assert render(hass, f"{{{{ as_datetime('{input}') }}}}") == expected
|
||||
assert render(hass, f"{{{{ '{input}' | as_datetime }}}}") == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("input", "output"),
|
||||
[
|
||||
(1469119144, "2016-07-21 16:39:04+00:00"),
|
||||
(1469119144.0, "2016-07-21 16:39:04+00:00"),
|
||||
(-1, "1969-12-31 23:59:59+00:00"),
|
||||
],
|
||||
)
|
||||
def test_as_datetime_from_timestamp(
|
||||
hass: HomeAssistant,
|
||||
input: float,
|
||||
output: str,
|
||||
) -> None:
|
||||
"""Test converting a UNIX timestamp to a date object."""
|
||||
assert render(hass, f"{{{{ as_datetime({input}) }}}}") == output
|
||||
assert render(hass, f"{{{{ {input} | as_datetime }}}}") == output
|
||||
assert render(hass, f"{{{{ as_datetime('{input}') }}}}") == output
|
||||
assert render(hass, f"{{{{ '{input}' | as_datetime }}}}") == output
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("input", "output"),
|
||||
[
|
||||
(
|
||||
"{% set dt = as_datetime('2024-01-01 16:00:00-08:00') %}",
|
||||
"2024-01-01 16:00:00-08:00",
|
||||
),
|
||||
(
|
||||
"{% set dt = as_datetime('2024-01-29').date() %}",
|
||||
"2024-01-29 00:00:00",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_as_datetime_from_datetime(
|
||||
hass: HomeAssistant, input: str, output: str
|
||||
) -> None:
|
||||
"""Test using datetime.datetime or datetime.date objects as input."""
|
||||
|
||||
assert render(hass, f"{input}{{{{ dt | as_datetime }}}}") == output
|
||||
|
||||
assert render(hass, f"{input}{{{{ as_datetime(dt) }}}}") == output
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("input", "default", "output"),
|
||||
[
|
||||
(1469119144, 123, "2016-07-21 16:39:04+00:00"),
|
||||
('"invalid"', ["default output"], ["default output"]),
|
||||
(["a", "list"], 0, 0),
|
||||
({"a": "dict"}, None, None),
|
||||
],
|
||||
)
|
||||
def test_as_datetime_default(
|
||||
hass: HomeAssistant, input: Any, default: Any, output: str
|
||||
) -> None:
|
||||
"""Test invalid input and return default value."""
|
||||
|
||||
assert render(hass, f"{{{{ as_datetime({input}, default={default}) }}}}") == output
|
||||
assert render(hass, f"{{{{ {input} | as_datetime({default}) }}}}") == output
|
||||
|
||||
|
||||
def test_as_local(hass: HomeAssistant) -> None:
|
||||
"""Test converting time to local."""
|
||||
|
||||
hass.states.async_set("test.object", "available")
|
||||
last_updated = hass.states.get("test.object").last_updated
|
||||
assert render(hass, "{{ as_local(states.test.object.last_updated) }}") == str(
|
||||
dt_util.as_local(last_updated)
|
||||
)
|
||||
assert render(hass, "{{ states.test.object.last_updated | as_local }}") == str(
|
||||
dt_util.as_local(last_updated)
|
||||
)
|
||||
|
||||
|
||||
def test_to_json(hass: HomeAssistant) -> None:
|
||||
"""Test the object to JSON string filter."""
|
||||
|
||||
@@ -892,53 +674,6 @@ def test_from_hex(hass: HomeAssistant) -> None:
|
||||
assert render(hass, "{{ '0F010003' | from_hex }}") == b"\x0f\x01\x00\x03"
|
||||
|
||||
|
||||
def test_timestamp_utc(hass: HomeAssistant) -> None:
|
||||
"""Test the timestamps to local filter."""
|
||||
now = dt_util.utcnow()
|
||||
tests = [
|
||||
(1469119144, "2016-07-21T16:39:04+00:00"),
|
||||
(dt_util.as_timestamp(now), now.isoformat()),
|
||||
]
|
||||
|
||||
for inp, out in tests:
|
||||
assert render(hass, f"{{{{ {inp} | timestamp_utc }}}}") == out
|
||||
|
||||
# Test handling of invalid input
|
||||
invalid_tests = [
|
||||
None,
|
||||
]
|
||||
|
||||
for inp in invalid_tests:
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, f"{{{{ {inp} | timestamp_utc }}}}")
|
||||
|
||||
# Test handling of default return value
|
||||
assert render(hass, "{{ None | timestamp_utc(1) }}") == 1
|
||||
assert render(hass, "{{ None | timestamp_utc(default=1) }}") == 1
|
||||
|
||||
|
||||
def test_as_timestamp(hass: HomeAssistant) -> None:
|
||||
"""Test the as_timestamp function."""
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, '{{ as_timestamp("invalid") }}')
|
||||
|
||||
hass.states.async_set("test.object", None)
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, "{{ as_timestamp(states.test.object) }}")
|
||||
|
||||
tpl = (
|
||||
'{{ as_timestamp(strptime("2024-02-03T09:10:24+0000", '
|
||||
'"%Y-%m-%dT%H:%M:%S%z")) }}'
|
||||
)
|
||||
assert render(hass, tpl) == 1706951424.0
|
||||
|
||||
# Test handling of default return value
|
||||
assert render(hass, "{{ 'invalid' | as_timestamp(1) }}") == 1
|
||||
assert render(hass, "{{ 'invalid' | as_timestamp(default=1) }}") == 1
|
||||
assert render(hass, "{{ as_timestamp('invalid', 1) }}") == 1
|
||||
assert render(hass, "{{ as_timestamp('invalid', default=1) }}") == 1
|
||||
|
||||
|
||||
@patch.object(random, "choice")
|
||||
def test_random_every_time(test_choice, hass: HomeAssistant) -> None:
|
||||
"""Ensure the random filter runs every time, not just once."""
|
||||
@@ -1334,503 +1069,6 @@ def test_has_value(hass: HomeAssistant) -> None:
|
||||
assert result == "yes"
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
def test_now(mock_is_safe, hass: HomeAssistant) -> None:
|
||||
"""Test now method."""
|
||||
now = dt_util.now()
|
||||
with freeze_time(now):
|
||||
info = render_to_info(hass, "{{ now().isoformat() }}")
|
||||
assert now.isoformat() == info.result()
|
||||
|
||||
assert info.has_time is True
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
def test_utcnow(mock_is_safe, hass: HomeAssistant) -> None:
|
||||
"""Test now method."""
|
||||
utcnow = dt_util.utcnow()
|
||||
with freeze_time(utcnow):
|
||||
info = render_to_info(hass, "{{ utcnow().isoformat() }}")
|
||||
assert utcnow.isoformat() == info.result()
|
||||
|
||||
assert info.has_time is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("now", "expected", "expected_midnight", "timezone_str"),
|
||||
[
|
||||
# Host clock in UTC
|
||||
(
|
||||
"2021-11-24 03:00:00+00:00",
|
||||
"2021-11-23T10:00:00-08:00",
|
||||
"2021-11-23T00:00:00-08:00",
|
||||
"America/Los_Angeles",
|
||||
),
|
||||
# Host clock in local time
|
||||
(
|
||||
"2021-11-23 19:00:00-08:00",
|
||||
"2021-11-23T10:00:00-08:00",
|
||||
"2021-11-23T00:00:00-08:00",
|
||||
"America/Los_Angeles",
|
||||
),
|
||||
],
|
||||
)
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
async def test_today_at(
|
||||
mock_is_safe, hass: HomeAssistant, now, expected, expected_midnight, timezone_str
|
||||
) -> None:
|
||||
"""Test today_at method."""
|
||||
freezer = freeze_time(now)
|
||||
freezer.start()
|
||||
|
||||
await hass.config.async_set_time_zone(timezone_str)
|
||||
|
||||
result = render(hass, "{{ today_at('10:00').isoformat() }}")
|
||||
assert result == expected
|
||||
|
||||
result = render(hass, "{{ today_at('10:00:00').isoformat() }}")
|
||||
assert result == expected
|
||||
|
||||
result = render(hass, "{{ ('10:00:00' | today_at).isoformat() }}")
|
||||
assert result == expected
|
||||
|
||||
result = render(hass, "{{ today_at().isoformat() }}")
|
||||
assert result == expected_midnight
|
||||
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, "{{ today_at('bad') }}")
|
||||
|
||||
info = render_to_info(hass, "{{ today_at('10:00').isoformat() }}")
|
||||
assert info.has_time is True
|
||||
|
||||
freezer.stop()
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
async def test_relative_time(mock_is_safe, hass: HomeAssistant) -> None:
|
||||
"""Test relative_time method."""
|
||||
await hass.config.async_set_time_zone("UTC")
|
||||
now = datetime.strptime("2000-01-01 10:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
relative_time_template = (
|
||||
'{{relative_time(strptime("2000-01-01 09:00:00", "%Y-%m-%d %H:%M:%S"))}}'
|
||||
)
|
||||
with freeze_time(now):
|
||||
result = render(hass, relative_time_template)
|
||||
assert result == "1 hour"
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" relative_time("
|
||||
" strptime("
|
||||
' "2000-01-01 09:00:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "2 hours"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" relative_time("
|
||||
" strptime("
|
||||
' "2000-01-01 03:00:00 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 hour"
|
||||
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 11:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" relative_time("
|
||||
" strptime("
|
||||
' "2000-01-01 11:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result1 == result2
|
||||
|
||||
result = render(hass, '{{relative_time("string")}}')
|
||||
assert result == "string"
|
||||
|
||||
# Test behavior when current time is same as the input time
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" relative_time("
|
||||
" strptime("
|
||||
' "2000-01-01 10:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "0 seconds"
|
||||
|
||||
# Test behavior when the input time is in the future
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" relative_time("
|
||||
" strptime("
|
||||
' "2000-01-01 11:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "2000-01-01 11:00:00+00:00"
|
||||
|
||||
info = render_to_info(hass, relative_time_template)
|
||||
assert info.has_time is True
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
async def test_time_since(mock_is_safe, hass: HomeAssistant) -> None:
|
||||
"""Test time_since method."""
|
||||
await hass.config.async_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 = render(hass, time_since_template)
|
||||
assert result == "1 hour"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 09:00:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "2 hours"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 03:00:00 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 hour"
|
||||
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 11:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 11:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result1 == result2
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 09:05:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 hour 55 minutes"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 hour 54 minutes 33 seconds"
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z")'
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "2 hours"
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "1999-02-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 0"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "11 months 4 days 1 hour 54 minutes 33 seconds"
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "1999-02-01 02:05:27 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z")'
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "11 months"
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 11:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_since("
|
||||
" strptime("
|
||||
' "2000-01-01 11:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result1 == result2
|
||||
|
||||
result = render(hass, '{{time_since("string")}}')
|
||||
assert result == "string"
|
||||
|
||||
info = render_to_info(hass, time_since_template)
|
||||
assert info.has_time is True
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
)
|
||||
async def test_time_until(mock_is_safe, hass: HomeAssistant) -> None:
|
||||
"""Test time_until method."""
|
||||
await hass.config.async_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 = render(hass, time_until_template)
|
||||
assert result == "1 hour"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 13:00:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "2 hours"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 05:00:00 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"'
|
||||
" )"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 hour"
|
||||
|
||||
result1 = str(
|
||||
template.strptime("2000-01-01 09:00:00 +00:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
)
|
||||
result2 = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 09:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result1 == result2
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 12:05:00 +01:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=2"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 hour 5 minutes"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 hour 54 minutes 33 seconds"
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z")'
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "2 hours"
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2001-02-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 0"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result == "1 year 1 month 2 days 1 hour 54 minutes 33 seconds"
|
||||
result = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2001-02-01 05:54:33 -06:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision = 4"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
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 = render(
|
||||
hass,
|
||||
(
|
||||
"{{"
|
||||
" time_until("
|
||||
" strptime("
|
||||
' "2000-01-01 09:00:00 +00:00",'
|
||||
' "%Y-%m-%d %H:%M:%S %z"),'
|
||||
" precision=3"
|
||||
" )"
|
||||
"}}"
|
||||
),
|
||||
)
|
||||
assert result1 == result2
|
||||
|
||||
result = render(hass, '{{time_until("string")}}')
|
||||
assert result == "string"
|
||||
|
||||
info = render_to_info(hass, time_until_template)
|
||||
assert info.has_time is True
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.helpers.template.TemplateEnvironment.is_safe_callable",
|
||||
return_value=True,
|
||||
@@ -3294,19 +2532,6 @@ def test_render_complex_handling_non_template_values(hass: HomeAssistant) -> Non
|
||||
) == {True: 1, False: 2}
|
||||
|
||||
|
||||
def test_as_timedelta(hass: HomeAssistant) -> None:
|
||||
"""Test the as_timedelta function/filter."""
|
||||
|
||||
result = render(hass, "{{ as_timedelta('PT10M') }}")
|
||||
assert result == "0:10:00"
|
||||
|
||||
result = render(hass, "{{ 'PT10M' | as_timedelta }}")
|
||||
assert result == "0:10:00"
|
||||
|
||||
result = render(hass, "{{ 'T10M' | as_timedelta }}")
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_iif(hass: HomeAssistant) -> None:
|
||||
"""Test the immediate if function/filter."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user