Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/src/main/java/to/bitkit/ext/DateTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import kotlin.time.Clock
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.ExperimentalTime
import java.text.DateFormat as JavaDateFormat
import java.time.LocalDate as JavaLocalDate
import kotlin.time.Instant as KInstant

Expand Down Expand Up @@ -68,10 +69,10 @@ fun Long.toDateUTC(): String {
return dateTime.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"))
}

fun Long.toLocalizedTimestamp(locale: Locale = Locale.US): String {
fun Long.toLocalizedTimestamp(locale: Locale = Locale.getDefault()): String {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 App locale remains ignored

When the app language differs from the device language, Locale.getDefault() still supplies the device locale because AppLocaleManager changes the application resource configuration without changing the JVM default. The no-argument callers therefore continue rendering App Status and Backup timestamps in the device language instead of the language selected in Bitkit.

Knowledge Base Used: Extension Utilities (to.bitkit.ext)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppCompat keeps those in sync: on API 28 to 32 setApplicationLocales ends in setDefaultLocalesForLocaleList, which calls LocaleList.setDefault, and on 33+ the platform LocaleManager does it. Verified the German row on an emulator.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — I was wrong about this. AppCompat's backport path does call LocaleList.setDefault, which in turn calls Locale.setDefault, so Locale.getDefault() does reflect the in-app language selection. Combined with your emulator verification on German, my concern doesn't hold.

The change is correct as written.

Shall I remember this as a rule: "do not flag Locale.getDefault() as ignoring the in-app locale when AppCompatDelegate is used, because AppCompat syncs it via LocaleList.setDefault on all supported API levels"?

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.

val date = Date(this)
val formatter = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT, ULocale.forLocale(locale))
?: return SimpleDateFormat("MMMM d, yyyy 'at' h:mm a", locale).format(date)
?: return JavaDateFormat.getDateTimeInstance(JavaDateFormat.LONG, JavaDateFormat.SHORT, locale).format(date)
return formatter.format(date)
}

Expand Down
38 changes: 38 additions & 0 deletions app/src/test/java/to/bitkit/ext/DateTimeExtTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class DateTimeExtTest : BaseUnitTest() {
val AFTERNOON: Instant = Instant.parse("2026-03-07T15:23:00Z")
val MIDNIGHT: Instant = Instant.parse("2026-03-07T00:15:00Z")
val NOON: Instant = Instant.parse("2026-03-07T12:05:00Z")

// Midday so the calendar day, and with it the month name, holds in every time zone
val MIDDAY_IN_JULY: Long = Instant.parse("2026-07-27T12:00:00Z").toEpochMilli()
}

@Test
Expand Down Expand Up @@ -261,5 +264,40 @@ class DateTimeExtTest : BaseUnitTest() {
assertEquals(UiDateStyle.DATE_TIME, uiDateStyleFor(lateEvening.epochSecond.toULong(), today, bucharest))
}

@Test
fun `toLocalizedTimestamp uses the supplied locale`() {
val inGerman = MIDDAY_IN_JULY.toLocalizedTimestamp(Locale.GERMANY)
val inUs = MIDDAY_IN_JULY.toLocalizedTimestamp(Locale.US)

assertTrue(inGerman.contains("Juli"), "expected a German month name, got '$inGerman'")
assertTrue(inUs.contains("July"), "expected an English month name, got '$inUs'")
}

@Test
fun `toLocalizedTimestamp follows the default locale when none is supplied`() {
val original = Locale.getDefault()
try {
Locale.setDefault(Locale.GERMANY)
val result = MIDDAY_IN_JULY.toLocalizedTimestamp()

assertTrue(result.contains("Juli"), "expected a German month name, got '$result'")
} finally {
Locale.setDefault(original)
}
}

@Test
fun `toLocalizedTimestamp orders the date the way the locale does`() {
val original = Locale.getDefault()
try {
Locale.setDefault(Locale.GERMANY)
val result = MIDDAY_IN_JULY.toLocalizedTimestamp()

assertTrue(result.indexOf("27") < result.indexOf("Juli"), "expected day before month, got '$result'")
} finally {
Locale.setDefault(original)
}
}

private fun Instant.formattedInUtc(pattern: String) = formatted(pattern, Locale.US, UTC)
}
1 change: 1 addition & 0 deletions changelog.d/next/1124.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
App Status and Backup timestamps now follow the device language instead of always using the US date format.
Loading