All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.io.islandtime.format.DateTimeTextProvider.kt Maven / Gradle / Ivy

The newest version!
package io.islandtime.format

import io.islandtime.DateTimeException
import io.islandtime.base.DateTimeField
import java.text.DateFormatSymbols
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.collections.HashMap

actual object PlatformDateTimeTextProvider : DateTimeTextProvider {
    private val monthText = ConcurrentHashMap>>()
    private val parsableText = ConcurrentHashMap()
    private val narrowEraSymbols = arrayOf("B", "A")
    private val englishLongEraSymbols = arrayOf("Before Christ", "Anno Domini")

    private val descendingTextComparator =
        compareByDescending> { it.first.length }.thenBy { it.second }

    private data class ParsableTextKey(
        val field: DateTimeField,
        val styles: Set,
        val locale: Locale
    )

    override fun parsableTextFor(
        field: DateTimeField,
        styles: Set,
        locale: Locale
    ): ParsableTextList {
        if (styles.isEmpty() || !supports(field)) {
            return emptyList()
        }

        val key = ParsableTextKey(field, styles, locale)

        return parsableText.getOrPut(key) {
            val valueMap = mutableMapOf>()

            styles.forEach { style ->
                allTextFor(field, style, locale).forEachIndexed { index, symbol ->
                    valueMap.getOrPut(symbol) { mutableSetOf() } += valueForArrayIndex(field, index)
                }
            }

            valueMap.mapNotNull {
                if (it.value.size == 1) {
                    it.key to it.value.first()
                } else {
                    null
                }
            }.sortedWith(descendingTextComparator)
        }
    }

    override fun dayOfWeekTextFor(value: Long, style: TextStyle, locale: Locale): String? {
        if (value !in 1L..7L) {
            throw DateTimeException("'$value' is outside the supported day of week field range")
        }

        val symbols = DateFormatSymbols.getInstance(locale)
        val index = if (value == 7L) 1 else value.toInt() + 1

        return when (style) {
            TextStyle.FULL,
            TextStyle.FULL_STANDALONE -> symbols.weekdays[index]
            TextStyle.SHORT,
            TextStyle.SHORT_STANDALONE -> symbols.shortWeekdays[index]
            TextStyle.NARROW,
            TextStyle.NARROW_STANDALONE -> symbols.weekdays[index].substring(0, 1)
        }
    }

    override fun monthTextFor(value: Long, style: TextStyle, locale: Locale): String? {
        if (value !in 1L..12L) {
            throw DateTimeException("'$value' is outside the supported month of year field range")
        }

        return allMonthTextFor(style, locale)[value.toInt() - 1]
    }

    override fun amPmTextFor(value: Long, locale: Locale): String? {
        if (value !in 0L..1L) {
            throw DateTimeException("'$value' is outside the supported AM/PM range")
        }

        return allAmPmTextFor(locale)[value.toInt()]
    }

    override fun eraTextFor(value: Long, style: TextStyle, locale: Locale): String? {
        if (value !in 0L..1L) {
            throw DateTimeException("'$value' is outside the supported era field range")
        }

        return allEraTextFor(style, locale)[value.toInt()]
    }

    private fun supports(field: DateTimeField): Boolean {
        return when (field) {
            DateTimeField.MONTH_OF_YEAR,
            DateTimeField.DAY_OF_WEEK,
            DateTimeField.AM_PM_OF_DAY,
            DateTimeField.ERA -> true
            else -> false
        }
    }

    private fun allTextFor(field: DateTimeField, style: TextStyle, locale: Locale): Array {
        return when (field) {
            DateTimeField.MONTH_OF_YEAR -> allMonthTextFor(style, locale)
            DateTimeField.DAY_OF_WEEK -> allDayOfWeekTextFor(style, locale)
            DateTimeField.AM_PM_OF_DAY -> allAmPmTextFor(locale)
            DateTimeField.ERA -> allEraTextFor(style, locale)
            else -> throw IllegalStateException("Unexpected field")
        }
    }

    private fun valueForArrayIndex(field: DateTimeField, index: Int): Long {
        return when (field) {
            DateTimeField.MONTH_OF_YEAR,
            DateTimeField.DAY_OF_WEEK -> index + 1L
            else -> index.toLong()
        }
    }

    private fun allDayOfWeekTextFor(style: TextStyle, locale: Locale): Array {
        val symbols = DateFormatSymbols.getInstance(locale)

        val array = when (style) {
            TextStyle.FULL,
            TextStyle.FULL_STANDALONE -> symbols.weekdays
            TextStyle.SHORT,
            TextStyle.SHORT_STANDALONE -> symbols.shortWeekdays
            TextStyle.NARROW,
            TextStyle.NARROW_STANDALONE -> symbols.weekdays
                .map { if (it.isNotEmpty()) it.substring(0, 1) else it }
                .toTypedArray()
        }

        return arrayOf(
            array[Calendar.MONDAY],
            array[Calendar.TUESDAY],
            array[Calendar.WEDNESDAY],
            array[Calendar.THURSDAY],
            array[Calendar.FRIDAY],
            array[Calendar.SATURDAY],
            array[Calendar.SUNDAY]
        )
    }

    private fun allMonthTextFor(style: TextStyle, locale: Locale): Array {
        return allMonthTextFor(locale).getValue(style)
    }

    private fun allMonthTextFor(locale: Locale): HashMap> {
        fun getNormalText(symbols: Array): Array {
            return symbols.sliceArray(Calendar.JANUARY..Calendar.DECEMBER)
        }

        fun getTextFromDateFormat(pattern: String): Array {
            val dateFormat = SimpleDateFormat(pattern, locale)

            return Array(12) {
                dateFormat.run {
                    calendar.set(Calendar.DAY_OF_MONTH, 1)
                    calendar.set(Calendar.MONTH, it)
                    format(calendar.time)
                }
            }
        }

        return monthText.getOrPut(locale) {
            val symbols = DateFormatSymbols.getInstance(locale)
            val fullArray = getNormalText(symbols.months)
            val fullStandaloneArray = getTextFromDateFormat("LLLL")
            val shortArray = getNormalText(symbols.shortMonths)
            val shortStandaloneArray = getTextFromDateFormat("LLL")
            val narrowArray = Array(fullStandaloneArray.size) { fullStandaloneArray[it].substring(0, 1) }

            hashMapOf(
                TextStyle.FULL to fullArray,
                TextStyle.FULL_STANDALONE to fullStandaloneArray,
                TextStyle.SHORT to shortArray,
                TextStyle.SHORT_STANDALONE to shortStandaloneArray,
                TextStyle.NARROW to narrowArray,
                TextStyle.NARROW_STANDALONE to narrowArray
            )
        }
    }

    private fun allAmPmTextFor(locale: Locale): Array {
        return DateFormatSymbols.getInstance(locale).amPmStrings
    }

    private fun allEraTextFor(style: TextStyle, locale: Locale): Array {
        return when (style) {
            TextStyle.FULL, TextStyle.FULL_STANDALONE -> if (locale.language == Locale.ENGLISH.language) {
                englishLongEraSymbols
            } else {
                DateFormatSymbols.getInstance(locale).eras
            }
            TextStyle.SHORT, TextStyle.SHORT_STANDALONE -> DateFormatSymbols.getInstance(locale).eras
            TextStyle.NARROW, TextStyle.NARROW_STANDALONE -> narrowEraSymbols
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy