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

Beneath is a full minimal example. Filtering for (?yesterday <= xsd:date(NOW())) works fine. Thank you in advance!

PREFIX xsd: <>

SELECT ?today ?yesterday ?works
BIND (xsd:date(substr(str(xsd:date(NOW())), 1, 10)) AS ?today)
BIND (xsd:date(substr(str(xsd:date((now()-"P1D"^^xsd:duration))), 1, 10)) AS ?yesterday)
FILTER (?yesterday <= xsd:date(NOW()))
BIND ("yes" AS ?success )
FILTER (?today <= xsd:date(NOW()))
BIND (IF(BOUND(?success), ?success, "no") AS ?works)


This text will be hidden

It's interesting that xsd:date(NOW()) is returning an xsd:date but the lexical representation is for a datetime. "2019-09-20-05:00"^^xsd:date

It looks like the comparison ?today <= xsd:date(NOW()) isn't returning a binding which I suppose makes sense because it's comparing a dateTime and a date and they have the same date components doesn't really make sense. Is 2019-09-20 less than 2019-09-20:05:00? It's definitely not equal.

It seems like the problem is with the xsd:date(NOW()) and it not being a valid xsd:date

As for as the spec is concerned it appears that xsd:date is not a specified operand datatype. In addition oporator mappings are only defined for datetime

I'm sure somone more familiar with the specs could give you a more complete answer here. I'm just enjoying the excuse to get more familiar with them myself.

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,

1 Like

Thank you so much Zachary and Evren, it makes sense now!

@kdentler You're very welcome although I think my main contribution is to say enough wrong things that @evren is obligated to correct me even on a weekend :wink:

That said, I've always found datatypes to be a somewhat dark corner of semantic web standards that I would love for someone to elucidate for me. It seems fairly straightforward when you just stick to using the built in types but as soon as you stray away from them with custom datatypes, facets, the difference between using datatypes with open world vs closed world semantics, etc things get fuzzy.