From e82d611a7829fe5f1ded9fb0503c0d0035af9c5f Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 3 Sep 2026 17:29:04 +0200 Subject: [PATCH 1/4] ref(time): Deprecate DateUtils.getCurrentDateTime (JAVA-571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It reads the wall clock into a java.util.Date at millisecond resolution. SentryDateProvider already owns wall time, is configurable, stubbable in tests and resolves finer, so there is no reason for new code to reach for the static. The class itself is not deprecated: its other fourteen statics are pure conversion and formatting helpers with no clock in them. Every caller keeps working and gets a suppression, because -Xlint:all -Werror turns the warning into a build failure. Each suppression carries a TODO [MAJOR] naming the replacement, since a suppressed warning is no longer a checklist entry — and nearly all of these callers are frozen until the next major anyway, as they stamp serialized timestamps. --- .../java/io/sentry/android/core/AndroidProfiler.java | 2 ++ .../android/core/AndroidTransactionProfiler.java | 2 ++ sentry/src/main/java/io/sentry/Breadcrumb.java | 6 ++++-- sentry/src/main/java/io/sentry/DateUtils.java | 6 +++++- sentry/src/main/java/io/sentry/ProfilingTraceData.java | 2 ++ sentry/src/main/java/io/sentry/SentryEvent.java | 2 ++ sentry/src/main/java/io/sentry/SentryReplayEvent.java | 2 ++ sentry/src/main/java/io/sentry/Session.java | 10 ++++++++++ .../src/main/java/io/sentry/cache/EnvelopeCache.java | 2 ++ .../io/sentry/clientreport/ClientReportRecorder.java | 2 ++ 10 files changed, 33 insertions(+), 3 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java index 3f569df5378..6f9501e4391 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java @@ -117,6 +117,8 @@ public AndroidProfiler( } @SuppressLint("NewApi") + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public @Nullable ProfileStartData start() { try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { // intervalUs is 0 only if there was a problem in the init diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java index e44ed746a08..baffeb2851c 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java @@ -89,6 +89,8 @@ public AndroidTransactionProfiler( () -> executorService); } + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public AndroidTransactionProfiler( final @NotNull Context context, final @NotNull BuildInfoProvider buildInfoProvider, diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index 24ac5561d10..10655243d61 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -99,7 +99,8 @@ public Breadcrumb(final long timestamp) { * @param options - the sentry options * @return the breadcrumb */ - @SuppressWarnings("unchecked") + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings({"unchecked", "deprecation"}) public static Breadcrumb fromMap( @NotNull Map map, @NotNull SentryOptions options) { @@ -915,7 +916,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger } public static final class Deserializer implements JsonDeserializer { - @SuppressWarnings("unchecked") + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings({"unchecked", "deprecation"}) @Override public @NotNull Breadcrumb deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { diff --git a/sentry/src/main/java/io/sentry/DateUtils.java b/sentry/src/main/java/io/sentry/DateUtils.java index 4be99b6322f..73191ec4f27 100644 --- a/sentry/src/main/java/io/sentry/DateUtils.java +++ b/sentry/src/main/java/io/sentry/DateUtils.java @@ -19,8 +19,12 @@ private DateUtils() {} * Get the current Date (UTC) * * @return the UTC Date + * @deprecated use {@code options.getDateProvider().now()}, which is configurable and resolves + * finer than a millisecond. */ - @SuppressWarnings("JavaUtilDate") + @Deprecated + // not @InlineMe: the replacement is options.getDateProvider().now(), not this method's body + @SuppressWarnings({"JavaUtilDate", "InlineMeSuggester"}) public static @NotNull Date getCurrentDateTime() { return new Date(); } diff --git a/sentry/src/main/java/io/sentry/ProfilingTraceData.java b/sentry/src/main/java/io/sentry/ProfilingTraceData.java index aee42fa07d4..f19f9414ce3 100644 --- a/sentry/src/main/java/io/sentry/ProfilingTraceData.java +++ b/sentry/src/main/java/io/sentry/ProfilingTraceData.java @@ -75,6 +75,8 @@ private ProfilingTraceData() { this(new File("dummy"), NoOpTransaction.getInstance()); } + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public ProfilingTraceData( final @NotNull File traceFile, final @NotNull ITransaction transaction) { this( diff --git a/sentry/src/main/java/io/sentry/SentryEvent.java b/sentry/src/main/java/io/sentry/SentryEvent.java index 8b8575fe7ec..7a1edde8cc1 100644 --- a/sentry/src/main/java/io/sentry/SentryEvent.java +++ b/sentry/src/main/java/io/sentry/SentryEvent.java @@ -103,6 +103,8 @@ public SentryEvent(final @Nullable Throwable throwable) { this.throwable = throwable; } + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public SentryEvent() { this(new SentryId(), DateUtils.getCurrentDateTime()); } diff --git a/sentry/src/main/java/io/sentry/SentryReplayEvent.java b/sentry/src/main/java/io/sentry/SentryReplayEvent.java index 0ed9dbf72f3..87188210b05 100644 --- a/sentry/src/main/java/io/sentry/SentryReplayEvent.java +++ b/sentry/src/main/java/io/sentry/SentryReplayEvent.java @@ -52,6 +52,8 @@ public static final class Deserializer implements JsonDeserializer { private @Nullable List segmentNames; private @Nullable Map unknown; + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public SentryReplayEvent() { super(); this.replayId = new SentryId(); diff --git a/sentry/src/main/java/io/sentry/Session.java b/sentry/src/main/java/io/sentry/Session.java index 598fe994fdc..a2296b5ebfc 100644 --- a/sentry/src/main/java/io/sentry/Session.java +++ b/sentry/src/main/java/io/sentry/Session.java @@ -112,6 +112,8 @@ public Session( this.abnormalMechanism = abnormalMechanism; } + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public Session( @Nullable String distinctId, final @Nullable User user, @@ -221,6 +223,8 @@ public boolean hasNonTerminatingUnhandledError() { * * @return whether the session was updated, i.e. false if it had already reached a terminal state */ + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") @ApiStatus.Internal public boolean recordNonTerminatingUnhandledError() { try (final @NotNull ISentryLifecycleToken ignored = sessionLock.acquire()) { @@ -242,6 +246,8 @@ public boolean recordNonTerminatingUnhandledError() { } /** Ends a session and update its values */ + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public void end() { end(DateUtils.getCurrentDateTime()); } @@ -251,6 +257,8 @@ public void end() { * * @param timestamp the timestamp or null */ + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public void end(final @Nullable Date timestamp) { try (final @NotNull ISentryLifecycleToken ignored = sessionLock.acquire()) { init = null; @@ -301,6 +309,8 @@ public boolean update( * @param abnormalMechanism the mechanism which caused the session to be abnormal * @return if the session has been updated */ + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") public boolean update( final @Nullable State status, final @Nullable String userAgent, diff --git a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java index d3f473cfbc9..393e29d9049 100644 --- a/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java +++ b/sentry/src/main/java/io/sentry/cache/EnvelopeCache.java @@ -279,6 +279,8 @@ private void tryEndPreviousSession(final @NotNull Hint hint) { } } + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") private void writeCrashMarkerFile() { final File crashMarkerFile = new File(options.getCacheDirPath(), CRASH_MARKER_FILE); try (final OutputStream outputStream = new FileOutputStream(crashMarkerFile)) { diff --git a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java index ba6d60e2563..2aa14cb2e59 100644 --- a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java +++ b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java @@ -170,6 +170,8 @@ private long itemCountFromHeader(final @NotNull SentryEnvelopeItem envelopeItem) return itemCount != null ? itemCount : 1L; } + // TODO [MAJOR]: replace DateUtils.getCurrentDateTime() with options.getDateProvider().now() + @SuppressWarnings("deprecation") @Nullable ClientReport resetCountsAndGenerateClientReport() { final Date currentDate = DateUtils.getCurrentDateTime(); From a432b871d0f3796625a114980cb457e4ba861736 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 3 Sep 2026 17:29:04 +0200 Subject: [PATCH 2/4] ref(time): Deprecate the legacy current-date providers (JAVA-571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One interface carried two incompatible clocks: CurrentDateProvider is System.currentTimeMillis() and AndroidCurrentDateProvider is SystemClock.uptimeMillis(). Nothing in the name or the type said which, which is the defect JAVA-571 is about — a field declared ICurrentDateProvider accepts either, and the two disagree by however long the device has been suspended. MonotonicClock names the guarantee it gives, and SentryDateProvider covers wall time. The annotation goes on the members rather than the types: the Android modules compile at Java 8, where javac still warns on imports of a deprecated type, and an import declaration cannot carry a @SuppressWarnings. Deprecating getCurrentTimeMillis() and getInstance() warns any caller just the same, and every warning it produces lands somewhere that can be suppressed. Each suppression names the replacement the site should take at the next major. Several of these must stay on the wall clock: AnrV2Integration, TombstoneIntegration and ApplicationExitInfoHistoryDispatcher compare against epoch ApplicationExitInfo timestamps, and LifecycleWatcher against Session.getStarted(). --- .../java/io/sentry/android/core/ANRWatchDog.java | 1 + .../android/core/AndroidOptionsInitializer.java | 3 +++ .../io/sentry/android/core/AnrV2Integration.java | 3 +++ .../core/ApplicationExitInfoHistoryDispatcher.java | 3 +++ .../io/sentry/android/core/LifecycleWatcher.java | 9 +++++++++ .../android/core/MemoryLimiterIntegration.java | 3 +++ .../sentry/android/core/TombstoneIntegration.java | 3 +++ .../android/core/cache/AndroidEnvelopeCache.java | 3 +++ .../internal/util/AndroidCurrentDateProvider.java | 2 ++ .../android/core/internal/util/Debouncer.java | 2 ++ .../io/sentry/transport/CurrentDateProvider.java | 14 ++++++++++++++ .../io/sentry/transport/ICurrentDateProvider.java | 11 ++++++++++- 12 files changed, 56 insertions(+), 1 deletion(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ANRWatchDog.java b/sentry-android-core/src/main/java/io/sentry/android/core/ANRWatchDog.java index 6f8fedcecea..3ca87d60edb 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ANRWatchDog.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ANRWatchDog.java @@ -123,6 +123,7 @@ final class ANRWatchDog extends Thread { } } + @SuppressWarnings("deprecation") @Override public void run() { // right when the watchdog gets started, let's assume there's no ANR diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 8ba216386b2..d700e07c25f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -412,6 +412,9 @@ private static void setupProfiler( } } + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider or + // MonotonicTicker + @SuppressWarnings("deprecation") static void installDefaultIntegrations( final @NotNull Context context, final @NotNull SentryAndroidOptions options, diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java index 5e5500297f8..343f065fa68 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java @@ -50,6 +50,9 @@ public class AnrV2Integration implements Integration, Closeable { private final @NotNull ICurrentDateProvider dateProvider; private @Nullable SentryAndroidOptions options; + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider; the epoch + // comparison it feeds must stay wall time + @SuppressWarnings("deprecation") public AnrV2Integration(final @NotNull Context context) { // using CurrentDateProvider instead of AndroidCurrentDateProvider as AppExitInfo uses // System.currentTimeMillis diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoHistoryDispatcher.java b/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoHistoryDispatcher.java index b60cd7e6914..f00875faf50 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoHistoryDispatcher.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoHistoryDispatcher.java @@ -53,6 +53,9 @@ final class ApplicationExitInfoHistoryDispatcher implements Runnable { private final @NotNull ApplicationExitInfoPolicy policy; private final long threshold; + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider; the epoch + // comparison it feeds must stay wall time + @SuppressWarnings("deprecation") ApplicationExitInfoHistoryDispatcher( final @NotNull Context context, final @NotNull IScopes scopes, diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java index ca874e714e1..91a0ead7c09 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java @@ -28,6 +28,9 @@ final class LifecycleWatcher implements AppState.AppStateListener { private final @NotNull ICurrentDateProvider currentDateProvider; + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider; the epoch + // comparison it feeds must stay wall time + @SuppressWarnings("deprecation") LifecycleWatcher( final @NotNull IScopes scopes, final long sessionIntervalMillis, @@ -60,6 +63,9 @@ public void onForeground() { addAppBreadcrumb("foreground"); } + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider; the epoch + // comparison it feeds must stay wall time + @SuppressWarnings("deprecation") private void startSession() { cancelTask(); @@ -90,6 +96,9 @@ private void startSession() { // App went to background and triggered this callback after 700ms // as no new screen was shown + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider; the epoch + // comparison it feeds must stay wall time + @SuppressWarnings("deprecation") @Override public void onBackground() { final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis(); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/MemoryLimiterIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/MemoryLimiterIntegration.java index f259250a733..f288129970a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/MemoryLimiterIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/MemoryLimiterIntegration.java @@ -89,6 +89,9 @@ public final class MemoryLimiterIntegration implements Integration, Closeable { private final @NotNull BuildInfoProvider buildInfoProvider; private @Nullable SentryAndroidOptions androidOptions; + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider; the epoch + // comparison it feeds must stay wall time + @SuppressWarnings("deprecation") public MemoryLimiterIntegration( final @NotNull Context context, final @NotNull BuildInfoProvider buildInfoProvider) { // Use CurrentDateProvider instead of AndroidCurrentDateProvider as ApplicationExitInfo uses diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/TombstoneIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/TombstoneIntegration.java index 43de4034ec5..c7de592cb81 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/TombstoneIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/TombstoneIntegration.java @@ -54,6 +54,9 @@ public class TombstoneIntegration implements Integration, Closeable { private final @NotNull ICurrentDateProvider dateProvider; private @Nullable SentryAndroidOptions options; + // TODO [MAJOR]: replace CurrentDateProvider (wall clock) with SentryDateProvider; the epoch + // comparison it feeds must stay wall time + @SuppressWarnings("deprecation") public TombstoneIntegration(final @NotNull Context context) { // using CurrentDateProvider instead of AndroidCurrentDateProvider as AppExitInfo uses // System.currentTimeMillis diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java b/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java index 13148097289..1158d186a31 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/cache/AndroidEnvelopeCache.java @@ -67,6 +67,9 @@ public boolean storeEnvelope(@NotNull SentryEnvelope envelope, @NotNull Hint hin return storeInternalAndroid(envelope, hint); } + // TODO [MAJOR]: this reading must stay paired with AppStartMetrics.getStartUptimeMs(), + // which is SystemClock.uptimeMillis(), so MonotonicTicker is not a drop-in replacement + @SuppressWarnings("deprecation") private boolean storeInternalAndroid(@NotNull SentryEnvelope envelope, @NotNull Hint hint) { final boolean didStore = super.storeEnvelope(envelope, hint); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidCurrentDateProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidCurrentDateProvider.java index b93beb4f578..b06d0896135 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidCurrentDateProvider.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidCurrentDateProvider.java @@ -19,6 +19,7 @@ @ApiStatus.Internal public final class AndroidCurrentDateProvider implements ICurrentDateProvider { + @SuppressWarnings("deprecation") private static final ICurrentDateProvider instance = new AndroidCurrentDateProvider(); /** @@ -32,6 +33,7 @@ public static ICurrentDateProvider getInstance() { private AndroidCurrentDateProvider() {} @Override + @SuppressWarnings("deprecation") public long getCurrentTimeMillis() { return SystemClock.uptimeMillis(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/Debouncer.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/Debouncer.java index 8808947c0cc..57f02eb95f4 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/Debouncer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/Debouncer.java @@ -30,6 +30,8 @@ public Debouncer( * @return true if the execution should be debounced due to maxExecutions executions being made * within waitTimeMs, otherwise false. */ + // TODO [MAJOR]: replace AndroidCurrentDateProvider with MonotonicTicker + @SuppressWarnings("deprecation") public boolean checkForDebounce() { final long now = timeProvider.getCurrentTimeMillis(); if (lastExecutionTime.get() == 0 || (lastExecutionTime.get() + waitTimeMs) <= now) { diff --git a/sentry/src/main/java/io/sentry/transport/CurrentDateProvider.java b/sentry/src/main/java/io/sentry/transport/CurrentDateProvider.java index 9421baac501..26caf99616a 100644 --- a/sentry/src/main/java/io/sentry/transport/CurrentDateProvider.java +++ b/sentry/src/main/java/io/sentry/transport/CurrentDateProvider.java @@ -2,11 +2,24 @@ import org.jetbrains.annotations.ApiStatus; +/** + * The wall clock, which jumps when the device time changes. + * + *

Superseded by {@link io.sentry.SentryDateProvider} for timestamps and {@link + * io.sentry.time.MonotonicTicker} for intervals. + */ @ApiStatus.Internal public final class CurrentDateProvider implements ICurrentDateProvider { + @SuppressWarnings("deprecation") private static final ICurrentDateProvider instance = new CurrentDateProvider(); + /** + * @deprecated use {@link io.sentry.SentryDateProvider} for a timestamp, or {@link + * io.sentry.time.MonotonicTicker} to measure an interval. + */ + @Deprecated + @SuppressWarnings("deprecation") public static ICurrentDateProvider getInstance() { return instance; } @@ -14,6 +27,7 @@ public static ICurrentDateProvider getInstance() { private CurrentDateProvider() {} @Override + @SuppressWarnings("deprecation") public final long getCurrentTimeMillis() { return System.currentTimeMillis(); } diff --git a/sentry/src/main/java/io/sentry/transport/ICurrentDateProvider.java b/sentry/src/main/java/io/sentry/transport/ICurrentDateProvider.java index b4f86cf2d33..0be0a999465 100644 --- a/sentry/src/main/java/io/sentry/transport/ICurrentDateProvider.java +++ b/sentry/src/main/java/io/sentry/transport/ICurrentDateProvider.java @@ -2,7 +2,13 @@ import org.jetbrains.annotations.ApiStatus; -/** Date Provider to make the Transport unit testable */ +/** + * Date Provider to make the Transport unit testable + * + *

Superseded by {@link io.sentry.time.MonotonicTicker}. The name here does not say which clock + * the value comes from, and implementations disagreed: {@link CurrentDateProvider} returns wall + * time while {@code AndroidCurrentDateProvider} returns uptime, through this one type. + */ @ApiStatus.Internal public interface ICurrentDateProvider { @@ -10,6 +16,9 @@ public interface ICurrentDateProvider { * Returns the current time in millis * * @return the time in millis + * @deprecated use {@link io.sentry.time.MonotonicTicker} to measure an interval, or {@link + * io.sentry.SentryDateProvider} for a wall-clock timestamp. */ + @Deprecated long getCurrentTimeMillis(); } From 80e5974cd05e06557386fd7bff7c175179d6e941 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 3 Sep 2026 17:29:05 +0200 Subject: [PATCH 3/4] ref(android): Deprecate AndroidDateUtils.getCurrentSentryDateTime (JAVA-571) Its own javadoc already told callers to prefer options.getDateProvider(); this makes the compiler say so. The static holder cannot be configured or stubbed, which is the whole reason the note was there. Annotated on the method rather than the class, for the same import reason as the current-date providers. --- .../android/core/ActivityLifecycleIntegration.java | 12 ++++++++++++ .../io/sentry/android/core/AndroidDateUtils.java | 3 +++ .../performance/ActivityLifecycleSpanHelper.java | 3 +++ 3 files changed, 18 insertions(+) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java index 505bb84ea2c..a843c0a30f2 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java @@ -549,6 +549,9 @@ private void finishTransaction( } } + // TODO [MAJOR]: replace AndroidDateUtils.getCurrentSentryDateTime() with + // options.getDateProvider().now() + @SuppressWarnings("deprecation") @Override public void onActivityPreCreated( final @NotNull Activity activity, final @Nullable Bundle savedInstanceState) { @@ -604,6 +607,9 @@ public void onActivityPostCreated( } } + // TODO [MAJOR]: replace AndroidDateUtils.getCurrentSentryDateTime() with + // options.getDateProvider().now() + @SuppressWarnings("deprecation") @Override public void onActivityPreStarted(final @NotNull Activity activity) { final ActivityLifecycleSpanHelper helper = activitySpanHelpers.get(activity); @@ -672,6 +678,9 @@ public void onActivityPostResumed(@NotNull Activity activity) { // empty override, required to avoid a api-level breaking super.onActivityPostResumed() calls } + // TODO [MAJOR]: replace AndroidDateUtils.getCurrentSentryDateTime() with + // options.getDateProvider().now() + @SuppressWarnings("deprecation") @Override public void onActivityPrePaused(@NotNull Activity activity) { // only executed if API >= 29 otherwise it happens on onActivityPaused @@ -1094,6 +1103,9 @@ private void onHeadlessAppStart() { * Standalone-only: this is only registered as a listener when standalone app start tracing is * enabled. */ + // TODO [MAJOR]: replace AndroidDateUtils.getCurrentSentryDateTime() with + // options.getDateProvider().now() + @SuppressWarnings("deprecation") private @Nullable AppStartExtension.ExtendedAppStart onExtendAppStartRequested() { if (scopes == null || options == null diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidDateUtils.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidDateUtils.java index 55c1e2c7cec..6e8d940e21f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidDateUtils.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidDateUtils.java @@ -17,7 +17,10 @@ public final class AndroidDateUtils { * invocations. * * @return the UTC SentryDate + * @deprecated use {@code options.getDateProvider()}. This static holder cannot be configured or + * stubbed, which is why the note above already asked callers to prefer the options. */ + @Deprecated public static @NotNull SentryDate getCurrentSentryDateTime() { return dateProvider.now(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java b/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java index accb56db0dc..ae58370a16f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java @@ -69,6 +69,9 @@ public void createAndStopOnStartSpan(final @Nullable ISpan parentSpan) { return onStartStartTimestamp; } + // TODO [MAJOR]: replace AndroidDateUtils.getCurrentSentryDateTime() with + // options.getDateProvider().now() + @SuppressWarnings("deprecation") public void saveSpanToAppStartMetrics() { if (onCreateSpan == null || onStartSpan == null) { return; From 3890d87f966d4aee4fe0ab7751929ee95bfa4719 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Thu, 3 Sep 2026 17:29:05 +0200 Subject: [PATCH 4/4] changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b842bb2abc8..44b432dfb79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,12 +18,17 @@ - Gradle builds now resolve all Sentry SDK modules to the same version, the highest one present, because every module depends on `sentry-bom` as a platform ([#6466](https://github.com/getsentry/sentry-java/pull/6466)) - Before, a build could mix module versions, for example a newer `sentry-android-core` with an older `sentry-android-fragment`, which failed at runtime. Maven builds resolve as before. - `sentry-bom` now also includes `sentry-android-distribution`. +- Deprecate `DateUtils.getCurrentDateTime()` in favor of `options.getDateProvider().now()`, which is configurable and resolves finer than a millisecond ([#6043](https://github.com/getsentry/sentry-java/pull/6043)) ### Fixes - Session Replay: Fix `SentryOkHttpInterceptor` hanging forever on responses whose body has no known length, such as Server-Sent Events or a gzipped response, when network details are enabled for the URL ([#6231](https://github.com/getsentry/sentry-java/pull/6231)) - Stop marking an OkHttp call span as failed when a connection attempt to one of the host's addresses fails but OkHttp connects on another ([#6472](https://github.com/getsentry/sentry-java/pull/6472)) +### Internal + +- Deprecate `ICurrentDateProvider.getCurrentTimeMillis()`, `CurrentDateProvider.getInstance()` and `AndroidDateUtils.getCurrentSentryDateTime()` in favor of `MonotonicTicker` and `SentryDateProvider` ([#6043](https://github.com/getsentry/sentry-java/pull/6043)) + ### Dependencies - Bump OkHttp from 4.9.2 to 5.5.1 ([#6472](https://github.com/getsentry/sentry-java/pull/6472))