Repository: thunderbird/thunderbird-android
File: legacy/ui/legacy/src/main/java/com/fsck/k9/ui/helper/RelativeDateTimeFormatter.kt (Kotlin)

Code before:
@OptIn(ExperimentalTime::class)
constructor(
    private val context: Context,
    private val clock: Clock,
) {

    fun formatDate(timestamp: Long, dateTimeFormat: MessageListDateTimeFormat): String {
        @OptIn(ExperimentalTime::class)
        val now = clock.now().toCalendar()
        val date = timestamp.toCalendar()
        return when (dateTimeFormat) {
            MessageListDateTimeFormat.Contextual -> {
                val flags = when {
                    date.isToday() -> FORMAT_SHOW_TIME
                    date.isWithinPastSevenDaysOf(now) -> FORMAT_SHOW_WEEKDAY or FORMAT_ABBREV_WEEKDAY
                    date.isSameYearAs(now) -> FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH
                    else -> FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR or FORMAT_NUMERIC_DATE
                }
                DateUtils.formatDateRange(context, timestamp, timestamp, flags)
            }
            MessageListDateTimeFormat.Full -> {
                val flags = FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR or FORMAT_NUMERIC_DATE
                DateUtils.formatDateRange(context, timestamp, timestamp, flags)
            }
            MessageListDateTimeFormat.ISO -> {
                val sdf = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm", java.util.Locale.US)
                sdf.format(java.util.Date(timestamp))
            }
        }
    }
}

private fun Long.toCalendar(): Calendar {
    val calendar = Calendar.getInstance()
    calendar.timeInMillis = this
    return calendar
}

@OptIn(ExperimentalTime::class)
private fun Instant.toCalendar(): Calendar {
    val calendar = Calendar.getInstance()
    calendar.timeInMillis = this.toEpochMilliseconds()
    return calendar
}

private fun Calendar.isToday() = DateUtils.isToday(this.timeInMillis)

private fun Calendar.isWithinPastSevenDaysOf(other: Calendar) = this.before(other) &&
    DateUtils.WEEK_IN_MILLIS > other.timeInMillis - this.timeInMillis &&
    this[DAY_OF_WEEK] != other[DAY_OF_WEEK]


Review comment (suggestion):
You should be able to add `import java.text.SimpleDateFormat` to not need the `java.text.` prefix here.

The Locale should be based on the user's locale. `Locale.getDefault()` should work.

I would normally recommend using the  `DateTimeFormatter` constant for ISO date/time, but I think for this usage, a space makes more sense here instead of the standard `T`, as it's more legible. This would be displaying with a large amount of text around it, and skimming legibility feels more important than strict adherence to ISO. Also, that would be `"${DateTimeFormatter.ISO_LOCAL_DATE} HH:mm"`, and I'm not sure if it's worth it to pull in the date time formatter just to pull in the localized date, so I think leaving it as a string like this is fine. 


Code after:
 */
class RelativeDateTimeFormatter
@OptIn(ExperimentalTime::class)
constructor(
    private val context: Context,
    private val clock: Clock,
) {

    fun formatDate(timestamp: Long, dateTimeFormat: MessageListDateTimeFormat): String {
        @OptIn(ExperimentalTime::class)
        val now = clock.now().toCalendar()
        val date = timestamp.toCalendar()
        return when (dateTimeFormat) {
            MessageListDateTimeFormat.Contextual -> {
                val flags = when {
                    date.isToday() -> FORMAT_SHOW_TIME
                    date.isWithinPastSevenDaysOf(now) -> FORMAT_SHOW_WEEKDAY or FORMAT_ABBREV_WEEKDAY
                    date.isSameYearAs(now) -> FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH
                    else -> FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR or FORMAT_NUMERIC_DATE
                }
                DateUtils.formatDateRange(context, timestamp, timestamp, flags)
            }
            MessageListDateTimeFormat.Full -> {
                val flags = FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_SHOW_YEAR or FORMAT_NUMERIC_DATE
                DateUtils.formatDateRange(context, timestamp, timestamp, flags)
            }
            MessageListDateTimeFormat.ISO -> {
                val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
                sdf.format(java.util.Date(timestamp))
            }
        }
    }
}

private fun Long.toCalendar(): Calendar {
    val calendar = Calendar.getInstance()
    calendar.timeInMillis = this
    return calendar
}

@OptIn(ExperimentalTime::class)
private fun Instant.toCalendar(): Calendar {
    val calendar = Calendar.getInstance()
    calendar.timeInMillis = this.toEpochMilliseconds()
    return calendar
}

private fun Calendar.isToday() = DateUtils.isToday(this.timeInMillis)

private fun Calendar.isWithinPastSevenDaysOf(other: Calendar) = this.before(other) &&
    DateUtils.WEEK_IN_MILLIS > other.timeInMillis - this.timeInMillis &&