Skip to content

fix: normalize timezone-aware cursor values to UTC before serializing state - #1077

Open
Mariano Benedettini (marianob-span1) wants to merge 1 commit into
airbytehq:mainfrom
Attuned-Corp:fix/normalize-cursor-state-to-utc
Open

fix: normalize timezone-aware cursor values to UTC before serializing state#1077
Mariano Benedettini (marianob-span1) wants to merge 1 commit into
airbytehq:mainfrom
Attuned-Corp:fix/normalize-cursor-state-to-utc

Conversation

@marianob-span1

@marianob-span1 Mariano Benedettini (marianob-span1) commented Jul 24, 2026

Copy link
Copy Markdown

DatetimeParser.format() and the concurrent state converters serialized a timezone-aware cursor datetime using its local wall-clock components, appending a literal Z without converting to UTC. A commit timestamp like 2026-07-23T21:59:38+05:30 was stored as 2026-07-23T21:59:38Z instead of the true 2026-07-23T16:29:38Z.

For a stream using a global client-side incremental cursor (global_substream_cursor + is_client_side_incremental), this relabeled value wins the global-cursor max and is then used, minus the runtime lookback, as the client-side filter floor — silently dropping legitimate records whose true instant is earlier than the corrupted floor. Observed in source-gitlab's merge_request_commits stream.

Fix

Normalize datetime values to UTC before serialization in the state converters' output_format (CustomFormatConcurrentStreamStateConverter and IsoMillisConcurrentStreamStateConverter), treating naive datetimes as UTC to match the existing parse()/from_datetime() convention. This also makes the global-cursor string comparison chronologically correct for fixed-width UTC formats.

Added regression test test_output_format_normalizes_offset_aware_datetime_to_utc covering positive/negative offsets, UTC-unchanged, and naive-as-UTC cases. It fails without the fix (serializing +05:30/-05:00 values as wall-clock Z).

Summary by CodeRabbit

  • Bug Fixes
    • Standardized serialized state timestamps to UTC.
    • Correctly handles timestamps with positive or negative timezone offsets.
    • Treats timezone-free timestamps as UTC for consistent formatting.

… state

DatetimeParser.format() and the concurrent state converters serialized a
timezone-aware cursor datetime using its local wall-clock components, appending a
literal `Z` without converting to UTC. A commit timestamp like
2026-07-23T21:59:38+05:30 was stored as 2026-07-23T21:59:38Z instead of the true
2026-07-23T16:29:38Z.

For a stream using a global client-side incremental cursor (global_substream_cursor +
is_client_side_incremental), this relabeled value wins the global-cursor max and is then
used, minus the runtime lookback, as the client-side filter floor — silently dropping
legitimate records whose true instant is earlier than the corrupted floor. Observed in
source-gitlab's merge_request_commits stream.

Fix: normalize datetime values to UTC before serialization in the state converters'
output_format (CustomFormat and IsoMillis), treating naive datetimes as UTC to match the
existing parse()/from_datetime() convention. This also makes the global-cursor string
comparison chronologically correct for fixed-width UTC formats.

Related: airbytehq#1014.
@github-actions github-actions Bot added the community PRs from community contributors label Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6cabb1ac-d3b5-41a7-97d7-3ee9905a6673

📥 Commits

Reviewing files that changed from the base of the PR and between 7e96546 and dc229de.

📒 Files selected for processing (2)
  • airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py
  • unit_tests/sources/streams/concurrent/test_datetime_state_converter.py

📝 Walkthrough

Walkthrough

DateTime state converters now serialize timestamps as UTC instants. Naive datetimes are treated as UTC, aware datetimes are converted to UTC, and tests cover both converter formats with positive and negative offsets.

Changes

UTC state serialization

Layer / File(s) Summary
Normalize and validate UTC output
airbyte_cdk/sources/streams/concurrent/state_converters/datetime_stream_state_converter.py, unit_tests/sources/streams/concurrent/test_datetime_state_converter.py
Adds _to_utc, applies it to ISO-millis and custom-format serialization, and tests aware and naive datetime inputs.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: normalizing cursor datetimes to UTC before state serialization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Labels

community PRs from community contributors

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant