How to show date of last Wednesday of the month?

And because I couldn’t let it rest…

this takes into account leap years:

{% set year = now().year %}
{% if year % 4 == 0 and year % 100 == 0 and year % 400 == 0 %}
  {% set leap = true %}
{% elif year % 4 == 0 and year % 100 != 0 %}
  {% set leap = true %}
{% else %}
  {% set leap = false %}
{% endif %}
{% 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 %}
{% elif now().month == 2 and leap %}
  {% set month_length = 29 %}
{% 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 %}

Again, I think…

3 Likes

@finity

Here’s another approach without a loop

{% 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() - 2 ) %}
{{ last_day if last_day.weekday() == 2 else last_wednesday }}
1 Like

that should be 2 not 4

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 }}
2 Likes

Lol, of course, I’m an idiot xD

I edited my post.

I’m guessing you wanted the last Friday originally and it was a copy/paste error?

Your guess is correct mister mindreader :wink:

1 Like

Lol, usually people want paydays which come in a variety of ‘Friday’ increments!

2 Likes

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, it can be reduced to:

      fourth_wednesday:
        value_template: >
          {{ now().date().replace(month=now().month).replace(day=1) +
              timedelta(days=(3 - d.isoweekday()) % 7) + timedelta(weeks=4-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.

4 Likes

I love watching the crazy solutions that come out of threads like this. I always learn a ton about how templates work!

I had just been trying out your suggestion (before petro stepped in) and noticed that it didn’t give the correct result.

:slightly_smiling_face:

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.

1 Like

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.

1 Like

I glanced over the explanation :rofl:

My bad

1 Like

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.

Thanks giving is 3rd Thursday I believe. I use that for my seasonal Christmas lights.

That’s the part I missed.

Thanks.

1 Like

USA: Fourth Thursday in November.
Canada: Second Monday in October.

Ah lots of replies, great! Thanks!

I’m not very familiar with the solutions (need to study them a bit more) but the most seem to do the trick! Nice work.

How could this be modified to find out if tomorrow is the last Wednesday of the month? I need a notification to take out the glass recycling bin the night before. I originally thought that finding the last Tuesday of the month would be appropriate, but then I realized that the last Tuesday could preceed the first Wednesday, which wouldn’t work.

I thought this might work, but it errors if tomorrow is next month.

{{ now().date().replace(day=now().day+1) }}