Manipulate string for template sensor - time is returning an incorrect value

Hi
I am getting some strange behaviour back from a template sensor that I created. The entity’s state returns a string that includes a time of day in 24hr format. I wanted it in AM/PM format. I am clearly doing something wrong because when my templated attempt returns a value, the time is different to the original value. 5 minutes different. Can anyone spot what I am doing wrong. Or suggest a more elegant way to do this.

The original entity returns something like this:
Low tide at 20:06

The below code does what I wanted it to but why the hec are the times different?

    sensors:
      mycity_tides_custom:
        value_template: >
          {{ states('sensor.mycity_tides').split(" at ")[0] }} at {{  as_timestamp(strptime(states('sensor.mycity_tides').split(" at ")[1], '%H:%M')) | timestamp_custom("%I:%M %p") }}

Below screengrab shows:

      - type: entities
        title: Tides
        show_header_toggle: false
        entities:
          - sensor.mycity_tides_custom
          - sensor.mycity_tides

2021-04-20_16-20-52

I got to this stage thanks to some earlier posts by @petro . So If he is good enough to review I’d be grateful. Thanks!

That is bizarre. I’ve just tried this, reducing it down to:

{{ as_timestamp(strptime("20:06",'%H:%M')) | timestamp_custom("%I:%M:%S %p") }}

and got 08:05:00 PM. I added the seconds display to eliminate rounding or leap seconds. Mine is exactly a minute out. But adding a recent date to the timestamp to prevent it thinking it’s 1900:

{{ as_timestamp(strptime("2021-01-01 20:06",'%H:%M')) | timestamp_custom("%I:%M:%S %p") }}

gives the correct time. So perhaps:

{{ states('sensor.mycity_tides').split(" at ")[0] }} at {{  as_timestamp("2021-01-01 " + strptime(states('sensor.mycity_tides').split(" at ")[1], '%H:%M')) | timestamp_custom("%I:%M %p") }}

will work, despite being a bit of a bodge. (note: later edited as per solution below, original had an error)

1 Like

Hey, thanks for looking at this.

So, the function gets unhappy when it does not see a full timestamp value which includes the date?

I can live with bodge! THANK YOU !

I was messing with the template editor and got the same result.

Have there really been that many leap seconds since 1900?

It gets even more weird, when I do in dev tools => template

{% set foo = "Low tide at 20:06" %}
{{ foo.split(" at ")[0] }} at {{  as_timestamp(strptime(foo.split(" at ")[1], '%H:%M')) | timestamp_custom("%I:%M %p") }}

I only get

OverflowError: timestamp out of range for platform time_t

But this does work for me:

{% set foo = "Low tide at 20:06" %}
{% set split = foo.split(" at ") %}
{{ now().time().fromisoformat(split[1]).strftime("%I:%M %p") }}

Also, you might want to add a case for when the string isn’t as expected (or unknown) to not throw errors then.

  1. I was born in the longest year in modern time (1972, with two leap seconds and one leap day).

This wasn’t part of the reason, but I thought it was worth eliminating from the enquiry.

Are you on a 32-bit platform? Try this (Y2K38):

{{ as_timestamp("2038-01-18") }}
{{ as_timestamp("2038-01-20") }}

Thanks everyone!
What says the group? Sholuld I be marking this as the solution?

Your call. My solution of adding a date and @septillion’s fromisoformat both do the trick. Mark whichever one you actually use as the solution — I don’t think the “loser” will be too upset :smiley: .

Yes I am (RPi4 when x64 was still experimental). And ahh, somehow strptime thinks it’s a good idea to place the time in 1900, doh. I assumed it would place it in 1970…

When I try

 {{ states('sensor.mycity_tides').split(" at ")[0] }} at {{  as_timestamp("2021-01-01 " + strptime(states('sensor.mycity_tides').split(" at ")[1], '%H:%M')) | timestamp_custom("%I:%M %p") }} 

Dev Tools outputs

TypeError: can only concatenate str (not "datetime.datetime") to str

Sorry, my mistake, I put the date string in the wrong place:

{{ states('sensor.mycity_tides').split(" at ")[0] }} at {{ as_timestamp(strptime("2021-01-01 " + states('sensor.mycity_tides').split(" at ")[1], '%H:%M')) | timestamp_custom("%I:%M %p") }} 

Champion. Thanks kindly!

I’m still getting the hang of templating. I presume you are suggesting an if statement to test if

sensor.mycity_tides

has a state before we try to template a new sensor?

If not too much bother could you suggest what that might look like?

your original template would have worked if you used

timestamp_custom("%I:%M %p", false)

false treats the timestamp as UTC and it would have returned the correct time.

I tried that. It was even worse, it came up as AM not PM.

working for me here:

{%- set a, b = "Low Tide at 20:15".split(" at ") %}
{{ a }} at {{  as_timestamp(strptime(b, '%H:%M')) | timestamp_custom("%I:%M %p", false) }}

strptime always assumes UTC and you can’t set a TZ on it. So whenever you use it you need to use it with , false on timestamp_custom

Otherswise, if you want to avoid strptime, you need to include the date and seconds with a space between date & time.

{%- set a, b = "Low Tide at 20:15".split(" at ") %}
{{ a ~ as_timestamp(now().date() ~ ' ' ~ b ~ ':00') | timestamp_custom(" at %I:%M %p", false) }}

Your system time is screwed up then

Looks ok to me:

Hmm maybe my vm time is screwed up then