I’ll just drop this here, as I have spent hours debugging time-issues - especially when it comes to comparing times from different sources.
So here is a summary I have learned.
This is what (some of) the different options you have using jinja2 for time-conversions will return.
Every option here returns the number of seconds since Unix Epoch, but with or without taking local timezone into consideration, and also some returns a float, and some returns a string so make sure you compare apples to apples. If you need to do any arithmetic, you also need to convert the strings to int or float before adding or subtracting seconds.
now:
UTC float {{ now().timestamp() }}
Local string {{ now().timestamp() | timestamp_custom("%s") }}
Local string {{ now().strftime('%s') }}
Local string {{ now().astimezone().strftime('%s') }}
UTC float {{ now().astimezone().timestamp() }}
last_changed
UTC float {{ states.binary_sensor.mysensor.last_changed.timestamp() }}
Local string {{ states.binary_sensor.mysensor.last_changed.timestamp() | timestamp_custom("%s") }}
Local string {{ states.binary_sensor.mysensor.last_changed.strftime("%s") }}
UTC string {{ states.binary_sensor.mysensor.last_changed.strftime("%s") | timestamp_custom("%s") }}
Local string {{ states.binary_sensor.mysensor.last_changed.strftime("%s") | int | timestamp_custom("%s") }}
Local string {{ states.binary_sensor.mysensor.last_changed.strftime("%s") | float | timestamp_custom("%s") }}
Local string {{ states.binary_sensor.mysensor.last_changed.astimezone().strftime("%s") }}
UTC float {{ states.binary_sensor.mysensor.last_changed.astimezone().timestamp() }}
input_datetime
UTC float {{ state_attr('input_datetime.mydatetime', 'timestamp') }}
Local string {{ state_attr('input_datetime.mydatetime', 'timestamp') | timestamp_custom("%s") }}
And just something to be aware of. If you have an input_datetime without date (eg. has_date: false) that you use to manipulate other time-entries (like adding X minutes to some timestamp in a script or automation)
{{ states('input_datetime.onlytime') }}
- Returns a string (eg. “00:10:00”)
{{ state_attr('input_datetime.onlytime', 'timestamp') }}
- Returns a float - the number of seconds that you would expect (e.g. “600” in this case)
But if you do some manipulation of this. Something like
{{ state_attr('input_datetime.onlytime', 'timestamp') | timestamp_custom("%s") }}
- You will get back a string, where the local timeset-offset is added (or subtracted) to/from the number of seconds. So if you are in UTC+1, you will get back 600 + 3600 = 4200 (as a string)
You can easily see what happens if you do a
{{ state_attr('input_datetime.onlytime', 'timestamp') | timestamp_custom("%Y-%m-%d %H:%M:%S") }}
- Which returns “1970-01-01 01:10:00”
So this leads us to the following
Check if last_changed is more than ten minutes ago:
You can either compare 2 UTC floats:
{% if states.binary_sensor.mysensor.last_changed.timestamp() < (now().timestamp() - state_attr('input_datetime.onlytime', 'timestamp')) %}
Or compare 2 local strings:
{% if states.binary_sensor.mysensor.last_changed.strftime("%s") | int < (now().strftime('%s') | int - state_attr('input_datetime.onlytime', 'timestamp') | int) %}
But don’t mix them up…