Daylight Savings Time (DST) - Template and Automation

now this doesn’t error anymore on the timestamp, and the new output of timestamp_local, please let me ask if there is a preferred syntax:

 {{([ns.spring,ns.fall]|min).isoformat()}}
 
{{as_timestamp([ns.spring,ns.fall]|min)|timestamp_local}}

both show a correct timestamp now, albeit with a different output:

Today I am getting the error “ValueError: day is out of range for month” for binary_sensor.daylight_saving_time, causing binary_sensor.daylight_saving_time to return as unavailable.

I just noticed I’m getting warnings in the log about this sensor.

2022-01-08 10:01:20 WARNING (MainThread) [homeassistant.helpers.template] Template warning: 'timestamp_local' got invalid input '64 days, 0:00:00' when rendering template '{%- set ns = namespace(previous = 2, spring=none, fall=none) %} {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %} {%- for i in range(365) %}
  {%- set day = (today + timedelta(days=i)).astimezone() %}
  {%- if ns.previous - day.hour == -1 %}
    {%- set ns.spring = today + timedelta(days=i) | timestamp_local %}
  {%- elif ns.previous - day.hour == 1 %}
    {%- set ns.fall = today + timedelta(days=i) | timestamp_local %}
  {%- endif %}
  {%- set ns.previous = day.hour %}
{%- endfor %} {{ [ns.spring, ns.fall] | min }}' but no default was specified. Currently 'timestamp_local' will return '64 days, 0:00:00', however this template will fail to render in Home Assistant core 2022.1
2022-01-08 10:01:20 WARNING (MainThread) [homeassistant.helpers.template] Template warning: 'timestamp_local' got invalid input '302 days, 0:00:00' when rendering template '{%- set ns = namespace(previous = 2, spring=none, fall=none) %} {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %} {%- for i in range(365) %}
  {%- set day = (today + timedelta(days=i)).astimezone() %}
  {%- if ns.previous - day.hour == -1 %}
    {%- set ns.spring = today + timedelta(days=i) | timestamp_local %}
  {%- elif ns.previous - day.hour == 1 %}
    {%- set ns.fall = today + timedelta(days=i) | timestamp_local %}
  {%- endif %}
  {%- set ns.previous = day.hour %}
{%- endfor %} {{ [ns.spring, ns.fall] | min }}' but no default was specified. Currently 'timestamp_local' will return '302 days, 0:00:00', however this template will fail to render in Home Assistant core 2022.1
2022-01-08 10:01:20 WARNING (MainThread) [homeassistant.helpers.template] Template warning: 'timestamp_local' got invalid input '64 days, 0:00:00' when rendering template '{%- set ns = namespace(previous = 2, spring=none, fall=none) %} {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %} {%- for i in range(365) %}
  {%- set day = (today + timedelta(days=i)).astimezone() %}
  {%- if ns.previous - day.hour == -1 %}
    {%- set ns.spring = today + timedelta(days=i) | timestamp_local %}
  {%- elif ns.previous - day.hour == 1 %}
    {%- set ns.fall = today + timedelta(days=i) | timestamp_local %}
  {%- endif %}
  {%- set ns.previous = day.hour %}
{%- endfor %} {{ [ns.spring, ns.fall] | min }}' but no default was specified. Currently 'timestamp_local' will return '64 days, 0:00:00', however this template will fail to render in Home Assistant core 2022.1
2022-01-08 10:01:20 WARNING (MainThread) [homeassistant.helpers.template] Template warning: 'timestamp_local' got invalid input '302 days, 0:00:00' when rendering template '{%- set ns = namespace(previous = 2, spring=none, fall=none) %} {%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %} {%- for i in range(365) %}
  {%- set day = (today + timedelta(days=i)).astimezone() %}
  {%- if ns.previous - day.hour == -1 %}
    {%- set ns.spring = today + timedelta(days=i) | timestamp_local %}
  {%- elif ns.previous - day.hour == 1 %}
    {%- set ns.fall = today + timedelta(days=i) | timestamp_local %}
  {%- endif %}
  {%- set ns.previous = day.hour %}
{%- endfor %} {{ [ns.spring, ns.fall] | min }}' but no default was specified. Currently 'timestamp_local' will return '302 days, 0:00:00', however this template will fail to render in Home Assistant core 2022.1

If I update the template to specify a default value for timestamp_local:

{%- set ns.spring = today + timedelta(days=i) | timestamp_local(0) %}

then I get a data type mismatch:

TypeError: unsupported operand type(s) for +: 'datetime.datetime' and 'int'

I’ve tried to figure out the needed changes but I’m not getting anywhere.

I’ve kind of lost track about what is supposed to be the “latest & greatest” version.

suggestions?

EDIT:

Do I just need to remove the “| timestamp_local” filters from the ns.spring & ns.fall calculations and convert it at the end?

this seems to work:

{%- set ns = namespace(previous = 2, spring=none, fall=none) %}
{%- set today = strptime(states('sensor.date'), '%Y-%m-%d').astimezone().replace(hour=ns.previous) %}
{%- for i in range(365) %}
   {%- set day = (today + timedelta(days=i)).astimezone() %}
   {%- if ns.previous - day.hour == -1 %}
     {%- set ns.spring = today + timedelta(days=i) %}
   {%- elif ns.previous - day.hour == 1 %}
     {%- set ns.fall = today + timedelta(days=i) %}
   {%- endif %}
   {%- set ns.previous = day.hour %}
{%- endfor %}
{{as_timestamp([ns.spring,ns.fall]|min)|timestamp_local}}

I haven’t tried it in the system yet to see if I still get warnings but it works in dev tools.

Moved away from using that template because it was resource heavy and finding the date beyond the next one basically changed whenever the DST changed. That made it a nightmare to manage. This is the current version I’m using.

with this automation:

2 Likes

What makes it resource heavy? Because it isn’t a trigger sensor and just runs every minute? Or is there something inherent in the template that makes it that way? Or both?

If it’s the former couldn’t it be converted to a trigger sensor that only runs at the same specified interval in your new one?

Is the new one more accurate than the other?

I compared the output of your new one to my slightly modified version of the old one (to get rid of the errors) and they are the same except one is a local timestamp and the other is in UTC. Which isn’t a big deal since I don’t care about the time just the date.

there is a lot more code in your newer version to wade thru to figure out so if the old version is accurate for at least the next DST then the original is way easier to understand.

Basically what I’m asking is what is the benefits of updating to the new more complex code? If there are big benefits I’ll gladly switch to the new version.

Not trying to be argumentative. Just trying to learn.

Just the minutely thing. But there was some other issues related to users with the one you’re running. It required you to configure the hour properly. People didn’t do that correctly so I made a new template that searches to the minute when the transition occurs (some guys TZ in another thread had the transition occur at 1:05 am). So, I reworked the template and it was REALLY resource heavy because it was looping 365 * 1440.

I optimized it again and now it loops at most 365 + 24 + 60, which is 84 more loops than the one you’re running.

Basically, the code iterates through every day of the year, when it finds the day it transitions, it stops looping the days. Then it iterates the hours, when it finds the hour transition, it stops looping the hours. Then in iterates the minute, when it finds the minute transition, it stops looping the minutes. Realistically, DST happens every 6 months, usually at 1, 2, or 3 am… so the most loops it will do is 183 + 3 (or 6 if you’re that guy with 5 minutes past 1am).

TBH, I might just make a DST integration at this point with the same code. I’m apprehensive because it might not get approved based on how janky it is. But, it’s really the only way to get the correct transition. I do not understand why this information can’t just be accessed, but no programming language displays the actual transition. They all hide it under the hood as a on/off. Makes no sense.

1 Like

that makes sense.

Since I really don’t care about the exact transition time whether it’s 1, 2, 3 or especially 1:05 :laughing: I’ll probably just transition it to a triggered sensor and hopefully be done with it since it seems to give me all the info I need.

Thanks for the additional info, tho.

and for anyone else coming around later, I actually put the latest code from my post above in production and it works as before with no more warnings.