Skip to content

Format date/time slider URL query params as ISO strings#14120

Merged
mayagbarnes merged 13 commits intodevelopfrom
slider-dates
Feb 28, 2026
Merged

Format date/time slider URL query params as ISO strings#14120
mayagbarnes merged 13 commits intodevelopfrom
slider-dates

Conversation

@mayagbarnes
Copy link
Copy Markdown
Collaborator

Describe your changes

  • Date/time/datetime sliders with bind="query-params" now format URL values as human-readable ISO strings (e.g., ?date=2024-06-15, ?time=14:30, ?dt=2024-06-15T14:30) instead of raw microsecond timestamps (e.g., ?date=1718409600000000).
  • The conversion is localized to the URL boundary: the frontend converts micros → ISO when writing to the URL, and the backend parses ISO → micros when reading from the URL. The internal wire format (double_array_value with microsecond floats) is unchanged.
  • Old bookmarked URLs with raw microsecond values continue to work (backward compatible).

Testing Plan

  • Unit Tests: ✅ Added
  • E2E Tests: ✅ Added
  • Manual Testing: ✅

@mayagbarnes mayagbarnes added security-assessment-completed change:refactor PR contains code refactoring without behavior change impact:users PR changes affect end users labels Feb 25, 2026
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Feb 25, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 25, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-14120/streamlit-1.54.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-14120.streamlit.app (☁️ Deploy here if not accessible)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request enhances the user experience of date/time/datetime sliders with bind="query-params" by formatting URL query parameters as human-readable ISO strings instead of raw microsecond timestamps.

Changes:

  • Date sliders now display ?date=2024-06-15 instead of ?date=1718409600000000
  • Time sliders format as ?time=14:30 and datetime as ?datetime=2024-06-15T14:30
  • Backend parses ISO strings when reading from URLs; frontend formats to ISO when writing to URLs
  • Wire protocol remains unchanged (still uses microsecond floats internally)
  • Old bookmarked URLs with raw microsecond values continue to work (backward compatible)

Reviewed changes

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

Show a summary per file
File Description
lib/streamlit/runtime/state/query_params.py Added _try_parse_iso_to_micros() function to parse ISO date/time/datetime strings to microseconds, integrated into parse_url_param() for double_array_value types
lib/tests/streamlit/runtime/state/query_params_test.py Added comprehensive unit tests for ISO parsing function and integration with parse_url_param, including backward compatibility tests
lib/streamlit/elements/widgets/slider.py Updated comment to cross-reference the TIME_BASE_DATE constant used in query_params.py
frontend/lib/src/WidgetStateManager.ts Added microsToIsoString() function and DateType type; modified registerQueryParamBinding() and convertToUrlValue() to handle date type formatting
frontend/lib/src/WidgetStateManager.test.ts Added unit tests for ISO formatting in URL updates, default value handling, and range sliders
frontend/lib/src/hooks/useQueryParamBinding.ts Added dateType to QueryParamBindingOptions interface and passed to registerQueryParamBinding()
frontend/lib/src/hooks/useBasicWidgetState.ts Added dateType to QueryParamBindingConfig and included in memoized options
frontend/lib/src/components/widgets/Slider/Slider.tsx Added dateType to query param binding configuration for date/time/datetime sliders
e2e_playwright/st_slider.py Added four new test sliders (date, time, datetime, date range) with query param binding
e2e_playwright/st_slider_test.py Added E2E tests verifying ISO URL seeding, invalid input handling, and out-of-range value handling

@mayagbarnes mayagbarnes added the ai-review If applied to PR or issue will run AI review workflow label Feb 25, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Feb 25, 2026
@github-actions

This comment was marked as duplicate.

@github-actions github-actions bot added the do-not-merge PR is blocked from merging label Feb 25, 2026
@mayagbarnes mayagbarnes removed the do-not-merge PR is blocked from merging label Feb 25, 2026
@mayagbarnes mayagbarnes added the ai-review If applied to PR or issue will run AI review workflow label Feb 25, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Feb 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Consolidated Code Review — PR #14120

Format date/time slider URL query params as ISO strings

Summary

This PR improves URL readability for date/time/datetime sliders when using bind="query-params". Previously, URLs contained raw microsecond timestamps (e.g., ?date=1718409600000000); now they display human-readable ISO strings (e.g., ?date=2024-06-15, ?time=14:30, ?dt=2024-06-15T14:30).

The conversion is localized to the URL boundary:

  • Frontend (WidgetStateManager.ts): Converts microsecond timestamps to ISO strings when writing to the URL via microsToIsoString().
  • Backend (query_params.py): Parses ISO strings back to microsecond floats when reading from the URL via _try_parse_iso_to_micros().

The internal wire format (protobuf double_array_value with microsecond floats) is unchanged.

Code Quality

Both reviewers agreed the code is well-structured and follows existing patterns:

  • microsToIsoString is clean and concise, appropriately slicing Date.toISOString() output and smartly omitting seconds when :00.
  • _try_parse_iso_to_micros uses good structural dispatch ("T" → datetime, "-" → date, ":" → time) with proper exception handling.
  • _delta_to_micros correctly computes microseconds from individual timedelta components, avoiding floating-point precision issues from total_seconds() * 1e6.
  • The DateType vs MomentKind type distinction is well-documented.
  • Cross-file dependency between _TIME_BASE_DATE and slider.py is properly commented in both locations.
  • Private module-level names consistently follow the underscore convention.

Timezone handling in _try_parse_iso_to_micros (both reviewers flagged)

Both reviewers identified that datetime.fromisoformat(s).replace(tzinfo=timezone.utc) on line 117 would silently produce incorrect results for timezone-aware inputs (e.g., 2024-06-15T14:30+05:00). The .replace() stamps UTC onto the parsed time without converting the instant, so 14:30+05:00 becomes 14:30+00:00 instead of the correct 09:30+00:00.

Reviewers disagreed on severity:

  • gpt-5.3-codex-high rated this as blocking — incorrect data behavior that needs fixing before merge.
  • opus-4.6-thinking rated this as non-blocking — unlikely in practice since the frontend's Date.toISOString() always outputs UTC (no offset), meaning timezone-bearing URLs can only originate from manual URL editing.

Consolidation judgment: I agree with opus-4.6-thinking that this is non-blocking. The frontend never generates timezone-offset strings (JS Date.toISOString() always returns UTC, and microsToIsoString slices off the Z suffix). Timezone-offset inputs can only arise from manual URL crafting, and in that case the backend's range validation in slider.py (lines 204–213) would reject out-of-range values anyway. However, the fix is trivial — adding if dt.tzinfo is not None: return None before the replace call — and would harden the parser. I'd recommend it as a minor improvement, not a blocker.

Test Coverage

Both reviewers agreed coverage is comprehensive across all three layers:

Frontend unit tests (WidgetStateManager.test.ts):

  • 8 test cases for microsToIsoString covering date, time, datetime formats, second-precision, and midnight edge cases.
  • Integration tests for binding registration, URL formatting, date range handling, and default-value clearing.

Backend unit tests (query_params_test.py):

  • Parameterized tests for valid dates, exact value verification, HH:MM and HH:MM:SS time formats, datetime with/without seconds, and 6 invalid input cases.
  • parse_url_param integration tests covering ISO dates, times, datetimes, date ranges, backward compatibility with numeric strings, and non-ISO passthrough for select_slider.

E2E tests (st_slider_test.py):

  • ISO seeding for date, time, datetime, date range, time with seconds, and datetime with seconds.
  • Default value not appearing in URL.
  • Invalid ISO date resetting to default.
  • URL updates on slider interaction.
  • Tests follow best practices: build_app_url, wait_for_app_loaded, expect for auto-wait, get_element_by_key, and expect_prefixed_markdown.

Backwards Compatibility

Both reviewers confirmed backward compatibility is well-maintained:

  1. Old bookmarked URLs with raw microsecond values continue to workfloat("1718409600000000") succeeds before ISO parsing is attempted. Explicitly tested in test_numeric_strings_still_work.
  2. Internal wire format is unchanged — protobuf double_array_value with microsecond floats remains the communication protocol.
  3. Select slider is unaffected — uses string_array_value, not double_array_value.

One edge case noted by opus-4.6-thinking: for non-date numeric sliders, a manually crafted URL like ?my_slider=2024-06-15 would now be converted to a microsecond float instead of kept as a string. In both cases, the backend's range validation would reject and reset to default — minimal risk.

Security & Risk

Both reviewers agreed: no security concerns. Changes are localized to URL serialization/deserialization using Python's standard library parsers with proper error handling. No regex vulnerabilities or injection risks.

Accessibility

Both reviewers agreed: no accessibility impact. This PR modifies URL formatting behavior only; no UI changes or interactive element modifications.

Recommendations

  1. (Non-blocking) Harden _try_parse_iso_to_micros to reject timezone-aware inputs by checking if dt.tzinfo is not None: return None before the replace(tzinfo=timezone.utc) call. Similarly for the time branch. This is a one-line defensive improvement.
  2. (Non-blocking) Consider adding a test for timezone-bearing ISO strings (e.g., "2024-06-15T14:30+05:00") to document the expected behavior (rejection via None), once recommendation 1 is implemented.
  3. (Non-blocking) Consider adding a test for the edge case where a non-date numeric slider receives an ISO-like URL param (e.g., ?my_number=2024-06-15) to explicitly document the range-validation fallback behavior.

Verdict

APPROVED — This is a well-implemented feature with comprehensive test coverage across frontend, backend, and E2E layers. Backward compatibility is solid. The timezone-aware input edge case identified by both reviewers is a valid observation but is non-blocking: the frontend never generates such strings, and the backend's range validation provides a safety net. The recommendations above are minor improvements that can be addressed in a follow-up if desired.


Consolidated review by opus-4.6-thinking. Includes reviews from 2/2 expected models.


📋 Review by `gpt-5.3-codex-high`

Summary

This PR introduces ISO-formatted query param support for date/time/datetime sliders, replacing raw microsecond URL values with human-readable values (for example, 2024-06-15 and 2024-06-15T14:30) while preserving the internal microsecond wire format. It also adds frontend/unit/e2e coverage around URL write/read behavior and default-value clearing logic.

Code Quality

Overall structure is good: the frontend serialization is cleanly isolated in microsToIsoString, and the backend parsing is localized to parse_url_param fallback handling.

Blocking issue:

  • lib/streamlit/runtime/state/query_params.py:117, lib/streamlit/runtime/state/query_params.py:135 — timezone-bearing ISO strings are parsed incorrectly due to replace(tzinfo=timezone.utc) (and equivalent handling for time). For aware inputs like 2024-03-20T09:30+02:00, replace changes metadata instead of converting the instant, producing the wrong microsecond value. This silently accepts unsupported formats and can seed sliders with incorrect values instead of rejecting or correctly normalizing.

Test Coverage

Coverage is generally strong for the intended feature:

  • Frontend unit tests validate ISO formatting and default-clearing behavior (frontend/lib/src/WidgetStateManager.test.ts).
  • E2E tests cover ISO seeding, URL updates, and invalid-input reset for date/time/datetime sliders (e2e_playwright/st_slider_test.py).
  • Backend unit tests cover ISO parsing paths and fallback behavior (lib/tests/streamlit/runtime/state/query_params_test.py).

Gap to close for the blocking issue:

  • No tests cover timezone-bearing ISO strings (e.g. +02:00 / Z) to assert either strict rejection or correct offset-aware conversion.

Backwards Compatibility

Mostly compatible:

  • Legacy numeric microsecond URL values still parse via float(...).
  • URL write format changes are intentional and more readable.

Risk:

  • Incorrect handling of timezone-bearing ISO inputs can produce wrong seeded values rather than a safe rejection.

Security & Risk

No direct security vulnerabilities identified. Main regression risk is data correctness/consistency for manual or externally generated URLs that include timezone offsets.

Accessibility

No accessibility regressions observed in this PR. Changes are in query param serialization/parsing paths and tests; no interactive semantics or ARIA behavior was modified.

Recommendations

  1. Fix _try_parse_iso_to_micros to either strictly reject timezone-bearing strings (if unsupported) or correctly normalize aware datetimes/times with offset-aware conversion (astimezone(timezone.utc) for aware values, and current behavior for naive values).
  2. Add backend tests for timezone-bearing datetime/time inputs to lock in expected behavior.
  3. Add one e2e or integration test that ensures unsupported timezone syntax is cleared (or normalized, if supported), matching the chosen contract.

Verdict

CHANGES REQUESTED: The feature is well-implemented overall, but timezone-bearing ISO inputs are currently parsed incorrectly in the backend and need correction before merge.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR improves the URL readability of date/time/datetime sliders when using bind="query-params". Previously, URLs contained raw microsecond timestamps (e.g., ?date=1718409600000000); now they display human-readable ISO strings (e.g., ?date=2024-06-15).

The conversion is localized to the URL boundary:

  • Frontend (WidgetStateManager.ts): Converts microsecond timestamps to ISO strings when writing to the URL via a new microsToIsoString() function.
  • Backend (query_params.py): Parses ISO strings back to microsecond floats when reading from the URL via a new _try_parse_iso_to_micros() function.

The internal wire format (protobuf double_array_value with microsecond floats) is unchanged.

Code Quality

The code is well-structured and follows existing patterns:

  • microsToIsoString (lines 126–143, WidgetStateManager.ts): Clean, concise function that leverages Date.toISOString() and slices the result appropriately. Smartly omits seconds when they are :00 to keep URLs minimal.

  • _try_parse_iso_to_micros (lines 103–140, query_params.py): Good use of structural dispatch ("T" → datetime, "-" → date, ":" → time) with proper exception handling. Each branch attempts parsing and gracefully returns None on failure.

  • _delta_to_micros (lines 95–100, query_params.py): Correctly computes microseconds from individual timedelta components rather than total_seconds() * 1e6, avoiding floating-point precision issues.

  • The DateType type is kept intentionally separate from MomentKind (both are "date" | "time" | "datetime") with a clear comment explaining the rationale — URL serialization vs display formatting are distinct concerns. This is a good design decision.

  • Cross-file dependency between _TIME_BASE_DATE in query_params.py and date(2000, 1, 1) in slider.py is documented with comments in both files.

  • Private module-level names follow the underscore convention (_UTC_EPOCH, _SECONDS_TO_MICROS, _delta_to_micros, etc.).

Minor observation (non-blocking): In _try_parse_iso_to_micros (line 117), datetime.fromisoformat(s).replace(tzinfo=timezone.utc) uses replace rather than assuming UTC. On Python 3.11+, fromisoformat can parse timezone-aware strings (e.g., "2024-06-15T14:30+05:00"), and replace(tzinfo=...) would silently drop the original timezone without converting the time. This is unlikely to occur in practice (URL params won't have timezone offsets), but could be made more robust by rejecting timezone-aware inputs. Not a blocker.

Test Coverage

Test coverage is comprehensive across all three layers:

Frontend unit tests (WidgetStateManager.test.ts):

  • microsToIsoString: 8 test cases covering date, time, datetime formats, second-precision handling, midnight edge cases, and seconds omission.
  • Integration tests for binding registration, URL formatting on value change, date range handling, and default-value clearing.

Backend unit tests (query_params_test.py):

  • _try_parse_iso_to_micros: Parameterized tests for valid dates, exact value verification, HH:MM and HH:MM:SS time formats, datetime with/without seconds, and 6 invalid input cases.
  • parse_url_param integration: ISO dates, times, datetimes, date ranges, backward compatibility with numeric strings, non-ISO non-numeric passthrough for select_slider, and mixed input handling.

E2E tests (st_slider_test.py):

  • ISO seeding for date, time, datetime, date range, time with seconds, and datetime with seconds.
  • Default value not appearing in URL.
  • Invalid ISO date resetting to default.
  • URL updates on slider interaction for date, time, and datetime.

The E2E tests follow best practices: using build_app_url, wait_for_app_loaded, expect for auto-wait assertions, get_element_by_key for targeting, and expect_prefixed_markdown for value verification.

Backwards Compatibility

Backward compatibility is well-maintained:

  1. Old bookmarked URLs with raw microsecond values continue to workfloat("1718409600000000") succeeds before ISO parsing is attempted, so existing URLs are unaffected. This is explicitly tested in test_numeric_strings_still_work.

  2. Internal wire format is unchanged — The protobuf double_array_value with microsecond floats remains the communication protocol. Changes are purely at the URL boundary.

  3. Select slider is unaffected — Select sliders use string_array_value, not double_array_value, so the ISO parsing path is never reached for them.

  4. One edge case worth noting: For non-date numeric sliders using double_array_value, if someone manually crafts a URL like ?my_slider=2024-06-15, the ISO parser would now convert it to a microsecond float. Previously, it would have been kept as a string. However, in both cases, the backend's range validation (lines 204–213 of slider.py) would reject the value and reset to default. The risk is minimal.

Security & Risk

No security concerns identified:

  • The changes are localized to URL serialization/deserialization and don't affect authentication, authorization, or data storage.
  • The backend _try_parse_iso_to_micros function uses Python's standard library fromisoformat parsers with proper error handling — no regex vulnerabilities or injection risks.
  • Invalid inputs are gracefully handled (returning None or resetting to defaults).

Accessibility

No accessibility impact. This PR modifies URL formatting behavior only; no UI changes, DOM structure changes, or interactive element modifications are involved.

Recommendations

  1. (Non-blocking) Consider adding a test for the edge case where a non-date numeric slider receives an ISO-like URL param (e.g., ?my_number=2024-06-15). While the backend's range validation handles this, an explicit test would document the expected behavior.

  2. (Non-blocking) In _try_parse_iso_to_micros (line 117), consider using datetime.fromisoformat(s) and checking if the result is timezone-aware before replacing. On Python 3.11+, inputs like "2024-06-15T14:30+05:00" would silently drop the timezone offset. A check like if dt.tzinfo is not None: return None before replace(tzinfo=timezone.utc) would make parsing stricter. This is extremely unlikely to affect real users though.

Verdict

APPROVED: Well-implemented feature that improves URL readability for date/time sliders with comprehensive test coverage across all layers and solid backward compatibility.


This is an automated AI review by opus-4.6-thinking.

@mayagbarnes mayagbarnes marked this pull request as ready for review February 25, 2026 21:09
@sfc-gh-bnisco sfc-gh-bnisco added the ai-review If applied to PR or issue will run AI review workflow label Feb 27, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 10 out of 13 changed files in this pull request and generated 3 comments.

@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Feb 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Consolidated Code Review

Summary

This PR improves the URL readability of date/time/datetime slider query parameters when using bind="query-params". Instead of raw microsecond timestamps (e.g., ?date=1718409600000000), URLs now display human-readable ISO strings (e.g., ?date=2024-06-15, ?time=14:30, ?dt=2024-06-15T14:30).

The conversion is cleanly localized to the URL boundary:

  • Frontend (WidgetStateManager.ts): Converts microsecond timestamps to ISO strings when writing to the URL via microsToIsoString().
  • Backend (query_params.py): Parses ISO strings back to microsecond floats when reading from the URL via _try_parse_iso_to_micros().
  • The internal wire format (double_array_value with microsecond floats) remains unchanged.

Reviewer Agreement

Both reviewers (gpt-5.3-codex-high and opus-4.6-thinking) are in strong agreement on all major points:

  • Design quality: Both praise the clean separation of concerns, with ISO conversion localized to the URL boundary and no changes to internal protobuf formats.
  • Backwards compatibility: Both confirm that legacy raw-microsecond URL values continue to parse correctly via the float() path, and the change is additive.
  • Test coverage: Both rate coverage as strong across all three layers (frontend unit, backend unit, E2E).
  • Security/risk: Both assess the risk as low.
  • Accessibility: Both confirm no accessibility impact.
  • Verdict: Both approve with only minor non-blocking suggestions.

Code Quality

The implementation is well-structured and follows existing codebase patterns:

  • The DateType type is intentionally kept separate from MomentKind to avoid coupling URL serialization with display formatting — a sound design choice with a clear explanatory comment.
  • The _TIME_BASE_DATE constant in query_params.py includes a cross-reference comment to slider.py's _time_to_datetime, and vice versa, reducing the risk of future desync. Verified: both reference date(2000, 1, 1).
  • The seconds-omission logic (showing HH:mm vs HH:mm:ss) is consistent between frontend and backend.

Minor observation (non-blocking): In useQueryParamBinding.ts line 73, the spread pattern ...(options?.dateType ? [options.dateType] : ([] as DateType[])) is more complex than necessary. Since dateType is the last optional parameter of registerQueryParamBinding, simply passing options?.dateType directly would be equivalent and more readable.

Test Coverage

Test coverage is thorough across all three testing layers:

  • Frontend unit tests (WidgetStateManager.test.ts): 9 tests for microsToIsoString covering date, time, datetime formats, midnight edge cases, and seconds-omission logic.
  • Backend unit tests (query_params_test.py): Comprehensive tests for ISO parsing (dates, times with HH:MM and HH:MM:SS, datetimes), correct microsecond roundtrip values, invalid inputs, timezone-aware string rejection, and backward-compatible numeric parsing.
  • E2E tests (st_slider_test.py): 12 new E2E tests covering ISO seeding for all slider types, second-resolution variants, URL updates on interaction, default value clearing, and invalid ISO fallback.

Backwards Compatibility

Backward compatibility is well-handled:

  1. Old bookmark URLs with raw microsecond values continue to work — the float() conversion is attempted first before ISO parsing.
  2. No protobuf changes; the internal wire format is unchanged.
  3. No changes to the Python public API (st.slider signature and behavior are identical).
  4. Select sliders are unaffected (they use string_array_value, not double_array_value).

Security & Risk

Low risk. The _try_parse_iso_to_micros function is conservative: it rejects timezone-aware strings, arbitrary text, and invalid formats by returning None. One subtle note: on Python 3.11+, date.fromisoformat accepts more ISO 8601 formats (e.g., ordinal dates), but this is extremely unlikely to cause issues in practice since such values would never be produced naturally by sliders.

Accessibility

No accessibility concerns. This PR does not modify any UI rendering, DOM structure, or ARIA attributes — changes are purely in URL serialization logic.

Recommendations (non-blocking)

  1. Simplify spread pattern: In useQueryParamBinding.ts line 73, replace the spread with a direct pass of options?.dateType.
  2. E2E legacy URL test: Consider adding one E2E test that seeds a date/time slider using legacy raw microsecond URL values to verify backward compatibility end-to-end (not just unit-level).
  3. Round-trip unit test: Consider adding a Python test that explicitly verifies _try_parse_iso_to_micros(iso_string) produces the same microsecond value as _datetime_to_micros(...) for the same logical date/time.
  4. E2E consolidation: Some of the 12 new E2E tests could potentially be consolidated into fewer aggregated scenario tests per E2E guidelines, reducing browser loads while maintaining coverage.

Verdict

APPROVED: Both reviewers approve. The change is well-designed, backwards-compatible, and thoroughly tested across all layers. The recommendations above are minor, non-blocking refinements.


Consolidated review by opus-4.6-thinking. Individual reviews from 2 of 2 expected models received.


📋 Review by `gpt-5.3-codex-high`

Summary

This PR changes date/time/datetime slider query-param behavior to use human-readable ISO strings in the URL while preserving the internal microsecond wire format. The frontend now serializes slider micros to ISO for URL writes, and the backend parses ISO URL values back to micros on seeding. The change is scoped to query-param boundaries and includes frontend unit tests, backend unit tests, and e2e coverage updates.

Code Quality

The implementation is cleanly split across boundaries and follows existing patterns:

  • Frontend serialization is localized in frontend/lib/src/WidgetStateManager.ts via microsToIsoString() and dateType-aware query binding.
  • Backend parsing is localized in lib/streamlit/runtime/state/query_params.py via _try_parse_iso_to_micros().
  • Cross-file invariants are documented (notably the shared time base date between slider.py and query-param parsing).

No code-quality issues were identified that should block merge.

Test Coverage

Coverage is strong overall:

  • Frontend unit tests in frontend/lib/src/WidgetStateManager.test.ts validate ISO formatting, default clearing behavior, range handling, and seconds handling.
  • Python unit tests in lib/tests/streamlit/runtime/state/query_params_test.py validate ISO parsing for date/time/datetime, timezone-aware rejection, and backward-compatible numeric parsing.
  • E2E tests in e2e_playwright/st_slider_test.py and expanded fixtures in e2e_playwright/st_slider.py validate URL seeding and UI-driven URL updates for date/time/datetime sliders (including second-resolution variants).

One remaining test gap is that legacy raw-microsecond URLs for date/time/datetime sliders are validated at unit level, but not explicitly by an end-to-end scenario.

Backwards Compatibility

Backwards compatibility looks preserved:

  • Existing raw numeric microsecond query params still parse correctly (float(...) path remains first).
  • Internal protobuf and widget state representations are unchanged.
  • URL formatting changes are additive at the boundary and do not alter backend slider serialization/deserialization internals.

Given the current tests, I see low compatibility risk.

Security & Risk

No security concerns were identified. The change is primarily data-format transformation for query parameters and does not introduce new sensitive data flows, auth paths, or unsafe execution behavior.

Regression risk is low and concentrated in query-param parsing/serialization paths.

Accessibility

No material accessibility impact observed. The frontend changes are in state/query-param handling and do not alter slider semantics, focus behavior, ARIA labels, or interaction affordances.

Recommendations

  1. Add one e2e test that seeds a date/time/datetime slider using legacy raw microsecond URL values to verify backward compatibility end-to-end (not just unit-level parsing).
  2. Consider adding one targeted regression test to document expected behavior for ISO-looking strings on non-date sliders when values are invalid/out-of-range.

Verdict

APPROVED: The change is well-scoped, well-tested, and ready to merge; only minor non-blocking test hardening is recommended.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR improves the URL readability of date/time/datetime slider query parameters when using bind="query-params". Instead of raw microsecond timestamps (e.g., ?date=1718409600000000), URLs now display human-readable ISO strings (e.g., ?date=2024-06-15, ?time=14:30, ?dt=2024-06-15T14:30).

The conversion is localized to the URL boundary:

  • Frontend (WidgetStateManager.ts): Converts microsecond timestamps → ISO strings when writing to the URL via microsToIsoString().
  • Backend (query_params.py): Parses ISO strings → microsecond floats when reading from the URL via _try_parse_iso_to_micros().
  • The internal wire format (double_array_value with microsecond floats) remains unchanged.

Code Quality

The code is well-structured with clear separation of concerns. The implementation follows existing patterns in the codebase and is well-documented with inline comments explaining design decisions.

Strengths:

  • The DateType type is intentionally kept separate from MomentKind (line 109-112 of WidgetStateManager.ts) to avoid coupling URL serialization with display formatting — a good design choice explained by a clear comment.
  • The _TIME_BASE_DATE constant in query_params.py (line 92) includes a cross-reference comment to slider.py's _time_to_datetime, and vice versa (new comment added to slider.py line 129-130), reducing the risk of future desync.
  • The seconds-omission logic (showing HH:mm vs HH:mm:ss and YYYY-MM-DDTHH:mm vs YYYY-MM-DDTHH:mm:ss) is consistent between frontend and backend.

Minor observations:

  1. In useQueryParamBinding.ts (line 73), the spread pattern ...(options?.dateType ? [options.dateType] : ([] as DateType[])) is more complex than necessary. Since dateType is the last optional parameter of registerQueryParamBinding, simply passing options?.dateType would be equivalent and more readable.

  2. The new TryParseIsoToMicrosTest and ParseUrlParamIsoDateSliderTest test classes extend DeltaGeneratorTestCase, which sets up a full Streamlit runtime context. Since _try_parse_iso_to_micros and parse_url_param are pure functions, a simpler base class would suffice. However, this is consistent with the existing pattern in the file where all test classes use DeltaGeneratorTestCase.

Test Coverage

Test coverage is thorough across all three testing layers:

Frontend unit tests (WidgetStateManager.test.ts):

  • 9 tests for microsToIsoString covering date, time, datetime formats, midnight edge cases, and seconds-omission logic.

Backend unit tests (query_params_test.py):

  • TryParseIsoToMicrosTest: Tests parsing of dates, times (HH:MM and HH:MM:SS), datetimes, correct microsecond values, invalid inputs, empty strings, and timezone-aware strings (rejected).
  • ParseUrlParamIsoDateSliderTest: Tests integration with parse_url_param for ISO dates, times, datetimes, date ranges, backward-compatible numeric strings, non-ISO strings (kept as strings for select_slider), and mixed ISO/numeric values.

E2E tests (st_slider_test.py):

  • 12 new E2E tests covering: ISO seeding for date, time, datetime, date range; second-resolution time/datetime seeding; URL updates on interaction; default value not appearing in URL; invalid ISO string fallback to default.

Potential gap: The E2E test suite adds many individual test functions. Per the E2E AGENTS.md guidance ("prefer aggregated scenario tests over many micro-tests"), some of the seeding tests (date, time, datetime, date range) could potentially be consolidated into a single aggregated test to reduce browser loads. However, the test isolation makes each test's intent very clear, which is valuable for debugging.

Backwards Compatibility

Backward compatibility is well-handled:

  1. Old bookmark URLs with raw microsecond values continue to work. In parse_url_param, the float(part) conversion is attempted first. Numeric strings like "1718409600000000" will parse as floats before the ISO fallback is reached. This is confirmed by test_numeric_strings_still_work.

  2. No protobuf changes. The internal wire format (double_array_value) is unchanged. The ISO formatting is purely a URL presentation concern.

  3. No changes to the Python public API. The st.slider function signature and behavior remain identical.

  4. Select sliders unaffected. They use string_array_value, not double_array_value, so the ISO parsing code path is never triggered for them.

Security & Risk

Low risk. The changes are localized to URL serialization/deserialization:

  • The _try_parse_iso_to_micros function is conservative: it rejects timezone-aware strings, arbitrary text, and invalid formats by returning None. The fallback behavior (keeping the string as-is) matches the previous behavior for unrecognized inputs.
  • One subtle consideration: on Python 3.11+, date.fromisoformat accepts more ISO 8601 formats (e.g., ordinal dates YYYY-DDD). If a non-date slider has a URL value that happens to match an extended ISO format, it would be parsed as microseconds instead of kept as a string. In practice, this is extremely unlikely to cause issues since: (a) numeric sliders would never produce such values naturally, and (b) the previous behavior (keeping as string) would also fail at the slider validation level.

Accessibility

No accessibility concerns. This PR does not modify any UI rendering, DOM structure, or ARIA attributes. The changes are purely in URL serialization logic. The only Slider.tsx change is passing dateType to the query param binding config.

The unrelated change to test_slider_with_float_formatting (adding mouse.up() after mouse.down()) fixes test interaction mechanics and doesn't affect accessibility.

Recommendations

  1. Minor simplification in useQueryParamBinding.ts line 73: Consider replacing the spread pattern with a direct pass:

    // Current (complex)
    ...(options?.dateType ? [options.dateType] : ([] as DateType[]))
    // Simpler alternative
    options?.dateType
  2. Consider consolidating some E2E tests: The 12 new E2E tests could be reduced to ~5-6 by combining related seeding tests (e.g., date + time + datetime seeding in one test) per the E2E AGENTS.md guidance to "prefer aggregated scenario tests over many micro-tests." This would reduce CI browser loads while maintaining coverage.

  3. Add a round-trip unit test: Consider adding a Python unit test that explicitly verifies the round-trip: _try_parse_iso_to_micros(iso_string) produces the same microsecond value as _datetime_to_micros(...) for the same logical date/time. The current tests verify parsing works but don't explicitly assert the values match the slider's serialization path.

Verdict

APPROVED: A well-designed, backwards-compatible improvement to URL readability for date/time sliders with comprehensive test coverage across all layers. The minor suggestions above are non-blocking refinements.


This is an automated AI review by opus-4.6-thinking.

@mayagbarnes mayagbarnes merged commit c8acdf9 into develop Feb 28, 2026
43 of 44 checks passed
@mayagbarnes mayagbarnes deleted the slider-dates branch February 28, 2026 01:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:refactor PR contains code refactoring without behavior change impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants