commonMain.io.islandtime.TimeZone.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
package io.islandtime
import io.islandtime.format.TimeZoneTextProvider
import io.islandtime.format.TimeZoneTextStyle
import io.islandtime.locale.Locale
import io.islandtime.locale.defaultLocale
import io.islandtime.measures.nanoseconds
import io.islandtime.measures.seconds
import io.islandtime.parser.*
import io.islandtime.zone.FixedTimeZoneRules
import io.islandtime.zone.TimeZoneRules
import io.islandtime.zone.TimeZoneRulesException
import io.islandtime.zone.TimeZoneRulesProvider
/**
* A time zone.
*/
sealed class TimeZone : Comparable {
/**
* An ID that uniquely identifies the time zone.
*/
abstract val id: String
/**
* Check if this is a valid time zone according to the current time zone rules provider.
*/
abstract val isValid: Boolean
/**
* Get the rules associated with this time zone.
* @throws TimeZoneRulesException if the current time zone rules provider doesn't support [id]
*/
abstract val rules: TimeZoneRules
/**
* Check if the time zone is valid and throw an exception if it isn't.
*
* @throws TimeZoneRulesException if the current time zone rules provider doesn't support [id]
* @see isValid
*/
fun validate() {
if (!isValid) {
throw TimeZoneRulesException("'$id' is not supported by the current time zone rules provider")
}
}
/**
* Ensure that the time zone is valid, throwing an exception if it isn't.
*
* @throws TimeZoneRulesException if the current time zone rules provider doesn't support [id]
* @see isValid
*/
fun validated(): TimeZone = apply { validate() }
/**
* The localized name of the time zone, if available for the locale in the specified style. The result depends on
* the configured [TimeZoneTextProvider] and may differ between platforms.
*
* Example output for the "America/New_York" ID and "en-US" locale:
* - Standard: "Eastern Standard Time"
* - Short standard: "EST"
* - Daylight Saving: "Eastern Daylight Time"
* - Short daylight saving: "EDT"
* - Generic: "Eastern Time"
* - Short generic: "ET"
*
* @see displayName
*/
fun localizedName(style: TimeZoneTextStyle, locale: Locale = defaultLocale()): String? {
return TimeZoneTextProvider.timeZoneTextFor(this, style, locale)
}
/**
* A textual representation of the time zone, suitable for display purposes. The localized name will be returned, if
* available for the locale in the specified style. If not, the [id] will be returned instead.
*
* The result depends on the configured [TimeZoneTextProvider] and may differ between platforms.
*
* Example output for the "America/New_York" ID and "en-US" locale:
* - Standard: "Eastern Standard Time"
* - Short standard: "EST"
* - Daylight Saving: "Eastern Daylight Time"
* - Short daylight saving: "EDT"
* - Generic: "Eastern Time"
* - Short generic: "ET"
*
* @see localizedName
* @see id
*/
fun displayName(style: TimeZoneTextStyle, locale: Locale = defaultLocale()): String {
return localizedName(style, locale) ?: id
}
/**
* Get a normalized time zone.
*
* Any time zone with a fixed offset will be converted to use a consistent identifier.
*
* @throws TimeZoneRulesException if the current time zone rules provider doesn't support [id]
*/
abstract fun normalized(): TimeZone
override fun compareTo(other: TimeZone): Int {
return id.compareTo(other.id)
}
override fun toString() = id
/**
* A named time zone, typically corresponding to a region identifier in the IANA Time Zone Database, but may be any
* name that can be understood by a [TimeZoneRulesProvider].
*
* @param id an ID that is understood by a time zone rules provider
*/
class Region internal constructor(
override val id: String
) : TimeZone() {
override val isValid: Boolean
get() = TimeZoneRulesProvider.hasRulesFor(id)
override val rules: TimeZoneRules
get() = TimeZoneRulesProvider.rulesFor(id)
override fun normalized(): TimeZone = rules.run {
if (hasFixedOffset) {
FixedOffset(offsetAt(0L.seconds, 0.nanoseconds))
} else {
this@Region
}
}
override fun equals(other: Any?): Boolean {
return this === other || (other is Region && id == other.id)
}
override fun hashCode(): Int = id.hashCode()
}
/**
* A time zone defined by a fixed offset from UTC.
*
* In general, region-based time zones are preferred, but there are situations where only a fixed offset may be
* available.
*
* @param offset a valid UTC offset
* @throws DateTimeException if [offset] is outside the valid range
*/
class FixedOffset internal constructor(
val offset: UtcOffset
) : TimeZone() {
init {
offset.validate()
}
override val id: String get() = offset.toString()
override val isValid: Boolean get() = true
override val rules: TimeZoneRules get() = FixedTimeZoneRules(offset)
override fun normalized() = this
override fun equals(other: Any?): Boolean {
return this === other || (other is FixedOffset && offset == other.offset)
}
override fun hashCode() = offset.hashCode()
}
companion object {
/**
* A fixed time zone representing UTC.
*/
val UTC: TimeZone = FixedOffset(UtcOffset.ZERO)
@Suppress("FunctionName")
fun FixedOffset(id: String): TimeZone {
return try {
FixedOffset(id.toUtcOffset(FIXED_TIME_ZONE_PARSER))
} catch (e: DateTimeParseException) {
throw TimeZoneRulesException("'$id' is an invalid ID", e)
}
}
}
}
/**
* Create a [TimeZone] from an identifier.
*/
@Suppress("FunctionName")
fun TimeZone(id: String): TimeZone {
return when {
id == "Z" -> TimeZone.UTC
id.length < 2 -> throw DateTimeException("'$id' is an invalid ID")
id.startsWith('-') || id.startsWith('+') -> TimeZone.FixedOffset(id)
else -> TimeZone.Region(id)
}
}
/**
* Convert a UTC offset into a [TimeZone] with a fixed offset.
*/
fun UtcOffset.asTimeZone(): TimeZone = TimeZone.FixedOffset(this)
/**
* Convert a string to a [TimeZone].
*/
fun String.toTimeZone() = TimeZone(this)
internal const val MAX_TIME_ZONE_STRING_LENGTH = 50
/**
* A strict parser that expects an offset identical to Island Time's string representation for [UtcOffset].
*/
private val FIXED_TIME_ZONE_PARSER = dateTimeParser {
utcOffsetSign()
utcOffsetHours(2)
+':'
utcOffsetMinutes(2)
optional {
+':'
utcOffsetSeconds(2)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy