Skip to content

Export large-year datetimes with the timezone designator - #3136

Open
hannahbast wants to merge 2 commits into
masterfrom
fix-large-year-datetime-export
Open

Export large-year datetimes with the timezone designator#3136
hannahbast wants to merge 2 commits into
masterfrom
fix-large-year-datetime-export

Conversation

@hannahbast

@hannahbast hannahbast commented Jul 29, 2026

Copy link
Copy Markdown
Member

A date with a year outside [-9999, 9999] was exported without the timezone designator, while dates within that range keep their timezone. For example, the value "-11700-01-01T00:00:00Z"^^xsd:dateTime (the beginning of the Holocene, from Wikidata) came back from a query as "-11700-01-01T00:00:00"^^xsd:dateTime, which is a different literal than the one that was inserted (an xsd:dateTime without a timezone denotes local time). The cutoff is exactly the year range of the Date class: "-9999-01-01T00:00:00Z" is exported unchanged, "-10000-01-01T00:00:00Z" loses the Z.

The reason is that for years outside the range of the Date class, DateYearOrDuration stores only the year (all payload bits are used by the year, so the timezone cannot be stored without shrinking the year range, which would change the meaning of the bits in existing indexes). This change exports the canonical UTC form (with the Z) for such values, which is consistent with the export of dates within the range and restores the round trip for all data whose times are in UTC (in particular, all of Wikidata).

NOTE: This was found by the new qlever check-sync-with-wikidata command (qlever-dev/qlever-control#308) on a random sample of entities: the start time of the Holocene on Q18092102 was the only difference between the endpoint and Special:EntityData for that entity.

A date with a year outside [-9999, 9999] was exported without the
timezone designator (for example, "-11700-01-01T00:00:00" for a value
that was inserted as "-11700-01-01T00:00:00Z"), while dates within
that range keep their timezone. For such large years, only the year is
stored, so the original timezone is not available; export the canonical
UTC form, consistent with the export of dates within the range.
Copilot AI review requested due to automatic review settings July 29, 2026 08:35
@hannahbast

hannahbast commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

@yarox-1 and @joka921 Can you have a quick look and tell me whether you agree with that change? It's nothing you did wrong, but it falls in your area of expertise

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes RDF export of xsd:dateTime literals with years outside [-9999, 9999] so they retain a timezone designator (exported as canonical UTC Z) instead of being emitted without a timezone, which changes the literal’s meaning.

Changes:

  • Export large-year xsd:dateTime values with a trailing Z in DateYearOrDuration::toStringAndType().
  • Update large-year datetime parsing/round-trip tests to account for canonical UTC export (including when the input had no timezone).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/util/DateYearDuration.cpp Changes large-year xsd:dateTime serialization to include Z (UTC designator).
test/DateYearDurationTest.cpp Updates tests to expect canonical UTC (Z) export for large-year datetimes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +492 to +497
testLargeYearDatetime("2039481726-01-01T00:00:00Z", 2039481726);
testLargeYearDatetime("-2039481726-01-01T00:00:00Z", -2039481726);
testLargeYearDatetime("2039481726-01-01T00:00:00", 2039481726,
"2039481726-01-01T00:00:00Z");
testLargeYearDatetime("-2039481726-01-01T00:00:00", -2039481726,
"-2039481726-01-01T00:00:00Z");
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.45%. Comparing base (5a83af7) to head (12fd323).
⚠️ Report is 6 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3136      +/-   ##
==========================================
+ Coverage   92.44%   92.45%   +0.01%     
==========================================
  Files         547      547              
  Lines       46547    46563      +16     
  Branches     6314     6314              
==========================================
+ Hits        43032    43052      +20     
  Misses       1680     1680              
+ Partials     1835     1831       -4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@yarox-1

yarox-1 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@yarox-1 and @joka921 Can you have a quick look and tell me whether you agree with that change? It's nothing you did wrong, but it falls in your area of expertise

I would agree with the change. Do I understand it correctly that for other time zones specified in the input, the result would still be UTC? For example:

"-11700-01-01T00:00:00+04:00"^^xsd:dateTime -> "-11700-01-01T00:00:00Z"^^xsd:dateTime

@hannahbast

Copy link
Copy Markdown
Member Author

@yarox-1 and @joka921 Can you have a quick look and tell me whether you agree with that change? It's nothing you did wrong, but it falls in your area of expertise

I would agree with the change. Do I understand it correctly that for other time zones specified in the input, the result would still be UTC? For example:

"-11700-01-01T00:00:00+04:00"^^xsd:dateTime -> "-11700-01-01T00:00:00Z"^^xsd:dateTime

Yes. The timezone is stripped for large years. So far, there was simply no timezone then. Now, it's Z by default because it is a more meaningful default

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/util/DateYearDuration.cpp:74

  • The comment claims this is "consistent with the export of dates within the range", but Date::toStringAndType() preserves and re-exports the original timezone/offset (it doesn’t canonicalize to Z). Consider rewording to avoid implying that in-range exports are always UTC/canonical Z.
      // NOTE: For years outside the range of the `Date` class, only the year
      // is stored, so the original timezone is not available. Export the
      // canonical UTC form (with the `Z`), consistent with the export of
      // dates within the range (where a `Date` stores its timezone).
      return impl(F{"%d-01-01T00:00:00Z"}, XSD_DATETIME_TYPE);

test/DateYearDurationTest.cpp:492

  • This test comment calls the export "canonical UTC form"; more precisely, the large-year representation always exports with the Z (UTC) designator because the original timezone isn’t stored (including the case where the input had no timezone). Rewording would make the intent clearer.
  // NOTE: Only the year is stored, so the export is always in the canonical
  // UTC form (with the `Z`), also when the input has no timezone.
  testLargeYearDatetime("2039481726-01-01T00:00:00Z", 2039481726);

@sparql-conformance

Copy link
Copy Markdown

Overview

Number of Tests Passed ✅ Intended ✅ Failed ❌ Not tested
498 414 73 11 0

Conformance check passed ✅

No test result changes.

Details: https://qlever.dev/sparql-conformance-ui?cur=12fd323e309101ddba69410535c4738b2ef9df71&prev=443391694083d32ff1fab560bcb2e2a509671d5e

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants