This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git
The following commit(s) were added to refs/heads/master by this push:
new 82c070d837 PromQL service: fix time parse issue when using RFC3339
time format for querying. (#13677)
82c070d837 is described below
commit 82c070d83709c580a7588654155e43f43a03e101
Author: Wan Kai <[email protected]>
AuthorDate: Tue Jan 20 20:32:23 2026 +0800
PromQL service: fix time parse issue when using RFC3339 time format for
querying. (#13677)
---
docs/en/api/promql-service.md | 16 +++----
docs/en/changes/changes.md | 1 +
.../oap/query/promql/handler/PromQLApiHandler.java | 23 +++++++--
.../promql/handler/PromQLApiHandlerTest.java | 55 ++++++++++++++++++++++
test/e2e-v2/cases/promql/promql-cases.yaml | 10 ++--
5 files changed, 89 insertions(+), 16 deletions(-)
diff --git a/docs/en/api/promql-service.md b/docs/en/api/promql-service.md
index 69cc3966ae..6acc59fae1 100644
--- a/docs/en/api/promql-service.md
+++ b/docs/en/api/promql-service.md
@@ -148,7 +148,7 @@ GET|POST /api/v1/query
| Parameter | Definition
| Support | Optional |
|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|------------|
| query | prometheus expression
| yes | no |
-| time | **The latest metrics value from current time to this time is
returned. If time is empty, the default look-back time is 2 minutes.** time
format: RFC3399 or unix_timestamp in seconds | yes | yes |
+| time | **The latest metrics value from current time to this time is
returned. If time is empty, the default look-back time is 2 minutes.** time
format: RFC3339 or unix_timestamp in seconds | yes | yes |
| timeout | evaluation timeout
| **no** | **ignore** |
For example:
@@ -198,8 +198,8 @@ GET|POST /api/v1/query_range
| Parameter | Definition
| Support | Optional |
|-----------|--------------------------------------------------------------------------------------|---------|------------|
| query | prometheus expression
| yes | no |
-| start | start timestamp, format: RFC3399 or unix_timestamp in seconds
| yes | no |
-| end | end timestamp, format: RFC3399 or unix_timestamp in seconds
| yes | no |
+| start | start timestamp, format: RFC3339 or unix_timestamp in seconds
| yes | no |
+| end | end timestamp, format: RFC3339 or unix_timestamp in seconds
| yes | no |
| step | **SkyWalking will automatically fit Step(DAY, HOUR, MINUTE)
through start and end.** | **no** | **ignore** |
| timeout | evaluation timeout
| **no** | **ignore** |
@@ -266,8 +266,8 @@ GET|POST /api/v1/series
| Parameter | Definition | Support |
Optional |
|-----------|-----------------------------------------------------|---------|----------|
| match[] | series selector | yes |
no |
-| start | start, format: RFC3399 or unix_timestamp in seconds | yes |
no |
-| end | end, format: RFC3399 or unix_timestamp in seconds | yes |
no |
+| start | start, format: RFC3339 or unix_timestamp in seconds | yes |
no |
+| end | end, format: RFC3339 or unix_timestamp in seconds | yes |
no |
| limit | integer, maximum number of returned series | yes |
yes |
**For metadata metrics**:
@@ -362,7 +362,7 @@ GET|POST /api/v1/labels
| Parameter | Definition
| Support | Optional |
|-----------|---------------------------------------------------------------------------------|---------|----------|
| match[] | series selector
| yes | yes |
-| start | start, format: RFC3399 or unix_timestamp in seconds
| **no** | yes |
+| start | start, format: RFC3339 or unix_timestamp in seconds
| **no** | yes |
| end | end timestamp, if end time is not present, use current time as
default end time | yes | yes |
| limit | integer, maximum number of returned labels, default 100
| yes | yes |
@@ -396,8 +396,8 @@ GET /api/v1/label/<label_name>/values
| Parameter | Definition
| Support | Optional |
|-----------|---------------------------------------------------------------------------------------------------------------------|---------|----------|
| match[] | series selector
| yes | yes |
-| start | start, format: RFC3399 or unix_timestamp in seconds
| **no** | yes |
-| end | end, format: RFC3399 or unix_timestamp in seconds, if end time
is not present, use current time as default end time | yes | yes |
+| start | start, format: RFC3339 or unix_timestamp in seconds
| **no** | yes |
+| end | end, format: RFC3339 or unix_timestamp in seconds, if end time
is not present, use current time as default end time | yes | yes |
| limit | integer, maximum number of returned label values, default 100
| yes | yes |
For example:
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 07d88d36ad..2ca4981b90 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -26,6 +26,7 @@
* Fix `BrowserErrorLog` BanyanDB storage query order.
* `BanyanDB Client`: Property query support `Order By`.
* MQE: trim the label values condition for the labeled metrics query to
enhance the readability.
+* PromQL service: fix time parse issue when using RFC3339 time format for
querying.
#### UI
* Fix the missing icon in new native trace view.
diff --git
a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
index e97594be03..4fa5a286a5 100644
---
a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
+++
b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
@@ -29,6 +29,10 @@ import com.linecorp.armeria.server.annotation.Get;
import com.linecorp.armeria.server.annotation.Param;
import com.linecorp.armeria.server.annotation.Path;
import com.linecorp.armeria.server.annotation.Post;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
@@ -102,7 +106,6 @@ import
org.apache.skywalking.oap.server.library.util.StringUtil;
import org.apache.skywalking.promql.rt.grammar.PromQLLexer;
import org.apache.skywalking.promql.rt.grammar.PromQLParser;
import org.joda.time.DateTime;
-import org.joda.time.format.ISODateTimeFormat;
import static
org.apache.skywalking.oap.query.promql.rt.PromOpUtils.formatDoubleValue;
@@ -115,6 +118,19 @@ public class PromQLApiHandler {
private AggregationQueryService aggregationQueryService;
private RecordQueryService recordQueryService;
private static final ObjectMapper MAPPER = new ObjectMapper();
+ public static final DateTimeFormatter RFC3339_FORMATTER = new
DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .append(DateTimeFormatter.ISO_LOCAL_DATE)
+ .appendLiteral('T')
+ .append(DateTimeFormatter.ISO_LOCAL_TIME)
+ .optionalStart()
+ .appendOffset("+HH:MM", "Z") // Time zone offset, support Z
+ .optionalEnd()
+ .optionalStart()
+ .appendOffset("+HHMM", "Z") // support +0800 format
+ .optionalEnd()
+ .parseDefaulting(ChronoField.OFFSET_SECONDS, 0) // default UTC
+ .toFormatter();
public PromQLApiHandler(ModuleManager moduleManager, PromQLConfig config) {
this.metadataQuery = new MetadataQueryV2(moduleManager);
@@ -596,8 +612,9 @@ public class PromQLApiHandler {
// if Unix timestamp in seconds, such as 1627756800
time = Double.valueOf(timestamp).longValue() * 1000;
} catch (NumberFormatException e) {
- // if RFC3399 format, such as 2024-09-19T20:11:00.781Z
- time = ISODateTimeFormat.dateTime().parseMillis(timestamp);
+ // if RFC3339 format, such as 2024-09-19T20:11:00.781Z
+ OffsetDateTime odt = OffsetDateTime.parse(timestamp,
RFC3339_FORMATTER);
+ time = odt.toEpochSecond() * 1000;
}
return time;
}
diff --git
a/oap-server/server-query-plugin/promql-plugin/src/test/java/org/apache/skywalking/promql/handler/PromQLApiHandlerTest.java
b/oap-server/server-query-plugin/promql-plugin/src/test/java/org/apache/skywalking/promql/handler/PromQLApiHandlerTest.java
new file mode 100644
index 0000000000..ce0cfb6f3b
--- /dev/null
+++
b/oap-server/server-query-plugin/promql-plugin/src/test/java/org/apache/skywalking/promql/handler/PromQLApiHandlerTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.promql.handler;
+
+import java.time.OffsetDateTime;
+import org.apache.skywalking.oap.query.promql.handler.PromQLApiHandler;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class PromQLApiHandlerTest {
+
+ @Test
+ public void testRFC3339Parse() {
+ String[] testCases = {
+ "2026-01-20T03:27:18Z",
+ "2026-01-20T03:27:18.0Z",
+ "2026-01-20T03:27:18.000Z",
+ "2026-01-20T03:27:18.123Z",
+ "2026-01-20T11:27:18+08:00",
+ "2026-01-20T11:27:18.0+08:00",
+ "2026-01-20T11:27:18.123+08:00",
+ "2026-01-20T11:27:18+0800",
+ "2026-01-20T11:27:18.0+0800",
+ "2026-01-20T11:27:18.123+0800",
+ "2026-01-19T19:27:18-08:00",
+ "2026-01-19T19:27:18.0-08:00",
+ "2026-01-19T19:27:18.123-08:00",
+ "2026-01-19T19:27:18-0800",
+ "2026-01-19T19:27:18.0-0800",
+ "2026-01-19T19:27:18.123-0800",
+ "2026-01-20T03:27:18"
+ };
+
+ for (String str : testCases) {
+ OffsetDateTime odt = OffsetDateTime.parse(str,
PromQLApiHandler.RFC3339_FORMATTER);
+ Assertions.assertEquals(1768879638, odt.toEpochSecond());
+ }
+ }
+}
diff --git a/test/e2e-v2/cases/promql/promql-cases.yaml
b/test/e2e-v2/cases/promql/promql-cases.yaml
index 480feaac78..6d57543dee 100644
--- a/test/e2e-v2/cases/promql/promql-cases.yaml
+++ b/test/e2e-v2/cases/promql/promql-cases.yaml
@@ -23,7 +23,7 @@ cases:
expected: expected/service-traffic-limit.yml
- query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/series -d
'match[]=service_traffic{layer="GENERAL",service=~".*-provider"}&start='$(($(date
+%s)-1800))'&end='$(date +%s)
expected: expected/service-traffic-limit.yml
- #RFC3399
+ #RFC3339
- query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/series -d
'match[]=service_traffic{layer="GENERAL"}&start='$(($(date
+%s)-1800))'&end='$(date -u +%Y-%m-%dT%H:%M:%S.111Z)
expected: expected/service-traffic.yml
- query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/series -d
'match[]=instance_traffic{layer="GENERAL",
service="e2e-service-provider"}&limit=1&start='$(($(date
+%s)-1800))'&end='$(date +%s)
@@ -88,8 +88,8 @@ cases:
expected: expected/service-metric-vector.yml
- query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query -d
'query=service_sla{service="e2e-service-consumer", layer="GENERAL"}[30m]'
expected: expected/service-metric-matrix.yml
- #RFC3399
- - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query -d
'query=service_sla{service="e2e-service-consumer",
layer="GENERAL"}[30m]&time='$(date -u +%Y-%m-%dT%H:%M:%S.111Z)
+ #RFC3339
+ - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query -d
'query=service_sla{service="e2e-service-consumer",
layer="GENERAL"}[30m]&time='$(date -u +%Y-%m-%dT%H:%M:%SZ)
expected: expected/service-metric-matrix.yml
- query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query -d
'query=service_percentile{service="e2e-service-consumer", layer="GENERAL",
p="50,75,90"}'
expected: expected/service-metric-labeled-vector.yml
@@ -113,8 +113,8 @@ cases:
## query_range
- query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query_range -d
'query=service_sla{service="e2e-service-consumer",
layer="GENERAL"}&start='$(($(date +%s)-1800))'&end='$(date +%s)
expected: expected/service-metric-matrix.yml
- #RFC3399
- - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query_range -d
'query=service_sla{service="e2e-service-consumer",
layer="GENERAL"}&start='$(($(date +%s)-1800))'&end='$(date -u
+%Y-%m-%dT%H:%M:%S.111Z)
+ #RFC3339
+ - query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query_range -d
'query=service_sla{service="e2e-service-consumer",
layer="GENERAL"}&start='$(($(date +%s)-1800))'&end='$(date -u
+%Y-%m-%dT%H:%M:%S.0Z)
expected: expected/service-metric-matrix.yml
- query: curl -X GET http://${oap_host}:${oap_9090}/api/v1/query_range -d
'query=service_percentile{service="e2e-service-consumer", layer="GENERAL",
p="50,75,90"}&start='$(($(date +%s)-1800))'&end='$(date +%s)
expected: expected/service-metric-labeled-matrix.yml