Fix all day date synchronization

pull/1336/head
Alex Baker 5 years ago
parent cb0823205a
commit 5ff6b885f7

@ -9,6 +9,7 @@ import org.junit.Assert.*
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.tasks.R import org.tasks.R
import org.tasks.TestUtilities.withTZ
import org.tasks.caldav.iCalendar.Companion.getParent import org.tasks.caldav.iCalendar.Companion.getParent
import org.tasks.data.CaldavAccount import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS
@ -22,10 +23,13 @@ import org.tasks.makers.CaldavTaskMaker.REMOTE_ID
import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT import org.tasks.makers.CaldavTaskMaker.REMOTE_PARENT
import org.tasks.makers.CaldavTaskMaker.TASK import org.tasks.makers.CaldavTaskMaker.TASK
import org.tasks.makers.CaldavTaskMaker.newCaldavTask import org.tasks.makers.CaldavTaskMaker.newCaldavTask
import org.tasks.makers.TaskMaker
import org.tasks.makers.TaskMaker.PARENT import org.tasks.makers.TaskMaker.PARENT
import org.tasks.makers.TaskMaker.RRULE import org.tasks.makers.TaskMaker.RRULE
import org.tasks.makers.TaskMaker.newTask import org.tasks.makers.TaskMaker.newTask
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTime
import java.util.*
import javax.inject.Inject import javax.inject.Inject
@UninstallModules(ProductionModule::class) @UninstallModules(ProductionModule::class)
@ -169,7 +173,121 @@ class OpenTasksSynchronizerTest : InjectingTestCase() {
assertEquals("1234", openTaskDao.getTask(listId.toLong(), "abcd")?.task?.getParent()) assertEquals("1234", openTaskDao.getTask(listId.toLong(), "abcd")?.task?.getParent())
} }
@Test
fun readDueDatePositiveOffset() = runBlocking {
val (listId, list) = openTaskDao.insertList()
openTaskDao.insertTask(listId, ALL_DAY_DUE)
withTZ(BERLIN) {
synchronizer.sync()
}
val caldavTask = caldavDao.getTaskByRemoteId(list.uuid!!, "3863299529704302692")
val task = taskDao.fetch(caldavTask!!.task)
assertEquals(
DateTime(2021, 2, 1, 12, 0, 0, 0, BERLIN).millis,
task?.dueDate
)
}
@Test
fun writeDueDatePositiveOffset() = withTZ(BERLIN) {
val (listId, list) = openTaskDao.insertList()
val taskId = taskDao.createNew(newTask(
with(TaskMaker.DUE_DATE, DateTime(2021, 2, 1))
))
caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid),
with(REMOTE_ID, "1234"),
with(TASK, taskId)
))
synchronizer.sync()
assertEquals(
1612137600000,
openTaskDao.getTask(listId.toLong(), "1234")?.task?.due?.date?.time
)
}
@Test
fun readDueDateNoOffset() = runBlocking {
val (listId, list) = openTaskDao.insertList()
openTaskDao.insertTask(listId, ALL_DAY_DUE)
withTZ(LONDON) {
synchronizer.sync()
}
val caldavTask = caldavDao.getTaskByRemoteId(list.uuid!!, "3863299529704302692")
val task = taskDao.fetch(caldavTask!!.task)
assertEquals(
DateTime(2021, 2, 1, 12, 0, 0, 0, LONDON).millis,
task?.dueDate
)
}
@Test
fun writeDueDateNoOffset() = withTZ(LONDON) {
val (listId, list) = openTaskDao.insertList()
val taskId = taskDao.createNew(newTask(
with(TaskMaker.DUE_DATE, DateTime(2021, 2, 1))
))
caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid),
with(REMOTE_ID, "1234"),
with(TASK, taskId)
))
synchronizer.sync()
assertEquals(
1612137600000,
openTaskDao.getTask(listId.toLong(), "1234")?.task?.due?.date?.time
)
}
@Test
fun readDueDateNegativeOffset() = runBlocking {
val (listId, list) = openTaskDao.insertList()
openTaskDao.insertTask(listId, ALL_DAY_DUE)
withTZ(NEW_YORK) {
synchronizer.sync()
}
val caldavTask = caldavDao.getTaskByRemoteId(list.uuid!!, "3863299529704302692")
val task = taskDao.fetch(caldavTask!!.task)
assertEquals(
DateTime(2021, 2, 1, 12, 0, 0, 0, NEW_YORK).millis,
task?.dueDate
)
}
@Test
fun writeDueDateNegativeOffset() = withTZ(NEW_YORK) {
val (listId, list) = openTaskDao.insertList()
val taskId = taskDao.createNew(newTask(
with(TaskMaker.DUE_DATE, DateTime(2021, 2, 1))
))
caldavDao.insert(newCaldavTask(
with(CALENDAR, list.uuid),
with(REMOTE_ID, "1234"),
with(TASK, taskId)
))
synchronizer.sync()
assertEquals(
1612137600000,
openTaskDao.getTask(listId.toLong(), "1234")?.task?.due?.date?.time
)
}
companion object { companion object {
val BERLIN = TimeZone.getTimeZone("Europe/Berlin")
val LONDON = TimeZone.getTimeZone("Europe/London")
val NEW_YORK = TimeZone.getTimeZone("America/New_York")
val SUBTASK = """ val SUBTASK = """
BEGIN:VCALENDAR BEGIN:VCALENDAR
VERSION:2.0 VERSION:2.0
@ -184,5 +302,20 @@ class OpenTasksSynchronizerTest : InjectingTestCase() {
END:VTODO END:VTODO
END:VCALENDAR END:VCALENDAR
""".trimIndent() """.trimIndent()
val ALL_DAY_DUE = """
BEGIN:VCALENDAR
VERSION:2.0
PRODID:+//IDN tasks.org//android-110304//EN
BEGIN:VTODO
DTSTAMP:20210129T155402Z
UID:3863299529704302692
CREATED:20210129T155318Z
LAST-MODIFIED:20210129T155329Z
SUMMARY:Due date
DUE;VALUE=DATE:20210201
END:VTODO
END:VCALENDAR
""".trimIndent()
} }
} }

@ -3,6 +3,7 @@ package org.tasks
import android.content.Context import android.content.Context
import at.bitfire.ical4android.Task.Companion.tasksFromReader import at.bitfire.ical4android.Task.Companion.tasksFromReader
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import kotlinx.coroutines.runBlocking
import org.tasks.caldav.CaldavConverter import org.tasks.caldav.CaldavConverter
import org.tasks.data.CaldavTask import org.tasks.data.CaldavTask
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
@ -13,13 +14,15 @@ import java.nio.file.Paths
import java.util.* import java.util.*
object TestUtilities { object TestUtilities {
fun withTZ(id: String, runnable: () -> Unit) = withTZ(TimeZone.getTimeZone(id), runnable) fun withTZ(id: String, runnable: suspend () -> Unit) = withTZ(TimeZone.getTimeZone(id), runnable)
fun withTZ(tz: TimeZone, runnable: () -> Unit) { fun withTZ(tz: TimeZone, runnable: suspend () -> Unit) {
val def = TimeZone.getDefault() val def = TimeZone.getDefault()
try { try {
TimeZone.setDefault(tz) TimeZone.setDefault(tz)
runnable() runBlocking {
runnable()
}
} finally { } finally {
TimeZone.setDefault(def) TimeZone.setDefault(def)
} }

@ -82,12 +82,12 @@ public class CaldavConverter {
long startDate = task.hasStartTime() ? task.getHideUntil() : startOfDay(task.getHideUntil()); long startDate = task.hasStartTime() ? task.getHideUntil() : startOfDay(task.getHideUntil());
if (dueDate > 0) { if (dueDate > 0) {
startDate = Math.min(dueDate, startDate); startDate = Math.min(dueDate, startDate);
remote.setDue(new Due(allDay ? new Date(dueDate) : getDateTime(dueDate))); remote.setDue(new Due(allDay ? getDate(dueDate) : getDateTime(dueDate)));
} else { } else {
remote.setDue(null); remote.setDue(null);
} }
if (startDate > 0) { if (startDate > 0) {
remote.setDtStart(new DtStart(allDay ? new Date(startDate) : getDateTime(startDate))); remote.setDtStart(new DtStart(allDay ? getDate(startDate) : getDateTime(startDate)));
} else { } else {
remote.setDtStart(null); remote.setDtStart(null);
} }
@ -121,6 +121,10 @@ public class CaldavConverter {
iCalendar.Companion.setParent(remote, task.getParent() == 0 ? null : caldavTask.getRemoteParent()); iCalendar.Companion.setParent(remote, task.getParent() == 0 ? null : caldavTask.getRemoteParent());
} }
private static Date getDate(long timestamp) {
return new Date(timestamp + newDateTime(timestamp).getOffset());
}
private static DateTime getDateTime(long timestamp) { private static DateTime getDateTime(long timestamp) {
net.fortuna.ical4j.model.TimeZone tz = net.fortuna.ical4j.model.TimeZone tz =
DateUtils.INSTANCE.ical4jTimeZone(TimeZone.getDefault().getID()); DateUtils.INSTANCE.ical4jTimeZone(TimeZone.getDefault().getID());

@ -26,8 +26,6 @@ import org.tasks.time.DateTime.UTC
import timber.log.Timber import timber.log.Timber
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.StringReader import java.io.StringReader
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -162,7 +160,6 @@ class iCalendar @Inject constructor(
} }
companion object { companion object {
private val DUE_DATE_FORMAT = SimpleDateFormat("yyyyMMdd", Locale.US)
private const val APPLE_SORT_ORDER = "X-APPLE-SORT-ORDER" private const val APPLE_SORT_ORDER = "X-APPLE-SORT-ORDER"
private val IS_PARENT = { r: RelatedTo -> private val IS_PARENT = { r: RelatedTo ->
r.parameters.getParameter<RelType>(Parameter.RELTYPE).let { r.parameters.getParameter<RelType>(Parameter.RELTYPE).let {
@ -198,21 +195,14 @@ class iCalendar @Inject constructor(
@JvmStatic @JvmStatic
fun getLocal(property: DateProperty): Long { fun getLocal(property: DateProperty): Long {
val dateTime = if (property.date is DateTime) { val dateTime: org.tasks.time.DateTime? = if (property.date is DateTime) {
val dt = property.date as DateTime val dt = property.date as DateTime
org.tasks.time.DateTime( org.tasks.time.DateTime(
dt.time, dt.time,
dt.timeZone ?: if (dt.isUtc) UTC else TimeZone.getDefault() dt.timeZone ?: if (dt.isUtc) UTC else TimeZone.getDefault()
) )
} else { } else {
try { org.tasks.time.DateTime(property.date.time).let { it.minusMillis(it.offset) }
DUE_DATE_FORMAT.parse(property.value)?.let {
org.tasks.time.DateTime(it)
}
} catch (e: ParseException) {
Timber.e(e)
null
}
} }
return dateTime?.toLocal()?.millis ?: 0 return dateTime?.toLocal()?.millis ?: 0
} }

@ -277,7 +277,7 @@ public class DateTime {
return subtract(Calendar.MINUTE, minutes); return subtract(Calendar.MINUTE, minutes);
} }
public DateTime minusMillis(int millis) { public DateTime minusMillis(long millis) {
return new DateTime(timestamp - millis, timeZone); return new DateTime(timestamp - millis, timeZone);
} }

Loading…
Cancel
Save