The EPIC Time Conversion and Manipulation Thread!

Does anyone here knows how to properly use relative_time? I can not find suitable documentation/examples for me, so I created a new topic for help with obtaining the relative_time from an input_datetime sensor.

I may be wrong but I didnā€™t find any mention of the fromtimestamp method in this thread.

FWIW, it provides a quick way to convert a timestamp to a datetime object such as the timestamp attribute of an input_datetime. Obviously you can use strptime to convert an input_datetimeā€™s state to a datetime object. However, hereā€™s another way to compute it.

First we get the input_datetimeā€™s timestamp:

{% set t = state_attr('input_datetime.test', 'timestamp') %}

We can convert it to a datetime object like this:

{% set dt = now().fromtimestamp(t) %}

The resulting datetime object is offset-naive which means it has no understanding of timezone. If we intend to use it in a calculation with an offset-aware datetime object, we will have to change it from ā€œnaiveā€ to ā€œawareā€.

We can do that by setting its timezone to the same as the result of the now() function (which is an offset-aware datetime object and is your local time).

{% set dt = (now().fromtimestamp(t)).replace(tzinfo=now().tzinfo) %}

Now that we have converted the timestamp into a datetime object, it can be used in date calculations as seen in the last three lines of this screenshot:

Here it is, all in one statement:

{% set dt = (now().fromtimestamp(state_attr('input_datetime.test', 'timestamp'))).replace(tzinfo=now().tzinfo) %}
6 Likes

At some point I just want to add a ton of new methods that do all these things in 1 line of code. Iā€™m busy until mid January though.

1 Like

for the wish list then, since you offer that 1 line of code in mid January :wink:

add a time_zone aware last_seen on the device_trackers, which is still somewhat of a mystery here and there.
check eg the life_360 last_seen, and compare that to the CC Composite last_seen, which allows to do https://github.com/pnbruckner/ha-composite-tracker#time-zone-examples

            {% set n = now() %}
            {{ (n.astimezone(state.attributes.last_seen.tzinfo).utcoffset() -
                n.utcoffset()).total_seconds()/3600 }}

I hope I managed to replace that for my person entities doing this:

          {% set state = states.person.marijn %}
          {{(state.last_updated.astimezone().utcoffset() -
            now().utcoffset()).total_seconds()/3600}}

since it has no last_seen, and last_updated comes closestā€¦? (no way to test though, since I am here now, and not thereā€¦)

Hey,

I have been trying for the last two hours to convert a timestamp taken from an entityā€™s attribute to local time, but everything I tried hasnā€™t worked so far. I am growing desperate ā€¦ maybe someone sees what I am not seeing.

This is how the entity looks:

The since attribute is in UTC.

This is how I create the sensor:

kala_last_changed:
      value_template: "{{ as_timestamp(states.binary_sensor.pet_kala.attributes.since) | timestamp_local }}"
      friendly_name: Kala Last Changed

I also tried to cast to a custom timestamp or use ā€œstate_attr(ā€¦)ā€ but I always get the sensor to display UTC time.

Any idea what I am doing wrong?

Thanks in advance!

{% set hrmn = secs | timestamp_custom('%H:%M', false) %}

Use the false option parameter to cast as local time otherwise it defaults to utc

Thanks, I played around a bit more. This is super weird.

{{ states.binary_sensor.pet_kala.attributes.since }}
{{ as_timestamp(states.binary_sensor.pet_kala.attributes.since) | timestamp_custom('%H:%M', false)}}
{{ as_timestamp(states.binary_sensor.pet_kala.attributes.since) | timestamp_custom('%H:%M', true)}}

gives

2020-11-21 18:12:52
17:12
18:12

However, the entity changed its status at 19:13:53 PM (UTC+1, my local time which is the correct time)

It seems the ā€œsinceā€ attribute is in UTC, but the system thinks it is local time or something like that. Is there anyway to manipulate this to get the correct time?

as_timestamp assumes local. So youā€™re converting UTC to local with as_timestamp which is incorrect.

I see - thanks!

I fixed it using this:

{{ as_timestamp(states.binary_sensor.pet_kala.attributes.since+'+00:00') | timestamp_local}}

Not sure if the most elegant, but it works :smiley:

If that makes it work itā€™s because the since attribute contains a string value (representing date and time) but is ā€˜offset-naiveā€™ because it lacks timezone information.

Tip: when concatenating strings, use the tilde character ~ instead of the plus symbol.

Thanks for the tip!

I received a missing timezone error when trying to convert the string to datetime which is why I added the timezone.

Thanks for the help everyone!

checking the config I suddenly realized having quite a few of these sensor.date sensors:

      year:
        friendly_name: Year
        value_template: >
          {{strptime(states('sensor.date'),'%Y-%m-%d').strftime('%Y')}}

given the complexity of these conversions, Id rather write

          {{now().strftime('%Y')}}

and, to post another one:

          {% set month =  now().strftime('%m') %}
          {% if month in ['1','3','5','7','8','10','12'] %} 31
          {% elif month in ['4','6','9','11'] %} 30
          {% else %} {{'29' if states('sensor.year')|int//4 == 0 else '28'}}
          {% endif %}

and was wondering what could be the reason not to do this, other than maybe the more frequent updating happening each minute compared to the daily updating of the more complex conversion template?

You can also use now().month and now().year.

However that returns an integer value, not a string, so you would have to change the listā€™s items from string to integer.

          {% if now().month in [ 1, 3, 5, 7, 8, 10, 12 ] %} 31
          {% elif now().month in [ 4, 6, 9, 11 ] %} 30
          {% else %} {{'29' if states('sensor.year')|int//4 == 0 else '28'}}
          {% endif %}
1 Like

a yes, did that already in templates like this:

      remaining_days:
        friendly_name: Remaining days
        value_template: >
          {% set this = now() %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set last = this.replace(month=next, day=1) %}
          {{(last.date() - this.date()).days}}

and will adapt to your suggestion too. 1 line less, and better readability indeed.
thanks.

Not sure how long you plan on running HA but your leap year detection wonā€™t work.
For instance it would have said 2000 was a leap year and itā€™ll also cock up in 2400

I like to plan ahead
No y2k crap for me
:rofl:

donā€™t know if thatā€™s true but this would get around that

{% set start = now().replace(day=1) %}
{% set month = start.month + 1 if start.month < 12 else 1 %}
{% set year = start.year + 1 if start.month == 12 else start.year %}
{% set end = start.replace(year=year, month=month) %}
{{ (end-start).days }}

they both are leap yearsā€¦? but yes, youā€™re right, I should have added the //400 in case of a centurialā€¦

@petro @mariusthvdb
My apologies, I misremembered the rules

From Wikipedia : -
ā€œEvery year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100, but these centurial years are leap years if they are exactly divisible by 400. For example, the years 1700, 1800, and 1900 are not leap years, but the years 1600 and 2000 are.[5].ā€

I think some Petro magic may be required as this is different to previous

the template here doesnā€™t care about any of that

True, I just re-read it.
Itā€™s a sledgehammer to crack a nut though
:rofl:

Weā€™ll test it when 2100 comes round

:rofl: