Site.time -> tomorrow

I run a site for a venue (student bar) that features a collection for events. Every event has a start date in the YAML header, that looks e.g. like this:

date: 2021-11-08 20:00

The home page lists all events that will happen in the future using this snippet:

{% assign future_events = site.events | where_exp: "event", "event.date > site.time %}
{% for event in future_events %}
    …
   {{ event.title }}
    …
{% endfor %}

Now that Corona makes us count the number of guests in the venue, we want to show this on the website. So there is a counter object in the YAML header of each event, that will be shown along with the event’s other data with every build.

Problem: the loop over future_events will dismiss today’s event right at the beginning of the event, because from that point in time site.time will be larger than event.date.

So I tried to create a midnight object to compare it with event.date like this:

{% assign tomorrow = 'now' | date: '%s' | plus: 86400 | date: '%F' %}
{% assign midnight = tomorrow | append: ' 0:00' | date: '%Y-%m-%d %H:%M' %}

assign future_events = site.events | where_exp: "event", "event.date > midnight

I assumed the date: … filters to make this a date object, but Jekyll 4.2.0 still rejects this with

Liquid Exception: Liquid error (line …): comparison of Time with String failed in …

So how can I make tomorrow and/or midnight a date object?
How is event.date a Time object anyway? Isn’t it just a random string from the YAML header?
What else could I compare event.date with?

For simplicity I would like to avoid the definition of an end date for every event, but rather assume the website to not show tonight’s event anymore after midnight.

Does this perhaps stay a date object if you end as

| date

i.e. without formatting as a string. Which is your error.

If you have trouble still in Liquid or want an approach that doesn’t require building the entire site, I have a JS solution from my site.

It has a list of events in tour. On the frontend, all the events on today or later are shown.

When the time rolls over to midnight for a new page, if you refresh the page then any events for the past day are hidden.


You may not want to keep the event data only in a JS object like I did.

What you could is take data that exists in collections and turn that into JS code.

maybe you have every event in the _events collection as a Markdown page with datetime in the frontmatter). Then you could do this.

<ul id="list"></ul>

<script>
let eventData = {
  {% for e in site.events %}
    { title: {{ e.title }}, datetime: {{ e.datetime }} },
  {% endfor %}
}

// e.g. 2021-11-02 13:45
const today = new Date();

// e.g. 2021-11-02 0:00
today.setUTCHours(0, 0, 0, 0);

// restrict events list to only events on or after midnight of today. 
evenrData = eventData.filter(event => new Date(event.datetime) >= today);

const list = document.getElementById("list")

for (const e of eventData) {
  const li = document.createElement('li');
  li.innerHTML = `<b>${e.datetime}</b> ${show.title}`;
  ul.appendChild(li);
}
</script>

You’ll just have to experiment with date from Jekyll to JS on a single item.

e.g. For a given event

---
datetime: 2021-11-02 13:45
---
<script>
  const datetimeJekyll = {{ page.datetime | date: "???" }}
  const datetimeJS = new Date( datetimeJekyll)
  console.log(datetimeJS)
</script>


JS keeps it dynamic. So the value for “now” is the time in the browser on page load and not the time the site was built, which means the list updates when you refresh the page and does not require a rebuild of the site. Though you can schedule a rebuild if you wanted to use the Jekyll approach without JS.

Your suggestion to apply the date filter without a format fails with

Liquid Exception: Liquid error (line …): wrong number of arguments (given 1, expected 2) in …

Also, thank you for your other approach, but restructuring everything to javascript is not an option for now.

Hello @mcnesium
Based on Liquid’s Ruby documentation for the date filter:
https://www.rubydoc.info/gems/liquid/4.0.3/Liquid/StandardFilters#date-instance_method

The result is meant to be a string and not date or time object.

So, you will have to use a custom filter (written in Ruby).
The downside being, you’ll have to forego automatic deploys from GitHub and either manually build and push the built site to GitHub or set up GitHub Actions to handle the deployment for you…

Either ways, there is no simpler solution as of now.

1 Like

Ok thanks, this sounds reasonable, especially because according to the docs, there is no such thing as a ‘Time’ type in Liquid.

The question remails, why event.date is considered a Time type, at least by jekyll serve.

Approaching it from the other side, how would I use the where_exp filter with converting event.date to a string. Would a > comparison work on strings in Liquid?

This is not on Github but a self-hosted solution which does deploy automatically :wink:

Because event is a Jekyll::Document from the site.events collection.
Documents have a builtin date attribute (unlike pages like /about.html) that is generated from either the filename (for posts) or the front matter (non-post docs).

No. The > comparison wouldn’t reliably work with strings. You’ll have to convert strings to integers for that.


I now realize that your best bet is to use JavaScript to alter the display CSS based on current time rather than depend on Jekyll’s site.time.
To illustrate, say, I have a {{ site.time }} somehere in your layout and had triggered a build on Nov 07, 2021 12:00:00 UTC with no other build triggered till now. Irrespective of the time of day thereafter, the markup rendered from {{ site.time }} is going to be outdated and will show 2021-11-07... even right now.

I agree with the integers comparison

If you want to compare timestamps, convert to Unix timestamp as integer or float then you can easily compare

12345670000 > 12349990000

%s Number of seconds since 1970-01-01 00:00:00 UTC e.g.
1452355261

e.g.

{% assign now = 'now' | date: '%s' %}
{% assign event_time = page.datetime | date: '%s' %}

{% if now > event_time %}

And site.time is noted somewhere as deprecated for Jekyll. It still works but you’re supposed to use

'now' | date: ...

@ashmaroli are you trying to suggest to turn event-related dom objects on and off client side? As I already stated, porting event handling from Jekyll collections to Javascript is not an option for now.

@MichaelCurrin thanks for the deprecation note, I will try to take this into account.

I am still not sure how I can fiddle with event.date within that assignment using where_exp:

{% assign future_events = site.events | where_exp: "event", "event.date > now "%}

Trying where_exp: "event", "( event.date‌ | date: '%s' )" > now returns

Liquid Exception: Liquid syntax error (line …): Expected dotdot but found pipe in …

Is this even a valid approach when looping over all site.events collection items? I could not yet find a way to alter data inside that piped where_exp.

@MichaelCurrin That’s news to me.
There has not been any official deprecation of {{ site.time }}, to my knowledge.
Could you point me to the source…?

@mcnesium I’m aware that you’re not onboard with turning to JS. I was trying to convince you why you should reconsider that for optimal results.

Oh I can’t find the original source.

In my own notes I found that was how “now” used to be done but from my searching on cheatsheets and Jekyll docs and source in site.rb it looks to be in use and not deprecated

BTW I remembered if you want to convert current time to an integer, you can add zero

{{ 'now' | date: '%s' |inspect }}

{{ 'now' | date: '%s' | plus: 0 | inspect }}
"1636922679"

1636922679

Though using plus to add 24 hours was already used above for tomorrow.

So I have learned:

  • event.date is a date because Jekyll makes it one as part of being a collection item
  • site.time is also a date, but after using the | date: … filter it is a string
  • where_exp can not handle filters within its condition part – discussions on SO and GH did not end up with a solution yet, in turn they are sort of abandoned.

I figured I might want to make my tomorrow string variable be a Time object again, so I can compare it to event.date. So I went on to create a filter plugin which converts a string to a Time object in _plugins/to_time.rb:

module Jekyll
    module AssetFilter
        def to_time(date_string)
            DateTime.parse(date_string).to_time
        end
    end
end

Liquid::Template.register_filter(Jekyll::AssetFilter)

First I tried to just use (date_string).to_time but this gave me errors and so I first parse the string to a DateTime object and then use this to_time function to make a Time object. Now it compares Time with Time and jekyll build does not complain anymore.

In the end I figured that I should not use tomorrow but today because I want the list of events to include the now happening event until midnight. So the assignment is are now as short as:

{% assign today = 'now' | date: '%F' | to_time %}

Using now, then using %F to just get the date part of it, then converting it back to Time, so today will be todays date at 0:00 in the morning.

For readability I replaced > with >= because that one second does not matter and it reads like “if event date is today or later”:

{% assign future_published_events = site.events | where_exp: "event", "event.date >= today and event.preview != true" %}

Thanks for everyone involved. I learned something today :slight_smile:

2 Likes