The EPIC Time Conversion and Manipulation Thread!

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?

Yes, there is no way to format a timedelta thatā€™s naturally built into python or jinja.

Just so you know, most languages donā€™t have this. I was surprised when I found out that C# does have it, but the implementation is terrible in comparison to the strftime equivalent.

Wow! Thatā€™s seems like something that shouldā€™ve been handled already. OK, thank you.

I use this on my ā€œRouterā€ uptime sensor , sure i didnā€™t bother about showing D/H/M, as i have a ā€œpersonal perspectiveā€ of the word ā€œUserFriendlyā€ :slight_smile: ā€¦ itā€™s my system, only i need to understand :grin:

        - type: custom:template-entity-row
          name: Uptime
          active: true
          state: >
            {% set s = states('sensor.rt_ax68u_d680_uptime')| int(3) %}{{
            '%02d:%02d:%02d' % ((s/86400)|int(3),((s%86400) /
            3600)|int(3),(s%3600) / 60) }}

Thanks. It doesnā€™t work for me. I get 00:00:00 using the whole thing. What type and format does your sensor output and what does the output of that template look like?

{% set s = states('sensor.uptime')| int(3) %}
{{'%02d:%02d:%02d' % ((s/86400)|int(3),((s%86400) / 3600)|int(3),(s%3600) / 60) }}
{{ s }}
{{ states('sensor.uptime') }}

gives me

image

if htatā€™s what you want, just use what I linked, no point in re inventing the wheel

          {%- set up_time = as_timestamp(now())-as_timestamp(states('sensor.uptime')) %}

          {%- macro phrase(name, divisor, mod=None) %}
            {%- set value = ((up_time // divisor) % (mod if mod else divisor)) | int %}
            {{- '{} {}'.format(value, name) if value | int > 0 else '' }}
          {%- endmacro %}
          
          {%- set values = [ 
                     phrase('w', 60*60*24*7), 
                     phrase('d', 60*60*24, 7),
                     phrase('h', 60*60, 24),
                     phrase('m', 60), 
                 ] | select('!=','') | list %}
                        
          {{ values | join(', ') }}

Thanks, and I probably will. I just need another day to process why this takes 12 LOC. :wink:

I made the template easily expandable for people who donā€™t understand code. They can copy the template and remove lines that they donā€™t want. This could be much shorter and custom, but why bother when this works for all needs/wants

2 Likes

After comparing/contrasting the three example ways to represent the same time at the beginning of this comment, why does:

{{ now() }}

give me this?

2022-05-20 12:37:00.029958-04:00

Which is my local time EDT (UTC-4). So, itā€™s providing me with a local time and the UTC offset which is not a combination that comment suggests is correct (either the time should be 16:37 or the offset should be dropped).

Because I made a mistake in my explanation?

But I think you are right. I should have made that example read:

time (my time zone in UTC representation): 2018-12-14T14:57:27-05:00

Thanks for pointing it out.