Repository navigation
Description
(Claude found this one, sorry)
It seems to affect .json, .jsonld and .json_api
Problem
The Bioschemas JSON-LD of events (/events/<id>.jsonld and the copy embedded in the event page) gives
startDate and endDate in Ruby's default Time#to_s format, and labels the event's local wall-clock time as UTC:
"startDate": "2015-11-23 10:16:33 UTC"There are two separate problems:
- Not ISO 8601. schema.org
startDate/endDateexpect aDateorDateTime
in ISO 8601 format, e.g.2015-11-23T10:16:33-05:00. Consumers that parse the value strictly (search engines'
structured data, JSON-LD/RDF tooling withxsd:dateTime) reject or misread it. - Wrong instant for events outside UTC. TeSS stores an event's
start/endas local wall-clock time in a
UTCdatetimecolumn, with the zone in the separatetimezonefield (seeEvent#start_local/#start_utc).
The generator emits the raw column, so the local time is presented as UTC.
Example (test fixtures)
Event timezone |
start column |
start_local |
Actual UTC (start_utc) |
Bioschemas startDate |
|---|---|---|---|---|
US/Eastern |
2015-11-23 10:16:33 | 2015-11-23 10:16:33 -0500 | 2015-11-23 15:16:33 UTC | 2015-11-23 10:16:33 UTC (5 h off) |
Australia/Melbourne |
2016-12-12 10:00:00 | 2016-12-12 10:00:00 +1100 | 2016-12-11 23:00:00 UTC | 2016-12-12 10:00:00 UTC (11 h off) |
Cause
-
lib/bioschemas/event_generator.rbandlib/bioschemas/course_instance_generator.rbmapstartDate/endDate
straight to thestart/endcolumns:property :startDate, :start property :endDate, :end
-
Bioschemas::Generator#to_jsonusesJSON.pretty_generate, which serialises anActiveSupport::TimeWithZone
withto_s("2015-11-23 10:16:33 UTC") rather than with ActiveSupport's ISO 8601as_json. -
test/integration/bioschemas_test.rbasserts the current format (e.g.'2021-09-20 23:15:00 UTC'), so the
tests encode the bug.
Materials are not affected: their dateCreated/dateModified/datePublished are date columns and already
serialise as ISO 8601, e.g. 2020-01-01.
Suggested fix
1. Emit local time with its offset in ISO 8601
property :startDate, ->(event) { event.start_local&.iso8601 }
property :endDate, ->(event) { event.end_local&.iso8601 }in both generators, and update test/integration/bioschemas_test.rb. For the fixtures this gives:
Event timezone |
startDate |
endDate |
|---|---|---|
US/Eastern |
2015-11-23T10:16:33-05:00 |
2016-12-23T10:16:33-05:00 |
Australia/Melbourne |
2016-12-12T10:00:00+11:00 |
2016-12-12T12:00:00+11:00 |
UTC |
2126-12-12T10:00:00Z |
2126-12-12T12:00:00Z |
2. Don't claim UTC when the timezone is unknown
When an event has no timezone, start_local falls back to the stored value, giving 2020-01-01T09:00:00+00:00.
That still asserts UTC. It would be more accurate to emit the local time without an offset
(2020-01-01T09:00:00), which ISO 8601 and schema.org allow and which means "local time, zone not known".
3. Fill in a missing timezone from the event's location
Many events have no timezone (see the TODO in Event#set_default_times, "Set timezone for online events. Where to get
it from, though?"), but in-person events are already geocoded to latitude/longitude by the Nominatim
geocoding worker. The timezone could be derived there, only when timezone is blank, so a timezone given by the
user or the ingestion source is never overwritten. Options, tried against the TeSS fixtures and a few known
places:
| Approach | Accuracy | Cost |
|---|---|---|
Country only, from tzinfo's country→zone data (already a dependency) |
Exact for single-zone countries (UK, Norway, most of Europe); no answer for e.g. US (29 zones), Australia (12), Russia (27) | Nothing new |
Country + coordinates, nearest of the country's zone reference cities in tzinfo |
Right for Melbourne, Perth, New York, Denver, Chicago, Canary Islands, Moscow; wrong near zone borders, e.g. Evansville, Indiana gives America/Indiana/Petersburg (Eastern) but Evansville is on Central time |
Nothing new |
| Point-in-polygon on timezone-boundary-builder data | Exact | wheretz gem (offline; last release 2021, but zone boundaries rarely change and the offset/DST rules still come from tzinfo-data), or the timezone gem with the GeoNames API (maintained; needs a GeoNames account and a network call) |
A reasonable combination: point-in-polygon from the coordinates when they are known, otherwise the country when it
has a single zone, otherwise leave timezone blank (and emit times without an offset, as in 2). Note that
country is free text (fixtures include USA and üK), so a country lookup needs to tolerate names as well as
codes; the countries gem already in the bundle helps.
Online events have no location to derive a timezone from; they still rely on the user or the ingestion source.
Also affected
The Detached RO-Crate export #1390 reuses the Bioschemas properties, so it inherits this and is fixed by the same change.
Activity
Infer missing event time zones from the location
Infer missing event time zones from the location
Infer missing event time zones from the location