The EPIC Time Conversion and Manipulation Thread!

Hi,
I am not very familiar with Jinja templates, mostly get syntax from all awesome guys posting in this community. however I couldn’t find what I am searching for.

I need to show the last changed time as follows:

  1. If less than 24hrs to show ‘15 hours ago’ or ‘Today at 16:25’
  2. If more between 24-48hrs to show ‘Yesterday at 16:25’
  3. If more than 48hrs to show ‘Tuesday at 16:25’

I really appreciate any advice
Thanks in advance
RS

Rami, this may be taking your brief too literally, but try the following:

{% set last_changed = states.sensor.XXXX.last_changed %}
{%- set delta = (now() - last_changed | as_local).total_seconds() / 3600 %}
{%- set weekday = last_changed.strftime("%A") %}
{%- set time = last_changed.strftime("%H:%M") %}
{%- if delta < 24 %} {{ [ "Today at "~time, delta|round(0)~" hours ago" ] | random }}
{%- elif 24 < delta < 48 %} Yesterday at {{ time }}
{%- else %} {{ weekday~" at "~time}} {%- endif %}

Thank you very much Drew, This is perfect… I tried it. below my observation:

  • Current time 18:52 (GMT+3)
  • At first it displays ‘Today at 14:52’ then about 30 seconds later it displays ‘1 hours ago’

I think it has something to do with when the code/entity refreshes!
I will give it more time and see how it goes.

Much appreciate it though on the logic/code a lot to be learnt from for other stuff as well :smiley:

That’s what I meant by “taking your brief too literally”… :grinning_face_with_smiling_eyes:

You wrote:

The following clause causes it to randomly shuffle between the two formats every time the template refreshes:

{{ [ "Today at "~time, delta|round(0)~" hours ago" ] | random }}

You can switch it out for either {{ "Today at "~time }} or {{ delta|round(0)~" hours ago" }}

this should get you what you want.
You will need to put your data in for the ‘time’ variable.

{% set time = states('input_datetime.both_date_and_time') %}
{% if as_timestamp(time) | timestamp_custom('%Y-%m-%d') == as_timestamp(now()) | timestamp_custom('%Y-%m-%d') %}
  Today at {{ as_timestamp(time) | timestamp_custom('%H:%M') }} ({{ ((as_timestamp(now()) - as_timestamp(time)) / 3600 ) | int}} hours ago)
{% elif as_timestamp(time) | timestamp_custom('%Y-%m-%d') == (as_timestamp(now().replace(hour=0, minute=0, second=0)) - 3600) | timestamp_custom('%Y-%m-%d') %}
  Yesterday at {{ as_timestamp(time) | timestamp_custom('%H:%M') }}
{% elif as_timestamp(time) < as_timestamp(now().replace(hour=0, minute=0, second=0)) - (3600 * 1) and as_timestamp(now()) - 84600 * 7 < as_timestamp(time) %}
  {{ as_timestamp(time) | timestamp_custom('%A at %H:%M') }}
{% else %}
  More than a week ago
{% endif %}

there may be a more elegant way to do it but this works in my testing.

see what you think.

2 Likes

LOL, indeed that was ‘literally’ but the ‘Today…’ format displays the GMT time not the local time

Ok, I understand now… Move the as_local filter so that it is applied to the variable last_changed the first two lines should be:

{% set last_changed = states.sensor.XXXX.last_changed | as_local %}
{%- set delta = (now() - last_changed).total_seconds() / 3600 %}
1 Like

Thank you @finity & @Didgeridrew for your help… Both solutions work flawlessly…

FYI, you guys can utilize today_at to make the template really easy to read without any fancy math.

{% set last_changed = states.binary_sensor.main_door.last_changed %}
{% if last_changed < today_at() - timedelta(days=1) %}
  {{ last_changed.strftime('%A at %H:%M') }}
{% elif last_changed < today_at() %}
  {{ last_changed.strftime('Yesterday at %H:%M') }}
{% else %}
  {{ last_changed.strftime('Today at %H:%M') }}
{% endif %}
2 Likes

Wow… and I just finished modifying @finity 's to be:

{% set time = states('input_datetime.test_datetime_3') %}
{% set hrs = ((as_timestamp(now()) - as_timestamp(time)) / 3600 ) | int %}
{% set mins = ((as_timestamp(now()) - as_timestamp(time))  ) | timestamp_custom('%M') |int %}
{% if as_timestamp(time) | timestamp_custom('%Y-%m-%d') == 
     as_timestamp(now()) | timestamp_custom('%Y-%m-%d') %}
  Today at {{ as_timestamp(time) | timestamp_custom('%H:%M') }} 

    {% if hrs == 0 %}
        ({{ mins | int}} minute(s) ago)
    {% elif hrs == 1 %}
        ({{ hrs | int}} hour ago)
    {% else %}
        ({{ hrs | int}} hours ago)
    {% endif %}


{% elif as_timestamp(time) | timestamp_custom('%Y-%m-%d') == 
       (as_timestamp(now().replace(hour=0, minute=0, second=0)) - 3600) 
         | timestamp_custom('%Y-%m-%d') %}
  Yesterday at {{ as_timestamp(time) | timestamp_custom('%H:%M') }}

{% elif as_timestamp(time) < as_timestamp(now().replace(hour=0, minute=0, second=0)) - (3600 * 1)
    and as_timestamp(now()) - 84600 * 7 < as_timestamp(time) %}
  {{ as_timestamp(time) | timestamp_custom('%A at %H:%M') }}
{% else %}
  More than a week ago
{% endif %}

adding the following lines:

{% set hrs = ((as_timestamp(now()) - as_timestamp(time)) / 3600 ) | int %}
{% set mins = ((as_timestamp(now()) - as_timestamp(time))  ) | timestamp_custom('%M') |int %}

    {% if hrs == 0 %}
        ({{ mins | int}} minute(s) ago)
    {% elif hrs == 1 %}
        ({{ hrs | int}} hour ago)
    {% else %}
        ({{ hrs | int}} hours ago)
    {% endif %}

I will try your code @petro right away :slight_smile:

Thanks Petro, this is great. Which Jinja reference would you recommend for learning and/or referencing such functions i.e. today_at()

The two links in the developer tools are always a good starting point.

https://jinja.palletsprojects.com/en/3.1.x/templates/

I checked the ones at the HA Templates page… will re-check again though.
Much appreciate your advice. Thanks
Rami

I think this is what you’re looking for:

{% set last_changed = states.binary_sensor.main_door.last_changed %}
{% if last_changed < today_at() - timedelta(days=6) %}
  More than a week ago
{% elif last_changed < today_at() - timedelta(days=1) %}
  {{ last_changed.strftime('%A at %H:%M') }}
{% elif last_changed < today_at() %}
  {{ last_changed.strftime('Yesterday at %H:%M') }}
{% else %}
  {{ last_changed.strftime('Today at %H:%M') }} ({{ relative_time(last_changed) }} ago)
{% endif %}

Hi Petro,
May I trouble you with one last thing… I’ve trying to get it to work with no luck after several trial, I guess I need to do some intensive reading about States and Attributes.

What if instead of last changed attribute (which changes after each restart, I want to use an input_datetime helper to make it persistant over re-start… how can I insert this in your code.
It keeps giving me error either no attribute or can’t use < between strings and datetime.

Sorry for the inconvenience

States are always strings, so you will need to convert the state of the Input datetime helper to a offset-aware datetime object by replacing the first line of the template:

{% set last_changed = states('input_datetime.XXXX') | as_datetime | as_local %}
2 Likes

Thank you big time. I can now see what I did wrong… I had the as_local filter before the | as_datetime… makes perfect sense now and works flawlessly.
Much appreciated
RS

I’m trying to format elapsed time into something like ##d ##h ##m or ##:##:##

Here’s what I’ve got so far:

{{ (as_timestamp(now()) - as_timestamp(states('sensor.uptime'))) | timestamp_custom('%dd %hh %mm') }}

Which gives me 31d Dech 12m off an epoch of ~9000 which should be more like 0d 2h 30m. What’s the proper syntax to depict elapsed time from 0 instead of the start of the epoch?

This will be formatted ddd hh:mm:ss

{{ now() - states('sensor.uptime') | as_datetime | as_local }}

If you want a custom format, you have to do the math.

Here’s the math laid out…

With all that Python/Jinja2 is capable of…is that really the most compact way to convert/format seconds into another timeframe?