@@ -18,7 +18,7 @@ package com.duckduckgo.app.attributed.metrics.store
1818
1919import com.duckduckgo.di.scopes.AppScope
2020import com.squareup.anvil.annotations.ContributesBinding
21- import java.time.LocalDate
21+ import java.time.*
2222import java.time.format.DateTimeFormatter
2323import java.time.temporal.ChronoUnit
2424import javax.inject.Inject
@@ -31,33 +31,41 @@ import javax.inject.Inject
3131 * - Calculating days between dates
3232 * - Generating dates relative to the current date
3333 *
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.
3637 *
3738 * Example usage:
3839 * ```
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)
4142 *
42- * // Get a date 7 days ago
43+ * // Get a date 7 days ago in ET
4344 * val lastWeek = dateUtils.getDateMinusDays(7) // returns "2025-09-26"
4445 *
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
4648 * val daysSince = dateUtils.daysSince("2025-09-01") // returns number of days
4749 * ```
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
4855 */
4956interface AttributedMetricsDateUtils {
5057 /* *
51- * Gets the current date formatted as "yyyy-MM-dd".
58+ * Gets the current date in Eastern Time formatted as "yyyy-MM-dd".
5259 *
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"
5461 */
5562 fun getCurrentDate (): String
5663
5764 /* *
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.
5967 *
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)
6169 * @return The number of days between the reference date and current date.
6270 * Positive if the reference date is in the past,
6371 * negative if it's in the future,
@@ -66,28 +74,35 @@ interface AttributedMetricsDateUtils {
6674 fun daysSince (date : String ): Int
6775
6876 /* *
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.
7079 *
7180 * @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)
7382 */
7483 fun getDateMinusDays (days : Int ): String
7584}
7685
7786@ContributesBinding(AppScope ::class )
7887class RealAttributedMetricsDateUtils @Inject constructor() : AttributedMetricsDateUtils {
79- override fun getCurrentDate (): String = getCurrentLocalDate ().format(DATE_FORMATTER )
88+ override fun getCurrentDate (): String = getCurrentZonedDateTime ().format(DATE_FORMATTER )
8089
8190 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()
8498 }
8599
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 )
87101
88- private fun getCurrentLocalDate (): LocalDate = LocalDate .now()
102+ private fun getCurrentZonedDateTime (): ZonedDateTime = ZonedDateTime .now(ET_ZONE )
89103
90104 companion object {
91105 private val DATE_FORMATTER = DateTimeFormatter .ofPattern(" yyyy-MM-dd" )
106+ private val ET_ZONE = ZoneId .of(" US/Eastern" )
92107 }
93108}
0 commit comments