diff --git a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/utils/MidnightTime.kt b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/utils/MidnightTime.kt index caab607afc..265aab9074 100644 --- a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/utils/MidnightTime.kt +++ b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/utils/MidnightTime.kt @@ -1,64 +1,86 @@ package app.aaps.core.interfaces.utils import android.util.LongSparseArray -import java.util.Calendar +import androidx.annotation.VisibleForTesting +import java.time.Instant +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.ZoneId object MidnightTime { + @VisibleForTesting val times = LongSparseArray() - private var hits: Long = 0 - private var misses: Long = 0 private const val THRESHOLD = 100000 - fun calc(): Long { - val c = Calendar.getInstance() - c[Calendar.HOUR_OF_DAY] = 0 - c[Calendar.MINUTE] = 0 - c[Calendar.SECOND] = 0 - c[Calendar.MILLISECOND] = 0 - return c.timeInMillis - } + /** + * Epoch time of last midnight + * + * @return epoch millis + */ + fun calc(): Long = + LocalDateTime.now().atZone(ZoneId.systemDefault()) + .with(LocalTime.of(0, 0, 0, 0)) + .toInstant().toEpochMilli() - fun calcPlusMinutes(minutes: Int): Long { - val h = minutes / 60 + /** + * Today's time with 'minutes' from midnight + * + * @param minutes minutes to add + * @return epoch millis of today with hh:mm:00 + */ + fun calcMidnightPlusMinutes(minutes: Int): Long { + val h = (minutes / 60) % 24 val m = minutes % 60 - val c = Calendar.getInstance() - c[Calendar.HOUR_OF_DAY] = h - c[Calendar.MINUTE] = m - c[Calendar.SECOND] = 0 - c[Calendar.MILLISECOND] = 0 - return c.timeInMillis + return LocalDateTime.now().atZone(ZoneId.systemDefault()) + .with(LocalTime.of(h, m, 0, 0)) + .toInstant().toEpochMilli() } + /** + * Epoch time of last midnight before 'time' + * + * @param time time of the day + * @return epoch millis + */ fun calc(time: Long): Long { - var m: Long? synchronized(times) { - m = times[time] - if (m != null) { - ++hits - return m!! - } - val c = Calendar.getInstance() - c.timeInMillis = time - c[Calendar.HOUR_OF_DAY] = 0 - c[Calendar.MINUTE] = 0 - c[Calendar.SECOND] = 0 - c[Calendar.MILLISECOND] = 0 - m = c.timeInMillis - times.append(time, m) - ++misses + val m = times[time] ?: Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()) + .with(LocalTime.of(0, 0, 0, 0)) + .toInstant().toEpochMilli() if (times.size() > THRESHOLD) resetCache() + return m } - return m!! } + /** + * Epoch time of last midnight 'days' back + * + * @param daysBack how many days back + * @return epoch millis of midnight + */ + fun calcDaysBack(daysBack: Long): Long = + LocalDateTime.now().atZone(ZoneId.systemDefault()) + .with(LocalTime.of(0, 0, 0, 0)) + .minusDays(daysBack) + .toInstant().toEpochMilli() + + /** + * Epoch time of last midnight 'days' back from time + * + * @param time start time + * @param daysBack how many days back + * @return epoch millis of midnight + */ + fun calcDaysBack(time: Long, daysBack: Long): Long = + Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()) + .with(LocalTime.of(0, 0, 0, 0)) + .minusDays(daysBack) + .toInstant().toEpochMilli() + + @VisibleForTesting fun resetCache() { - hits = 0 - misses = 0 times.clear() } - - fun log(): String = - "Hits: " + hits + " misses: " + misses + " stored: " + times.size() } \ No newline at end of file diff --git a/core/main/src/test/kotlin/app/aaps/core/interfaces/utils/MidnightTimeTest.kt b/core/main/src/test/kotlin/app/aaps/core/interfaces/utils/MidnightTimeTest.kt index 79c9ccb624..a4e86b2259 100644 --- a/core/main/src/test/kotlin/app/aaps/core/interfaces/utils/MidnightTimeTest.kt +++ b/core/main/src/test/kotlin/app/aaps/core/interfaces/utils/MidnightTimeTest.kt @@ -25,24 +25,40 @@ class MidnightTimeTest { assertThat(midnight).isAtMost(now) val c = Calendar.getInstance() c.timeInMillis = MidnightTime.calc(now) - assertThat(c[Calendar.HOUR_OF_DAY].toLong()).isEqualTo(0L) - assertThat(c[Calendar.MINUTE].toLong()).isEqualTo(0L) - assertThat(c[Calendar.SECOND].toLong()).isEqualTo(0L) - assertThat(c[Calendar.MILLISECOND].toLong()).isEqualTo(0L) + assertThat(c[Calendar.HOUR_OF_DAY]).isEqualTo(0) + assertThat(c[Calendar.MINUTE]).isEqualTo(0) + assertThat(c[Calendar.SECOND]).isEqualTo(0) + assertThat(c[Calendar.MILLISECOND]).isEqualTo(0) // Assure we get the same time from cache assertThat(midnight).isEqualTo(MidnightTime.calc(now)) } + @Test fun calcMidnightPlusMinutesTest() { + val c = Calendar.getInstance() + c.timeInMillis = MidnightTime.calcMidnightPlusMinutes(121) + assertThat(c[Calendar.HOUR_OF_DAY]).isEqualTo(2) + assertThat(c[Calendar.MINUTE]).isEqualTo(1) + assertThat(c[Calendar.SECOND]).isEqualTo(0) + assertThat(c[Calendar.MILLISECOND]).isEqualTo(0) + } + + @Test fun calcDaysBackTest() { + // We get real midnight + val now = System.currentTimeMillis() + val c = Calendar.getInstance() + c.timeInMillis = MidnightTime.calc(now) + c.add(Calendar.DAY_OF_MONTH, -5) + assertThat(c[Calendar.HOUR_OF_DAY]).isEqualTo(0) + assertThat(c[Calendar.MINUTE]).isEqualTo(0) + assertThat(c[Calendar.SECOND]).isEqualTo(0) + assertThat(c[Calendar.MILLISECOND]).isEqualTo(0) + assertThat(MidnightTime.calcDaysBack(5)).isEqualTo(c.timeInMillis) + } + @Test fun resetCache() { val now = System.currentTimeMillis() MidnightTime.calc(now) MidnightTime.resetCache() assertThat(MidnightTime.times.size().toLong()).isEqualTo(0L) } - - @Test fun log() { - val now = System.currentTimeMillis() - MidnightTime.calc(now) - assertThat(MidnightTime.log()).startsWith("Hits:") - } } diff --git a/implementation/src/main/kotlin/app/aaps/implementation/stats/DexcomTirCalculatorImpl.kt b/implementation/src/main/kotlin/app/aaps/implementation/stats/DexcomTirCalculatorImpl.kt index f262c18d7b..9927f6ea15 100644 --- a/implementation/src/main/kotlin/app/aaps/implementation/stats/DexcomTirCalculatorImpl.kt +++ b/implementation/src/main/kotlin/app/aaps/implementation/stats/DexcomTirCalculatorImpl.kt @@ -9,7 +9,6 @@ import app.aaps.core.interfaces.stats.DexcomTIR import app.aaps.core.interfaces.stats.DexcomTirCalculator import app.aaps.core.interfaces.utils.DateUtil import app.aaps.core.interfaces.utils.MidnightTime -import app.aaps.core.interfaces.utils.T import app.aaps.database.impl.AppRepository import javax.inject.Inject import javax.inject.Singleton @@ -24,7 +23,7 @@ class DexcomTirCalculatorImpl @Inject constructor( val days = 14L override fun calculate(): DexcomTIR { - val startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) + val startTime = MidnightTime.calcDaysBack(days) val endTime = MidnightTime.calc(dateUtil.now()) val bgReadings = repository.compatGetBgReadingsDataFromTime(startTime, endTime, true).blockingGet() diff --git a/implementation/src/main/kotlin/app/aaps/implementation/stats/TddCalculatorImpl.kt b/implementation/src/main/kotlin/app/aaps/implementation/stats/TddCalculatorImpl.kt index 6cd60c4ec9..c017d163cb 100644 --- a/implementation/src/main/kotlin/app/aaps/implementation/stats/TddCalculatorImpl.kt +++ b/implementation/src/main/kotlin/app/aaps/implementation/stats/TddCalculatorImpl.kt @@ -39,9 +39,8 @@ class TddCalculatorImpl @Inject constructor( ) : TddCalculator { override fun calculate(days: Long, allowMissingDays: Boolean): LongSparseArray? { - var startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) + var startTime = MidnightTime.calcDaysBack(days) val endTime = MidnightTime.calc(dateUtil.now()) - //val stepSize = T.hours(24).msecs() // this is not true on DST change val result = LongSparseArray() // Try to load cached values diff --git a/implementation/src/main/kotlin/app/aaps/implementation/stats/TirCalculatorImpl.kt b/implementation/src/main/kotlin/app/aaps/implementation/stats/TirCalculatorImpl.kt index a88454bbed..935561f5bf 100644 --- a/implementation/src/main/kotlin/app/aaps/implementation/stats/TirCalculatorImpl.kt +++ b/implementation/src/main/kotlin/app/aaps/implementation/stats/TirCalculatorImpl.kt @@ -15,7 +15,6 @@ import app.aaps.core.interfaces.stats.TIR import app.aaps.core.interfaces.stats.TirCalculator import app.aaps.core.interfaces.utils.DateUtil import app.aaps.core.interfaces.utils.MidnightTime -import app.aaps.core.interfaces.utils.T import app.aaps.database.impl.AppRepository import javax.inject.Inject import javax.inject.Singleton @@ -31,7 +30,7 @@ class TirCalculatorImpl @Inject constructor( override fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray { if (lowMgdl < 39) throw RuntimeException("Low below 39") if (lowMgdl > highMgdl) throw RuntimeException("Low > High") - val startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) + val startTime = MidnightTime.calcDaysBack(days) val endTime = MidnightTime.calc(dateUtil.now()) val bgReadings = repository.compatGetBgReadingsDataFromTime(startTime, endTime, true).blockingGet() diff --git a/plugins/aps/src/main/kotlin/app/aaps/plugins/aps/autotune/AutotunePlugin.kt b/plugins/aps/src/main/kotlin/app/aaps/plugins/aps/autotune/AutotunePlugin.kt index 623ceb64f2..c608a01b40 100644 --- a/plugins/aps/src/main/kotlin/app/aaps/plugins/aps/autotune/AutotunePlugin.kt +++ b/plugins/aps/src/main/kotlin/app/aaps/plugins/aps/autotune/AutotunePlugin.kt @@ -331,18 +331,21 @@ class AutotunePlugin @Inject constructor( jsonSettings.put("tune_insulin_curve", false) val peakTime: Int = insulinInterface.peak - if (insulinInterface.id === Insulin.InsulinType.OREF_ULTRA_RAPID_ACTING) - jsonSettings.put("curve", "ultra-rapid") - else if (insulinInterface.id === Insulin.InsulinType.OREF_RAPID_ACTING) - jsonSettings.put("curve", "rapid-acting") - else if (insulinInterface.id === Insulin.InsulinType.OREF_LYUMJEV) { - jsonSettings.put("curve", "ultra-rapid") - jsonSettings.put("useCustomPeakTime", true) - jsonSettings.put("insulinPeakTime", peakTime) - } else if (insulinInterface.id === Insulin.InsulinType.OREF_FREE_PEAK) { - jsonSettings.put("curve", if (peakTime > 55) "rapid-acting" else "ultra-rapid") - jsonSettings.put("useCustomPeakTime", true) - jsonSettings.put("insulinPeakTime", peakTime) + when { + insulinInterface.id === Insulin.InsulinType.OREF_ULTRA_RAPID_ACTING -> jsonSettings.put("curve", "ultra-rapid") + insulinInterface.id === Insulin.InsulinType.OREF_RAPID_ACTING -> jsonSettings.put("curve", "rapid-acting") + + insulinInterface.id === Insulin.InsulinType.OREF_LYUMJEV -> { + jsonSettings.put("curve", "ultra-rapid") + jsonSettings.put("useCustomPeakTime", true) + jsonSettings.put("insulinPeakTime", peakTime) + } + + insulinInterface.id === Insulin.InsulinType.OREF_FREE_PEAK -> { + jsonSettings.put("curve", if (peakTime > 55) "rapid-acting" else "ultra-rapid") + jsonSettings.put("useCustomPeakTime", true) + jsonSettings.put("insulinPeakTime", peakTime) + } } jsonString = jsonSettings.toString(4).replace("\\/", "/") } catch (e: JSONException) { @@ -392,7 +395,7 @@ class AutotunePlugin @Inject constructor( } } for (i in days.weekdays.indices) { - json.put(WeekDay.DayOfWeek.values()[i].name, days.weekdays[i]) + json.put(WeekDay.DayOfWeek.entries[i].name, days.weekdays[i]) } json.put("result", result) json.put("updateButtonVisibility", updateButtonVisibility) @@ -429,7 +432,7 @@ class AutotunePlugin @Inject constructor( } } for (i in days.weekdays.indices) - days.weekdays[i] = JsonHelper.safeGetBoolean(json, WeekDay.DayOfWeek.values()[i].name, true) + days.weekdays[i] = JsonHelper.safeGetBoolean(json, WeekDay.DayOfWeek.entries[i].name, true) result = JsonHelper.safeGetString(json, "result", "") updateButtonVisibility = JsonHelper.safeGetInt(json, "updateButtonVisibility") lastRunSuccess = true @@ -440,8 +443,8 @@ class AutotunePlugin @Inject constructor( fun calcDays(daysBack: Int): Int { var endTime = MidnightTime.calc(dateUtil.now()) + autotuneStartHour * 60 * 60 * 1000L - if (endTime > dateUtil.now()) endTime -= T.days(1).msecs() // Check if 4 AM is before now - val startTime = endTime - daysBack * T.days(1).msecs() + if (endTime > dateUtil.now()) endTime = MidnightTime.calcDaysBack(1) // Check if 4 AM is before now + val startTime = MidnightTime.calcDaysBack(endTime, daysBack.toLong()) var result = 0 for (i in 0 until daysBack) { if (days.isSet(startTime + i * T.days(1).msecs())) diff --git a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTime.kt b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTime.kt index 4174bdb988..8052c8bf3e 100644 --- a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTime.kt +++ b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTime.kt @@ -57,7 +57,7 @@ class InputTime(private val rh: ResourceHelper, private val dateUtil: DateUtil) }) } - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcMidnightPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = MidnightUtils.secondsFromMidnight(time) / 60 diff --git a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTimeRange.kt b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTimeRange.kt index c8810d75e2..989318855e 100644 --- a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTimeRange.kt +++ b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/elements/InputTimeRange.kt @@ -83,7 +83,7 @@ class InputTimeRange(private val rh: ResourceHelper, private val dateUtil: DateU }) } - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcMidnightPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = MidnightUtils.secondsFromMidnight(time) / 60 diff --git a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerRecurringTime.kt b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerRecurringTime.kt index a12086445b..aa7ead4bc3 100644 --- a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerRecurringTime.kt +++ b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerRecurringTime.kt @@ -50,7 +50,7 @@ class TriggerRecurringTime(injector: HasAndroidInjector) : Trigger(injector) { val data = JSONObject() .put("time", time.value) for (i in days.weekdays.indices) { - data.put(WeekDay.DayOfWeek.values()[i].name, days.weekdays[i]) + data.put(WeekDay.DayOfWeek.entries[i].name, days.weekdays[i]) } return data } @@ -58,7 +58,7 @@ class TriggerRecurringTime(injector: HasAndroidInjector) : Trigger(injector) { override fun fromJSON(data: String): Trigger { val o = JSONObject(data) for (i in days.weekdays.indices) - days.weekdays[i] = JsonHelper.safeGetBoolean(o, WeekDay.DayOfWeek.values()[i].name) + days.weekdays[i] = JsonHelper.safeGetBoolean(o, WeekDay.DayOfWeek.entries[i].name) if (o.has("hour")) { // do conversion from 2.5.1 format val hour = JsonHelper.safeGetInt(o, "hour") @@ -90,7 +90,7 @@ class TriggerRecurringTime(injector: HasAndroidInjector) : Trigger(injector) { override fun duplicate(): Trigger = TriggerRecurringTime(injector, this) - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcMidnightPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = MidnightUtils.secondsFromMidnight(time) / 60 diff --git a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRange.kt b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRange.kt index 0bd6e39f04..afc0111f30 100644 --- a/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRange.kt +++ b/plugins/automation/src/main/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRange.kt @@ -70,7 +70,7 @@ class TriggerTimeRange(injector: HasAndroidInjector) : Trigger(injector) { override fun duplicate(): Trigger = TriggerTimeRange(injector, range.start, range.end) - private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) + private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcMidnightPlusMinutes(minutesSinceMidnight) private fun getMinSinceMidnight(time: Long): Int = MidnightUtils.secondsFromMidnight(time) / 60 diff --git a/plugins/automation/src/test/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRangeTest.kt b/plugins/automation/src/test/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRangeTest.kt index 5eb4f3bca1..8ce0dbdbe6 100644 --- a/plugins/automation/src/test/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRangeTest.kt +++ b/plugins/automation/src/test/kotlin/app/aaps/plugins/automation/triggers/TriggerTimeRangeTest.kt @@ -16,7 +16,7 @@ class TriggerTimeRangeTest : TriggerTestBase() { @BeforeEach fun mock() { now = 754 // in minutes from midnight - val nowMills = MidnightTime.calcPlusMinutes(now.toInt()) + val nowMills = MidnightTime.calcMidnightPlusMinutes(now.toInt()) `when`(dateUtil.now()).thenReturn(nowMills) `when`(rh.gs(R.string.timerange_value)).thenReturn("Time is between %1\$s and %2\$s") }