Skip to content

Commit

Permalink
PrettyTime.
Browse files Browse the repository at this point in the history
  • Loading branch information
ychescale9 committed Dec 3, 2023
1 parent 0afb5ad commit e248ecb
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 4 deletions.
1 change: 1 addition & 0 deletions android/feature/home/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ android {
dependencies {
implementation(project(":feature:common"))
implementation(project(":common-ui:feed"))
implementation(project(":kmp:presentation:home"))
implementation(project(":kmp:data"))
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.reactivecircus.kstreamlined.kmp.data.utils
package io.github.reactivecircus.kstreamlined.kmp.core.utils

import kotlin.coroutines.cancellation.CancellationException

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.reactivecircus.kstreamlined.kmp.data.utils
package io.github.reactivecircus.kstreamlined.kmp.core.utils

import kotlin.coroutines.cancellation.CancellationException
import kotlin.test.Test
Expand Down
14 changes: 14 additions & 0 deletions kmp/presentation/home/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id("kstreamlined.kmp.common")
id("kstreamlined.kmp.test")
}

kotlin {
sourceSets {
commonMain {
dependencies {
implementation(project(":kmp:pretty-time"))
}
}
}
}
14 changes: 14 additions & 0 deletions kmp/pretty-time/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id("kstreamlined.kmp.common")
id("kstreamlined.kmp.test")
}

kotlin {
sourceSets {
commonMain {
dependencies {
api(libs.kotlinx.datetime)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.github.reactivecircus.kstreamlined.kmp.prettytime

import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

@Suppress("MagicNumber")
public fun Instant.weeksAgo(): String {
val duration = Clock.System.now().minus(this)
val weekDifference = duration.inWholeDays / 7
return when (weekDifference) {
0L -> "This week"
1L -> "Last week"
else -> "Earlier"
}
}

@Suppress("MagicNumber")
public fun Instant.timeAgo(
clock: Clock = Clock.System,
timeZone: TimeZone = TimeZone.currentSystemDefault()
): String {
val now = clock.now()
val duration = now.minus(this)
return when {
duration.inWholeMinutes < 1 -> "Moments ago"
duration.inWholeHours < 1 -> if (duration.inWholeMinutes == 1L) {
"${duration.inWholeMinutes} minute ago"
} else {
"${duration.inWholeMinutes} minutes ago"
}
duration.inWholeDays < 1 -> if (duration.inWholeHours == 1L) {
"${duration.inWholeHours} hour ago"
} else {
"${duration.inWholeHours} hours ago"
}
duration.inWholeDays < 2 -> "Yesterday"
duration.inWholeDays < 7 -> "${duration.inWholeDays} days ago"
else -> toFormattedTime(timeZone)
}
}

/**
* Format the [Instant] to a string in the format of `dd MMM yyyy`.
*/
@Suppress("MagicNumber")
public fun Instant.toFormattedTime(
timeZone: TimeZone = TimeZone.currentSystemDefault()
): String {
return toLocalDateTime(timeZone).let { localDateTime ->
buildString {
append(
localDateTime.dayOfMonth.toString().padStart(2, '0')
)
append(" ")
append(
localDateTime.month.name.take(3)
.lowercase().replaceFirstChar { it.titlecase() }
)
append(" ")
append(localDateTime.year)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.github.reactivecircus.kstreamlined.kmp.prettytime

import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

class PrettyTimeTest {

@Test
fun weeksAgo() {
val now = Clock.System.now()
assertEquals("This week", now.minus(1.seconds).weeksAgo())
assertEquals("This week", now.minus(7.days - 1.seconds).weeksAgo())
assertEquals("Last week", now.minus(7.days).weeksAgo())
assertEquals("Last week", now.minus(14.days - 1.seconds).weeksAgo())
assertEquals("Earlier", now.minus(14.days).weeksAgo())
assertEquals("Earlier", now.minus(100.days).weeksAgo())
}

@Test
fun timeAgp() {
val fixedClock = object : Clock {
override fun now(): Instant {
return "2023-12-03T03:10:54Z".toInstant()
}
}
val now = fixedClock.now()
val timeZone = TimeZone.UTC
assertEquals("Moments ago", now.minus(1.seconds).timeAgo(fixedClock, timeZone))
assertEquals("Moments ago", now.minus(59.seconds).timeAgo(fixedClock, timeZone))
assertEquals("1 minute ago", now.minus(60.seconds).timeAgo(fixedClock, timeZone))
assertEquals("59 minutes ago", now.minus(59.minutes).timeAgo(fixedClock, timeZone))
assertEquals("1 hour ago", now.minus(60.minutes).timeAgo(fixedClock, timeZone))
assertEquals("23 hours ago", now.minus(23.hours).timeAgo(fixedClock, timeZone))
assertEquals("Yesterday", now.minus(24.hours).timeAgo(fixedClock, timeZone))
assertEquals("Yesterday", now.minus(47.hours).timeAgo(fixedClock, timeZone))
assertEquals("2 days ago", now.minus(48.hours).timeAgo(fixedClock, timeZone))
assertEquals("6 days ago", now.minus(7.days - 1.seconds).timeAgo(fixedClock, timeZone))
assertEquals("26 Nov 2023", now.minus(7.days).timeAgo(fixedClock, timeZone))
assertEquals("03 Dec 2022", now.minus(365.days).timeAgo(fixedClock, timeZone))
}

@Test
fun toFormattedTime() {
val timeZone = TimeZone.UTC
assertEquals("03 Dec 2023", "2023-12-03T03:10:54Z".toInstant().toFormattedTime(timeZone))
assertEquals("23 Oct 2023", "2023-10-23T12:00:54Z".toInstant().toFormattedTime(timeZone))
assertEquals("21 Nov 2022", "2022-11-21T23:00:00Z".toInstant().toFormattedTime(timeZone))
}
}
6 changes: 4 additions & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,16 @@ plugins {
rootProject.name = "kstreamlined-mobile"

// KMP
include(":kmp:core-utils")
include(":kmp:data")
include(":kmp:feed-datasource:common")
include(":kmp:feed-datasource:cloud")
include(":kmp:feed-datasource:edge")
include(":kmp:feed-datasource:testing")
include(":kmp:persistence")
include(":kmp:model")
include(":kmp:core-utils")
include(":kmp:persistence")
include(":kmp:presentation:home")
include(":kmp:pretty-time")
include(":kmp:test-utils")

val isXCFrameworkBuild = startParameter.taskNames.any { it.endsWith("XCFramework") }
Expand Down

0 comments on commit e248ecb

Please sign in to comment.