coveralls: 
[![Coverage Status](https://coveralls.io/builds/33118429/badge)](https://coveralls.io/builds/33118429)

Coverage increased (+0.01%) to 75.595% when pulling **51a17fd407d901703d3e0dd063e0999ddcfab3c5 on FlorianLudwig:support_date_type** into **652d2e60eff064bfa803474919c88c0e84134cef on RDFLib:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/33118429/badge)](https://coveralls.io/builds/33118429)

Coverage increased (+0.01%) to 75.595% when pulling **51a17fd407d901703d3e0dd063e0999ddcfab3c5 on FlorianLudwig:support_date_type** into **652d2e60eff064bfa803474919c88c0e84134cef on RDFLib:master**.

white-gecko: According to the SPARQL 1.1 Specification the builtin functions are only defined on `xsd:dataTime` not on `xsd:date`. So I'm not sure if it is a good idea to extend to to `xsd:date` as well. If we want to extend it we might also want to extend it to `xsd:gDay`, `xsd:gMonth`, `xsd:gMonthDay`, `xsd:gYear`, and `xsd:gYearMonth`. While some of the builtins would result in an empty string in some cases.
FlorianLudwig: @white-gecko I am no expert on the specs, I searched through it and could not find any specification on what to do if the argument is of the wrong type (`date` vs `dateTime`) - except for the string-function it is explicitly said that it should result in an error if the argument is not a string. So this might be undefined behaviour?

If you would be interested in merging this I would look into supporting the other types as well and update this MR.

Note: `xsd:date` (and others) are not part of sparql 1.1 at all. Interestingly the first - and so far only - written [enhancement proposal for 1.2](https://github.com/w3c/sparql-12/blob/master/SEP/SEP-0002/sep-0002.md#specification). So I guess there seems to be interest in this from others as well :)
white-gecko: I have seen it in different implementations that only `dateTime` was supported for these builtins. So it is a tough decision to make weather to support all of the extra datatype or non.
Maybe we should introduce a new switch for this behavior like:
https://rdflib.readthedocs.io/en/stable/apidocs/rdflib.plugins.sparql.html#module-rdflib.plugins.sparql

    rdflib.plugins.sparql.EXTRA_DATA_TYPES = true

or

    rdflib.plugins.sparql.EXTRA_DATE_DATA_TYPES = true
FlorianLudwig: @white-gecko to be honest i have developed a dislike for switches in software as they complicate code and testing as resulting combinations do increase exponentially. So **if** it does not clash with the specs I prefer to not have a switch.

I would really like to simplify the code base for sparql evaluation a bit - I am running with a few local changes which gave my use case 25% performance boost - and I would like to work on it a bit more, as it is still to slow for my use case. Those where really low hanging fruit and I am sure there is more. Introducing a switch means supporting more different combinations - and probably breaking those, making it harder to move the code forward. See #1155 for an example of this - my local refactoring broke the semantic of comparing a `Literal` with `None` - keeping those would have made the code more complex for a benefit I did see (or understand). 
white-gecko: In an informative section the standard says.

> 3.3 Other Term Constraints
> 
> In addition to numeric types, SPARQL supports types xsd:string, xsd:boolean and xsd:dateTime (see Operand Data Types). Section Operator Mapping describes the operators and section Function Definitions the functions that can be that can be applied to RDF terms.

Based on this I can not judge if we should support the additional types or not.

Your argument that it might be supported in SPARQL 1.2 sounds good to me.

Anyhow I ask @ashleysommer and @nicholascar for their opinion.
ashleysommer: >I am running with a few local changes which gave my use case 25% performance boost - and I would like to work on it a bit more, as it is still to slow for my use case

That sounds great! I've had a mental note for months to go in to see where SPARQL could be made faster in RDFLib.
It is the only thing that significantly slows down execution of [PySHACL](https://github.com/RDFLib/pySHACL). When not using any SPARQL features, the SHACL validation engine executes approx 10x faster than when SPARQL features are used.
diff --git a/rdflib/plugins/sparql/operators.py b/rdflib/plugins/sparql/operators.py
index 29bdd5c0..3393f18a 100644
--- a/rdflib/plugins/sparql/operators.py
+++ b/rdflib/plugins/sparql/operators.py
@@ -12,6 +12,7 @@ import math
 import random
 import uuid
 import hashlib
+import datetime as py_datetime  # naming conflict with function within this module
 
 from functools import reduce
 
@@ -449,17 +450,17 @@ def Builtin_NOW(e, ctx):
 
 
 def Builtin_YEAR(e, ctx):
-    d = datetime(e.arg)
+    d = date(e.arg)
     return Literal(d.year)
 
 
 def Builtin_MONTH(e, ctx):
-    d = datetime(e.arg)
+    d = date(e.arg)
     return Literal(d.month)
 
 
 def Builtin_DAY(e, ctx):
-    d = datetime(e.arg)
+    d = date(e.arg)
     return Literal(d.day)
 
 
@@ -998,6 +999,17 @@ def datetime(e):
     return e.toPython()
 
 
+def date(e) -> py_datetime.date:
+    if not isinstance(e, Literal):
+        raise SPARQLError("Non-literal passed as date: %r" % e)
+    if e.datatype not in (XSD.date, XSD.dateTime):
+        raise SPARQLError("Literal with wrong datatype passed as date: %r" % e)
+    result = e.toPython()
+    if isinstance(result, py_datetime.datetime):
+        return result.date()
+    return result
+
+
 def string(s):
     """
     Make sure the passed thing is a string literal

sparql builtin date functions only work with datetime, not with date
the functions `day`, `month` and `year` only work with datetime literals, not with date literals.

Example to reproduce:
```
import rdflib
import datetime

ns = rdflib.Namespace("http://example.com/")

graph = rdflib.Graph()

graph.add((ns["id:1"], ns["date"], rdflib.Literal(datetime.date(2016,8, 12))))

result = graph.query(
"""
SELECT ?a ?f
WHERE {
    ?a e:date ?c .
    BIND(DAY(?c) AS ?f).
}

""",
initNs={"e": ns}
)

for row in result:
    print(row)
```
diff --git a/test/test_sparql_operators.py b/test/test_sparql_operators.py
new file mode 100644
index 00000000..b4e4dacd
--- /dev/null
+++ b/test/test_sparql_operators.py
@@ -0,0 +1,36 @@
+import datetime
+
+import nose.tools
+
+import rdflib
+from rdflib.plugins.sparql import operators
+from rdflib.plugins.sparql import sparql
+
+
+def test_date_cast():
+    now = datetime.datetime.now()
+    today = now.date()
+
+    literal = rdflib.Literal(now)
+    result = operators.date(literal)
+    assert isinstance(result, datetime.date)
+    assert result == today
+
+    literal = rdflib.Literal(today)
+    result = operators.date(literal)
+    assert isinstance(result, datetime.date)
+    assert result == today
+
+
+def test_datetime_cast():
+    now = datetime.datetime.now()
+    literal = rdflib.Literal(now)
+    result = operators.datetime(literal)
+    assert isinstance(result, datetime.datetime)
+    assert result == now
+
+
+@nose.tools.raises(sparql.SPARQLError)
+def test_datetime_cast_type_error():
+    literal = rdflib.Literal("2020-01-02")
+    operators.date(literal)

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov coverage coveralls nose-timer
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi==2025.1.31
charset-normalizer==3.4.1
coverage==7.8.0
coveralls==4.0.1
docopt==0.6.2
exceptiongroup==1.2.2
idna==3.10
iniconfig==2.1.0
isodate==0.7.2
nose==1.3.7
nose-timer==1.0.1
packaging==24.2
pluggy==1.5.0
pyparsing==3.2.3
pytest==8.3.5
pytest-cov==6.0.0
-e git+https://github.com/RDFLib/rdflib.git@dde9db804d30fe0ab0c3291c105093ed691b0ef4#egg=rdflib
requests==2.32.3
tomli==2.2.1
urllib3==2.3.0

name: rdflib
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - coverage==7.8.0
      - coveralls==4.0.1
      - docopt==0.6.2
      - exceptiongroup==1.2.2
      - idna==3.10
      - iniconfig==2.1.0
      - isodate==0.7.2
      - nose==1.3.7
      - nose-timer==1.0.1
      - packaging==24.2
      - pluggy==1.5.0
      - pyparsing==3.2.3
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - requests==2.32.3
      - tomli==2.2.1
      - urllib3==2.3.0
prefix: /opt/conda/envs/rdflib

test/test_sparql_operators.py::test_date_cast test/test_sparql_operators.py::test_datetime_cast_type_error
test/test_sparql_operators.py::test_datetime_cast
rdflib/plugins/sparql/operators.py
rdflib/plugins/sparql/operators.py