return f(jamid, *args, **kwargs)
     return _wrapper
 
+def _sort_events(events):
+    now = datetime.now(timezone.utc)
+    # Only show events with valid timestamps
+    events = [e for e in events if e.startdate and e.enddate]
+    ongoing_events = [e for e in events if e.startdate <= now and e.enddate >= now]
+    upcoming_events = [e for e in events if e.startdate > now]
+    past_events = [e for e in events if e.enddate < now]
+    return ongoing_events, upcoming_events, past_events
 
 @bp.get("")
 def jams():
             """)
     jams = [Jam.from_row(r) for r in rows]
 
+    all_events = []
+    for j in jams:
+        all_events.extend(j.events)
+
+    # Only show events with valid timestamps
+    all_events = [e for e in all_events if e.startdate and e.enddate]
+
+    ongoing_events, upcoming_events, past_events = _sort_events(all_events)
+    past_events = past_events[-5:] # Only show 5 most recent events
+
     # TODO: Sort into groups based on start/end dates
-    return render_template("jams-main.html", ongoing=jams, upcoming=[], past=[])
+    return render_template(
+            "jams-main.html",
+            ongoing=ongoing_events,
+            upcoming=upcoming_events,
+            past=past_events,
+            jams=jams)
 
 
 @bp.get("/create")
 @bp.get("/<int:jamid>")
 def jam(jamid):
     jam = _get_jam_by_id(jamid)
+    ongoing_events, upcoming_events, past_events = _sort_events(jam.events)
     # Show the main jam page
-    return render_template("jam.html", jam=jam)
+    return render_template(
+            "jam.html",
+            jam=jam,
+            ongoing=ongoing_events,
+            upcoming=upcoming_events,
+            past=past_events)
 
 
 @bp.post("/<int:jamid>/update")
 
-from datetime import datetime, timezone
+from datetime import datetime, timedelta, timezone
 
 import pytest
 
     response = client.get(f"/jams/{jam}/events/{event}/delete", follow_redirects=True)
     assert response.status_code == 403
 
+def _create_event(client, jam, title, startdate, enddate):
+    response = client.get(f"/jams/{jam}/events/create", follow_redirects=True)
+    eventid = int(response.request.path[-1])
+    client.post(
+            f"/jams/{jam}/events/{eventid}/update",
+            data=_get_event_data(title=title, startdate=startdate, enddate=enddate))
+
+def _assert_appear_in_order(page, values):
+    last_index = 0
+    for v in values:
+        assert v in page
+        index = page.index(v, last_index+1)
+        assert index > last_index
+        last_index = index
+
+def _create_past_present_future_events(client, jam):
+    today = datetime.now(timezone.utc)
+    yesterday = (today - timedelta(days=1)).isoformat()
+    tomorrow = (today + timedelta(days=1)).isoformat()
+    
+    _create_event(client, jam, "PastJam", yesterday, yesterday)
+    _create_event(client, jam, "OngoingJam", yesterday, tomorrow)
+    _create_event(client, jam, "UpcomingJam", tomorrow, tomorrow)
+
+    response = client.get("/jams/create", follow_redirects=True)
+    otherjam = int(response.request.path[-1])
+    _create_event(client, otherjam, "OtherJam", tomorrow, tomorrow)
+
+def test_jam_events_sorted_on_jams_page(client, user, jam):
+    _create_past_present_future_events(client, jam)
+    
+    response = client.get("/jams")
+    _assert_appear_in_order(
+            response.data,
+            [
+                b"Ongoing Events",
+                b"OngoingJam",
+
+                b"Upcoming Events",
+                b"UpcomingJam",
+                b"OtherJam",
+
+                b"Recent Events",
+                b"PastJam",
+
+                b"All Jams",
+                b"New Jam",
+                b"New Jam",
+            ])
+
+def test_jam_events_sorted_on_jam_info_page(client, user, jam):
+    _create_past_present_future_events(client, jam)
+    
+    response = client.get(f"/jams/{jam}")
+    assert b"OtherJam" not in response.data  # Only events for this jam
+
+    _assert_appear_in_order(
+            response.data,
+            [
+                b"Ongoing Events",
+                b"OngoingJam",
+
+                b"Upcoming Events",
+                b"UpcomingJam",
+
+                b"Past Events",
+                b"PastJam",
+            ])
+