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

commonMain.epicarchitect.calendar.compose.basis.EpicMonth.kt Maven / Gradle / Ivy

package epicarchitect.calendar.compose.basis

import androidx.compose.runtime.Immutable
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.Month
import kotlinx.datetime.TimeZone
import kotlinx.datetime.number
import kotlinx.datetime.toLocalDateTime
import kotlin.math.abs
import kotlin.math.floor

@Immutable
data class EpicMonth(
    val year: Int,
    val month: Month
) : Comparable {

    val numberOfDays = length(isLeapYear(year.toLong()))

    private fun length(leapYear: Boolean): Int {
        return when (month) {
            Month.FEBRUARY -> if (leapYear) 29 else 28
            Month.APRIL, Month.JUNE, Month.SEPTEMBER, Month.NOVEMBER -> 30
            else -> 31
        }
    }

    override fun compareTo(other: EpicMonth): Int {
        var cmp = year - other.year
        if (cmp == 0) {
            cmp = month.number - other.month.number
        }
        return cmp
    }

    companion object {
        fun now(timeZone: TimeZone = TimeZone.currentSystemDefault()) =
            Clock.System.now().toLocalDateTime(timeZone).date.epicMonth
    }
}

fun EpicMonth.addYears(amount: Int) = copy(year = year + amount)

fun EpicMonth.addMonths(monthsToAdd: Int): EpicMonth {
    if (monthsToAdd == 0) return this
    val monthCount = year * 12 + (month.number - 1)
    val calcMonths = monthCount + monthsToAdd
    val newYear = floor(calcMonths.div(12.0)).toInt()
    val newMonth = floor(calcMonths.mod(12).toDouble()).toInt() + 1
    return EpicMonth(newYear, Month(newMonth))
}

fun EpicMonth.previous(): EpicMonth = addMonths(-1)

fun EpicMonth.next(): EpicMonth = addMonths(1)

val LocalDate.epicMonth: EpicMonth get() = EpicMonth(year, month)

fun EpicMonth.atDay(dayOfMonth: Int) = LocalDate(year, month, dayOfMonth)

fun EpicMonth.atStartDay() = atDay(1)

fun EpicMonth.atEndDay() = atDay(numberOfDays)

fun EpicMonth.lastDayOfWeek() = atDay(numberOfDays).dayOfWeek

// copied from IsoChronology
fun isLeapYear(year: Long): Boolean {
    return year and 3L == 0L && (year % 100 != 0L || year % 400 == 0L)
}

operator fun EpicMonth.contains(date: LocalDate): Boolean {
    val start = LocalDateTime(year, month, 1, 0, 0, 0).date
    val end = LocalDateTime(year, month, numberOfDays, 23, 59, 59).date
    return date in start..end
}

fun ClosedRange.getByIndex(index: Int) = start.addMonths(
    if (start < endInclusive) index
    else -index
)

fun ClosedRange.indexOf(month: EpicMonth): Int? {
    val (start, end) = if (start < endInclusive) start to month else endInclusive to month
    val startMonthCount = start.year * 12 + start.month.number
    val endMonthCount = end.year * 12 + end.month.number
    val result = endMonthCount - startMonthCount
    return if (result < 0 || result >= size()) null else result
}

fun ClosedRange.size(): Int {
    val startMonthCount = start.year * 12 + start.month.number
    val endMonthCount = endInclusive.year * 12 + endInclusive.month.number
    return abs(endMonthCount - startMonthCount) + 1
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy