Format the "updated" timestamp as a locale-aware date/time
Replaces the raw "2026-09-10 10:03:54" with each platform's own locale-formatted, no-seconds rendering (e.g. "Sep 10, 2026, 10:03 AM" in en-US) — mirroring how amounts are already locale-formatted elsewhere (NumberFormat.kt). Android uses the pre-java.time Calendar/DateFormat APIs since minSdk 24 predates java.time (API 26) and the project has no core library desugaring configured. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
package xyz.tyiu.satsprice.domain
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
// java.time requires API 26+ (this app's minSdk is 24) without core library desugaring, so this
|
||||||
|
// uses the older Calendar/DateFormat APIs instead, same as pre-java.time Android code always did.
|
||||||
|
actual fun localizedDateTime(dateTime: LocalDateTime): String {
|
||||||
|
val calendar = Calendar.getInstance().apply {
|
||||||
|
clear()
|
||||||
|
set(dateTime.year, dateTime.monthNumber - 1, dateTime.dayOfMonth, dateTime.hour, dateTime.minute)
|
||||||
|
}
|
||||||
|
val formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
|
||||||
|
return formatter.format(calendar.time)
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package xyz.tyiu.satsprice.domain
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import platform.Foundation.NSCalendar
|
||||||
|
import platform.Foundation.NSDateComponents
|
||||||
|
import platform.Foundation.NSDateFormatter
|
||||||
|
import platform.Foundation.NSDateFormatterMediumStyle
|
||||||
|
import platform.Foundation.NSDateFormatterShortStyle
|
||||||
|
import platform.Foundation.NSLocale
|
||||||
|
import platform.Foundation.currentLocale
|
||||||
|
|
||||||
|
actual fun localizedDateTime(dateTime: LocalDateTime): String {
|
||||||
|
val components = NSDateComponents().apply {
|
||||||
|
year = dateTime.year.toLong()
|
||||||
|
month = dateTime.monthNumber.toLong()
|
||||||
|
day = dateTime.dayOfMonth.toLong()
|
||||||
|
hour = dateTime.hour.toLong()
|
||||||
|
minute = dateTime.minute.toLong()
|
||||||
|
}
|
||||||
|
val date = NSCalendar.currentCalendar.dateFromComponents(components) ?: return dateTime.toString()
|
||||||
|
val formatter = NSDateFormatter().apply {
|
||||||
|
dateStyle = NSDateFormatterMediumStyle
|
||||||
|
timeStyle = NSDateFormatterShortStyle
|
||||||
|
locale = NSLocale.currentLocale
|
||||||
|
}
|
||||||
|
return formatter.stringFromDate(date)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package xyz.tyiu.satsprice.domain
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A locale-aware, human-readable rendering of [dateTime] (e.g. "Sep 10, 2026, 10:03 AM" in
|
||||||
|
* en-US), using each platform's own date/time formatter — mirroring how [localizedGroupedInteger]
|
||||||
|
* and [localizedDecimalSeparator] format numbers. Deliberately drops seconds: a "last updated"
|
||||||
|
* label doesn't need second-level precision.
|
||||||
|
*/
|
||||||
|
expect fun localizedDateTime(dateTime: LocalDateTime): String
|
||||||
@@ -5,6 +5,7 @@ import kotlinx.datetime.toLocalDateTime
|
|||||||
import xyz.tyiu.satsprice.CurrencyInfo
|
import xyz.tyiu.satsprice.CurrencyInfo
|
||||||
import xyz.tyiu.satsprice.domain.CurrencyConverter
|
import xyz.tyiu.satsprice.domain.CurrencyConverter
|
||||||
import xyz.tyiu.satsprice.domain.formatAmount
|
import xyz.tyiu.satsprice.domain.formatAmount
|
||||||
|
import xyz.tyiu.satsprice.domain.localizedDateTime
|
||||||
import xyz.tyiu.satsprice.domain.toBigDecimalOrNull
|
import xyz.tyiu.satsprice.domain.toBigDecimalOrNull
|
||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
|
|
||||||
@@ -52,7 +53,5 @@ fun ConverterUiState.unselectedCurrencies(): List<CurrencyInfo> =
|
|||||||
/** Whether the active price source quotes a rate for [code] — every currency is listed regardless. */
|
/** Whether the active price source quotes a rate for [code] — every currency is listed regardless. */
|
||||||
fun ConverterUiState.isPriced(code: String): Boolean = code in pricedCurrencyCodes
|
fun ConverterUiState.isPriced(code: String): Boolean = code in pricedCurrencyCodes
|
||||||
|
|
||||||
private fun Instant.toDateTimeString(): String {
|
private fun Instant.toDateTimeString(): String =
|
||||||
val local = toLocalDateTime(TimeZone.currentSystemDefault())
|
localizedDateTime(toLocalDateTime(TimeZone.currentSystemDefault()))
|
||||||
return local.toString().substringBefore('.').replace('T', ' ')
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package xyz.tyiu.satsprice.domain
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.datetime.toJavaLocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.FormatStyle
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
actual fun localizedDateTime(dateTime: LocalDateTime): String {
|
||||||
|
val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)
|
||||||
|
.withLocale(Locale.getDefault())
|
||||||
|
return dateTime.toJavaLocalDateTime().format(formatter)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package xyz.tyiu.satsprice.domain
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
|
||||||
|
// Individual Int fields (rather than the LocalDateTime itself) cross the js() boundary, matching
|
||||||
|
// the rest of this file's web interop — see SystemCurrencies.web.kt for why.
|
||||||
|
private fun jsFormatDateTime(year: Int, month: Int, day: Int, hour: Int, minute: Int): String = js(
|
||||||
|
"""(function() {
|
||||||
|
var date = new Date(year, month - 1, day, hour, minute);
|
||||||
|
var fmt = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' });
|
||||||
|
return fmt.format(date);
|
||||||
|
})()""",
|
||||||
|
)
|
||||||
|
|
||||||
|
actual fun localizedDateTime(dateTime: LocalDateTime): String =
|
||||||
|
jsFormatDateTime(dateTime.year, dateTime.monthNumber, dateTime.dayOfMonth, dateTime.hour, dateTime.minute)
|
||||||
Reference in New Issue
Block a user