Skip to content

Commit 28521b7

Browse files
committed
refactor date usage and complete date api
1 parent 9fd5ba3 commit 28521b7

File tree

8 files changed

+355
-88
lines changed

8 files changed

+355
-88
lines changed

attributed-metrics/attributed-metrics-impl/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ dependencies {
5454
testImplementation "androidx.lifecycle:lifecycle-runtime-testing:_"
5555
testImplementation project(path: ':common-test')
5656
testImplementation project(':data-store-test')
57+
testImplementation project(':feature-toggles-test')
5758
testImplementation CashApp.turbine
5859
testImplementation Testing.robolectric
5960
testImplementation(KotlinX.coroutines.test) {

attributed-metrics/attributed-metrics-impl/src/main/java/com/duckduckgo/app/attributed/metrics/impl/AttributedMetricsState.kt

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package com.duckduckgo.app.attributed.metrics.impl
1919
import androidx.lifecycle.LifecycleOwner
2020
import com.duckduckgo.app.attributed.metrics.AttributedMetricsConfigFeature
2121
import com.duckduckgo.app.attributed.metrics.store.AttributedMetricsDataStore
22-
import com.duckduckgo.app.attributed.metrics.store.DateProvider
22+
import com.duckduckgo.app.attributed.metrics.store.AttributedMetricsDateUtils
2323
import com.duckduckgo.app.di.AppCoroutineScope
2424
import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver
2525
import com.duckduckgo.app.statistics.api.AtbLifecyclePlugin
@@ -33,9 +33,6 @@ import dagger.SingleInstanceIn
3333
import kotlinx.coroutines.CoroutineScope
3434
import kotlinx.coroutines.launch
3535
import logcat.logcat
36-
import java.time.LocalDate
37-
import java.time.format.DateTimeFormatter
38-
import java.time.temporal.ChronoUnit
3936
import javax.inject.Inject
4037

4138
/**
@@ -72,7 +69,7 @@ class RealAttributedMetricsState @Inject constructor(
7269
private val dataStore: AttributedMetricsDataStore,
7370
private val attributedMetricsConfigFeature: AttributedMetricsConfigFeature,
7471
private val appBuildConfig: AppBuildConfig,
75-
private val dateProvider: DateProvider,
72+
private val attributedMetricsDateUtils: AttributedMetricsDateUtils,
7673
) : AttributedMetricsState, MainProcessLifecycleObserver, AtbLifecyclePlugin, PrivacyConfigCallbackPlugin {
7774

7875
override fun onCreate(owner: LifecycleOwner) {
@@ -99,7 +96,7 @@ class RealAttributedMetricsState @Inject constructor(
9996
logcat(tag = "AttributedMetrics") {
10097
"Setting initialization date for Attributed Metrics"
10198
}
102-
val currentDate = dateProvider.getCurrentDate()
99+
val currentDate = attributedMetricsDateUtils.getCurrentDate()
103100
dataStore.setInitializationDate(currentDate)
104101
if (appBuildConfig.isAppReinstall()) {
105102
logcat(tag = "AttributedMetrics") {
@@ -140,10 +137,7 @@ class RealAttributedMetricsState @Inject constructor(
140137
return
141138
}
142139

143-
val initLocalDate = LocalDate.parse(initDate, DATE_FORMATTER)
144-
val currentDate = LocalDate.now()
145-
146-
val daysSinceInit = ChronoUnit.DAYS.between(initLocalDate, currentDate)
140+
val daysSinceInit = attributedMetricsDateUtils.daysSince(initDate)
147141
val isWithinPeriod = daysSinceInit <= COLLECTION_PERIOD_DAYS
148142
val isClientActive = isWithinPeriod && dataStore.isActive()
149143

@@ -154,7 +148,6 @@ class RealAttributedMetricsState @Inject constructor(
154148
}
155149

156150
companion object {
157-
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd")
158151
private const val COLLECTION_PERIOD_DAYS = 168 // 24 weeks * 7 days (6 months in weeks)
159152
}
160153
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) 2024 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.attributed.metrics.store
18+
19+
import com.duckduckgo.di.scopes.AppScope
20+
import com.squareup.anvil.annotations.ContributesBinding
21+
import java.time.LocalDate
22+
import java.time.format.DateTimeFormatter
23+
import java.time.temporal.ChronoUnit
24+
import javax.inject.Inject
25+
26+
/**
27+
* Utility interface for handling date operations in the Attributed Metrics feature.
28+
*
29+
* This interface provides methods for:
30+
* - Getting the current date in a standardized format
31+
* - Calculating days between dates
32+
* - Generating dates relative to the current date
33+
*
34+
* All dates are handled in the format "yyyy-MM-dd" for consistency across the feature.
35+
* This format is used for both storage and calculations.
36+
*
37+
* Example usage:
38+
* ```
39+
* // Get today's date
40+
* val today = dateUtils.getCurrentDate() // returns "2025-10-03"
41+
*
42+
* // Get a date 7 days ago
43+
* val lastWeek = dateUtils.getDateMinusDays(7) // returns "2025-09-26"
44+
*
45+
* // Calculate days since a specific date
46+
* val daysSince = dateUtils.daysSince("2025-09-01") // returns number of days
47+
* ```
48+
*/
49+
interface AttributedMetricsDateUtils {
50+
/**
51+
* Gets the current date formatted as "yyyy-MM-dd".
52+
*
53+
* @return The current date as a string in the format "yyyy-MM-dd"
54+
*/
55+
fun getCurrentDate(): String
56+
57+
/**
58+
* Calculates the number of days between a given date and the current date.
59+
*
60+
* @param date The reference date in "yyyy-MM-dd" format
61+
* @return The number of days between the reference date and current date.
62+
* Positive if the reference date is in the past,
63+
* negative if it's in the future,
64+
* zero if it's today.
65+
*/
66+
fun daysSince(date: String): Int
67+
68+
/**
69+
* Gets a date that is a specified number of days before the current date.
70+
*
71+
* @param days The number of days to subtract from the current date
72+
* @return The calculated date as a string in "yyyy-MM-dd" format
73+
*/
74+
fun getDateMinusDays(days: Int): String
75+
}
76+
77+
@ContributesBinding(AppScope::class)
78+
class RealAttributedMetricsDateUtils @Inject constructor() : AttributedMetricsDateUtils {
79+
override fun getCurrentDate(): String = getCurrentLocalDate().format(DATE_FORMATTER)
80+
81+
override fun daysSince(date: String): Int {
82+
val initDate = LocalDate.parse(date, DATE_FORMATTER)
83+
return ChronoUnit.DAYS.between(initDate, getCurrentLocalDate()).toInt()
84+
}
85+
86+
override fun getDateMinusDays(days: Int): String = getCurrentLocalDate().minusDays(days.toLong()).format(DATE_FORMATTER)
87+
88+
private fun getCurrentLocalDate(): LocalDate = LocalDate.now()
89+
90+
companion object {
91+
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd")
92+
}
93+
}

attributed-metrics/attributed-metrics-impl/src/main/java/com/duckduckgo/app/attributed/metrics/store/DateProvider.kt

Lines changed: 0 additions & 40 deletions
This file was deleted.

attributed-metrics/attributed-metrics-impl/src/main/java/com/duckduckgo/app/attributed/metrics/store/EventRepository.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ interface EventRepository {
3838
@ContributesBinding(AppScope::class)
3939
class RealEventRepository @Inject constructor(
4040
private val eventDao: EventDao,
41-
private val dateProvider: DateProvider,
41+
private val attributedMetricsDateUtils: AttributedMetricsDateUtils,
4242
@AppCoroutineScope private val coroutineScope: CoroutineScope,
4343
) : EventRepository {
4444
override suspend fun collectEvent(eventName: String) {
45-
val today = dateProvider.getCurrentDate()
45+
val today = attributedMetricsDateUtils.getCurrentDate()
4646
val currentCount = eventDao.getEventCount(eventName, today)
4747

4848
if (currentCount == null) {
@@ -56,7 +56,7 @@ class RealEventRepository @Inject constructor(
5656
eventName: String,
5757
days: Int,
5858
): EventStats {
59-
val startDay = dateProvider.getDateMinusDays(days)
59+
val startDay = attributedMetricsDateUtils.getDateMinusDays(days)
6060

6161
val daysWithEvents = eventDao.getDaysWithEvents(eventName, startDay)
6262
val totalEvents = eventDao.getTotalEvents(eventName, startDay) ?: 0
@@ -71,7 +71,7 @@ class RealEventRepository @Inject constructor(
7171

7272
override suspend fun deleteOldEvents(olderThanDays: Int) {
7373
coroutineScope.launch {
74-
val cutoffDay = dateProvider.getDateMinusDays(olderThanDays)
74+
val cutoffDay = attributedMetricsDateUtils.getDateMinusDays(olderThanDays)
7575
eventDao.deleteEventsOlderThan(cutoffDay)
7676
}
7777
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.attributed.metrics
18+
19+
import com.duckduckgo.app.attributed.metrics.store.AttributedMetricsDateUtils
20+
import java.time.LocalDate
21+
import java.time.format.DateTimeFormatter
22+
import java.time.temporal.ChronoUnit
23+
24+
class FakeAttributedMetricsDateUtils(var testDate: LocalDate) : AttributedMetricsDateUtils {
25+
26+
override fun getCurrentDate(): String = getCurrentLocalDate().format(DATE_FORMATTER)
27+
28+
override fun daysSince(date: String): Int {
29+
val initDate = LocalDate.parse(date, DATE_FORMATTER)
30+
return ChronoUnit.DAYS.between(initDate, getCurrentLocalDate()).toInt()
31+
}
32+
33+
override fun getDateMinusDays(days: Int): String = getCurrentLocalDate().minusDays(days.toLong()).format(DATE_FORMATTER)
34+
35+
private fun getCurrentLocalDate(): LocalDate = testDate
36+
37+
companion object {
38+
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd")
39+
}
40+
}

0 commit comments

Comments
 (0)