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:
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:
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.
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.
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.
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.
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
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.
@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:
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.
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
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”: