@@ -18,7 +18,7 @@ package com.duckduckgo.app.attributed.metrics.store
18
18
19
19
import com.duckduckgo.di.scopes.AppScope
20
20
import com.squareup.anvil.annotations.ContributesBinding
21
- import java.time.LocalDate
21
+ import java.time.*
22
22
import java.time.format.DateTimeFormatter
23
23
import java.time.temporal.ChronoUnit
24
24
import javax.inject.Inject
@@ -31,33 +31,41 @@ import javax.inject.Inject
31
31
* - Calculating days between dates
32
32
* - Generating dates relative to the current date
33
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.
34
+ * All dates are handled in Eastern Time (ET) and formatted as "yyyy-MM-dd" for consistency.
35
+ * This format is used for both storage and calculations. The timezone ensures that day
36
+ * boundaries align with business operations in ET.
36
37
*
37
38
* Example usage:
38
39
* ```
39
- * // Get today's date
40
- * val today = dateUtils.getCurrentDate() // returns "2025-10-03"
40
+ * // Get today's date in ET
41
+ * val today = dateUtils.getCurrentDate() // returns "2025-10-03" (if it's Oct 3rd in ET)
41
42
*
42
- * // Get a date 7 days ago
43
+ * // Get a date 7 days ago in ET
43
44
* val lastWeek = dateUtils.getDateMinusDays(7) // returns "2025-09-26"
44
45
*
45
- * // Calculate days since a specific date
46
+ * // Calculate days since a specific date in ET
47
+ * // Note: The calculation uses ET midnight as the boundary for day changes
46
48
* val daysSince = dateUtils.daysSince("2025-09-01") // returns number of days
47
49
* ```
50
+ *
51
+ * Note: All date operations use Eastern Time (ET) timezone. This means:
52
+ * - Day changes occur at midnight ET
53
+ * - Date comparisons and calculations are based on ET dates
54
+ * - The returned date strings represent dates in ET
48
55
*/
49
56
interface AttributedMetricsDateUtils {
50
57
/* *
51
- * Gets the current date formatted as "yyyy-MM-dd".
58
+ * Gets the current date in Eastern Time formatted as "yyyy-MM-dd".
52
59
*
53
- * @return The current date as a string in the format "yyyy-MM-dd"
60
+ * @return The current date in ET as a string in the format "yyyy-MM-dd"
54
61
*/
55
62
fun getCurrentDate (): String
56
63
57
64
/* *
58
- * Calculates the number of days between a given date and the current date.
65
+ * Calculates the number of days between a given date and the current date in Eastern Time.
66
+ * Day boundaries are determined using midnight ET.
59
67
*
60
- * @param date The reference date in "yyyy-MM-dd" format
68
+ * @param date The reference date in "yyyy-MM-dd" format (interpreted in ET)
61
69
* @return The number of days between the reference date and current date.
62
70
* Positive if the reference date is in the past,
63
71
* negative if it's in the future,
@@ -66,28 +74,35 @@ interface AttributedMetricsDateUtils {
66
74
fun daysSince (date : String ): Int
67
75
68
76
/* *
69
- * Gets a date that is a specified number of days before the current date.
77
+ * Gets a date that is a specified number of days before the current date in Eastern Time.
78
+ * Day boundaries are determined using midnight ET.
70
79
*
71
80
* @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
81
+ * @return The calculated date as a string in "yyyy-MM-dd" format (in ET)
73
82
*/
74
83
fun getDateMinusDays (days : Int ): String
75
84
}
76
85
77
86
@ContributesBinding(AppScope ::class )
78
87
class RealAttributedMetricsDateUtils @Inject constructor() : AttributedMetricsDateUtils {
79
- override fun getCurrentDate (): String = getCurrentLocalDate ().format(DATE_FORMATTER )
88
+ override fun getCurrentDate (): String = getCurrentZonedDateTime ().format(DATE_FORMATTER )
80
89
81
90
override fun daysSince (date : String ): Int {
82
- val initDate = LocalDate .parse(date, DATE_FORMATTER )
83
- return ChronoUnit .DAYS .between(initDate, getCurrentLocalDate()).toInt()
91
+ // Parse the input date and set it to start of day (midnight) in ET
92
+ val initDate = ZonedDateTime .of(
93
+ LocalDate .parse(date, DATE_FORMATTER ),
94
+ LocalTime .MIDNIGHT ,
95
+ ET_ZONE ,
96
+ )
97
+ return ChronoUnit .DAYS .between(initDate, getCurrentZonedDateTime()).toInt()
84
98
}
85
99
86
- override fun getDateMinusDays (days : Int ): String = getCurrentLocalDate ().minusDays(days.toLong()).format(DATE_FORMATTER )
100
+ override fun getDateMinusDays (days : Int ): String = getCurrentZonedDateTime ().minusDays(days.toLong()).format(DATE_FORMATTER )
87
101
88
- private fun getCurrentLocalDate (): LocalDate = LocalDate .now()
102
+ private fun getCurrentZonedDateTime (): ZonedDateTime = ZonedDateTime .now(ET_ZONE )
89
103
90
104
companion object {
91
105
private val DATE_FORMATTER = DateTimeFormatter .ofPattern(" yyyy-MM-dd" )
106
+ private val ET_ZONE = ZoneId .of(" America/New_York" )
92
107
}
93
108
}
0 commit comments