commonMain.io.islandtime.Year.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-metadata Show documentation
Show all versions of core-metadata Show documentation
A multiplatform library for working with dates and times
The newest version!
package io.islandtime
import io.islandtime.base.DateTimeField
import io.islandtime.internal.toZeroPaddedString
import io.islandtime.measures.*
import io.islandtime.parser.*
import io.islandtime.ranges.DateRange
import kotlin.math.absoluteValue
/**
* A year as defined by ISO-8601.
* @constructor Creates a [Year].
* @param value the year
* @property value The year value.
*/
inline class Year(val value: Int) : Comparable {
/**
* Checks if this year is within the supported range.
*/
val isValid: Boolean get() = value in MIN_VALUE..MAX_VALUE
/**
* Checks if this is a leap year.
*/
val isLeap: Boolean
get() = value % 4 == 0 && (value % 100 != 0 || value % 400 == 0)
/**
* The length of the year in days.
*/
val length: IntDays
get() = if (isLeap) 366.days else 365.days
/**
* The last day of the year. This will be either `365` or `366` depending on whether this is a common or leap year.
*/
val lastDay: Int get() = length.value
/**
* The day range of the year. This will be either `1..365` or `1.366` depending on whether this is a common or leap
* year.
*/
val dayRange: IntRange get() = 1..lastDay
/**
* The date range of the year.
*/
val dateRange: DateRange get() = DateRange(startDate, endDate)
/**
* The first date of the year.
*/
val startDate: Date get() = Date(value, Month.JANUARY, 1)
/**
* The last date of the year.
*/
val endDate: Date get() = Date(value, Month.DECEMBER, 31)
operator fun plus(years: LongYears): Year {
val newValue = checkValidYear(value + years.value)
return Year(newValue)
}
operator fun plus(years: IntYears) = plus(years.toLongYears())
operator fun minus(years: LongYears): Year {
return if (years.value == Long.MIN_VALUE) {
this + Long.MAX_VALUE.years + 1L.years
} else {
plus(years.negateUnchecked())
}
}
operator fun minus(years: IntYears) = plus(years.toLongYears().negateUnchecked())
operator fun contains(yearMonth: YearMonth): Boolean = yearMonth.year == value
operator fun contains(date: Date): Boolean = date.year == value
/**
* Ensures that this year is valid, throwing an exception if it isn't.
* @throws DateTimeException if the year is invalid
* @see isValid
*/
fun validated(): Year {
if (!isValid) {
throw DateTimeException(getInvalidYearMessage(value.toLong()))
}
return this
}
override fun compareTo(other: Year): Int = value - other.value
/**
* Converts this year to a string in ISO-8601 extended format. For example, `2012`, `-0001`, or `+10000`.
*/
override fun toString(): String {
val absValue = value.absoluteValue
return when {
absValue < 1000 -> if (value < 0) {
"-${absValue.toZeroPaddedString(4)}"
} else {
absValue.toZeroPaddedString(4)
}
value > 9999 -> "+$value"
else -> value.toString()
}
}
companion object {
/**
* The earliest supported year value.
*/
const val MIN_VALUE = -999_999_999
/**
* The latest supported year value.
*/
const val MAX_VALUE = 999_999_999
/**
* The earliest supported [Year], which can be used as a "far past" sentinel.
*/
val MIN = Year(MIN_VALUE)
/**
* The latest supported [Year], which can be used as a "far future" sentinel.
*/
val MAX = Year(MAX_VALUE)
}
}
/**
* Converts a string to a [Year].
*
* The string is assumed to be an ISO-8601 year. For example, `2010`, `+002010`, or `Y12345`. The output of
* [Year.toString] can be safely parsed using this method.
*
* @throws DateTimeParseException if parsing fails
* @throws DateTimeException if the parsed year is invalid
*/
fun String.toYear(): Year = toYear(DateTimeParsers.Iso.YEAR)
/**
* Converts a string to a [Year] using a specific parser.
*
* A set of predefined parsers can be found in [DateTimeParsers].
*
* The parser must be capable of supplying [DateTimeField.YEAR].
*
* @throws DateTimeParseException if parsing fails
* @throws DateTimeException if the parsed year is invalid
*/
fun String.toYear(
parser: DateTimeParser,
settings: DateTimeParserSettings = DateTimeParserSettings.DEFAULT
): Year {
val result = parser.parse(this, settings)
return result.toYear() ?: throwParserFieldResolutionException(this)
}
internal fun DateTimeParseResult.toYear(): Year? {
val value = fields[DateTimeField.YEAR]
return if (value != null) {
Year(checkValidYear(value))
} else {
null
}
}
internal fun isLeapYear(year: Int) = Year(year).isLeap
internal fun lengthOfYear(year: Int) = Year(year).length
internal fun lastDayOfYear(year: Int): Int = Year(year).lastDay
internal fun checkValidYear(year: Int) = Year(year).validated().value
internal fun checkValidYear(year: Long): Int {
if (!isValidYear(year)) {
throw DateTimeException(getInvalidYearMessage(year))
}
return year.toInt()
}
internal fun isValidYear(year: Long): Boolean {
return year in Year.MIN_VALUE..Year.MAX_VALUE
}
internal fun StringBuilder.appendYear(year: Int): StringBuilder {
val absValue = year.absoluteValue
return when {
absValue < 1000 -> {
if (year < 0) append('-')
append(absValue.toZeroPaddedString(4))
}
year > 9999 -> append('+').append(year)
else -> append(year)
}
}
private fun getInvalidYearMessage(year: Long): String {
return "The year '${year}' is outside the supported range of ${Year.MIN_VALUE}..${Year.MAX_VALUE}"
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy