core-time: add formatDateTimeCompact for dense list rows
Drops the year in the current year and drops the time when it carries no information (all-day items or a midnight time), so list rows show glanceable dates like "20 Jun" or "20 Jun · 14:30". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,13 @@
|
|||||||
package de.jeanlucmakiola.floret.time
|
package de.jeanlucmakiola.floret.time
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
|
import java.time.chrono.IsoChronology
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.DateTimeFormatterBuilder
|
||||||
import java.time.format.FormatStyle
|
import java.time.format.FormatStyle
|
||||||
|
import java.util.Locale
|
||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,3 +31,28 @@ fun Instant.formatTime(): String = atSystemZone().format(timeFormatter)
|
|||||||
/** Date alone for all-day items, otherwise date + time. */
|
/** Date alone for all-day items, otherwise date + time. */
|
||||||
fun Instant.formatDateTime(allDay: Boolean): String =
|
fun Instant.formatDateTime(allDay: Boolean): String =
|
||||||
if (allDay) formatDate() else "${formatDate()} · ${formatTime()}"
|
if (allDay) formatDate() else "${formatDate()} · ${formatTime()}"
|
||||||
|
|
||||||
|
// The locale's medium date pattern with the year token stripped, e.g. "20 Jun" /
|
||||||
|
// "Jun 20" — derived from the pattern so day/month order still follows the locale.
|
||||||
|
// Built once; locale changes within a running process are rare enough to ignore.
|
||||||
|
private val dateNoYearFormatter: DateTimeFormatter = run {
|
||||||
|
val medium = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
|
||||||
|
FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.getDefault(),
|
||||||
|
)
|
||||||
|
val noYear = medium.replace(Regex("[\\s.,/'’-]*y+[\\s.,/'’-]*"), " ").trim()
|
||||||
|
DateTimeFormatter.ofPattern(noYear, Locale.getDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compact form for dense list rows: the year is dropped when the date is in the
|
||||||
|
* current year, and the time is dropped when it carries no information (all-day
|
||||||
|
* items, or a midnight time). Examples: "20 Jun", "20 Jun · 14:30", "20 Jun 2027".
|
||||||
|
*/
|
||||||
|
fun Instant.formatDateTimeCompact(allDay: Boolean): String {
|
||||||
|
val zdt = atSystemZone()
|
||||||
|
val datePart =
|
||||||
|
if (zdt.year == LocalDate.now(ZoneId.systemDefault()).year) zdt.format(dateNoYearFormatter)
|
||||||
|
else formatDate()
|
||||||
|
return if (allDay || zdt.toLocalTime() == LocalTime.MIDNIGHT) datePart
|
||||||
|
else "$datePart · ${formatTime()}"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user