Add default parameter to as_datetime template function/filter (#107229)

* improve as_datetime

* Improve `as_datetime` Jinja filter/function

* review

* resolve more review items

* change test for datetime input

* Update docstring

* update docstrings for tests

* remove whitespace

* only_default

* Update do string and comment

* improve comment

* Adjust comment

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
pull/109066/head
Martijn van der Pol 2024-01-29 18:05:44 +01:00 committed by GitHub
parent be8af7bea3
commit b386960661
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 28 deletions

View File

@ -1836,14 +1836,24 @@ def forgiving_as_timestamp(value, default=_SENTINEL):
return default
def as_datetime(value):
def as_datetime(value: Any, default: Any = _SENTINEL) -> Any:
"""Filter and to convert a time string or UNIX timestamp to datetime object."""
try:
# Check for a valid UNIX timestamp string, int or float
timestamp = float(value)
return dt_util.utc_from_timestamp(timestamp)
except ValueError:
return dt_util.parse_datetime(value)
except (ValueError, TypeError):
# Try to parse datetime string to datetime object
try:
return dt_util.parse_datetime(value, raise_on_error=True)
except (ValueError, TypeError):
if default is _SENTINEL:
# Return None on string input
# to ensure backwards compatibility with HA Core 2024.1 and before.
if isinstance(value, str):
return None
raise_no_default("as_datetime", value)
return default
def as_timedelta(value: str) -> timedelta | None:

View File

@ -1151,7 +1151,6 @@ def test_as_datetime(hass: HomeAssistant, input) -> None:
expected = dt_util.parse_datetime(input)
if expected is not None:
expected = str(expected)
assert (
template.Template(f"{{{{ as_datetime('{input}') }}}}", hass).async_render()
== expected
@ -1162,34 +1161,64 @@ def test_as_datetime(hass: HomeAssistant, input) -> None:
)
def test_as_datetime_from_timestamp(hass: HomeAssistant) -> None:
"""Test converting a UNIX timestamp to a date object."""
tests = [
@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"),
]
for input, output in tests:
# expected = dt_util.parse_datetime(input)
if output is not None:
output = str(output)
],
)
def test_as_datetime_from_timestamp(
hass: HomeAssistant,
input: int | float,
output: str,
) -> None:
"""Test converting a UNIX timestamp to a date object."""
assert (
template.Template(f"{{{{ as_datetime({input}) }}}}", hass).async_render()
== output
)
assert (
template.Template(f"{{{{ {input} | as_datetime }}}}", hass).async_render()
== output
)
assert (
template.Template(f"{{{{ as_datetime('{input}') }}}}", hass).async_render()
== output
)
assert (
template.Template(f"{{{{ '{input}' | as_datetime }}}}", hass).async_render()
== output
)
assert (
template.Template(f"{{{{ as_datetime({input}) }}}}", hass).async_render()
== output
)
assert (
template.Template(f"{{{{ {input} | as_datetime }}}}", hass).async_render()
== output
)
assert (
template.Template(f"{{{{ as_datetime('{input}') }}}}", hass).async_render()
== output
)
assert (
template.Template(f"{{{{ '{input}' | as_datetime }}}}", hass).async_render()
== 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 (
template.Template(
f"{{{{ as_datetime({input}, default={default}) }}}}", hass
).async_render()
== output
)
assert (
template.Template(
f"{{{{ {input} | as_datetime({default}) }}}}", hass
).async_render()
== output
)
def test_as_local(hass: HomeAssistant) -> None: