Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.

Commit cb1808e

Browse files
committed
Fix timestamp alignment for Android <12 bug in toTimestamp()
1 parent a9ecfe7 commit cb1808e

3 files changed

Lines changed: 8 additions & 3 deletions

File tree

lib/src/androidTest/kotlin/at/bitfire/synctools/mapping/tasks/DmfsTaskBuilderTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ class DmfsTaskBuilderTest (
515515
}.let { result ->
516516
// Note: iCalendar does not allow COMPLETED to be all-day [RFC 5545 3.8.2.1]
517517
assertEquals(0, result.getAsInteger(TaskContract.Tasks.COMPLETED_IS_ALLDAY))
518-
Assert.assertEquals(now.toEpochMilli(), result.getAsLong(TaskContract.Tasks.COMPLETED))
518+
Assert.assertEquals(now.toTimestamp(), result.getAsLong(TaskContract.Tasks.COMPLETED))
519519
}
520520
}
521521

lib/src/main/kotlin/at/bitfire/synctools/util/AndroidTimeUtils.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,12 @@ object AndroidTimeUtils {
7575

7676
/**
7777
* Same as [toInstant], but returns a UNIX timestamp (in milliseconds) instead of an [Instant].
78+
* Aligned to full seconds, since Android <12 has a bug.
79+
*
80+
* See [at.bitfire.synctools.storage.calendar.AndroidCalendarProvider.matchesExceptionsWithMilliseconds].
7881
*/
7982
fun Temporal.toTimestamp(): Long =
80-
toInstant().toEpochMilli()
83+
toInstant().epochSecond * 1000
8184

8285
/**
8386
* Converts this [Temporal] to a [ZonedDateTime] that is created from the timestamp returned by

lib/src/test/kotlin/at/bitfire/synctools/mapping/tasks/builder/CompletedBuilderTest.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.core.content.contentValuesOf
1212
import at.bitfire.DefaultTimezoneRule
1313
import at.bitfire.ical4android.Task
1414
import at.bitfire.synctools.test.assertContentValuesEqual
15+
import at.bitfire.synctools.util.AndroidTimeUtils.toTimestamp
1516
import net.fortuna.ical4j.model.property.Completed
1617
import org.dmfs.tasks.contract.TaskContract.Tasks
1718
import org.junit.Rule
@@ -20,6 +21,7 @@ import org.junit.runner.RunWith
2021
import org.robolectric.RobolectricTestRunner
2122
import java.time.Instant
2223
import java.time.LocalDateTime
24+
import java.time.ZoneId
2325

2426
@RunWith(RobolectricTestRunner::class)
2527
class CompletedBuilderTest {
@@ -67,7 +69,7 @@ class CompletedBuilderTest {
6769
)
6870
// floating date-time is interpreted in system default timezone
6971
val expectedTimestamp = LocalDateTime.of(2024, 6, 1, 12, 0, 0)
70-
.atZone(tzRule.defaultZoneId).toInstant().toEpochMilli()
72+
.atZone(ZoneId.systemDefault()).toInstant().toTimestamp()
7173
assertContentValuesEqual(contentValuesOf(
7274
Tasks.COMPLETED to expectedTimestamp,
7375
Tasks.COMPLETED_IS_ALLDAY to 0

0 commit comments

Comments
 (0)