Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import com.paligot.confily.resources.Resource
import com.paligot.confily.resources.title_notif_reminder_talk
import com.paligot.confily.schedules.ui.models.TalkItemUi
import kotlinx.coroutines.coroutineScope
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.TimeZone
import kotlinx.datetime.minus
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime
import org.jetbrains.compose.resources.getString
import java.util.Locale

Expand All @@ -34,11 +30,11 @@ actual class AlarmScheduler(
val pendingIntent =
PendingIntent.getBroadcast(context, talkItem.id.hashCode(), intent, flags)
if (isFavorite) {
val triggerAtMillis = talkItem.startTime
.toLocalDateTime()
.toInstant(TimeZone.UTC)
.minus(ReminderInMinutes, DateTimeUnit.MINUTE)
.toEpochMilliseconds()
val triggerAtMillis = reminderTriggerAtMillis(
startTime = talkItem.startTime,
timeZone = TimeZone.currentSystemDefault(),
reminderInMinutes = ReminderInMinutes
)
alarmManager.setAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
triggerAtMillis,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.paligot.confily.core

import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.TimeZone
import kotlinx.datetime.minus
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime

/**
* Computes the absolute trigger instant (epoch millis) of a session reminder alarm.
*
* [startTime] is the session's wall-clock start time at the event location (e.g. "2026-06-11T08:30")
* and carries no timezone. It is interpreted in [timeZone] before being turned into an absolute
* instant: pass the device timezone so the alarm fires at the time displayed in the app rather than
* being offset by the difference from UTC. The reminder is scheduled [reminderInMinutes] earlier.
*/
internal fun reminderTriggerAtMillis(
startTime: String,
timeZone: TimeZone,
reminderInMinutes: Int
): Long = startTime
.toLocalDateTime()
.toInstant(timeZone)
.minus(reminderInMinutes, DateTimeUnit.MINUTE)
.toEpochMilliseconds()
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.paligot.confily.core

import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals

class ReminderTimeTest {
@Test
fun reminder_trigger_uses_event_local_wall_clock_not_utc() {
// "2026-06-11T08:30" is a wall-clock time at the event (Lille / Europe/Paris, CEST = UTC+2
// in June) and carries no timezone. A 10-minute reminder must fire at 08:20 local = 06:20 UTC.
val triggerAtMillis = reminderTriggerAtMillis(
startTime = "2026-06-11T08:30",
timeZone = TimeZone.of("Europe/Paris"),
reminderInMinutes = 10
)

assertEquals(
Instant.parse("2026-06-11T06:20:00Z").toEpochMilliseconds(),
triggerAtMillis
)
// Regression guard: the previous code interpreted the wall-clock time as UTC, which would
// have fired the alarm at 08:20 UTC (10:20 local) — two hours late.
assertNotEquals(
Instant.parse("2026-06-11T08:20:00Z").toEpochMilliseconds(),
triggerAtMillis
)
}
}
Loading