Skip to content

ESQL: TRange timezone support#139911

Merged
ivancea merged 17 commits intoelastic:mainfrom
ivancea:esql-trange-timezone
Jan 12, 2026
Merged

ESQL: TRange timezone support#139911
ivancea merged 17 commits intoelastic:mainfrom
ivancea:esql-trange-timezone

Conversation

@ivancea
Copy link
Contributor

@ivancea ivancea commented Dec 22, 2025

  • Added timezone support to TRange(<date_period>). Now a TRange(-1d) will honor DST
  • Changed the Configuration.now field to be a constructor param. Currently, it was created inside, which made testing harder
    • Also changed it to be an Instant instead of a ZonedDateTime. It was also converted back to an instant, and there's no meaning in a zone here, as it's an absolute point in time. Plus there's a separated "zoneId" field.

@ivancea ivancea added >feature Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) :Analytics/ES|QL AKA ESQL v9.4.0 labels Dec 22, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-analytical-engine (Team:Analytics)

@elasticsearchmachine
Copy link
Collaborator

Hi @ivancea, I've created a changelog YAML for you.

@github-actions
Copy link
Contributor

ℹ️ Important: Docs version tagging

👋 Thanks for updating the docs! Just a friendly reminder that our docs are now cumulative. This means all 9.x versions are documented on the same page and published off of the main branch, instead of creating separate pages for each minor version.

We use applies_to tags to mark version-specific features and changes.

Expand for a quick overview

When to use applies_to tags:

✅ At the page level to indicate which products/deployments the content applies to (mandatory)
✅ When features change state (e.g. preview, ga) in a specific version
✅ When availability differs across deployments and environments

What NOT to do:

❌ Don't remove or replace information that applies to an older version
❌ Don't add new information that applies to a specific version without an applies_to tag
❌ Don't forget that applies_to tags can be used at the page, section, and inline level

🤔 Need help?

value = literal.fold(FoldContext.small());
}

if (value instanceof Instant instantValue) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was only being reached by the tests, which were inserting Instants as params. This should never happen in production code, so I instead fixed the tests and removed this from here

String projectRouting
) {
this.zoneId = zi.normalized();
this.now = ZonedDateTime.now(Clock.tick(Clock.system(zoneId), Duration.ofNanos(1)));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This line was moved as-is to EsqlSession, which is the only place where a productive Configuration is created.

For other usages in tests, I used either Instant.now() or randomInstant()

DataType.DATE_PERIOD,
Period.ofDays(1),
timestampDataType,
parse("2020-02-03T10:11:12.123456780Z"),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For easier reviewing, those parse() dates are, in this order:

  1. The row timestamp to be evaluated
  2. The "now" configuration value (Which is the "end" of the range, and the reference point for the date period maths
  3. An expected start date, to double-check that the "now - period" math is correct

Comment on lines -276 to -280
Clock fixedClock = Clock.fixed(fixedNow.toInstant(), fixedNow.getZone().normalized());
ZonedDateTime fixedNow = ZonedDateTime.now(Clock.tick(fixedClock, Duration.ofNanos(1)));

Configuration spyConfig = Mockito.spy(configuration);
Mockito.doReturn(fixedNow).when(spyConfig).now();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No need to mock the "now" anymore!

@ivancea
Copy link
Contributor Author

ivancea commented Dec 22, 2025

I'm open to move the configuration change to another PR. It's just that this PR tests depend on it, and the changes were quite straightforward. Open to opinions

Copy link
Contributor

@luigidellaquila luigidellaquila left a comment

Choose a reason for hiding this comment

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

Thanks @ivancea, the general behavior is correct, so it LGTM

I only have a concern about how we declare support for keyword parameters, see comments below

| date | date | boolean |
| date_nanos | date_nanos | boolean |
| date_period | | boolean |
| keyword | keyword | boolean |
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a side effect of relying on implicit casting.
If we don't have tests for the KEYWORD type we'll lose this information. I'm not sure we want it.

**Parameters**

`start_time_or_offset`
: Offset from NOW for the single parameter mode. Start time for two parameter mode. In two parameter mode, the start time value can be a date string, date, date_nanos or epoch milliseconds.
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

"params" : [
{
"name" : "start_time_or_offset",
"type" : "keyword",
Copy link
Contributor

@luigidellaquila luigidellaquila Jan 8, 2026

Choose a reason for hiding this comment

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

Will Kibana report an error now in case we pass a string?
I think it would be a problem, but I wouldn't consider this as a blocker for this specific issue, ie. every time we accept a date we should also accept a (properly formatted) string

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Answering both comments here:

My train of thought is that implicit casting works for other functions too, and those functions don't accept keywords (for dates, I mean). I tested Kibana with DATE_DIFF("day", "2020-10-10", 1::date), and it works correctly.

While testing this, however, I just found that we only implicit-cast literals, so this fails in both Kibana and esql, and would fail also in TRANGE after this change: DATE_DIFF("day", "2020-10-10", 1::string)

So, quite a shame, but I'll revert this change, and apply the timezone to the custom string conversion, as removing this now is a breaking change. I'll track it in a different issue, as I think it's worth doing for standardization

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Restored the keyword handling in this PR, and created an issue here: #140386
Also, added the tests for the edge case that would be breaking, so we don't forget it

Copy link
Contributor

@leontyevdv leontyevdv left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks Iván!

# Conflicts:
#	x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/EsqlSession.java
@ivancea ivancea merged commit ccf8c3c into elastic:main Jan 12, 2026
35 checks passed
@ivancea ivancea deleted the esql-trange-timezone branch January 12, 2026 14:35
szybia added a commit to szybia/elasticsearch that referenced this pull request Jan 12, 2026
…i-project-tests

* upstream/main: (23 commits)
  Fix `testAckListenerReceivesNacksIfPublicationTimesOut` (elastic#140514)
  Reduce priority of clear-cache tasks (elastic#139685)
  Add docs and tests about `StreamOutput` to memory (elastic#140365)
  ES|QL - dense_vector support for COUNT, PRESENT, ABSENT aggregator functions (elastic#139914)
  Add release notes for v9.2.4 release (elastic#140487)
  Add release notes for v9.1.10 release (elastic#140488)
  Add conncectors release notes for 9.1.10, 9.2.4 (elastic#140499)
  Add parameter support in PromQL query durations (elastic#139873)
  Improve testing of STS credentials reloading (elastic#140114)
  Fix zstd native binary publishing script to support newer versions (elastic#140485)
  Add FlattenedFieldBinaryVsSortedSetDocValuesSyntheticSourceIT (elastic#140489)
  Store fallback match only text fields in binary doc values (elastic#140189)
  [DiskBBQ] Use the new merge executor for intra-merge parallelism (elastic#139942)
  ESQL: introduce support for mapping-unavailable fields (elastic#140463)
  Add ESNextOSQVectorsScorerTests (elastic#140436)
  Disable high cardinality tests on release builds (elastic#140503)
  ESQL: TRange timezone support (elastic#139911)
  Directly compressing `StreamOutput` (elastic#140502)
  ES|QL - fix dense vector enrich bug (elastic#139774)
  Use CrossProjectModeDecider in RemoteClusterService (elastic#140481)
  ...
spinscale pushed a commit to spinscale/elasticsearch that referenced this pull request Jan 21, 2026
- Added timezone support to `TRange(<date_period>)`. Now a `TRange(-1d)` will honor DST
- Changed the `Configuration.now` field to be a constructor param. Currently, it was created inside, which made testing harder
  - Also changed it to be an `Instant` instead of a ZonedDateTime. It was also converted back to an instant, and there's no meaning in a zone here, as it's an absolute point in time. Plus there's a separated "zoneId" field.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Analytics/ES|QL AKA ESQL >feature Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants