Comparing Dates in Twig

In Twig (and Craft) we work with dates a lot. Here's how you compare them.

Image

In Twig (and Craft) we work with dates a lot. Most of the time we’re prob­a­bly just out­putting a date from an entry or some oth­er Craft ele­ment. How­ev­er, we also need to com­pare them, too. This is how you do it.

When we get a date back from Craft to use in our Twig tem­plates, this will usu­al­ly come as a Date­Time object. This can make com­par­ing dates (like today’s date ver­sus the date of an Entry) a lit­tle trick­i­er because there’s more to a Date­Time object than you might think.

Here I have the postDate of an entry from Craft CMS and anoth­er Date­Time field called lessonDate. They’re both Date­Time objects.

I’m com­par­ing them and if they’re the same I out­put same”.

{% if entry.postDate == entry.lessonDate %} same {% endif %}

But can we reli­ably com­pare them like this?

We can. Twig will inspect the objects’ prop­er­ties and do the com­par­i­son for us. Pret­ty great, right?

Like an equiv­a­len­cy com­par­i­son in the first exam­ple, we can also do a greater-less-than comparison:

{% if entry.postDate > entry.lessonDate %} postDate is later/greater {% endif %}

But there’s an impor­tant catch: this type of com­par­i­son is fine as long as we know the full Date and Time are being com­pared and we are okay with that lev­el of detail.

Why is that?

A Date­Time object com­par­i­son is a full com­par­i­son of the object prop­er­ties. Let’s dump the prop­er­ties and val­ues of an instance of an object to see (I’m using the excel­lent Dumper plu­g­in to get nice­ly for­mat­ted output):

{{ dd(entry.postDate) }}

And the out­put we get is this:

DateTime {#836 ▼
  +"date": "2019-03-15 14:18:00.000000"
  +"timezone_type": 3
  +"timezone": "America/Chicago"
}

Let’s cre­ate our own Date­Time object and then com­pare it to the entry.postDate.

{% set ryansDate = date("2019-03-15") %}

To cre­ate a Date­Time object in Twig we use the date() func­tion and pass in a date string. I’m using the same exact year-month-day string as in the entry.postDate Date­Time object.

The con­tents of this object are (again using {{ dd(ryansDate) }}:

DateTime {#1596 ▼
  +"date": "2019-03-15 00:00:00.000000"
  +"timezone_type": 3
  +"timezone": "America/Chicago"
}

The only thing to point out here is that the time­stamp is not the same as the oth­er object. This means that any equiv­a­len­cy com­par­i­son between the two would not eval­u­ate true.

{% if entry.postDate == ryansDate %} same {% endif %}

Noth­ing would show because it eval­u­ates false. We could be more spe­cif­ic when cre­at­ing the object by adding the same timestamps. 

Or, even, bet­ter, just do the com­par­i­son with­out time.

Com­par­ing the Date with­out Time

The two objects from above have the same date but dif­fer­ent times. There­fore, the com­par­i­son will fail. But what if we are look­ing to com­pare just the date and not the time?

We can do that using the date fil­ter in Twig. This fil­ter con­verts a Date­Time object to a string.

{{ entry.postDate | date }}

That out­puts:

Mar 15, 2019 

Now let’s use the date fil­ter in our comparison:

{% if entry.postDate | date == ryansDate | date %} same {% endif %}

And that will eval­u­ate to true and out­put same”.

(By using the date fil­ter with­out a for­mat­ting argu­ment, we get the default date out­put: short month, day num­ber, 4 dig­it year. But we can use any valid for­mat we want.)

Com­par­ing the Month #

In the date fil­ter we can fil­ter to what­ev­er part of the Date­Time object we need. Let’s say we want to check if the two dates are in the same month. How would we do that?

{{entry.postDate | date('M') }}

This will out­put the 3 let­ter month name. Like Jul. 

The com­par­i­son we’d do to see if both dates are in July would be like this:

{% if entry.postDate | date("M") == ryansDate | date("M") %} same month {% endif %}

Com­par­ing the Name of the Day #

Just like with the month, if want to com­pare the name of the day, we need to get the week­day from each date. To do that we’d need to con­vert the date to just the num­ber of the day and then com­pare it to today, which we also con­vert to the num­ber of the day.

{% if entry.postDate | date("l") == ryansDate | date("l") %} same day {% endif %}

A more prac­ti­cal exam­ple you might run into in your work is check­ing if the date of some­thing is the week day as today. 

The first thing we need to do is get today. We do that using the now string in Twig. We can pass this string through the date fil­ter and get a string of today’s date.

{{ "now" | date }}

This is handy because now we can con­vert that string to a more usable date for­mat that we can then com­pare to some­thing we get from the (Craft) database.

We’ll do that using the a para­me­ter on the date fil­ter to for­mat the date to a string. In this case we just want to out­put the day of the week via the date filter:

{{ entry.postDate | date("l") }}

And now we can do a com­par­i­son with that:

{% if entry.postDate | date("l") == "now" | date("l") %}
same day!
{% endif %}

Or using a ternary oper­a­tor we could write it as:

{{ entry.postDate | date("l") == "now" | date('l') ? 'same day!' : 'different day' }}

An exam­ple of some code I use here on CraftQuest for the dash­board, I used the following:

{{ item.dateAdded | date("l") == "now" | date('l') ? 'Today' : 'on ' ~ item.dateAdded | date("l") }}

This checks if the day the item was added is the same today. If so, it’s prints out Today’. If not, it prints out the name of the day.

This isn’t fool­proof, how­ev­er, because using the l for­mat on the date will just out­put the name of the day as the string. If the date is last Tues­day, it will show as same day” even though it’s a week apart. 

In my imple­men­ta­tion this is okay because I’m lim­it­ing the out­putted entries to only the last five days, so the days them­selves will nev­er repeat, there­fore offer­ing reli­able comparisons.

But what if want­ed to match beyond 5 days and still know whether it’s the same day or not?

We can do that using a dif­fer­ent for­mat­ting option in the date fil­ter. This time we’ll fil­ter to the numer­i­cal day, month, and year and then com­pare that with the for­mat­ted now” string.

{{ entry.postDate | date  == "now" ? 'Today' : 'not today' }}

In this case we are not using any for­mat­ting para­me­ters on the date fil­ter because by default Twig will out­put the date to the string e.g. April 26, 2019, as we learned earlier.

So this code will also let us know if the day now is the same as the entry.postDate.

Using Epoch Time to Com­pare Dates

We could also use the Epoch time string as a way to fil­ter the date.

To do that we’d pass a string now” into the date fil­ter and then use the U” string for­mat­ting to out­put Unix time (or Epoch time). 

{{ "now" | date("U") }}

And this will out­put epoch time for right now (which will change every second).

This can be handy for com­par­ing time but it’s very exact — down to the sec­ond. Almost too exact. 

Wrap­ping Up

So that’s how we can com­pare dates in Twig. The main thing is to get both dates in the same for­mat and then com­pare. If you need speci­fici­ty, you can use Epoch time; if you need less speci­fici­ty, you can con­vert the date to a string and com­pare them that way.

Also, take advan­tage of the now string if you need to com­pare some­thing to this sec­ond, minute, hour, day, month, or year.