Skip to content

Replace ZonedDateTime with Instant#6554

Merged
david-livefront merged 1 commit intomainfrom
replace-zoned-date-time
Feb 20, 2026
Merged

Replace ZonedDateTime with Instant#6554
david-livefront merged 1 commit intomainfrom
replace-zoned-date-time

Conversation

@david-livefront
Copy link
Collaborator

🎟️ Tracking

N/A

📔 Objective

This PR replaces the usage of ZonedDateTime with Instant. All the dates in the app are in UTC already and the Bitwarden SDK uses Instant as well. This should simplify logic within the app and remove the need to convert class types when interacting with the SDK.

@david-livefront david-livefront added t:tech-debt Change Type - Tech debt ai-review Request a Claude code review labels Feb 18, 2026
@github-actions github-actions bot added app:password-manager Bitwarden Password Manager app context app:authenticator Bitwarden Authenticator app context and removed t:tech-debt Change Type - Tech debt labels Feb 18, 2026

@ColumnInfo(name = "date")
val date: ZonedDateTime,
val date: Instant,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The schema does not need to be updated because we have a TypeConvertor that is transforming the value to a Long. So the stored datatype has not actually changed.

@claude
Copy link
Contributor

claude bot commented Feb 18, 2026

Claude finished @david-livefront's task in 4m 20s —— View job


Pull Request Review: Replace ZonedDateTime with Instant

Summary

This PR replaces ZonedDateTime with Instant across the entire codebase (112 files, +566/-680 lines). The migration is well-motivated: all dates are already in UTC, the Bitwarden SDK uses Instant, and removing the unnecessary timezone wrapping simplifies the code and eliminates conversion overhead.

The migration is clean and mechanically correct. Key areas verified:

  • Serialization compatibility: InstantSerializer preserves the same ISO 8601 format. Deserialization still parses through ZonedDateTime.parse().toInstant() for robust timezone handling. The addition of .withZone(ZoneOffset.UTC) on the serialization formatter is correct since Instant does not carry zone info.
  • Database compatibility: The InstantTypeConverter stores/retrieves the same Long (epoch seconds) as the old ZonedDateTimeTypeConverter, so no schema migration is needed.
  • Binary long conversion: getInstantFromBinaryLong/getBinaryLongFromInstant correctly replicate their ZonedDateTime predecessors with round-trip correctness verified by tests.
  • SDK-to-network conversions: All ZonedDateTime.ofInstant()/.toInstant() adapter code is correctly removed now that both sides use Instant.
  • Azure Blob Storage date header: RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC).format(clock.instant()) is semantically equivalent to the previous ZonedDateTime approach.
  • Parcelize compatibility: Instant implements java.io.Serializable, so @Parcelize state classes with Instant fields work correctly.
  • No stale ZonedDateTime usage: Only two remaining references -- InstantSerializer.kt (intentional, for robust deserialization) and LegacySecureStorageImpl.kt (out of scope, used for Android Keystore certificate generation).
  • Test coverage: Comprehensive -- serializer, type converter, binary long utils, and all consuming ViewModel/Repository/Manager tests are updated.

Findings

No issues found. The migration is consistent and correct across all changed files.


Re-reviewed after rebase onto latest main. No substantive code changes from the previous review.

reprompt = reprompt.toNetworkRepromptType(),
passwordHistory = passwordHistory?.toEncryptedNetworkPasswordHistoryList(),
lastKnownRevisionDate = ZonedDateTime.ofInstant(revisionDate, ZoneOffset.UTC),
lastKnownRevisionDate = revisionDate,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Little conversions like this are just not needed anymore.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 18, 2026

Logo
Checkmarx One – Scan Summary & Details7af56545-6cfd-4ff1-ac26-b5e8000e504e

Great job! No new security vulnerabilities introduced in this pull request

override fun deserialize(decoder: Decoder): Instant =
ZonedDateTime
.parse(decoder.decodeString(), dateTimeFormatterDeserialization)
.toInstant()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is one of the few remaining places that still uses the ZonedDateTime.

I did test out using Instant.parse(decoder.decodeString()) and did not see any negative ramification of the change but I am unsure of a good way to validate that it works for any and all variations the server could send back to us.

So for the moment, I have left it as a ZonedDateTime, which we know works, and then convert it to an Instant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Any concerns with migrating directly to Instant with this:

override fun deserialize(decoder: Decoder): Instant = Instant.parse(decoder.decodeString())

Copy link
Contributor

Choose a reason for hiding this comment

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

I like that ZonedDateTime is more permissive. I'm assuming the DateTimeFormatter pattern accepts . or : as a separator for a reason, and if : is received, Instant.parse() is going to fail. ZonedDateTime also handles various timezone offset formats more gracefully.

If we're not able to guarantee the server's format now and in the future, ZonedDateTime feels like the safer approach. The flexibility is worth the couple of extra lines, imo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fair enough, we will run with this for now.

@david-livefront david-livefront force-pushed the replace-zoned-date-time branch 2 times, most recently from 3362c29 to 964bf6b Compare February 18, 2026 19:46
@david-livefront david-livefront added the t:tech-debt Change Type - Tech debt label Feb 18, 2026
@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 92.42424% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.41%. Comparing base (d1cf808) to head (13b18e8).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...addedit/components/AddEditSendCustomDateChooser.kt 33.33% 2 Missing ⚠️
...bitwarden/data/platform/manager/PushManagerImpl.kt 50.00% 0 Missing and 1 partial ⚠️
...dedit/components/AddEditSendDeletionDateChooser.kt 0.00% 1 Missing ⚠️
...otlin/com/bitwarden/core/util/InstantExtensions.kt 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6554      +/-   ##
==========================================
- Coverage   86.45%   86.41%   -0.04%     
==========================================
  Files         786      802      +16     
  Lines       56712    57144     +432     
  Branches     8258     8255       -3     
==========================================
+ Hits        49028    49380     +352     
- Misses       4817     4904      +87     
+ Partials     2867     2860       -7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions bot removed the t:tech-debt Change Type - Tech debt label Feb 19, 2026
Copy link
Contributor

@SaintPatrck SaintPatrck left a comment

Choose a reason for hiding this comment

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

LGTM!

@david-livefront david-livefront marked this pull request as ready for review February 20, 2026 19:02
@david-livefront david-livefront requested a review from a team as a code owner February 20, 2026 19:02
@david-livefront
Copy link
Collaborator Author

Thanks @SaintPatrck

@david-livefront david-livefront added this pull request to the merge queue Feb 20, 2026
Merged via the queue into main with commit c6b4c49 Feb 20, 2026
29 checks passed
@david-livefront david-livefront deleted the replace-zoned-date-time branch February 20, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review app:authenticator Bitwarden Authenticator app context app:password-manager Bitwarden Password Manager app context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants