Skip to content

[fix](function) Preserve subsecond precision in convert_tz DST gaps - #67493

Open
jacktengg wants to merge 1 commit into
apache:masterfrom
jacktengg:wt-fix-bug
Open

[fix](function) Preserve subsecond precision in convert_tz DST gaps#67493
jacktengg wants to merge 1 commit into
apache:masterfrom
jacktengg:wt-fix-bug

Conversation

@jacktengg

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: CONVERT_TZ mapped nonexistent local times in daylight-saving gaps to the transition instant while explicitly discarding the DateTimeV2 fractional component. This made BE execution inconsistent with timezone-bearing casts, and FE constant folding had the same precision loss. Preserve and round the fractional component consistently in BE and FE. Because preserving fractions introduces resets at civil-second boundaries inside a gap, also disable the monotonic partition-pruning shortcut for affected positive-scale ranges.

Release note

CONVERT_TZ now preserves DateTimeV2 fractional seconds when a source local time falls in a daylight-saving gap.

Check List (For Author)

  • Test: Unit Test and Regression test
    • BE unit test: VTimestampFunctionsTest.convert_tz_test
    • FE unit tests: DateTimeExtractAndTransformTest, ConvertTzTest
    • Regression test: query_p0/sql_functions/datetime_functions/test_convert_tz
  • Behavior changed: Yes. CONVERT_TZ preserves fractional seconds in DST gaps.
  • Does this need documentation: No

What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

Release note

None

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: CONVERT_TZ mapped nonexistent local times in daylight-saving gaps to the transition instant while explicitly discarding the DateTimeV2 fractional component. This made BE execution inconsistent with timezone-bearing casts, and FE constant folding had the same precision loss. Preserve and round the fractional component consistently in BE and FE. Because preserving fractions introduces resets at civil-second boundaries inside a gap, also disable the monotonic partition-pruning shortcut for affected positive-scale ranges.

### Release note

CONVERT_TZ now preserves DateTimeV2 fractional seconds when a source local time falls in a daylight-saving gap.

### Check List (For Author)

- Test: Unit Test and Regression test
    - BE unit test: VTimestampFunctionsTest.convert_tz_test
    - FE unit tests: DateTimeExtractAndTransformTest, ConvertTzTest
    - Regression test: query_p0/sql_functions/datetime_functions/test_convert_tz
- Behavior changed: Yes. CONVERT_TZ preserves fractional seconds in DST gaps.
- Does this need documentation: No
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@jacktengg

Copy link
Copy Markdown
Contributor Author

/review

@jacktengg

Copy link
Copy Markdown
Contributor Author

run buildall

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requesting changes: the intended same-version convert_tz result is implemented consistently, but two P1 planner/executor compatibility paths can silently prune matching rows.

Critical checkpoints:

  • Goal and proof: Both BE paths and FE folding preserve the already-rounded fraction for skipped local times, and the new semantic tests distinguish the old behavior. The goal is met only for same-version execution; the two inline findings block correctness across supported deployments.
  • Scope: The production edits are focused on the two BE conversion sites, the shared FE resolver, and monotonicity certification.
  • Concurrency and lifecycle: No new threads, locks, shared mutable state, static initialization dependency, or lifecycle ownership is introduced. The timestamp pair is row-local and cached timezone state is unchanged.
  • Configuration: No configuration is added or changed.
  • Compatibility: Blocking. FE-only timezone rules can disagree with independently loaded BE zoneinfo (MAIN-001), and the unconditional BE result change is unsafe during the documented BE-first rolling-upgrade interval (MAIN-002).
  • Parallel paths: Row-varying BE execution, cached constant-zone BE execution, FE constant folding, timezone-bearing casts, and scale-0 behavior were all traced; both BE implementation sites changed together.
  • Conditional logic: With identical timezone rules, the source-gap reset scan is boundary-correct for entry, intra-gap second resets, exit, exact endpoints, one-sided bounds, and target fallbacks. Its positive result is unsafe when FE and BE rule sets differ.
  • Test coverage: Semantic coverage spans row-varying BE, cached BE, FE folding, scale-6 rounding, gaps, normal times, and overlaps. It does not exercise actual partition/runtime-filter pruning, FE/BE tzdata skew, or old-FE/new-BE execution; those missing end-to-end cases directly expose the accepted findings.
  • Test results: The changed expected rows are deterministic and the generated output marker is present. I did not run builds or tests per the automated-review instructions. GitHub CheckStyle, Clang Formatter, license, and secret checks pass; BE UT, FE UT, compile, and performance jobs were still pending when reviewed.
  • Observability: No additional logging or metrics are warranted for this deterministic conversion/planning change.
  • Persistence, transactions, and data writes: No EditLog, metadata, storage-format, transaction, atomicity, or data-write path is touched.
  • FE/BE fields and protocol: No new cross-process variable is added. Existing execution-version propagation has no convert_tz capability, alias, or guard for this semantic change, which is part of MAIN-002.
  • Error, null, range, and memory behavior: Existing invalid-zone/null/output-range handling is unchanged; the new pair is fully initialized and stack-local.
  • Performance: No material BE hot-path regression was found. Conservative loss of pruning for some one-sided named-zone ranges is correctness-preserving without a shared timezone-version contract.
  • Other issues: No additional substantiated issue remained after three review rounds and a final sweep of all eight changed files and both monotonicity consumers.

Review status: complete and converged after three rounds. Two distinct P1 findings are submitted inline; all other candidates were deduplicated or dismissed with code evidence.

User focus: no additional focus was provided, so the full PR was reviewed.

if (upperDateTime.isBefore(lowerDateTime)) {
return false;
}
if (mayHaveFractionalSecondSourceGap(fromZone)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Avoid FE-only timezone rules when certifying source-gap monotonicity

hasGapResetInRange can return false merely because the FE JVM's tzdb has no transition, but BEs load independently versioned TZDIR/system tzdata, so the resulting true is not safe. A released example is America/Asuncion: tzdb 2025a makes it permanent -03, while tzdb 2024b still has the 2025-10-05 00:00-to-01:00 spring gap. With a 2025a FE and 2024b BE:

Filter(convert_tz(ts,'America/Asuncion','UTC') >= '2025-10-05 04:00:00.500000')
Scan p: ts in [2025-10-05 00:00:00, 2025-10-05 01:00:00)

FE derives an image around [03:00,04:00] and can prune p, but under this PR's retained-fraction behavior the BE maps stored 00:00:00.500000 to 04:00:00.500000, so that row matches. RuntimeFilterPruneClassifier trusts the same certification. Unless FE and every BE share a versioned timezone-rules contract, please keep positive-scale named/non-fixed source zones conservatively non-monotonic and add an end-to-end pruning regression.


ts_value2.from_unixtime(unix_timestamp_for_convert_tz(ts_value, from_tz), to_tz);
std::pair<int64_t, int64_t> timestamp;
ts_value.unix_timestamp(&timestamp, from_tz);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Preserve old-FE pruning semantics during BE-first upgrades

Doris upgrades BEs before FEs, so this unconditional result change runs while an old FE can still certify every fixed-target convert_tz as monotonic and fold source-gap endpoints with the old zero-fraction policy. With the same Europe/Paris rules on both sides, an old FE can project a DATETIMEV2(6) partition [2021-03-28 02:00:00, 2021-03-28 03:00:00) to the singleton 01:00:00 and prune it for convert_tz(ts,'Europe/Paris','UTC') > '2021-03-28 01:00:00.500000'. This new BE path maps a stored 02:30:00.900000 to 01:00:00.900000, so that pruned row actually matches.

Please stage this semantic change behind an old-FE/new-BE compatibility contract (for example, a query capability or versioned function implementation), and add a mixed-version pruning regression, so the supported rolling-upgrade window cannot drop rows.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 63.02% (29551/46892)
Line Coverage 48.07% (309882/644709)
Region Coverage 43.67% (250465/573588)
Branch Coverage 45.23% (116502/257551)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 1.18% (1/85) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.28% (34646/45418)
Line Coverage 61.36% (391044/637346)
Region Coverage 57.38% (327877/571404)
Branch Coverage 58.35% (149845/256812)

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 16786 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 978189211c13c81fe7569add4405d654bf5eebef, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17636	3113	3095	3095
q2	2086	258	234	234
q3	10242	870	528	528
q4	4673	246	202	202
q5	7679	610	386	386
q6	134	120	94	94
q7	536	544	383	383
q8	9235	873	910	873
q9	3425	2372	2391	2372
q10	6535	856	702	702
q11	398	202	182	182
q12	618	267	203	203
q13	18131	1544	1158	1158
q14	155	146	135	135
q15	q16	439	391	363	363
q17	1428	900	821	821
q18	3156	2245	2230	2230
q19	1250	900	788	788
q20	374	292	210	210
q21	5702	1598	1797	1598
q22	326	264	229	229
Total cold run time: 94158 ms
Total hot run time: 16786 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3457	3408	3390	3390
q2	531	400	375	375
q3	2236	2260	2114	2114
q4	1196	1165	898	898
q5	2166	2134	2123	2123
q6	171	118	84	84
q7	1014	944	839	839
q8	1599	1415	1428	1415
q9	3128	3108	3119	3108
q10	1850	1797	1645	1645
q11	354	272	253	253
q12	450	428	349	349
q13	1521	1535	1150	1150
q14	166	169	157	157
q15	q16	391	391	359	359
q17	3585	3308	3198	3198
q18	4792	4376	4734	4376
q19	842	920	872	872
q20	1003	978	828	828
q21	3865	3267	3239	3239
q22	405	360	335	335
Total cold run time: 34722 ms
Total hot run time: 31107 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 81954 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 978189211c13c81fe7569add4405d654bf5eebef, data reload: false

query5	4239	416	334	334
query6	390	151	130	130
query7	4936	402	223	223
query8	283	125	114	114
query9	8659	2898	2916	2898
query10	414	219	175	175
query11	5396	1032	939	939
query12	127	75	74	74
query13	1189	433	339	339
query14	6124	2220	2095	2095
query14_1	1990	1954	1969	1954
query15	176	121	109	109
query16	930	382	357	357
query17	811	478	380	380
query18	2337	338	246	246
query19	165	139	111	111
query20	97	68	72	68
query21	202	102	89	89
query22	5421	5432	5355	5355
query23	6706	6244	5893	5893
query23_1	6097	6011	6040	6011
query24	7279	1091	761	761
query24_1	793	786	783	783
query25	441	313	271	271
query26	1236	246	132	132
query27	2768	423	260	260
query28	4651	1512	1507	1507
query29	947	466	356	356
query30	253	157	134	134
query31	821	408	336	336
query32	167	79	73	73
query33	466	223	186	186
query34	1001	809	493	493
query35	403	406	333	333
query36	589	569	532	532
query37	124	85	71	71
query38	1017	863	833	833
query39	504	496	498	496
query39_1	493	490	478	478
query40	216	97	80	80
query41	59	56	56	56
query42	79	76	77	76
query43	247	240	211	211
query44	1023	528	556	528
query45	108	102	96	96
query46	808	819	534	534
query47	755	765	718	718
query48	318	301	209	209
query49	555	238	191	191
query50	717	258	197	197
query51	8361	8138	8376	8138
query52	73	64	56	56
query53	193	204	150	150
query54	242	165	202	165
query55	82	73	62	62
query56	195	170	173	170
query57	663	659	663	659
query58	228	168	162	162
query59	1242	1253	1103	1103
query60	232	181	177	177
query61	119	117	114	114
query62	351	206	176	176
query63	169	152	137	137
query64	2757	716	618	618
query65	1647	1652	1635	1635
query66	1829	253	205	205
query67	10081	9670	9463	9463
query68	2986	1278	694	694
query69	334	225	204	204
query70	668	613	641	613
query71	244	177	161	161
query72	2329	1791	1541	1541
query73	643	611	340	340
query74	2015	1234	1149	1149
query75	1172	1108	970	970
query76	2355	718	527	527
query77	259	257	211	211
query78	3944	3785	3159	3159
query79	2285	786	607	607
query80	1588	327	276	276
query81	501	152	133	133
query82	947	125	100	100
query83	276	217	191	191
query84	297	110	91	91
query85	812	350	297	297
query86	438	174	167	167
query87	1022	957	908	908
query88	2762	2126	2090	2090
query89	277	198	177	177
query90	1988	126	130	126
query91	144	119	101	101
query92	87	73	72	72
query93	1484	1036	679	679
query94	674	265	208	208
query95	552	317	229	229
query96	765	534	259	259
query97	1079	1070	1022	1022
query98	173	130	132	130
query99	434	360	310	310
Total cold run time: 178945 ms
Total hot run time: 81954 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 14.68 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 978189211c13c81fe7569add4405d654bf5eebef, data reload: false

query1	0.00	0.00	0.01
query2	0.07	0.03	0.04
query3	0.24	0.10	0.10
query4	1.60	0.10	0.09
query5	0.17	0.17	0.15
query6	1.28	0.67	0.68
query7	0.03	0.01	0.00
query8	0.05	0.03	0.03
query9	0.29	0.21	0.21
query10	0.35	0.35	0.35
query11	0.16	0.11	0.11
query12	0.15	0.12	0.12
query13	0.30	0.31	0.30
query14	0.44	0.46	0.45
query15	0.37	0.36	0.35
query16	0.24	0.23	0.21
query17	0.64	0.70	0.68
query18	0.19	0.18	0.17
query19	1.13	1.05	1.19
query20	0.01	0.01	0.01
query21	15.44	0.15	0.11
query22	5.11	0.04	0.05
query23	16.18	0.24	0.10
query24	3.02	0.31	0.26
query25	0.13	0.04	0.05
query26	0.80	0.16	0.12
query27	0.03	0.03	0.03
query28	3.69	0.57	0.27
query29	12.42	3.21	2.60
query30	0.26	0.10	0.12
query31	2.75	0.36	0.16
query32	3.53	0.33	0.23
query33	1.41	1.52	1.60
query34	15.35	2.19	1.79
query35	1.74	1.73	1.71
query36	0.47	0.32	0.29
query37	0.07	0.04	0.04
query38	0.04	0.03	0.03
query39	0.03	0.02	0.02
query40	0.11	0.07	0.08
query41	0.08	0.03	0.03
query42	0.03	0.02	0.02
query43	0.03	0.03	0.02
Total cold run time: 90.43 s
Total hot run time: 14.68 s

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.

2 participants