Skip to content

Bioschemas event dates are not ISO 8601 and ignore the event's timezone #1391

Open

Description

@stain

(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:

  1. Not ISO 8601. schema.org startDate/endDate expect a Date or DateTime
    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 with xsd:dateTime) reject or misread it.
  2. Wrong instant for events outside UTC. TeSS stores an event's start/end as local wall-clock time in a
    UTC datetime column, with the zone in the separate timezone field (see Event#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.rb and lib/bioschemas/course_instance_generator.rb map startDate/endDate
    straight to the start/end columns:

    property :startDate, :start
    property :endDate, :end
  • Bioschemas::Generator#to_json uses JSON.pretty_generate, which serialises an ActiveSupport::TimeWithZone
    with to_s ("2015-11-23 10:16:33 UTC") rather than with ActiveSupport's ISO 8601 as_json.

  • test/integration/bioschemas_test.rb asserts 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions