I’m searching for a way to display a sensor which shows the date which will be the next last wednesday of the month, this is needed for some garbage collection.
For example; the upcoming last wednesday of the month is: 27 January 2021
So today (18 january) it should show: “27 january 2021”, and on 28 january it should show “24 february 2021”.
Can anyone help me how to set this up?
I think i should do something with template sensors, but im not sure how…
{% if now().month in [1, 3, 5, 7, 8, 10, 12] %}
{% set month_length = 31 %}
{% elif now().month in [4, 6, 9, 11] %}
{% set month_length = 30 %}
{% else %}
{% set month_length = 28 %}
{% endif %}
{% for i in range(month_length - 6, month_length + 1) %}
{% if as_timestamp(now().replace(day = i))| timestamp_custom('%a') == 'Wed' %}
{{ as_timestamp(now().replace(day = i)) | timestamp_custom('%d %B %Y') }}
{% endif %}
{% endfor %}
It should show you the date of the last Wednesday of the month for the current month even if that date has passed. IOW, if the last Wednesday of the month is the 27th this month even on the 28th it will still show the date of the last Wednesday as the 27th. It won’t roll over to show next months last Wednesday until the beginning of next month.
But I think it should meet your needs.
I think…Obviously I haven’t been able to fully test it for every month.
EDIT: something more generic that could work for any last day would be:
{% set day = 'wed' %}
{% set days = ['mon', 'tue', 'wed', 'thur', 'fri', 'sat', 'sun'] %}
{% set i = days.index(day) if day in days else 0 %}
{% set next_month = now().date().replace(day=28) + timedelta(days=4) %}
{% set last_day = next_month - timedelta(days=next_month.day) %}
{% set last_wednesday = last_day - timedelta(days=last_day.weekday() - i) %}
{{ last_day if last_day.weekday() == i else last_wednesday }}
or if you’re comfortable with numbers representing the days
{% set day = 2 %}
{% set next_month = now().date().replace(day=28) + timedelta(days=4) %}
{% set last_day = next_month - timedelta(days=next_month.day) %}
{% set last_wednesday = last_day - timedelta(days=last_day.weekday() - day) %}
{{ last_day if last_day.weekday() == day else last_wednesday }}
Here’s the sensor I use to calculate the “last given weekday in a given month in a given year”. It’s been adapted to find the “last Wednesday in the current month in the current year”.
last_wednesday:
value_template: >
{% set isoweekday = 3 %}
{% set month = now().month %}
{% set year = now().year %}
{% if month == 12 %} {% set month = 1 %} {% set year = year + 1 %}
{% else %} {% set month = month + 1 %}
{% endif %}
{% set lastday = now().date().replace(year=year).replace(month=month).replace(day=1) - timedelta(days=1) %}
{% set ns = namespace(lastday = lastday) %}
{% for i in range(1, 8) if ns.lastday.isoweekday() != isoweekday %}
{% set ns.lastday = ns.lastday - timedelta(days=1) %}
{% endfor %}
{{ ns.lastday }}
It’s easily adapted for any day, month, and year. For example, to find the last Friday of July in 2022, simply set:
{% set isoweekday = 5 %}
{% set month = 7 %}
{% set year = 2022 %}
Another popular need is to find the nth weekday of a month (4th Wednesday of the current month) in the current year.
fourth_wednesday:
value_template: >
{% set nth = 4 %}
{% set month = now().month %}
{% set isoweekday = 3 %}
{% set d = now().date().replace(month=month).replace(day=1) %}
{% set adj = (isoweekday - d.isoweekday()) % 7 %}
{{ d + timedelta(days=adj) + timedelta(weeks=nth-1) }}
FWIW, the techniques used are based on common python examples. I had a need to to do this in python_script and it was straightforward to convert them to Jinja2.
I had just been trying out your suggestion (before petro stepped in) and noticed that it didn’t give the correct result.
But…
after seeing yours and @petro’s suggestion I’m not seeing how they could be used for a “generic” date?
I’m following things up until this line:
{% set next_month = now().date().replace(day=28) + timedelta(days=4) %}
where it seems you have to do the math for the days of the month and the time delta to get the start date of the next month manually.
I noticed if I changed either the ‘replace’ or ‘timedelta’ values it changed the date of the next_month.
So would someone need to change those values every month depending on how many days there are in the month or s there some behind the scenes magic that always gives the first day of the next month using that line?
That line gets you into next month regardless of the month. Every month has 28 days. To get to 31 days from 28 you need 3. But that’s still this month. So 4 gets you to next month. The line after adjusts to the last day of this month and the last ‘day’ calc starts from there.
Only problem with that is that there’s sometimes 5 weeks in a month, and in rare cases 6 (but it never gets to Wednesday). Actually, this month is a 6 week month. nth month calculations also only work from the start of the month. So to get the last xxx of any month you have to move from the last day.
The first example I provided calculates the last given weekday of a given month (regardless of how many weeks in that month).
The second example was a bonus to solve a completely different requirement: to find the nth weekday in a given month. It shouldn’t be used to find the last weekday of a given month (for the reasons you provided) and it can be misused if the requested “nth” doesn’t exist in the given month.
FWIW, the examples I posted were a result of a need to schedule floating holidays.
For example, Labor Day is the first Monday in September and Victoria Day (Canada) is the last Monday in May preceding May 25th and Memorial Day is the last Monday in May (which explains why in some years, like 2021, the two don’t land on the same day).
Of course, the most interesting floating holiday calculation is for Easter but that’s grist for another mill.