Replace joda.time by java.time

This commit is contained in:
Shinokuni 2024-08-05 17:29:12 +02:00
parent 3af1220666
commit 7abec11b55
30 changed files with 189 additions and 183 deletions

View File

@ -47,5 +47,4 @@ dependencies {
implementation(libs.moshi) implementation(libs.moshi)
implementation(libs.jsoup) implementation(libs.jsoup)
implementation(libs.jodatime)
} }

View File

@ -4,13 +4,13 @@ import com.gitlab.mvysny.konsumexml.Konsumer
import com.gitlab.mvysny.konsumexml.Names import com.gitlab.mvysny.konsumexml.Names
import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore
import com.readrops.api.localfeed.XmlAdapter import com.readrops.api.localfeed.XmlAdapter
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nonNullText import com.readrops.api.utils.extensions.nonNullText
import com.readrops.api.utils.extensions.nullableText import com.readrops.api.utils.extensions.nullableText
import com.readrops.api.utils.extensions.nullableTextRecursively import com.readrops.api.utils.extensions.nullableTextRecursively
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import org.joda.time.LocalDateTime import com.readrops.db.util.DateUtils
import java.time.LocalDateTime
class ATOMItemAdapter : XmlAdapter<Item> { class ATOMItemAdapter : XmlAdapter<Item> {

View File

@ -1,15 +1,15 @@
package com.readrops.api.localfeed.json package com.readrops.api.localfeed.json
import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nextNonEmptyString import com.readrops.api.utils.extensions.nextNonEmptyString
import com.readrops.api.utils.extensions.nextNullableString import com.readrops.api.utils.extensions.nextNullableString
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.util.DateUtils
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter import com.squareup.moshi.JsonWriter
import org.joda.time.LocalDateTime import java.time.LocalDateTime
class JSONItemsAdapter : JsonAdapter<List<Item>>() { class JSONItemsAdapter : JsonAdapter<List<Item>>() {

View File

@ -5,13 +5,13 @@ import com.gitlab.mvysny.konsumexml.Names
import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore
import com.readrops.api.localfeed.XmlAdapter import com.readrops.api.localfeed.XmlAdapter
import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nonNullText import com.readrops.api.utils.extensions.nonNullText
import com.readrops.api.utils.extensions.nullableText import com.readrops.api.utils.extensions.nullableText
import com.readrops.api.utils.extensions.nullableTextRecursively import com.readrops.api.utils.extensions.nullableTextRecursively
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import org.joda.time.LocalDateTime import com.readrops.db.util.DateUtils
import java.time.LocalDateTime
class RSS1ItemAdapter : XmlAdapter<Item> { class RSS1ItemAdapter : XmlAdapter<Item> {

View File

@ -7,13 +7,13 @@ import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore
import com.readrops.api.localfeed.XmlAdapter import com.readrops.api.localfeed.XmlAdapter
import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX
import com.readrops.api.utils.ApiUtils import com.readrops.api.utils.ApiUtils
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nonNullText import com.readrops.api.utils.extensions.nonNullText
import com.readrops.api.utils.extensions.nullableText import com.readrops.api.utils.extensions.nullableText
import com.readrops.api.utils.extensions.nullableTextRecursively import com.readrops.api.utils.extensions.nullableTextRecursively
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import org.joda.time.LocalDateTime import com.readrops.db.util.DateUtils
import java.time.LocalDateTime
class RSS2ItemAdapter : XmlAdapter<Item> { class RSS2ItemAdapter : XmlAdapter<Item> {

View File

@ -6,11 +6,10 @@ import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nextNonEmptyString import com.readrops.api.utils.extensions.nextNonEmptyString
import com.readrops.api.utils.extensions.nextNullableString import com.readrops.api.utils.extensions.nextNullableString
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.util.DateUtils
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter import com.squareup.moshi.JsonWriter
import org.joda.time.DateTimeZone
import org.joda.time.LocalDateTime
class FreshRSSItemsAdapter : JsonAdapter<List<Item>>() { class FreshRSSItemsAdapter : JsonAdapter<List<Item>>() {
@ -46,8 +45,7 @@ class FreshRSSItemsAdapter : JsonAdapter<List<Item>>() {
with(item) { with(item) {
when (reader.selectName(NAMES)) { when (reader.selectName(NAMES)) {
0 -> remoteId = reader.nextNonEmptyString() 0 -> remoteId = reader.nextNonEmptyString()
1 -> pubDate = LocalDateTime(reader.nextLong() * 1000L, 1 -> pubDate = DateUtils.fromEpochSeconds(reader.nextLong())
DateTimeZone.getDefault())
2 -> title = reader.nextNonEmptyString() 2 -> title = reader.nextNonEmptyString()
3 -> content = getContent(reader) 3 -> content = getContent(reader)
4 -> link = getLink(reader) 4 -> link = getLink(reader)

View File

@ -6,11 +6,10 @@ import com.readrops.api.utils.exceptions.ParseException
import com.readrops.api.utils.extensions.nextNonEmptyString import com.readrops.api.utils.extensions.nextNonEmptyString
import com.readrops.api.utils.extensions.nextNullableString import com.readrops.api.utils.extensions.nextNullableString
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.util.DateUtils
import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter import com.squareup.moshi.JsonWriter
import org.joda.time.DateTimeZone
import org.joda.time.LocalDateTime
class NextcloudNewsItemsAdapter : JsonAdapter<List<Item>>() { class NextcloudNewsItemsAdapter : JsonAdapter<List<Item>>() {
@ -42,7 +41,7 @@ class NextcloudNewsItemsAdapter : JsonAdapter<List<Item>>() {
1 -> link = reader.nextNullableString() 1 -> link = reader.nextNullableString()
2 -> title = reader.nextNonEmptyString() 2 -> title = reader.nextNonEmptyString()
3 -> author = reader.nextNullableString() 3 -> author = reader.nextNullableString()
4 -> pubDate = LocalDateTime(reader.nextLong() * 1000L, DateTimeZone.getDefault()) 4 -> pubDate = DateUtils.fromEpochSeconds(reader.nextLong())
5 -> content = reader.nextNullableString() 5 -> content = reader.nextNullableString()
6 -> enclosureMime = reader.nextNullableString() 6 -> enclosureMime = reader.nextNullableString()
7 -> enclosureLink = reader.nextNullableString() 7 -> enclosureLink = reader.nextNullableString()

View File

@ -1,79 +0,0 @@
package com.readrops.api.utils
import org.joda.time.LocalDateTime
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.DateTimeFormatterBuilder
import java.util.Locale
object DateUtils {
private val TAG = DateUtils::class.java.simpleName
/**
* Base of common RSS 2 date formats.
* Examples :
* Fri, 04 Jan 2019 22:21:46 GMT
* Fri, 04 Jan 2019 22:21:46 +0000
*/
private const val RSS_2_BASE_PATTERN = "EEE, dd MMM yyyy HH:mm:ss"
private const val GMT_PATTERN = "ZZZ"
private const val OFFSET_PATTERN = "Z"
private const val ISO_PATTERN = ".SSSZZ"
private const val EDT_PATTERN = "zzz"
/**
* Date pattern for format : 2019-01-04T22:21:46+00:00
*/
private const val ATOM_JSON_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"
@JvmStatic
fun parse(value: String?): LocalDateTime? = if (value == null) {
null
} else try {
val formatter = DateTimeFormatterBuilder()
.appendOptional(DateTimeFormat.forPattern("$RSS_2_BASE_PATTERN ").parser) // with timezone
.appendOptional(DateTimeFormat.forPattern(RSS_2_BASE_PATTERN).parser) // no timezone, important order here
.appendOptional(DateTimeFormat.forPattern(ATOM_JSON_DATE_FORMAT).parser)
.appendOptional(DateTimeFormat.forPattern(GMT_PATTERN).parser)
.appendOptional(DateTimeFormat.forPattern(OFFSET_PATTERN).parser)
.appendOptional(DateTimeFormat.forPattern(ISO_PATTERN).parser)
.appendOptional(DateTimeFormat.forPattern(EDT_PATTERN).parser)
.toFormatter()
.withLocale(Locale.ENGLISH)
.withOffsetParsed()
formatter.parseLocalDateTime(value)
} catch (e: Exception) {
null
}
@JvmStatic
fun formattedDateByLocal(dateTime: LocalDateTime): String {
return DateTimeFormat.mediumDate()
.withLocale(Locale.getDefault())
.print(dateTime)
}
@JvmStatic
fun formattedDateTimeByLocal(dateTime: LocalDateTime): String {
return DateTimeFormat.forPattern("dd MMM yyyy · HH:mm")
.withLocale(Locale.getDefault())
.print(dateTime)
}
fun formattedDate(dateTime: LocalDateTime): String {
val pattern = if (dateTime.year != LocalDateTime.now().year) {
"dd MMMM yyyy"
} else {
"dd MMMM"
}
return DateTimeFormat.forPattern(pattern)
.withLocale(Locale.getDefault())
.print(dateTime)
}
}

View File

@ -2,8 +2,8 @@ package com.readrops.api.localfeed.atom
import com.gitlab.mvysny.konsumexml.konsumeXml import com.gitlab.mvysny.konsumexml.konsumeXml
import com.readrops.api.TestUtils import com.readrops.api.TestUtils
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.db.util.DateUtils
import junit.framework.TestCase import junit.framework.TestCase
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertEquals
import org.junit.Assert.assertThrows import org.junit.Assert.assertThrows

View File

@ -1,10 +1,10 @@
package com.readrops.api.localfeed.json package com.readrops.api.localfeed.json
import com.readrops.api.TestUtils import com.readrops.api.TestUtils
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.db.entities.Feed import com.readrops.db.entities.Feed
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.util.DateUtils
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
import junit.framework.TestCase import junit.framework.TestCase

View File

@ -2,8 +2,8 @@ package com.readrops.api.localfeed.rss1
import com.gitlab.mvysny.konsumexml.konsumeXml import com.gitlab.mvysny.konsumexml.konsumeXml
import com.readrops.api.TestUtils import com.readrops.api.TestUtils
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.db.util.DateUtils
import junit.framework.Assert.assertEquals import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertNotNull
import junit.framework.TestCase import junit.framework.TestCase

View File

@ -2,8 +2,8 @@ package com.readrops.api.localfeed.rss2
import com.gitlab.mvysny.konsumexml.konsumeXml import com.gitlab.mvysny.konsumexml.konsumeXml
import com.readrops.api.TestUtils import com.readrops.api.TestUtils
import com.readrops.api.utils.DateUtils
import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.exceptions.ParseException
import com.readrops.db.util.DateUtils
import junit.framework.TestCase import junit.framework.TestCase
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertTrue import junit.framework.TestCase.assertTrue

View File

@ -2,12 +2,12 @@ package com.readrops.api.services.freshrss.adapters
import com.readrops.api.TestUtils import com.readrops.api.TestUtils
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.util.DateUtils
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertNotNull
import okio.Buffer import okio.Buffer
import org.joda.time.LocalDateTime
import org.junit.Test import org.junit.Test
class FreshRSSItemsAdapterTest { class FreshRSSItemsAdapterTest {
@ -29,7 +29,7 @@ class FreshRSSItemsAdapterTest {
assertNotNull(content) assertNotNull(content)
assertEquals(link, "http://feedproxy.google.com/~r/d0od/~3/4Zk-fncSuek/adwaita-borderless-theme-in-development-gnome-41") assertEquals(link, "http://feedproxy.google.com/~r/d0od/~3/4Zk-fncSuek/adwaita-borderless-theme-in-development-gnome-41")
assertEquals(author, "Joey Sneddon") assertEquals(author, "Joey Sneddon")
assertEquals(pubDate, LocalDateTime(1625234040 * 1000L)) assertEquals(pubDate, DateUtils.fromEpochSeconds(1625234040))
assertEquals(isRead, false) assertEquals(isRead, false)
assertEquals(isStarred, false) assertEquals(isStarred, false)
} }

View File

@ -2,11 +2,11 @@ package com.readrops.api.services.nextcloudnews.adapters
import com.readrops.api.TestUtils import com.readrops.api.TestUtils
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.util.DateUtils
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertEquals
import okio.Buffer import okio.Buffer
import org.joda.time.LocalDateTime
import org.junit.Test import org.junit.Test
class NextcloudNewsItemsAdapterTest { class NextcloudNewsItemsAdapterTest {
@ -32,7 +32,7 @@ class NextcloudNewsItemsAdapterTest {
assertEquals(feedRemoteId, "67") assertEquals(feedRemoteId, "67")
assertEquals(isRead, false) assertEquals(isRead, false)
assertEquals(isStarred, false) assertEquals(isStarred, false)
assertEquals(pubDate, LocalDateTime(1367270544000)) assertEquals(pubDate, DateUtils.fromEpochSeconds(1367270544))
assertEquals(imageLink, null) assertEquals(imageLink, null)
} }

View File

@ -1,58 +0,0 @@
package com.readrops.api.utils;
import org.joda.time.LocalDateTime;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class DateUtilsTest {
@Test
public void rssDateTest() {
LocalDateTime dateTime = new LocalDateTime(2019, 1, 4, 22, 21, 46);
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 04 Jan 2019 22:21:46 GMT")));
}
@Test
public void rssDate2Test() {
LocalDateTime dateTime = new LocalDateTime(2019, 1, 4, 22, 21, 46);
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 04 Jan 2019 22:21:46 +0000")));
}
@Test
public void rssDate3Test() {
LocalDateTime dateTime = new LocalDateTime(2019, 1, 4, 22, 21, 46);
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 04 Jan 2019 22:21:46")));
}
@Test
public void atomJsonDateTest() {
LocalDateTime dateTime = new LocalDateTime(2019, 1, 4, 22, 21, 46);
assertEquals(0, dateTime.compareTo(DateUtils.parse("2019-01-04T22:21:46+00:00")));
}
@Test
public void atomJsonDate2Test() {
LocalDateTime dateTime = new LocalDateTime(2019, 1, 4, 22, 21, 46);
assertEquals(0, dateTime.compareTo(DateUtils.parse("2019-01-04T22:21:46-0000")));
}
@Test
public void isoPatternTest() {
LocalDateTime dateTime = new LocalDateTime(2020, 6, 30, 11, 39, 37, 206);
assertEquals(0, dateTime.compareTo(DateUtils.parse("2020-06-30T11:39:37.206-07:00")));
}
@Test
public void edtPatternTest() {
LocalDateTime dateTime = new LocalDateTime(2020, 7, 17, 16, 30, 0);
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 17 Jul 2020 16:30:00 EDT")));
}
}

View File

@ -61,7 +61,6 @@ dependencies {
implementation(libs.browser) implementation(libs.browser)
implementation(libs.jsoup) implementation(libs.jsoup)
implementation(libs.jodatime)
testImplementation(libs.junit4) testImplementation(libs.junit4)
androidTestImplementation(libs.bundles.test) androidTestImplementation(libs.bundles.test)

View File

@ -13,7 +13,7 @@ import com.readrops.db.entities.account.AccountType
import com.readrops.db.filters.MainFilter import com.readrops.db.filters.MainFilter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.joda.time.LocalDateTime import java.time.LocalDateTime
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue

View File

@ -12,7 +12,7 @@ import com.readrops.db.entities.Item
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import com.readrops.db.entities.account.AccountType import com.readrops.db.entities.account.AccountType
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.joda.time.LocalDateTime import java.time.LocalDateTime
import org.junit.After import org.junit.After
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue

View File

@ -52,7 +52,6 @@ import androidx.core.view.children
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cafe.adriel.voyager.koin.getScreenModel import cafe.adriel.voyager.koin.getScreenModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.readrops.api.utils.DateUtils
import com.readrops.app.R import com.readrops.app.R
import com.readrops.app.item.view.ItemNestedScrollView import com.readrops.app.item.view.ItemNestedScrollView
import com.readrops.app.item.view.ItemWebView import com.readrops.app.item.view.ItemWebView
@ -63,6 +62,7 @@ import com.readrops.app.util.theme.MediumSpacer
import com.readrops.app.util.theme.ShortSpacer import com.readrops.app.util.theme.ShortSpacer
import com.readrops.app.util.theme.spacing import com.readrops.app.util.theme.spacing
import com.readrops.db.pojo.ItemWithFeed import com.readrops.db.pojo.ItemWithFeed
import com.readrops.db.util.DateUtils
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import kotlin.math.roundToInt import kotlin.math.roundToInt

View File

@ -12,7 +12,6 @@ import com.readrops.db.entities.Folder
import com.readrops.db.entities.Item import com.readrops.db.entities.Item
import com.readrops.db.entities.ItemState import com.readrops.db.entities.ItemState
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import org.joda.time.DateTime
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
class FreshRSSRepository( class FreshRSSRepository(
@ -63,7 +62,7 @@ class FreshRSSRepository(
syncType = SyncType.INITIAL_SYNC syncType = SyncType.INITIAL_SYNC
} }
val newLastModified = DateTime.now().millis / 1000L val newLastModified = System.currentTimeMillis() / 1000L
return dataSource.synchronize(syncType, syncData, account.writeToken!!).run { return dataSource.synchronize(syncType, syncData, account.writeToken!!).run {
insertFolders(folders) insertFolders(folders)

View File

@ -16,7 +16,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.joda.time.DateTime
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.get import org.koin.core.component.get
@ -63,7 +62,7 @@ class NextcloudNewsRepository(
SyncType.INITIAL_SYNC SyncType.INITIAL_SYNC
} }
val newLastModified = DateTime.now().millis / 1000L val newLastModified = System.currentTimeMillis() / 1000L
return dataSource.synchronize(syncType, syncData).run { return dataSource.synchronize(syncType, syncData).run {
insertFolders(folders) insertFolders(folders)
@ -163,7 +162,6 @@ class NextcloudNewsRepository(
itemsFeedsIds[item.feedRemoteId] = feedId itemsFeedsIds[item.feedRemoteId] = feedId
} }
// TODO itemExists request isn't the right one for this case
if (!initialSync && feedId > 0 && database.itemDao().itemExists(item.remoteId!!, feedId)) { if (!initialSync && feedId > 0 && database.itemDao().itemExists(item.remoteId!!, feedId)) {
database.itemDao() database.itemDao()
.updateReadAndStarState(item.remoteId!!, item.isRead, item.isStarred) .updateReadAndStarState(item.remoteId!!, item.isRead, item.isStarred)

View File

@ -6,7 +6,7 @@ import com.readrops.app.util.DefaultPreview
import com.readrops.app.util.theme.ReadropsTheme import com.readrops.app.util.theme.ReadropsTheme
import com.readrops.db.entities.Folder import com.readrops.db.entities.Folder
import com.readrops.db.pojo.ItemWithFeed import com.readrops.db.pojo.ItemWithFeed
import org.joda.time.LocalDateTime import java.time.LocalDateTime
enum class TimelineItemSize { enum class TimelineItemSize {
COMPACT, COMPACT,

View File

@ -34,13 +34,13 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.readrops.api.utils.DateUtils
import com.readrops.app.R import com.readrops.app.R
import com.readrops.app.util.components.FeedIcon import com.readrops.app.util.components.FeedIcon
import com.readrops.app.util.theme.ShortSpacer import com.readrops.app.util.theme.ShortSpacer
import com.readrops.app.util.theme.spacing import com.readrops.app.util.theme.spacing
import com.readrops.db.pojo.ItemWithFeed import com.readrops.db.pojo.ItemWithFeed
import org.joda.time.LocalDateTime import com.readrops.db.util.DateUtils
import java.time.LocalDateTime
import kotlin.math.roundToInt import kotlin.math.roundToInt
@Composable @Composable

View File

@ -0,0 +1,51 @@
package com.readrops.app
import com.readrops.db.util.DateUtils
import junit.framework.TestCase.assertEquals
import org.junit.Test
import java.time.LocalDateTime
class DateUtilsTest {
@Test
fun rssDateTest() {
val dateTime = LocalDateTime.of(2019, 1, 4, 22, 21, 46)
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 04 Jan 2019 22:21:46 GMT")))
}
@Test
fun rssDate2Test() {
val dateTime = LocalDateTime.of(2019, 1, 4, 22, 21, 46)
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 04 Jan 2019 22:21:46 +0000")))
}
@Test
fun rssDate3Test() {
val dateTime = LocalDateTime.of(2019, 1, 4, 22, 21, 46)
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 04 Jan 2019 22:21:46")))
}
@Test
fun edtPatternTest() {
val dateTime = LocalDateTime.of(2020, 7, 17, 16, 30, 0)
assertEquals(0, dateTime.compareTo(DateUtils.parse("Fri, 17 Jul 2020 16:30:00 EDT")))
}
@Test
fun atomJsonDateTest() {
val dateTime = LocalDateTime.of(2019, 1, 4, 22, 21, 46)
assertEquals(0, dateTime.compareTo(DateUtils.parse("2019-01-04T22:21:46+00:00")))
}
@Test
fun atomJsonDate2Test() {
val dateTime = LocalDateTime.of(2019, 1, 4, 22, 21, 46)
assertEquals(0, dateTime.compareTo(DateUtils.parse("2019-01-04T22:21:46-0000")))
}
@Test
fun isoPatternTest() {
val dateTime = LocalDateTime.of(2020, 6, 30, 11, 39, 37, 206000000)
assertEquals(0, dateTime.compareTo(DateUtils.parse("2020-06-30T11:39:37.206-07:00")))
}
}

View File

@ -42,8 +42,6 @@ dependencies {
implementation(libs.bundles.paging) implementation(libs.bundles.paging)
implementation(libs.jodatime)
implementation(libs.bundles.koin) implementation(libs.bundles.koin)
testImplementation(libs.bundles.kointest) testImplementation(libs.bundles.kointest)

View File

@ -17,6 +17,7 @@ import com.readrops.db.entities.Item
import com.readrops.db.entities.ItemState import com.readrops.db.entities.ItemState
import com.readrops.db.entities.ItemStateChange import com.readrops.db.entities.ItemStateChange
import com.readrops.db.entities.account.Account import com.readrops.db.entities.account.Account
import com.readrops.db.util.Converters
@Database( @Database(
entities = [Feed::class, Item::class, Folder::class, Account::class, entities = [Feed::class, Item::class, Folder::class, Account::class,

View File

@ -5,7 +5,7 @@ import androidx.room.Entity
import androidx.room.ForeignKey import androidx.room.ForeignKey
import androidx.room.Ignore import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.joda.time.LocalDateTime import java.time.LocalDateTime
@Entity( @Entity(
foreignKeys = [ foreignKeys = [

View File

@ -1,24 +1,24 @@
package com.readrops.db package com.readrops.db.util
import androidx.room.TypeConverter import androidx.room.TypeConverter
import com.readrops.db.entities.account.AccountType import com.readrops.db.entities.account.AccountType
import org.joda.time.LocalDateTime import java.time.LocalDateTime
class Converters { class Converters {
@TypeConverter @TypeConverter
fun fromTimeStamp(value: Long): LocalDateTime { fun fromTimeStamp(value: Long): LocalDateTime {
return LocalDateTime(value) return DateUtils.fromEpochSeconds(value / 1000L)
} }
@TypeConverter @TypeConverter
fun fromLocalDateTime(localDateTime: LocalDateTime): Long { fun fromLocalDateTime(localDateTime: LocalDateTime): Long {
return localDateTime.toDateTime().millis return localDateTime.toInstant(DateUtils.defaultOffset).toEpochMilli()
} }
@TypeConverter @TypeConverter
fun fromAccountTypeCode(ordinal: Int): AccountType { fun fromAccountTypeCode(ordinal: Int): AccountType {
return AccountType.values()[ordinal] return AccountType.entries[ordinal]
} }
@TypeConverter @TypeConverter

View File

@ -0,0 +1,102 @@
package com.readrops.db.util
import android.annotation.SuppressLint
import android.util.Log
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.format.FormatStyle
import java.util.Locale
import java.util.TimeZone
object DateUtils {
private val TAG = DateUtils::class.java.simpleName
/**
* Base of common RSS 2 date formats.
* Examples :
* Fri, 04 Jan 2019 22:21:46 GMT
* Fri, 04 Jan 2019 22:21:46 +0000
*/
private const val RSS_2_BASE_PATTERN = "EEE, dd MMM yyyy HH:mm:ss"
private const val GMT_PATTERN = "ZZZ"
private const val OFFSET_PATTERN = "Z"
private const val ISO_PATTERN = ".SSSZZ"
private const val EDT_PATTERN = "zzz"
private const val ZONE_OFFSET_PATTERN = ".SSSxxx"
/**
* Date pattern for format : 2019-01-04T22:21:46+00:00
*/
private const val ATOM_JSON_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"
val defaultOffset: ZoneOffset
get() = OffsetDateTime.now(TimeZone.getDefault().toZoneId())
.offset
/**
* Attempts to parse a date string representation.
* If the provided value is null or the parsing fails, [LocalDateTime.now] is returned.
* @return parsed date or [LocalDateTime.now]
*/
@SuppressLint("NewApi") // works with API 21+ so the lint might be buggy
@JvmStatic
fun parse(value: String?): LocalDateTime {
return if (value == null) {
LocalDateTime.now()
} else try {
val formatter = DateTimeFormatterBuilder()
.appendOptional(DateTimeFormatter.ofPattern("$RSS_2_BASE_PATTERN ")) // with timezone
.appendOptional(DateTimeFormatter.ofPattern(RSS_2_BASE_PATTERN)) // no timezone, important order here
.appendOptional(DateTimeFormatter.ofPattern(ATOM_JSON_DATE_FORMAT))
.appendOptional(DateTimeFormatter.ofPattern(EDT_PATTERN))
.appendOptional(DateTimeFormatter.ofPattern(ZONE_OFFSET_PATTERN))
.appendOptional(DateTimeFormatter.ofPattern(GMT_PATTERN))
.appendOptional(DateTimeFormatter.ofPattern(OFFSET_PATTERN))
.appendOptional(DateTimeFormatter.ofPattern(ISO_PATTERN))
.toFormatter()
.withLocale(Locale.ENGLISH)
LocalDateTime.from(formatter.parse(value))
} catch (e: Exception) {
Log.d(TAG, "Unable to parse $value: ${e.message}")
LocalDateTime.now()
}
}
/**
* Be aware of giving a second epoch value and not a millisecond one!
*/
fun fromEpochSeconds(epoch: Long): LocalDateTime {
return LocalDateTime.ofEpochSecond(
epoch,
0,
defaultOffset
)
}
fun formattedDateByLocal(dateTime: LocalDateTime): String {
return DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.getDefault())
.format(dateTime)
}
fun formattedDate(dateTime: LocalDateTime): String {
val pattern = if (dateTime.year != LocalDateTime.now().year) {
"dd MMMM yyyy"
} else {
"dd MMMM"
}
return DateTimeFormatter.ofPattern(pattern)
.format(dateTime)
}
}

View File

@ -82,11 +82,10 @@ aboutlibraries-composem3 = { module = "com.mikepenz:aboutlibraries-compose-m3",
konsumexml = "com.gitlab.mvysny.konsume-xml:konsume-xml:1.1" konsumexml = "com.gitlab.mvysny.konsume-xml:konsume-xml:1.1"
kotlinxmlbuilder = "org.redundent:kotlin-xml-builder:1.7.3" #TODO update this kotlinxmlbuilder = "org.redundent:kotlin-xml-builder:1.7.3" #TODO update this
jdk-desugar = "com.android.tools:desugar_jdk_libs:2.0.4" jdk-desugar = "com.android.tools:desugar_jdk_libs_nio:2.0.4"
jsoup = "org.jsoup:jsoup:1.16.1" jsoup = "org.jsoup:jsoup:1.16.1"
moshi = "com.squareup.moshi:moshi:1.15.1" moshi = "com.squareup.moshi:moshi:1.15.1"
jodatime = "joda-time:joda-time:2.10.10" #TODO replace by java.time
# androidx # androidx
corektx = "androidx.core:core-ktx:1.13.1" corektx = "androidx.core:core-ktx:1.13.1"