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>
This commit is contained in:
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 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.""" """Filter and to convert a time string or UNIX timestamp to datetime object."""
try: try:
# Check for a valid UNIX timestamp string, int or float # Check for a valid UNIX timestamp string, int or float
timestamp = float(value) timestamp = float(value)
return dt_util.utc_from_timestamp(timestamp) return dt_util.utc_from_timestamp(timestamp)
except ValueError: except (ValueError, TypeError):
return dt_util.parse_datetime(value) # 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: 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) expected = dt_util.parse_datetime(input)
if expected is not None: if expected is not None:
expected = str(expected) expected = str(expected)
assert ( assert (
template.Template(f"{{{{ as_datetime('{input}') }}}}", hass).async_render() template.Template(f"{{{{ as_datetime('{input}') }}}}", hass).async_render()
== expected == expected
@ -1162,34 +1161,64 @@ def test_as_datetime(hass: HomeAssistant, input) -> None:
) )
def test_as_datetime_from_timestamp(hass: HomeAssistant) -> None: @pytest.mark.parametrize(
"""Test converting a UNIX timestamp to a date object.""" ("input", "output"),
tests = [ [
(1469119144, "2016-07-21 16:39:04+00:00"), (1469119144, "2016-07-21 16:39:04+00:00"),
(1469119144.0, "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"), (-1, "1969-12-31 23:59:59+00:00"),
] ],
for input, output in tests: )
# expected = dt_util.parse_datetime(input) def test_as_datetime_from_timestamp(
if output is not None: hass: HomeAssistant,
output = str(output) 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() @pytest.mark.parametrize(
== output ("input", "default", "output"),
) [
assert ( (1469119144, 123, "2016-07-21 16:39:04+00:00"),
template.Template(f"{{{{ {input} | as_datetime }}}}", hass).async_render() ('"invalid"', ["default output"], ["default output"]),
== output (["a", "list"], 0, 0),
) ({"a": "dict"}, None, None),
assert ( ],
template.Template(f"{{{{ as_datetime('{input}') }}}}", hass).async_render() )
== output def test_as_datetime_default(
) hass: HomeAssistant, input: Any, default: Any, output: str
assert ( ) -> None:
template.Template(f"{{{{ '{input}' | as_datetime }}}}", hass).async_render() """Test invalid input and return default value."""
== output
) 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: def test_as_local(hass: HomeAssistant) -> None: