Date filter fails for (?today <= xsd:date(NOW()))

Kathrin, Zachary,

Welcome to the confusing world of dates and times! Let's start with the xsd:date() function which actually returns an xsd:date value as expected. That is the lexical representation of an xsd:date value that contains a time zone (yes, that's a thing). The xsd:date casting function is not defined in the SPARQL spec (which only defines the xsd:dateTime() cast) but Stardog supports xsd:date() cast based on the XPath definition.

So xsd:date(NOW()) works fine but there are two other problems with the query:

  1. The filter with ?today is inside an OPTIONAL that has no BGPs. Based on the bottom-up semantics of SPARQL the variable ?today is not bound inside that optional and the filter will always return false. Instead of using an OPTIONAL and two BINDs we can simply use BIND (?today <= xsd:date(NOW()) AS ?works) but...

  2. The string functions used at the beginning are stripping the time zone away. Then we end up comparing a date with a timezone and a date without a timezone. The XSD spec defines a partial order on date and time values due to the ambiguity introduced by missing timezones. These comparisons might return an "indeterminate" result unless the difference between dates is larger than the max timezone difference possible. Indeterminate result is equivalent to raising an error and it would cause filters to fail or variables in bind expressions to be left unbound. The following illustrates how some comparisons might not bind a value:

select * {
    bind("2019-09-20-04:00"^^xsd:date AS ?today)
    bind("2019-09-19"^^xsd:date <= ?today as ?x)
    bind("2019-09-20"^^xsd:date <= ?today as ?y)
    bind("2019-09-20-04:00"^^xsd:date <= ?today as ?z)
}

+------------------------------+-------+-------+-------+
|            today             |   x   |   y   |   z   |
+------------------------------+-------+-------+-------+
| "2019-09-20-04:00"^^xsd:date | true  |       | true  |
+------------------------------+-------+-------+-------+

You should always try to compare date/time values that are in the same timezone or no date/time should have a timezone. A single timezone is totally ordered within itself and would not have these problems. For this specific query this would mean either not stripping the timezone using the string functions at the beginning or doing the same thing every time. Stardog's stored functions can be useful if you'll repeatedly use the same function patterns.

There are even more issues related to mixing timezones if you are not confused enough:

  • Stardog uses Java's built-in XMLGregorianCalendar implementation which seems to behave strangely with missing timezones and comparing dates from different timezones. It also follows the older XSD 1.0 spec and not the latest XSD 1.1 spec. We are looking into moving to a new implementation that conforms to the XSD 1.1 spec.

  • Things get even more complicated when date subtraction comes into play because subtraction results might be inconsistent with comparison results when there are missing timezones. This is due to the fact that XPath subtract-dates function that Stardog uses. XPath says every date/time missing a timezone should be automatically added an implicit timezone from a dynamic context for subtraction. Stardog initializes the dynamic context with the timezone of the server. This means even though two date/time values are incomparable you can subtract them and get a result but the result will depend on the locale.

I think it is appropriate to end this message with the following XKCD reference:

Have a good weekend,
Evren

1 Like