From 7ab21871cd3c0edbf665c0f3b29c5a331b50a9ee Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 21 Dec 2023 14:05:27 +0600 Subject: [PATCH] Revert "add klock module" This reverts commit 65d01b1fb306ab43ff3ca4a39977e499f0076440. --- CHANGELOG.md | 3 - README.md | 6 - klock/LICENSE | 47 -- klock/build.gradle | 7 - .../kotlin/korlibs/time/Time.internal.jvm.kt | 37 -- .../kotlin/korlibs/time/Time.jvm.kt | 8 - .../commonMain/kotlin/korlibs/time/Date.kt | 72 --- .../kotlin/korlibs/time/DateException.kt | 6 - .../kotlin/korlibs/time/DateFormat.kt | 43 -- .../kotlin/korlibs/time/DateTime.kt | 458 --------------- .../kotlin/korlibs/time/DateTimeRange.kt | 154 ----- .../kotlin/korlibs/time/DateTimeRangeSet.kt | 270 --------- .../kotlin/korlibs/time/DateTimeSpan.kt | 143 ----- .../kotlin/korlibs/time/DateTimeSpanFormat.kt | 12 - .../kotlin/korlibs/time/DateTimeTz.kt | 125 ----- .../kotlin/korlibs/time/DayOfWeek.kt | 95 ---- .../kotlin/korlibs/time/Frequency.kt | 42 -- .../commonMain/kotlin/korlibs/time/ISO8601.kt | 446 --------------- .../kotlin/korlibs/time/KlockLocale.kt | 122 ---- .../kotlin/korlibs/time/KlockLocaleContext.kt | 14 - .../commonMain/kotlin/korlibs/time/Measure.kt | 37 -- .../commonMain/kotlin/korlibs/time/Month.kt | 131 ----- .../kotlin/korlibs/time/MonthSpan.kt | 66 --- .../kotlin/korlibs/time/NumberOfTimes.kt | 28 - .../kotlin/korlibs/time/PatternDateFormat.kt | 318 ----------- .../kotlin/korlibs/time/PatternTimeFormat.kt | 183 ------ .../kotlin/korlibs/time/PerformanceCounter.kt | 29 - .../kotlin/korlibs/time/RangesExt.kt | 4 - .../commonMain/kotlin/korlibs/time/Sleep.kt | 7 - .../kotlin/korlibs/time/Stopwatch.kt | 25 - .../commonMain/kotlin/korlibs/time/Time.kt | 50 -- .../kotlin/korlibs/time/TimeFormat.kt | 35 -- .../kotlin/korlibs/time/TimeProvider.kt | 23 - .../kotlin/korlibs/time/TimeSampler.kt | 18 - .../kotlin/korlibs/time/TimeSpan.kt | 208 ------- .../kotlin/korlibs/time/TimedCache.kt | 45 -- .../kotlin/korlibs/time/Timezone.kt | 208 ------- .../kotlin/korlibs/time/TimezoneNames.kt | 29 - .../kotlin/korlibs/time/TimezoneOffset.kt | 69 --- .../commonMain/kotlin/korlibs/time/Year.kt | 144 ----- .../kotlin/korlibs/time/YearMonth.kt | 65 --- .../kotlin/korlibs/time/_TImeBenchmark.kt | 107 ---- .../kotlin/korlibs/time/_Time.internal.kt | 219 -------- .../korlibs/time/internal/KlockInternal.kt | 29 - .../time/locale/ExtendedTimezoneNames.kt | 218 ------- .../kotlin/korlibs/time/locale/de.kt | 81 --- .../kotlin/korlibs/time/locale/es.kt | 35 -- .../kotlin/korlibs/time/locale/fr.kt | 45 -- .../kotlin/korlibs/time/locale/it.kt | 35 -- .../kotlin/korlibs/time/locale/ja.kt | 37 -- .../kotlin/korlibs/time/locale/ko.kt | 37 -- .../kotlin/korlibs/time/locale/nb.kt | 46 -- .../kotlin/korlibs/time/locale/nl.kt | 35 -- .../kotlin/korlibs/time/locale/pt.kt | 48 -- .../kotlin/korlibs/time/locale/ru.kt | 50 -- .../kotlin/korlibs/time/locale/sv.kt | 45 -- .../kotlin/korlibs/time/locale/tr.kt | 51 -- .../kotlin/korlibs/time/locale/uk.kt | 39 -- .../kotlin/korlibs/time/locale/zh.kt | 39 -- .../kotlin/korlibs/time/DateFormatExt.kt | 5 - .../kotlin/korlibs/time/DateFormatTest.kt | 52 -- .../kotlin/korlibs/time/DateRangeTest.kt | 93 --- .../kotlin/korlibs/time/DateTest.kt | 78 --- .../korlibs/time/DateTimeRangeSetTest.kt | 155 ----- .../kotlin/korlibs/time/DateTimeRangeTest.kt | 58 -- .../kotlin/korlibs/time/DateTimeSpanTest.kt | 39 -- .../korlibs/time/DateTimeStartOfEndOfTest.kt | 53 -- .../kotlin/korlibs/time/DateTimeTest.kt | 531 ------------------ .../korlibs/time/DateTimeWithOffsetTest.kt | 59 -- .../kotlin/korlibs/time/DayOfWeekTest.kt | 73 --- .../kotlin/korlibs/time/FrequencyTest.kt | 24 - .../kotlin/korlibs/time/ISO8601Test.kt | 181 ------ .../kotlin/korlibs/time/IssuesTest.kt | 24 - .../kotlin/korlibs/time/KlockTest.kt | 23 - .../time/KotlinNativeImmutabilityTest.kt | 42 -- .../kotlin/korlibs/time/MeasureTest.kt | 20 - .../kotlin/korlibs/time/MonthTest.kt | 109 ---- .../kotlin/korlibs/time/NumberOfTimesTest.kt | 27 - .../korlibs/time/SimplerDateFormatTest.kt | 129 ----- .../kotlin/korlibs/time/StopwatchTest.kt | 26 - .../kotlin/korlibs/time/TimeFormatTest.kt | 67 --- .../kotlin/korlibs/time/TimeSpanTest.kt | 108 ---- .../kotlin/korlibs/time/TimeTest.kt | 45 -- .../kotlin/korlibs/time/TimedCacheTest.kt | 37 -- .../kotlin/korlibs/time/TimezoneTest.kt | 26 - .../kotlin/korlibs/time/YearMonthTest.kt | 40 -- .../kotlin/korlibs/time/YearTest.kt | 18 - .../korlibs/time/internal/PaddedTest.kt | 15 - .../time/internal/TimeZoneParserTest.kt | 22 - .../korlibs/time/locale/KlockLocaleTest.kt | 340 ----------- .../kotlin/korlibs/time/Klock.internal.js.kt | 26 - .../jsMain/kotlin/korlibs/time/Klock.js.kt | 9 - klock/src/jvmMain/kotlin/Time.internal.jvm.kt | 37 -- klock/src/jvmMain/kotlin/Time.jvm.kt | 8 - .../linuxMain/kotlin/Klock.internal.linux.kt | 49 -- .../mingwMain/kotlin/Klockinternal.mingw.kt | 68 --- .../korlibs/time/Klock.internal.native.kt | 5 - settings.gradle | 2 - 98 files changed, 7751 deletions(-) delete mode 100644 klock/LICENSE delete mode 100644 klock/build.gradle delete mode 100644 klock/src/androidMain/kotlin/korlibs/time/Time.internal.jvm.kt delete mode 100644 klock/src/androidMain/kotlin/korlibs/time/Time.jvm.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Date.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateException.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateFormat.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateTime.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateTimeRange.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateTimeRangeSet.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateTimeSpan.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateTimeSpanFormat.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DateTimeTz.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/DayOfWeek.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Frequency.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/ISO8601.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/KlockLocale.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/KlockLocaleContext.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Measure.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Month.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/MonthSpan.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/NumberOfTimes.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/PatternDateFormat.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/PatternTimeFormat.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/PerformanceCounter.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/RangesExt.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Sleep.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Stopwatch.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Time.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/TimeFormat.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/TimeProvider.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/TimeSampler.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/TimeSpan.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/TimedCache.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Timezone.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/TimezoneNames.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/TimezoneOffset.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/Year.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/YearMonth.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/_TImeBenchmark.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/_Time.internal.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/internal/KlockInternal.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/ExtendedTimezoneNames.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/de.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/es.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/fr.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/it.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/ja.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/ko.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/nb.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/nl.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/pt.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/ru.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/sv.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/tr.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/uk.kt delete mode 100644 klock/src/commonMain/kotlin/korlibs/time/locale/zh.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateFormatExt.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateFormatTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateRangeTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateTimeRangeSetTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateTimeRangeTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateTimeSpanTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateTimeStartOfEndOfTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateTimeTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DateTimeWithOffsetTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/DayOfWeekTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/FrequencyTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/ISO8601Test.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/IssuesTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/KlockTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/KotlinNativeImmutabilityTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/MeasureTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/MonthTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/NumberOfTimesTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/SimplerDateFormatTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/StopwatchTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/TimeFormatTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/TimeSpanTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/TimeTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/TimedCacheTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/TimezoneTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/YearMonthTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/YearTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/internal/PaddedTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/internal/TimeZoneParserTest.kt delete mode 100644 klock/src/commonTest/kotlin/korlibs/time/locale/KlockLocaleTest.kt delete mode 100644 klock/src/jsMain/kotlin/korlibs/time/Klock.internal.js.kt delete mode 100644 klock/src/jsMain/kotlin/korlibs/time/Klock.js.kt delete mode 100644 klock/src/jvmMain/kotlin/Time.internal.jvm.kt delete mode 100644 klock/src/jvmMain/kotlin/Time.jvm.kt delete mode 100644 klock/src/linuxMain/kotlin/Klock.internal.linux.kt delete mode 100644 klock/src/mingwMain/kotlin/Klockinternal.mingw.kt delete mode 100644 klock/src/nativeMain/kotlin/korlibs/time/Klock.internal.native.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index eea6d6eed02..6ccc74208fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,6 @@ ## 0.20.23 -* `Klock`: - * Inited as copypaste from [korlibs/korge](https://github.com/korlibs/korge) and [korlibs/korlibs4](https://github.com/korlibs/korlibs4) - ## 0.20.22 * `Common`: diff --git a/README.md b/README.md index 64253d241b3..b813b2c758d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ # MicroUtils ---- - -**`Klock` module initial commit based on [korlibs/korge](https://github.com/korlibs/korge) and [korlibs/korlibs4](https://github.com/korlibs/korlibs4)** - ---- - This is a library with collection of tools for working in Kotlin environment. First of all, this library collection is oriented to use next technologies: * [`Kotlin Coroutines`](https://github.com/Kotlin/kotlinx.coroutines) diff --git a/klock/LICENSE b/klock/LICENSE deleted file mode 100644 index bb512118833..00000000000 --- a/klock/LICENSE +++ /dev/null @@ -1,47 +0,0 @@ -MIT License - -Copyright (c) 2023 Ovsiannikov Aleksei - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------------- - -MIT License - -Copyright (c) 2017-2019 Carlos Ballesteros Velasco and contributors -* https://github.com/korlibs/korge/graphs/contributors -* https://github.com/korlibs-archive/ - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/klock/build.gradle b/klock/build.gradle deleted file mode 100644 index d425197852e..00000000000 --- a/klock/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" - id "org.jetbrains.kotlin.plugin.serialization" - id "com.android.library" -} - -apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath" diff --git a/klock/src/androidMain/kotlin/korlibs/time/Time.internal.jvm.kt b/klock/src/androidMain/kotlin/korlibs/time/Time.internal.jvm.kt deleted file mode 100644 index ca4ea81dc00..00000000000 --- a/klock/src/androidMain/kotlin/korlibs/time/Time.internal.jvm.kt +++ /dev/null @@ -1,37 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") -package korlibs.time.internal - -import korlibs.time.* -import java.util.* - -internal actual object KlockInternal { - actual val currentTime: Double get() = CurrentKlockInternalJvm.currentTime - actual val now: TimeSpan get() = CurrentKlockInternalJvm.hrNow - actual fun localTimezoneOffsetMinutes(time: DateTime): TimeSpan = CurrentKlockInternalJvm.localTimezoneOffsetMinutes(time) - actual fun sleep(time: TimeSpan) { - val nanos = time.nanoseconds.toLong() - Thread.sleep(nanos / 1_000_000, (nanos % 1_000_000).toInt()) - } -} - -inline fun TemporalKlockInternalJvm(impl: KlockInternalJvm, callback: () -> T): T { - val old = CurrentKlockInternalJvm - CurrentKlockInternalJvm = impl - try { - return callback() - } finally { - CurrentKlockInternalJvm = old - } -} - -var CurrentKlockInternalJvm = object : KlockInternalJvm { -} - -interface KlockInternalJvm { - val currentTime: Double get() = (System.currentTimeMillis()).toDouble() - val microClock: Double get() = hrNow.microseconds - val hrNow: TimeSpan get() = TimeSpan.fromNanoseconds(System.nanoTime().toDouble()) - fun localTimezoneOffsetMinutes(time: DateTime): TimeSpan = TimeZone.getDefault().getOffset(time.unixMillisLong).milliseconds -} - -actual typealias Serializable = java.io.Serializable diff --git a/klock/src/androidMain/kotlin/korlibs/time/Time.jvm.kt b/klock/src/androidMain/kotlin/korlibs/time/Time.jvm.kt deleted file mode 100644 index 7bf88bc45bf..00000000000 --- a/klock/src/androidMain/kotlin/korlibs/time/Time.jvm.kt +++ /dev/null @@ -1,8 +0,0 @@ -@file:Suppress("PackageDirectoryMismatch") -package korlibs.time.internal - -import korlibs.time.* -import java.util.Date - -fun Date.toDateTime() = DateTime(this.time) -fun DateTime.toDate() = Date(this.unixMillisLong) diff --git a/klock/src/commonMain/kotlin/korlibs/time/Date.kt b/klock/src/commonMain/kotlin/korlibs/time/Date.kt deleted file mode 100644 index d965035e212..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/Date.kt +++ /dev/null @@ -1,72 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.Serializable -import kotlin.jvm.JvmInline -import kotlin.math.abs - -/** - * Represents a triple of [year], [month] and [day]. - * - * It is packed in a value class wrapping an Int to prevent allocations. - */ -@JvmInline -value class Date(val encoded: Int) : Comparable, Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - /** Constructs a new [Date] from the [year], [month] and [day] components. */ - operator fun invoke(year: Int, month: Int, day: Int) = Date((year shl 16) or (month shl 8) or (day shl 0)) - /** Constructs a new [Date] from the [year], [month] and [day] components. */ - operator fun invoke(year: Int, month: Month, day: Int) = Date(year, month.index1, day) - /** Constructs a new [Date] from the [year], [month] and [day] components. */ - operator fun invoke(year: Year, month: Month, day: Int) = Date(year.year, month.index1, day) - /** Constructs a new [Date] from the [yearMonth] and [day] components. */ - operator fun invoke(yearMonth: YearMonth, day: Int) = Date(yearMonth.yearInt, yearMonth.month1, day) - } - - /** The [year] part as [Int]. */ - val year: Int get() = encoded shr 16 - /** The [month] part as [Int] where [Month.January] is 1. */ - val month1: Int get() = (encoded ushr 8) and 0xFF - /** The [month] part. */ - val month: Month get() = Month[month1] - /** The [day] part. */ - val day: Int get() = (encoded ushr 0) and 0xFF - /** The [year] part as [Year]. */ - val yearYear: Year get() = Year(year) - - /** A [DateTime] instance representing this date and time from the beginning of the [day]. */ - val dateTimeDayStart get() = DateTime(year, month, day) - - /** The [dayOfYear] part. */ - val dayOfYear get() = dateTimeDayStart.dayOfYear - /** The [dayOfWeek] part. */ - val dayOfWeek get() = dateTimeDayStart.dayOfWeek - /** The [dayOfWeek] part as [Int]. */ - val dayOfWeekInt get() = dateTimeDayStart.dayOfWeekInt - - /** Converts this date to String using [format] for representing it. */ - fun format(format: String) = dateTimeDayStart.format(format) - /** Converts this date to String using [format] for representing it. */ - fun format(format: DateFormat) = dateTimeDayStart.format(format) - - /** Converts this date to String formatting it like "2020-01-01", "2020-12-31" or "-2020-12-31" if the [year] is negative */ - override fun toString(): String = "${if (year < 0) "-" else ""}${abs(year).toString()}-${abs(month1).toString().padStart(2, '0')}-${abs(day).toString().padStart(2, '0')}" - - override fun compareTo(other: Date): Int = this.encoded.compareTo(other.encoded) -} - -operator fun Date.plus(time: TimeSpan) = (this.dateTimeDayStart + time).date -operator fun Date.plus(time: MonthSpan) = (this.dateTimeDayStart + time).date -operator fun Date.plus(time: DateTimeSpan) = (this.dateTimeDayStart + time).date -operator fun Date.plus(time: Time) = DateTime.createAdjusted(year, month1, day, time.hour, time.minute, time.second, time.millisecond) - -operator fun Date.minus(time: TimeSpan) = (this.dateTimeDayStart - time).date -operator fun Date.minus(time: MonthSpan) = (this.dateTimeDayStart - time).date -operator fun Date.minus(time: DateTimeSpan) = (this.dateTimeDayStart - time).date -operator fun Date.minus(time: Time) = DateTime.createAdjusted(year, month1, day, -time.hour, -time.minute, -time.second, -time.millisecond) - -fun Date.inThisWeek(dayOfWeek: DayOfWeekWithLocale): Date = - this + (dayOfWeek.index0 - this.dayOfWeek.withLocale(dayOfWeek.locale).index0).days -fun Date.inThisWeek(dayOfWeek: DayOfWeek, locale: KlockLocale = KlockLocale.default): Date = inThisWeek(dayOfWeek.withLocale(locale)) diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateException.kt b/klock/src/commonMain/kotlin/korlibs/time/DateException.kt deleted file mode 100644 index a1a5d022722..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package korlibs.time - -/** - * An exception for Date operations. - */ -class DateException(msg: String) : RuntimeException(msg) diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateFormat.kt b/klock/src/commonMain/kotlin/korlibs/time/DateFormat.kt deleted file mode 100644 index d1ab3215e90..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateFormat.kt +++ /dev/null @@ -1,43 +0,0 @@ -package korlibs.time - -/** Allows to [format] and [parse] instances of [Date], [DateTime] and [DateTimeTz] */ -interface DateFormat { - fun format(dd: DateTimeTz): String - fun tryParse(str: String, doThrow: Boolean = false, doAdjust: Boolean = true): DateTimeTz? - - companion object { - val DEFAULT_FORMAT = DateFormat("EEE, dd MMM yyyy HH:mm:ss z") - val FORMAT1 = DateFormat("yyyy-MM-dd'T'HH:mm:ssXXX") - val FORMAT2 = DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") - val FORMAT_DATE = DateFormat("yyyy-MM-dd") - - val FORMATS = listOf(DEFAULT_FORMAT, FORMAT1, FORMAT2, FORMAT_DATE) - - fun parse(date: String): DateTimeTz { - var lastError: Throwable? = null - for (format in FORMATS) { - try { - return format.parse(date) - } catch (e: Throwable) { - lastError = e - } - } - throw lastError!! - } - - operator fun invoke(pattern: String) = PatternDateFormat(pattern) - } -} - -fun DateFormat.parse(str: String, doAdjust: Boolean = true): DateTimeTz = - tryParse(str, doThrow = true, doAdjust = doAdjust) ?: throw DateException("Not a valid format: '$str' for '$this'") -fun DateFormat.parseDate(str: String): Date = parse(str).local.date - -fun DateFormat.parseUtc(str: String): DateTime = parse(str).utc -fun DateFormat.parseLocal(str: String): DateTime = parse(str).local - -fun DateFormat.format(date: Double): String = format(DateTime.fromUnixMillis(date)) -fun DateFormat.format(date: Long): String = format(DateTime.fromUnixMillis(date)) - -fun DateFormat.format(dd: DateTime): String = format(dd.toOffsetUnadjusted(0.minutes)) -fun DateFormat.format(dd: Date): String = format(dd.dateTimeDayStart) diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateTime.kt b/klock/src/commonMain/kotlin/korlibs/time/DateTime.kt deleted file mode 100644 index 5b2b98d39b8..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateTime.kt +++ /dev/null @@ -1,458 +0,0 @@ -package korlibs.time - -import korlibs.time.DateTime.Companion.EPOCH -import korlibs.time.internal.* -import kotlin.jvm.JvmInline -import kotlin.math.* - -/** - * Represents a Date in UTC (GMT+00) with millisecond precision. - * - * It is internally represented as an inlined double, thus doesn't allocate in any target including JS. - * It can represent without loss dates between (-(2 ** 52) and (2 ** 52)): - * - Thu Aug 10 -140744 07:15:45 GMT-0014 (Central European Summer Time) - * - Wed May 23 144683 18:29:30 GMT+0200 (Central European Summer Time) - */ -@JvmInline -value class DateTime( - /** Number of milliseconds since UNIX [EPOCH] */ - val unixMillis: Double -) : Comparable, Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - /** It is a [DateTime] instance representing 00:00:00 UTC, Thursday, 1 January 1970. */ - val EPOCH = DateTime(0.0) - - /** - * Constructs a new [DateTime] from date and time information. - * - * This might throw a [DateException] on invalid dates. - */ - operator fun invoke( - year: Year, - month: Month, - day: Int, - hour: Int = 0, - minute: Int = 0, - second: Int = 0, - milliseconds: Int = 0 - ): DateTime = DateTime( - DateTime.dateToMillis(year.year, month.index1, day) + DateTime.timeToMillis( - hour, - minute, - second - ) + milliseconds - ) - - /** - * Constructs a new [DateTime] from date and time information. - * - * This might throw a [DateException] on invalid dates. - */ - operator fun invoke( - date: Date, - time: Time = Time(0.milliseconds) - ): DateTime = DateTime( - date.year, date.month1, date.day, - time.hour, time.minute, time.second, time.millisecond - ) - - /** - * Constructs a new [DateTime] from date and time information. - * - * This might throw a [DateException] on invalid dates. - */ - operator fun invoke( - year: Int, - month: Month, - day: Int, - hour: Int = 0, - minute: Int = 0, - second: Int = 0, - milliseconds: Int = 0 - ): DateTime = DateTime( - DateTime.dateToMillis(year, month.index1, day) + DateTime.timeToMillis( - hour, - minute, - second - ) + milliseconds - ) - - /** - * Constructs a new [DateTime] from date and time information. - * - * This might throw a [DateException] on invalid dates. - */ - operator fun invoke( - year: Int, - month: Int, - day: Int, - hour: Int = 0, - minute: Int = 0, - second: Int = 0, - milliseconds: Int = 0 - ): DateTime = DateTime( - DateTime.dateToMillis(year, month, day) + DateTime.timeToMillis( - hour, - minute, - second - ) + milliseconds - ) - - /** - * Constructs a new [DateTime] from date and time information. - * - * On invalid dates, this function will try to adjust the specified invalid date to a valid one by clamping components. - */ - fun createClamped( - year: Int, - month: Int, - day: Int, - hour: Int = 0, - minute: Int = 0, - second: Int = 0, - milliseconds: Int = 0 - ): DateTime { - val clampedMonth = month.coerceIn(1, 12) - return createUnchecked( - year = year, - month = clampedMonth, - day = day.coerceIn(1, Month(month).days(year)), - hour = hour.coerceIn(0, 23), - minute = minute.coerceIn(0, 59), - second = second.coerceIn(0, 59), - milliseconds = milliseconds - ) - } - - /** - * Constructs a new [DateTime] from date and time information. - * - * On invalid dates, this function will try to adjust the specified invalid date to a valid one by adjusting other components. - */ - fun createAdjusted( - year: Int, - month: Int, - day: Int, - hour: Int = 0, - minute: Int = 0, - second: Int = 0, - milliseconds: Int = 0 - ): DateTime { - var dy = year - var dm = month - var dd = day - var th = hour - var tm = minute - var ts = second - - tm += ts.cycleSteps(0, 59); ts = ts.cycle(0, 59) // Adjust seconds, adding minutes - th += tm.cycleSteps(0, 59); tm = tm.cycle(0, 59) // Adjust minutes, adding hours - dd += th.cycleSteps(0, 23); th = th.cycle(0, 23) // Adjust hours, adding days - - while (true) { - val dup = Month(dm).days(dy) - - dm += dd.cycleSteps(1, dup); dd = dd.cycle(1, dup) // Adjust days, adding months - dy += dm.cycleSteps(1, 12); dm = dm.cycle(1, 12) // Adjust months, adding years - - // We have already found a day that is valid for the adjusted month! - if (dd.cycle(1, Month(dm).days(dy)) == dd) { - break - } - } - - return createUnchecked(dy, dm, dd, th, tm, ts, milliseconds) - } - - /** - * Constructs a new [DateTime] from date and time information. - * - * On invalid dates, this function will have an undefined behaviour. - */ - fun createUnchecked( - year: Int, - month: Int, - day: Int, - hour: Int = 0, - minute: Int = 0, - second: Int = 0, - milliseconds: Int = 0 - ): DateTime { - return DateTime( - DateTime.dateToMillisUnchecked(year, month, day) + DateTime.timeToMillisUnchecked( - hour, - minute, - second - ) + milliseconds - ) - } - - /** Constructs a new [DateTime] from a [unix] timestamp in milliseconds. */ - operator fun invoke(unix: Long) = fromUnixMillis(unix) - /** Constructs a new [DateTime] from a [unix] timestamp in milliseconds. */ - operator fun invoke(unix: Double) = fromUnixMillis(unix) - - /** Constructs a new [DateTime] from a [unix] timestamp in milliseconds. */ - fun fromUnixMillis(unix: Double): DateTime = DateTime(unix) - /** Constructs a new [DateTime] from a [unix] timestamp in milliseconds. */ - fun fromUnixMillis(unix: Long): DateTime = fromUnixMillis(unix.toDouble()) - - /** Constructs a new [DateTime] by parsing the [str] using standard date formats. */ - fun fromString(str: String) = DateFormat.parse(str) - /** Constructs a new [DateTime] by parsing the [str] using standard date formats. */ - fun parse(str: String) = DateFormat.parse(str) - - /** Returns the current time as [DateTime]. Note that since [DateTime] is inline, this property doesn't allocate on JavaScript. */ - fun now(): DateTime = DateTime(KlockInternal.currentTime) - /** Returns the current local time as [DateTimeTz]. */ - fun nowLocal(): DateTimeTz = DateTimeTz.nowLocal() - - /** Returns the total milliseconds since unix epoch. The same as [nowUnixMillisLong] but as double. To prevent allocation on targets without Long support. */ - fun nowUnixMillis(): Double = KlockInternal.currentTime - /** Returns the total milliseconds since unix epoch. */ - fun nowUnixMillisLong(): Long = KlockInternal.currentTime.toLong() - - internal const val EPOCH_INTERNAL_MILLIS = - 62135596800000.0 // Millis since 00-00-0000 00:00 UTC to UNIX EPOCH - - internal enum class DatePart { Year, DayOfYear, Month, Day } - - internal fun dateToMillisUnchecked(year: Int, month: Int, day: Int): Double = - (Year(year).daysSinceOne + Month(month).daysToStart(year) + day - 1) * MILLIS_PER_DAY.toDouble() - EPOCH_INTERNAL_MILLIS - - private fun timeToMillisUnchecked(hour: Int, minute: Int, second: Int): Double = - hour.toDouble() * MILLIS_PER_HOUR + minute.toDouble() * MILLIS_PER_MINUTE + second.toDouble() * MILLIS_PER_SECOND - - private fun dateToMillis(year: Int, month: Int, day: Int): Double { - //Year.checked(year) - Month.checked(month) - if (day !in 1..Month(month).days(year)) throw DateException("Day $day not valid for year=$year and month=$month") - return dateToMillisUnchecked(year, month, day) - } - - private fun timeToMillis(hour: Int, minute: Int, second: Int): Double { - if (hour !in 0..23) throw DateException("Hour $hour not in 0..23") - if (minute !in 0..59) throw DateException("Minute $minute not in 0..59") - if (second !in 0..59) throw DateException("Second $second not in 0..59") - return timeToMillisUnchecked(hour, minute, second) - } - - // millis are 00-00-0000 based. - internal fun getDatePart(millis: Double, part: DatePart): Int { - val totalDays = (millis / MILLIS_PER_DAY).toInt2() - - // Year - val year = Year.fromDays(totalDays) - if (part == DatePart.Year) return year.year - - // Day of Year - val isLeap = year.isLeap - val startYearDays = year.daysSinceOne - val dayOfYear = 1 + ((totalDays - startYearDays) umod year.days) - if (part == DatePart.DayOfYear) return dayOfYear - - // Month - val month = Month.fromDayOfYear(dayOfYear, isLeap) - ?: error("Invalid dayOfYear=$dayOfYear, isLeap=$isLeap") - if (part == DatePart.Month) return month.index1 - - // Day - val dayOfMonth = dayOfYear - month.daysToStart(isLeap) - if (part == DatePart.Day) return dayOfMonth - - error("Invalid DATE_PART") - } - } - - /** Number of milliseconds since the 00:00:00 UTC, Monday, 1 January 1 */ - val yearOneMillis: Double get() = EPOCH_INTERNAL_MILLIS + unixMillis - - /** The local offset for this date for the timezone of the device */ - val localOffset: TimezoneOffset get() = TimezoneOffset.local(DateTime(unixMillisDouble)) - - /** Number of milliseconds since UNIX [EPOCH] as [Double] */ - val unixMillisDouble: Double get() = unixMillis - - /** Number of milliseconds since UNIX [EPOCH] as [Long] */ - val unixMillisLong: Long get() = unixMillisDouble.toLong() - - /** The [Year] part */ - val year: Year get() = Year(yearInt) - /** The [Year] part as [Int] */ - val yearInt: Int get() = getDatePart(yearOneMillis, DatePart.Year) - - /** The [Month] part */ - val month: Month get() = Month[month1] - /** The [Month] part as [Int] where January is represented as 0 */ - val month0: Int get() = month1 - 1 - /** The [Month] part as [Int] where January is represented as 1 */ - val month1: Int get() = getDatePart(yearOneMillis, DatePart.Month) - - /** Represents a couple of [Year] and [Month] that has leap information and thus allows to get the number of days of that month */ - val yearMonth: YearMonth get() = YearMonth(year, month) - - /** The [dayOfMonth] part */ - val dayOfMonth: Int get() = getDatePart(yearOneMillis, DatePart.Day) - - /** The [dayOfWeek] part */ - val dayOfWeek: DayOfWeek get() = DayOfWeek[dayOfWeekInt] - /** The [dayOfWeek] part as [Int] */ - val dayOfWeekInt: Int get() = (yearOneMillis / MILLIS_PER_DAY + 1).toIntMod(7) - - /** The [dayOfYear] part */ - val dayOfYear: Int get() = getDatePart(yearOneMillis, DatePart.DayOfYear) - - /** The [hours] part */ - val hours: Int get() = (yearOneMillis / MILLIS_PER_HOUR).toIntMod(24) - /** The [minutes] part */ - val minutes: Int get() = (yearOneMillis / MILLIS_PER_MINUTE).toIntMod(60) - /** The [seconds] part */ - val seconds: Int get() = (yearOneMillis / MILLIS_PER_SECOND).toIntMod(60) - /** The [milliseconds] part */ - val milliseconds: Int get() = (yearOneMillis).toIntMod(1000) - - /** Returns a new local date that will match these components. */ - val localUnadjusted: DateTimeTz get() = DateTimeTz.local(this, localOffset) - /** Returns a new local date that will match these components but with a different [offset]. */ - fun toOffsetUnadjusted(offset: TimeSpan) = toOffsetUnadjusted(offset.offset) - /** Returns a new local date that will match these components but with a different [offset]. */ - fun toOffsetUnadjusted(offset: TimezoneOffset) = DateTimeTz.local(this, offset) - - /** Returns this date with the local offset of this device. Components might change because of the offset. */ - val local: DateTimeTz get() = DateTimeTz.utc(this, localOffset) - /** Returns this date with a local offset. Components might change because of the [offset]. */ - fun toOffset(offset: TimeSpan) = toOffset(offset.offset) - /** Returns this date with a local offset. Components might change because of the [offset]. */ - fun toOffset(offset: TimezoneOffset) = DateTimeTz.utc(this, offset) - /** Returns this date with a local offset. Components might change because of the [timeZone]. */ - fun toTimezone(timeZone: Timezone) = toOffset(timeZone.offset) - /** Returns this date with a 0 offset. Components are equal. */ - val utc: DateTimeTz get() = DateTimeTz.utc(this, TimezoneOffset(0.minutes)) - - /** Returns a [DateTime] of [this] day with the hour at 00:00:00 */ - val dateDayStart get() = DateTime(year, month, dayOfMonth, 0, 0, 0, 0) - /** Returns a [DateTime] of [this] day with the hour at 23:59:59.999 */ - val dateDayEnd get() = DateTime(year, month, dayOfMonth, 23, 59, 59, 999) - - /** Returns the quarter 1, 2, 3 or 4 */ - val quarter get() = (month0 / 3) + 1 - - // startOf - - val startOfYear get() = DateTime(year, Month.January, 1) - val startOfMonth get() = DateTime(year, month, 1) - val startOfQuarter get() = DateTime(year, Month[(quarter - 1) * 3 + 1], 1) - fun startOfDayOfWeek(day: DayOfWeek): DateTime { - for (n in 0 until 7) { - val date = (this - n.days) - if (date.dayOfWeek == day) return date.startOfDay - } - error("Shouldn't happen") - } - val startOfWeek: DateTime get() = startOfDayOfWeek(DayOfWeek.Sunday) - val startOfIsoWeek: DateTime get() = startOfDayOfWeek(DayOfWeek.Monday) - val startOfDay get() = DateTime(year, month, dayOfMonth) - val startOfHour get() = DateTime(year, month, dayOfMonth, hours) - val startOfMinute get() = DateTime(year, month, dayOfMonth, hours, minutes) - val startOfSecond get() = DateTime(year, month, dayOfMonth, hours, minutes, seconds) - - // endOf - - val endOfYear get() = DateTime(year, Month.December, 31, 23, 59, 59, 999) - val endOfMonth get() = DateTime(year, month, month.days(year), 23, 59, 59, 999) - val endOfQuarter get() = DateTime(year, Month[(quarter - 1) * 3 + 3], month.days(year), 23, 59, 59, 999) - fun endOfDayOfWeek(day: DayOfWeek): DateTime { - for (n in 0 until 7) { - val date = (this + n.days) - if (date.dayOfWeek == day) return date.endOfDay - } - error("Shouldn't happen") - } - val endOfWeek: DateTime get() = endOfDayOfWeek(DayOfWeek.Monday) - val endOfIsoWeek: DateTime get() = endOfDayOfWeek(DayOfWeek.Sunday) - val endOfDay get() = DateTime(year, month, dayOfMonth, 23, 59, 59, 999) - val endOfHour get() = DateTime(year, month, dayOfMonth, hours, 59, 59, 999) - val endOfMinute get() = DateTime(year, month, dayOfMonth, hours, minutes, 59, 999) - val endOfSecond get() = DateTime(year, month, dayOfMonth, hours, minutes, seconds, 999) - - val date get() = Date(yearInt, month1, dayOfMonth) - val time get() = Time(hours, minutes, seconds, milliseconds) - - operator fun plus(delta: MonthSpan): DateTime = this.add(delta.totalMonths, 0.0) - operator fun plus(delta: DateTimeSpan): DateTime = this.add(delta.totalMonths, delta.totalMilliseconds) - operator fun plus(delta: TimeSpan): DateTime = add(0, delta.milliseconds) - - operator fun minus(delta: MonthSpan): DateTime = this + -delta - operator fun minus(delta: DateTimeSpan): DateTime = this + -delta - operator fun minus(delta: TimeSpan): DateTime = this + (-delta) - - operator fun minus(other: DateTime): TimeSpan = (this.unixMillisDouble - other.unixMillisDouble).milliseconds - - override fun compareTo(other: DateTime): Int = this.unixMillis.compareTo(other.unixMillis) - - /** Constructs a new [DateTime] after adding [deltaMonths] and [deltaMilliseconds] */ - fun add(deltaMonths: Int, deltaMilliseconds: Double): DateTime = when { - deltaMonths == 0 && deltaMilliseconds == 0.0 -> this - deltaMonths == 0 -> DateTime(this.unixMillis + deltaMilliseconds) - else -> { - var year = this.year - var month = this.month.index1 - var day = this.dayOfMonth - val i = month - 1 + deltaMonths - - if (i >= 0) { - month = i % Month.Count + 1 - year += i / Month.Count - } else { - month = Month.Count + (i + 1) % Month.Count - year += (i - (Month.Count - 1)) / Month.Count - } - //Year.checked(year) - val days = Month(month).days(year) - if (day > days) day = days - - DateTime(dateToMillisUnchecked(year.year, month, day) + (yearOneMillis % MILLIS_PER_DAY) + deltaMilliseconds) - } - } - - /** Constructs a new [DateTime] after adding [dateSpan] and [timeSpan] */ - fun add(dateSpan: MonthSpan, timeSpan: TimeSpan): DateTime = add(dateSpan.totalMonths, timeSpan.milliseconds) - - fun copyDayOfMonth( - year: Year = this.year, - month: Month = this.month, - dayOfMonth: Int = this.dayOfMonth, - hours: Int = this.hours, - minutes: Int = this.minutes, - seconds: Int = this.seconds, - milliseconds: Int = this.milliseconds - ) = DateTime(year, month, dayOfMonth, hours, minutes, seconds, milliseconds) - - /** Converts this date to String using [format] for representing it */ - fun format(format: DateFormat): String = format.format(this) - /** Converts this date to String using [format] for representing it */ - fun format(format: String): String = DateFormat(format).format(this) - - /** Converts this date to String using [format] for representing it */ - fun toString(format: String): String = DateFormat(format).format(this) - /** Converts this date to String using [format] for representing it */ - fun toString(format: DateFormat): String = format.format(this) - - /** Converts this date to String using the [DateFormat.DEFAULT_FORMAT] for representing it */ - fun toStringDefault(): String = DateFormat.DEFAULT_FORMAT.format(this) - //override fun toString(): String = DateFormat.DEFAULT_FORMAT.format(this) - override fun toString(): String = "DateTime($unixMillisLong)" -} - -fun max(a: DateTime, b: DateTime): DateTime = - DateTime.fromUnixMillis(max(a.unixMillis, b.unixMillis)) -fun min(a: DateTime, b: DateTime): DateTime = - DateTime.fromUnixMillis(min(a.unixMillis, b.unixMillis)) -fun DateTime.clamp(min: DateTime, max: DateTime): DateTime = when { - this < min -> min - this > max -> max - else -> this -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateTimeRange.kt b/klock/src/commonMain/kotlin/korlibs/time/DateTimeRange.kt deleted file mode 100644 index e77175db774..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateTimeRange.kt +++ /dev/null @@ -1,154 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.Serializable - -/** - * Represents a right-opened range between two dates. - */ -data class DateTimeRange(val from: DateTime, val to: DateTime) : Comparable, Serializable { - val valid get() = from <= to - - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - operator fun invoke(base: Date, from: Time, to: Time): DateTimeRange = DateTimeRange(base + from, base + to) - } - - val size: TimeSpan get() = to - from - - val min get() = from - val max get() = to - /** - * Duration [TimeSpan] without having into account actual months/years. - */ - val duration: TimeSpan get() = to - from - - /** - * [DateTimeSpan] distance between two dates, month and year aware. - */ - val span: DateTimeSpan by lazy { - val reverse = to < from - val rfrom = if (!reverse) from else to - val rto = if (!reverse) to else from - - var years = 0 - var months = 0 - - var pivot = rfrom - - // Compute years - val diffYears = (rto.year - pivot.year) - pivot += diffYears.years - years += diffYears - if (pivot > rto) { - pivot -= 1.years - years-- - } - - // Compute months (at most an iteration of 12) - while (true) { - val t = pivot + 1.months - if (t <= rto) { - months++ - pivot = t - } else { - break - } - } - - val out = DateTimeSpan(years.years + months.months, rto - pivot) - if (reverse) -out else out - } - - /** - * Checks if a date is contained in this range. - */ - operator fun contains(date: DateTime): Boolean { - val unix = date.unixMillisDouble - val from = from.unixMillisDouble - val to = to.unixMillisDouble - return if (unix < from) false else unix < to - } - - operator fun contains(other: DateTimeRange): Boolean { - return other.min >= this.min && other.max <= this.max - } - - private inline fun _intersectionWith(that: DateTimeRange, rightOpen: Boolean, handler: (from: DateTime, to: DateTime, matches: Boolean) -> T): T { - val from = max(this.from, that.from) - val to = min(this.to, that.to) - return handler(from, to, if (rightOpen) from < to else from <= to) - } - - /** - * Returns new [DateTimeRange] or null - the result of intersection of this and [that] DateTimeRanges. - */ - fun intersectionWith(that: DateTimeRange, rightOpen: Boolean = true): DateTimeRange? { - return _intersectionWith(that, rightOpen) { from, to, matches -> - when { - matches -> DateTimeRange(from, to) - else -> null - } - } - } - - /** - * Returns true if this and [that] DateTimeRanges have intersection otherwise false. - */ - fun intersectsWith(that: DateTimeRange, rightOpen: Boolean = true): Boolean = _intersectionWith(that, rightOpen) { _, _, matches -> matches } - - /** - * Returns true if this and [that] DateTimeRanges have intersection or at least a common end otherwise false. - */ - fun intersectsOrInContactWith(that: DateTimeRange): Boolean = intersectsWith(that, rightOpen = false) - - /** - * Returns new [DateTimeRange] or null - the result of merging this and [that] DateTimeRanges if they have intersection. - */ - fun mergeOnContactOrNull(that: DateTimeRange): DateTimeRange? { - if (!intersectsOrInContactWith(that)) return null - val min = min(this.min, that.min) - val max = max(this.max, that.max) - return DateTimeRange(min, max) - } - - /** - * Returns a [List] of 0, 1 or 2 [DateTimeRange]s - the result of removing [that] DateTimeRange from this one - */ - fun without(that: DateTimeRange): List = when { - // Full remove - (that.min <= this.min) && (that.max >= this.max) -> listOf() - // To the right or left, nothing to remove - (that.min >= this.max) || (that.max <= this.min) -> listOf(this) - // In the middle - else -> { - val p0 = this.min - val p1 = that.min - val p2 = that.max - val p3 = this.max - val c1 = if (p0 < p1) DateTimeRange(p0, p1) else null - val c2 = if (p2 < p3) DateTimeRange(p2, p3) else null - listOfNotNull(c1, c2) - } - } - - fun toString(format: DateFormat): String = "${min.toString(format)}..${max.toString(format)}" - fun toStringLongs(): String = "${min.unixMillisLong}..${max.unixMillisLong}" - fun toStringDefault(): String = toString(DateFormat.FORMAT1) - //override fun toString(): String = toString(DateFormat.FORMAT1) - override fun toString(): String = "$min..$max" - - override fun compareTo(other: DateTime): Int { - if (this.max <= other) return -1 - if (this.min > other) return +1 - return 0 - } -} - -fun List.toStringLongs() = this.map { it.toStringLongs() }.toString() - -/** - * Generates a right-opened range between two [DateTime]s - */ -infix fun DateTime.until(other: DateTime) = DateTimeRange(this, other) diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateTimeRangeSet.kt b/klock/src/commonMain/kotlin/korlibs/time/DateTimeRangeSet.kt deleted file mode 100644 index bd5e5bb7d9c..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateTimeRangeSet.kt +++ /dev/null @@ -1,270 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.BSearchResult -import korlibs.time.internal.Serializable -import korlibs.time.internal.fastForEach -import korlibs.time.internal.genericBinarySearch - -// Properties: -// - ranges are sorted -// - ranges do not overlap/intersect between each other (they are merged and normalized) -// These properties allows to do some tricks and optimizations like binary search and a lot of O(n) operations. -data class DateTimeRangeSet private constructor(val dummy: Boolean, val ranges: List) : Serializable { - - /** [DateTimeRange] from the beginning of the first element to the end of the last one. */ - val bounds = DateTimeRange( - ranges.firstOrNull()?.from ?: DateTime.EPOCH, - ranges.lastOrNull()?.to ?: DateTime.EPOCH - ) - - /** Total time of all [ranges]. */ - val size: TimeSpan by lazy { - var out = 0.seconds - ranges.fastForEach { out += it.size } - out - } - - constructor(ranges: List) : this(false, Fast.combine(ranges)) - constructor(range: DateTimeRange) : this(listOf(range)) - constructor(vararg ranges: DateTimeRange) : this(ranges.toList()) - - operator fun plus(range: DateTimeRange): DateTimeRangeSet = this + DateTimeRangeSet(range) - operator fun plus(right: DateTimeRangeSet): DateTimeRangeSet = DateTimeRangeSet(this.ranges + right.ranges) - - operator fun minus(range: DateTimeRange): DateTimeRangeSet = this - DateTimeRangeSet(range) - operator fun minus(right: DateTimeRangeSet): DateTimeRangeSet = Fast.minus(this, right) - - operator fun contains(time: DateTime): Boolean = Fast.contains(time, this) - operator fun contains(time: DateTimeRange): Boolean = Fast.contains(time, this) - - fun intersection(range: DateTimeRange): DateTimeRangeSet = this.intersection(DateTimeRangeSet(range)) - fun intersection(vararg range: DateTimeRange): DateTimeRangeSet = this.intersection(DateTimeRangeSet(*range)) - fun intersection(right: DateTimeRangeSet): DateTimeRangeSet = Fast.intersection(this, right) - - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - fun toStringLongs(ranges: List): String = "${ranges.map { it.toStringLongs() }}" - } - - object Fast { - internal fun combine(ranges: List): List { - if (ranges.isEmpty()) return ranges - - val sorted = ranges.sortedBy { it.from.unixMillis } - val out = arrayListOf() - var pivot = sorted.first() - for (n in 1 until sorted.size) { - val current = sorted[n] - val result = pivot.mergeOnContactOrNull(current) - pivot = if (result != null) { - result - } else { - out.add(pivot) - current - } - } - return out + listOf(pivot) - } - - internal fun minus(left: DateTimeRangeSet, right: DateTimeRangeSet): DateTimeRangeSet { - if (left.ranges.isEmpty() || right.ranges.isEmpty()) return left - - val ll = left.ranges - val rr = right.ranges.filter { it.intersectsWith(left.bounds) } - var lpos = 0 - var rpos = 0 - var l = ll.getOrNull(lpos++) - var r = rr.getOrNull(rpos++) - val out = arrayListOf() - //debug { "-----------------" } - //debug { "Minus:" } - //debug { " - ll=${toStringLongs(ll)}" } - //debug { " - rr=${toStringLongs(rr)}" } - while (l != null && r != null) { - val result = l.without(r) - //debug { "Minus ${l!!.toStringLongs()} with ${r!!.toStringLongs()} -- ${toStringLongs(result)}" } - when (result.size) { - 0 -> { - //debug { " - Full remove" } - l = ll.getOrNull(lpos++) - } - 1 -> { - //debug { " - Result 1" } - when { - r.from >= l.to -> { - //debug { " - Move left. Emit ${result[0].toStringLongs()}" } - out.add(result[0]) - l = ll.getOrNull(lpos++) - } - l == result[0] -> { - //debug { " - Move right. Change l from ${l!!.toStringLongs()} to ${result[0].toStringLongs()}" } - r = rr.getOrNull(rpos++) - } - else -> { - //debug { " - Use this l=${result[0].toStringLongs()} from ${l!!.toStringLongs()}" } - l = result[0] - } - } - } - else -> { - //debug { " - One chunk removed: ${result.map { it.toStringLongs() }}" } - //debug { " - Emit: ${result[0].toStringLongs()}" } - //debug { " - Keep: ${result[1].toStringLongs()}" } - out.add(result[0]) - l = result[1] - } - } - } - if (l != null) { - out.add(l) - } - while (lpos < ll.size) out.add(ll[lpos++]) - - //debug { toStringLongs(out) } - return DateTimeRangeSet(out) - } - - fun intersection(left: DateTimeRangeSet, right: DateTimeRangeSet): DateTimeRangeSet { - if (left.ranges.isEmpty() || right.ranges.isEmpty()) return DateTimeRangeSet(listOf()) - - val ll = left.ranges.filter { it.intersectsWith(right.bounds) } - val rr = right.ranges.filter { it.intersectsWith(left.bounds) } - val out = arrayListOf() - //debug { "-----------------" } - //debug { "Intersection:" } - //debug { " - ll=${toStringLongs(ll)}" } - //debug { " - rr=${toStringLongs(rr)}" } - var rpos = 0 - for (l in ll) { - rpos = 0 - // We should be able to do this because the time ranges doesn't intersect each other - //while (rpos > 0) { - // val r = rr.getOrNull(rpos) ?: break - // if ((r.from < l.from) && (r.to < l.from)) break // End since we are already - // rpos-- - //} - while (rpos < rr.size) { - val r = rr.getOrNull(rpos) ?: break - if (r.min > l.max) break // End since the rest are going to be farther - val res = l.intersectionWith(r) - if (res != null) { - out.add(res) - } - rpos++ - } - } - - //debug { toStringLongs(out) } - return DateTimeRangeSet(out) - } - - fun contains(time: DateTime, rangeSet: DateTimeRangeSet): Boolean { - if (time !in rangeSet.bounds) return false // Early guard clause - val ranges = rangeSet.ranges - val result = BSearchResult(genericBinarySearch(0, ranges.size) { index -> ranges[index].compareTo(time) }) - return result.found - } - - fun contains(time: DateTimeRange, rangeSet: DateTimeRangeSet): Boolean { - if (time !in rangeSet.bounds) return false // Early guard clause - val ranges = rangeSet.ranges - val result = BSearchResult(genericBinarySearch(0, ranges.size) { index -> - val range = ranges[index] - when { - time in range -> 0 - time.min < range.min -> +1 - else -> -1 - } - }) - return result.found - } - //private inline fun debug(gen: () -> String) { println(gen()) } - } - - object Slow { - // @TODO: Optimize - internal fun minus(l: DateTimeRangeSet, r: DateTimeRangeSet): DateTimeRangeSet { - val rightList = r.ranges - var out = l.ranges.toMutableList() - restart@ while (true) { - for ((leftIndex, left) in out.withIndex()) { - for (right in rightList) { - val result = left.without(right) - if (result.size != 1 || result[0] != left) { - out = (out.slice(0 until leftIndex) + result + out.slice(leftIndex + 1 until out.size)).toMutableList() - continue@restart - } - } - } - break - } - return DateTimeRangeSet(out) - } - - internal fun combine(ranges: List): List { - // @TODO: Improve performance and verify fast combiner - val ranges = ranges.toMutableList() - restart@ while (true) { - for (i in ranges.indices) { - for (j in ranges.indices) { - if (i == j) continue - val ri = ranges[i] - val rj = ranges[j] - val concat = ri.mergeOnContactOrNull(rj) - if (concat != null) { - //println("Combining $ri and $rj : $concat") - ranges.remove(rj) - ranges[i] = concat - continue@restart - } - } - } - break - } - return ranges - } - - fun intersection(left: DateTimeRangeSet, right: DateTimeRangeSet): DateTimeRangeSet { - val leftList = left.ranges - val rightList = right.ranges - val out = arrayListOf() - for (l in leftList) { - for (r in rightList) { - if (r.min > l.max) break - val result = l.intersectionWith(r) - if (result != null) { - out.add(result) - } - } - //val chunks = rightList.mapNotNull { r -> l.intersectionWith(r) } - //out.addAll(DateTimeRangeSet(chunks).ranges) - } - return DateTimeRangeSet(out) - } - - fun contains(time: DateTime, rangeSet: DateTimeRangeSet): Boolean { - if (time !in rangeSet.bounds) return false // Early guard clause - // @TODO: Fast binary search, since the ranges doesn't intersect each other - rangeSet.ranges.fastForEach { range -> - if (time in range) return true - } - return false - } - - fun contains(time: DateTimeRange, rangeSet: DateTimeRangeSet): Boolean { - if (time !in rangeSet.bounds) return false // Early guard clause - // @TODO: Fast binary search, since the ranges doesn't intersect each other - rangeSet.ranges.fastForEach { range -> - if (time in range) return true - } - return false - } - } - - fun toStringLongs(): String = "${ranges.map { it.toStringLongs() }}" - override fun toString(): String = "$ranges" -} - -fun Iterable.toRangeSet() = DateTimeRangeSet(this.toList()) diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateTimeSpan.kt b/klock/src/commonMain/kotlin/korlibs/time/DateTimeSpan.kt deleted file mode 100644 index 513f6c33fb0..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateTimeSpan.kt +++ /dev/null @@ -1,143 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.MILLIS_PER_DAY -import korlibs.time.internal.MILLIS_PER_HOUR -import korlibs.time.internal.MILLIS_PER_MINUTE -import korlibs.time.internal.MILLIS_PER_SECOND -import korlibs.time.internal.MILLIS_PER_WEEK -import korlibs.time.internal.Moduler -import korlibs.time.internal.Serializable - -/** - * Immutable structure representing a set of a [monthSpan] and a [timeSpan]. - * This structure loses information about which months are included, that makes it impossible to generate a real [TimeSpan] including months. - * You can use [DateTimeRange.duration] to get this information from two real [DateTime]. - */ -data class DateTimeSpan( - /** The [MonthSpan] part */ - val monthSpan: MonthSpan, - /** The [TimeSpan] part */ - val timeSpan: TimeSpan -) : Comparable, Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - } - - constructor( - years: Int = 0, - months: Int = 0, - weeks: Int = 0, - days: Int = 0, - hours: Int = 0, - minutes: Int = 0, - seconds: Int = 0, - milliseconds: Double = 0.0 - ) : this( - years.years + months.months, - weeks.weeks + days.days + hours.hours + minutes.minutes + seconds.seconds + milliseconds.milliseconds - ) - - operator fun unaryMinus() = DateTimeSpan(-monthSpan, -timeSpan) - operator fun unaryPlus() = DateTimeSpan(+monthSpan, +timeSpan) - - operator fun plus(other: TimeSpan) = DateTimeSpan(monthSpan, timeSpan + other) - operator fun plus(other: MonthSpan) = DateTimeSpan(monthSpan + other, timeSpan) - operator fun plus(other: DateTimeSpan) = DateTimeSpan(monthSpan + other.monthSpan, timeSpan + other.timeSpan) - - operator fun minus(other: TimeSpan) = this + -other - operator fun minus(other: MonthSpan) = this + -other - operator fun minus(other: DateTimeSpan) = this + -other - - operator fun times(times: Double) = DateTimeSpan((monthSpan * times), (timeSpan * times)) - operator fun times(times: Int) = this * times.toDouble() - operator fun times(times: Float) = this * times.toDouble() - - operator fun div(times: Double) = times(1.0 / times) - operator fun div(times: Int) = this / times.toDouble() - operator fun div(times: Float) = this / times.toDouble() - - /** From the date part, all months represented as a [totalYears] [Double] */ - val totalYears: Double get() = monthSpan.totalYears - - /** From the date part, all months including months and years */ - val totalMonths: Int get() = monthSpan.totalMonths - - /** From the time part, all the milliseconds including milliseconds, seconds, minutes, hours, days and weeks */ - val totalMilliseconds: Double get() = timeSpan.milliseconds - - /** The [years] part as an integer. */ - val years: Int get() = monthSpan.years - /** The [months] part as an integer. */ - val months: Int get() = monthSpan.months - - /** The [weeks] part as an integer. */ - val weeks: Int get() = computed.weeks - - val daysNotIncludingWeeks: Int get() = days - - /** The [daysIncludingWeeks] part as an integer including days and weeks. */ - val daysIncludingWeeks: Int get() = computed.days + (computed.weeks * DayOfWeek.Count) - - /** The [days] part as an integer. */ - val days: Int get() = computed.days - - /** The [hours] part as an integer. */ - val hours: Int get() = computed.hours - - /** The [minutes] part as an integer. */ - val minutes: Int get() = computed.minutes - - /** The [seconds] part as an integer. */ - val seconds: Int get() = computed.seconds - - /** The [milliseconds] part as a double. */ - val milliseconds: Double get() = computed.milliseconds - - /** The [secondsIncludingMilliseconds] part as a doble including seconds and milliseconds. */ - val secondsIncludingMilliseconds: Double get() = computed.seconds + computed.milliseconds / MILLIS_PER_SECOND - - /** - * Note that if milliseconds overflow months this could not be exactly true. But probably will work in most cases. - * This structure doesn't have information about which months are counted. So some months could have 28-31 days and thus can't be done. - * You can use [DateTimeRange.duration] to compare this with real precision using a range between two [DateTime]. - */ - override fun compareTo(other: DateTimeSpan): Int { - if (this.totalMonths != other.totalMonths) return this.monthSpan.compareTo(other.monthSpan) - return this.timeSpan.compareTo(other.timeSpan) - } - - /** - * Represents this [DateTimeSpan] as a string like `50Y 10M 3W 6DH 30m 15s`. - * Parts that are zero, won't be included. You can omit weeks and represent them - * as days by adjusting the [includeWeeks] parameter. - */ - fun toString(includeWeeks: Boolean): String = arrayListOf().apply { - if (years != 0) add("${years}Y") - if (months != 0) add("${months}M") - if (includeWeeks && weeks != 0) add("${weeks}W") - if (days != 0 || (!includeWeeks && weeks != 0)) add("${if (includeWeeks) days else daysIncludingWeeks}D") - if (hours != 0) add("${hours}H") - if (minutes != 0) add("${minutes}m") - if (seconds != 0 || milliseconds != 0.0) add("${secondsIncludingMilliseconds}s") - if (monthSpan == 0.years && ((timeSpan == 0.seconds) || (timeSpan == (-0).seconds))) add("0s") - }.joinToString(" ") - - override fun toString(): String = toString(includeWeeks = true) - - private class ComputedTime(val weeks: Int, val days: Int, val hours: Int, val minutes: Int, val seconds: Int, val milliseconds: Double) { - companion object { - operator fun invoke(time: TimeSpan): ComputedTime = Moduler(time.milliseconds).run { - val weeks = int(MILLIS_PER_WEEK) - val days = int(MILLIS_PER_DAY) - val hours = int(MILLIS_PER_HOUR) - val minutes = int(MILLIS_PER_MINUTE) - val seconds = int(MILLIS_PER_SECOND) - val milliseconds = double(1) - return ComputedTime(weeks, days, hours, minutes, seconds, milliseconds) - } - } - } - - private val computed by lazy { ComputedTime(timeSpan) } -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateTimeSpanFormat.kt b/klock/src/commonMain/kotlin/korlibs/time/DateTimeSpanFormat.kt deleted file mode 100644 index 9259a413d78..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateTimeSpanFormat.kt +++ /dev/null @@ -1,12 +0,0 @@ -package korlibs.time - -interface DateTimeSpanFormat { - fun format(dd: DateTimeSpan): String - fun tryParse(str: String, doThrow: Boolean): DateTimeSpan? -} - -fun DateTimeSpanFormat.format(dd: TimeSpan): String = format(dd + 0.months) -fun DateTimeSpanFormat.format(dd: MonthSpan): String = format(dd + 0.seconds) - -fun DateTimeSpanFormat.parse(str: String): DateTimeSpan = - tryParse(str, doThrow = true) ?: throw DateException("Not a valid format: '$str' for '$this'") diff --git a/klock/src/commonMain/kotlin/korlibs/time/DateTimeTz.kt b/klock/src/commonMain/kotlin/korlibs/time/DateTimeTz.kt deleted file mode 100644 index bfca9fb9578..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DateTimeTz.kt +++ /dev/null @@ -1,125 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.Serializable - -/** [DateTime] with an associated [TimezoneOffset] */ -class DateTimeTz private constructor( - /** The [adjusted] part */ - private val adjusted: DateTime, - /** The [offset] part */ - val offset: TimezoneOffset -) : Comparable, Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - /** Creates a new [DateTimeTz] with the [utc] date and an [offset]. The [utc] components will be the same as this independently on the [offset]. */ - fun local(local: DateTime, offset: TimezoneOffset) = DateTimeTz(local, offset) - - /** Creates a new [DateTimeTz] with the [utc] date and an [offset]. The [utc] components might be different depending on the [offset]. */ - fun utc(utc: DateTime, offset: TimezoneOffset) = DateTimeTz(utc + offset.time, offset) - - /** Creates a new local [DateTimeTz] from a [unix] time */ - fun fromUnixLocal(unix: Long): DateTimeTz = DateTime(unix).localUnadjusted - - /** Creates a new local [DateTimeTz] from a [unix] time applied*/ - fun fromUnix(unix: Long): DateTimeTz { - val unixDateTime = DateTime(unix) - return utc(unixDateTime, TimezoneOffset.local(unixDateTime)) - } - - /** Returns the current local [DateTimeTz] */ - fun nowLocal(): DateTimeTz = DateTime.now().local - } - - /** Returns a new UTC date that will match these components without being the same time */ - val local: DateTime get() = adjusted - - /** Returns a new UTC date that might not match these components, but it is the same time as UTC */ - val utc: DateTime get() = (adjusted - offset.time) - - /** The [Year] part */ - val year: Year get() = adjusted.year - /** The [Year] part as [Int] */ - val yearInt: Int get() = adjusted.yearInt - - /** The [Month] part */ - val month: Month get() = adjusted.month - /** The [Month] part as [Int] where January is represented as 0 */ - val month0: Int get() = adjusted.month0 - /** The [Month] part as [Int] where January is represented as 1 */ - val month1: Int get() = adjusted.month1 - - /** Represents a couple of [Year] and [Month] that has leap information and thus allows to get the number of days of that month */ - val yearMonth: YearMonth get() = adjusted.yearMonth - - /** The [dayOfMonth] part */ - val dayOfMonth: Int get() = adjusted.dayOfMonth - - /** The [dayOfWeek] part */ - val dayOfWeek: DayOfWeek get() = adjusted.dayOfWeek - /** The [dayOfWeek] part as [Int] */ - val dayOfWeekInt: Int get() = adjusted.dayOfWeekInt - - /** The [dayOfYear] part */ - val dayOfYear: Int get() = adjusted.dayOfYear - - /** The [hours] part */ - val hours: Int get() = adjusted.hours - /** The [minutes] part */ - val minutes: Int get() = adjusted.minutes - /** The [seconds] part */ - val seconds: Int get() = adjusted.seconds - /** The [milliseconds] part */ - val milliseconds: Int get() = adjusted.milliseconds - - /** Constructs this local date with a new [offset] without changing its components */ - fun toOffsetUnadjusted(offset: TimeSpan) = toOffsetUnadjusted(offset.offset) - /** Constructs this local date with a new [offset] without changing its components */ - fun toOffsetUnadjusted(offset: TimezoneOffset) = DateTimeTz.local(this.local, offset) - - /** Constructs this local date by adding an additional [offset] without changing its components */ - fun addOffsetUnadjusted(offset: TimeSpan) = addOffsetUnadjusted(offset.offset) - /** Constructs this local date by adding an additional [offset] without changing its components */ - fun addOffsetUnadjusted(offset: TimezoneOffset) = DateTimeTz.local(this.local, (this.offset.time + offset.time).offset) - - /** Constructs the UTC part of this date with a new [offset] */ - fun toOffset(offset: TimeSpan) = toOffset(offset.offset) - /** Constructs the UTC part of this date with a new [offset] */ - fun toOffset(offset: TimezoneOffset) = DateTimeTz.utc(this.utc, offset) - - /** Constructs the UTC part of this date by adding an additional [offset] */ - fun addOffset(offset: TimeSpan) = addOffset(offset.offset) - /** Constructs the UTC part of this date by adding an additional [offset] */ - fun addOffset(offset: TimezoneOffset) = DateTimeTz.utc(this.utc, (this.offset.time + offset.time).offset) - - /** Constructs a new [DateTimeTz] after adding [dateSpan] and [timeSpan] */ - fun add(dateSpan: MonthSpan, timeSpan: TimeSpan): DateTimeTz = DateTimeTz(adjusted.add(dateSpan, timeSpan), offset) - - operator fun plus(delta: MonthSpan) = add(delta, 0.milliseconds) - operator fun plus(delta: DateTimeSpan) = add(delta.monthSpan, delta.timeSpan) - operator fun plus(delta: TimeSpan) = add(0.months, delta) - - operator fun minus(delta: MonthSpan) = this + (-delta) - operator fun minus(delta: DateTimeSpan) = this + (-delta) - operator fun minus(delta: TimeSpan) = this + (-delta) - - operator fun minus(other: DateTimeTz) = (this.utc.unixMillisDouble - other.utc.unixMillisDouble).milliseconds - - override fun hashCode(): Int = this.local.hashCode() + offset.totalMinutesInt - override fun equals(other: Any?): Boolean = other is DateTimeTz && this.utc.unixMillisDouble == other.utc.unixMillisDouble - override fun compareTo(other: DateTimeTz): Int = this.utc.unixMillis.compareTo(other.utc.unixMillis) - - /** Converts this date to String using [format] for representing it */ - fun format(format: DateFormat): String = format.format(this) - /** Converts this date to String using [format] for representing it */ - fun format(format: String): String = DateFormat(format).format(this) - /** Converts this date to String using [format] for representing it */ - fun toString(format: DateFormat): String = format.format(this) - /** Converts this date to String using [format] for representing it */ - fun toString(format: String): String = DateFormat(format).format(this) - /** Converts this date to String using the [DateFormat.DEFAULT_FORMAT] for representing it */ - fun toStringDefault(): String = DateFormat.DEFAULT_FORMAT.format(this) - - override fun toString(): String = "DateTimeTz($adjusted, $offset)" -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/DayOfWeek.kt b/klock/src/commonMain/kotlin/korlibs/time/DayOfWeek.kt deleted file mode 100644 index 77b5dd85af1..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/DayOfWeek.kt +++ /dev/null @@ -1,95 +0,0 @@ -package korlibs.time - -import korlibs.time.DayOfWeek.Friday -import korlibs.time.DayOfWeek.Monday -import korlibs.time.DayOfWeek.Saturday -import korlibs.time.DayOfWeek.Sunday -import korlibs.time.DayOfWeek.Thursday -import korlibs.time.DayOfWeek.Tuesday -import korlibs.time.DayOfWeek.Wednesday -import korlibs.time.internal.* - -/** Represents the day of the week. [Sunday], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday], [Saturday]. */ -enum class DayOfWeek( - /** 0: [Sunday], 1: [Monday], 2: [Tuesday], 3: [Wednesday], 4: [Thursday], 5: [Friday], 6: [Saturday] */ - val index0: Int -) : Serializable { - Sunday(0), - Monday(1), - Tuesday(2), - Wednesday(3), - Thursday(4), - Friday(5), - Saturday(6); - - /** - * 1: [Sunday], 2: [Monday], 3: [Tuesday], 4: [Wednesday], 5: [Thursday], 6: [Friday], 7: [Saturday] - */ - val index1 get() = index0 + 1 - - val index0Sunday get() = index0 - val index1Sunday get() = index1 - - /** 0: [Monday], 1: [Tuesday], 2: [Wednesday], 3: [Thursday], 4: [Friday], 5: [Saturday], 6: [Sunday] */ - val index0Monday get() = (index0 - 1) umod 7 - - /** 1: [Monday], 2: [Tuesday], 3: [Wednesday], 4: [Thursday], 5: [Friday], 6: [Saturday], 7: [Sunday] */ - val index1Monday get() = index0Monday + 1 - - fun index0Locale(locale: KlockLocale): Int = (index0 - locale.firstDayOfWeek.index0) umod 7 - fun index1Locale(locale: KlockLocale): Int = index0Locale(locale) + 1 - - /** Returns if this day of the week is weekend for a specific [locale] */ - fun isWeekend(locale: KlockLocale = KlockLocale.default) = locale.isWeekend(this) - - val localName get() = localName(KlockLocale.default) - fun localName(locale: KlockLocale) = locale.daysOfWeek[index0] - - val localShortName get() = localShortName(KlockLocale.default) - fun localShortName(locale: KlockLocale) = locale.daysOfWeekShort[index0] - - val prev get() = DayOfWeek[index0 - 1] - val next get() = DayOfWeek[index0 + 1] - - fun prev(offset: Int = 1) = DayOfWeek[index0 - offset] - fun next(offset: Int = 1) = DayOfWeek[index0 + offset] - - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - /** - * Number of days in a wekk. - */ - const val Count = 7 - - private val BY_INDEX0 = values() - - /** - * 0: [Sunday], 1: [Monday], 2: [Tuesday], 3: [Wednesday], 4: [Thursday], 5: [Friday], 6: [Saturday] - */ - operator fun get(index0: Int) = BY_INDEX0[index0 umod 7] - - fun get0(index0: Int, locale: KlockLocale = KlockLocale.default): DayOfWeek = DayOfWeek[index0 + locale.firstDayOfWeek.index0] - fun get1(index1: Int, locale: KlockLocale = KlockLocale.default): DayOfWeek = get0((index1 - 1) umod 7, locale) - - /** - * Returns the first day of the week for a specific [locale]. - */ - fun firstDayOfWeek(locale: KlockLocale = KlockLocale.default) = locale.firstDayOfWeek - - fun comparator(locale: KlockLocale = KlockLocale.default) = locale.daysOfWeekComparator - } -} - -fun DayOfWeek.withLocale(locale: KlockLocale) = locale.localizedDayOfWeek(this) - -data class DayOfWeekWithLocale(val dayOfWeek: DayOfWeek, val locale: KlockLocale) : Comparable { - val index0: Int get() = dayOfWeek.index0Locale(locale) - val index1: Int get() = dayOfWeek.index1Locale(locale) - - override fun compareTo(other: DayOfWeekWithLocale): Int { - if (other.locale != this.locale) error("Can't compare two day of weeks with different locales") - return locale.daysOfWeekComparator.compare(dayOfWeek, other.dayOfWeek) - } -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/Frequency.kt b/klock/src/commonMain/kotlin/korlibs/time/Frequency.kt deleted file mode 100644 index 395ef17b7eb..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/Frequency.kt +++ /dev/null @@ -1,42 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.* -import kotlin.jvm.* - -val TimeSpan.hz: Frequency get() = timesPerSecond -val Int.hz: Frequency get() = timesPerSecond -val Double.hz: Frequency get() = timesPerSecond - -fun TimeSpan.toFrequency(): Frequency = timesPerSecond - -val TimeSpan.timesPerSecond get() = Frequency(1.0 / this.seconds) -val Int.timesPerSecond get() = Frequency(this.toDouble()) -val Double.timesPerSecond get() = Frequency(this) - -@JvmInline -value class Frequency(val hertz: Double) : Comparable, Serializable { - companion object { - fun from(timeSpan: TimeSpan) = timeSpan.toFrequency() - } - - override fun compareTo(other: Frequency): Int = this.hertz.compareTo(other.hertz) - - operator fun unaryMinus() = Frequency(-this.hertz) - operator fun unaryPlus() = this - - operator fun plus(other: Frequency): Frequency = Frequency(this.hertz + other.hertz) - operator fun minus(other: Frequency): Frequency = Frequency(this.hertz - other.hertz) - - operator fun times(scale: Int): Frequency = Frequency(this.hertz * scale) - operator fun times(scale: Float): Frequency = Frequency(this.hertz * scale) - operator fun times(scale: Double): Frequency = Frequency(this.hertz * scale) - - operator fun div(scale: Int): Frequency = Frequency(this.hertz / scale) - operator fun div(scale: Float): Frequency = Frequency(this.hertz / scale) - operator fun div(scale: Double): Frequency = Frequency(this.hertz / scale) - - operator fun rem(other: Frequency): Frequency = Frequency(this.hertz % other.hertz) - infix fun umod(other: Frequency): Frequency = Frequency(this.hertz umod other.hertz) - - val timeSpan get() = (1.0 / this.hertz).seconds -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/ISO8601.kt b/klock/src/commonMain/kotlin/korlibs/time/ISO8601.kt deleted file mode 100644 index 319873067da..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/ISO8601.kt +++ /dev/null @@ -1,446 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.MicroStrReader -import korlibs.time.internal.fastForEach -import korlibs.time.internal.padded -import korlibs.time.internal.readTimeZoneOffset -import kotlin.math.absoluteValue - -// https://en.wikipedia.org/wiki/ISO_8601 -object ISO8601 { - data class BaseIsoTimeFormat(val format: String) : TimeFormat { - companion object { - private val ref = DateTime(1900, 1, 1) - } - private val dateTimeFormat = BaseIsoDateTimeFormat(format) - - override fun format(dd: TimeSpan): String = dateTimeFormat.format(ref + dd) - - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): TimeSpan? = - dateTimeFormat.tryParse(str, doThrow, doAdjust)?.let { it.utc - ref } - } - - data class BaseIsoDateTimeFormat(val format: String, val twoDigitBaseYear: Int = 1900) : DateFormat { - override fun format(dd: DateTimeTz): String = buildString { - val d = dd.local - val s = d.copyDayOfMonth(hours = 0, minutes = 0, seconds = 0, milliseconds = 0) - val time = d - s - val fmtReader = MicroStrReader(format) - while (fmtReader.hasMore) { - when { - fmtReader.tryRead("Z") -> { - //if (dd.offset != TimezoneOffset.UTC) { - if (dd.offset != TimezoneOffset.UTC) { - dd.offset.deltaHoursAbs - append(if (dd.offset.positive) "+" else "-") - append(dd.offset.deltaHoursAbs.padded(2)) - append(":") - append(dd.offset.deltaMinutesAbs.padded(2)) - } else { - append("Z") - } - } - fmtReader.tryRead("YYYYYY") -> append(d.yearInt.absoluteValue.padded(6)) - fmtReader.tryRead("YYYY") -> append(d.yearInt.absoluteValue.padded(4)) - fmtReader.tryRead("YY") -> append((d.yearInt.absoluteValue % 100).padded(2)) - fmtReader.tryRead("MM") -> append(d.month1.padded(2)) - fmtReader.tryRead("DD") -> append(d.dayOfMonth.padded(2)) - fmtReader.tryRead("DDD") -> append(d.dayOfWeekInt.padded(3)) - fmtReader.tryRead("ww") -> append(d.weekOfYear1.padded(2)) - fmtReader.tryRead("D") -> append(d.dayOfWeek.index1Monday) - fmtReader.tryRead("hh") -> { - val nextComma = fmtReader.tryRead(',') - val result = if (nextComma || fmtReader.tryRead('.')) { - var decCount = 0 - while (fmtReader.tryRead('h')) decCount++ - time.hours.padded(2, decCount) - } else { - d.hours.padded(2) - } - append(if (nextComma) result.replace('.', ',') else result) - } - fmtReader.tryRead("mm") -> { - val nextComma = fmtReader.tryRead(',') - val result = if (nextComma || fmtReader.tryRead('.')) { - var decCount = 0 - while (fmtReader.tryRead('m')) decCount++ - (time.minutes % 60.0).padded(2, decCount) - } else { - d.minutes.padded(2) - } - append(if (nextComma) result.replace('.', ',') else result) - } - fmtReader.tryRead("ss") -> { - val nextComma = fmtReader.tryRead(',') - val result = if (nextComma || fmtReader.tryRead('.')) { - var decCount = 0 - while (fmtReader.tryRead('s')) decCount++ - (time.seconds % 60.0).padded(2, decCount) - } else { - d.seconds.padded(2) - } - append(if (nextComma) result.replace('.', ',') else result) - } - fmtReader.tryRead("±") -> append(if (d.yearInt < 0) "-" else "+") - else -> append(fmtReader.readChar()) - } - } - } - - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): DateTimeTz? { - return _tryParse(str, doAdjust).also { - if (doThrow && it == null) throw DateException("Can't parse $str with $format") - } - } - - private fun reportParse(reason: String): DateTimeTz? { - //println("reason: $reason") - return null - } - - private fun _tryParse(str: String, doAdjust: Boolean): DateTimeTz? { - var sign = +1 - var tzOffset: TimeSpan? = null - var year = twoDigitBaseYear - var month = 1 - var dayOfMonth = 1 - - var dayOfWeek = -1 - var dayOfYear = -1 - var weekOfYear = -1 - - var hours = 0.0 - var minutes = 0.0 - var seconds = 0.0 - - val reader = MicroStrReader(str) - val fmtReader = MicroStrReader(format) - - while (fmtReader.hasMore) { - when { - fmtReader.tryRead("Z") -> tzOffset = reader.readTimeZoneOffset() - fmtReader.tryRead("YYYYYY") -> year = reader.tryReadInt(6) ?: return reportParse("YYYYYY") - fmtReader.tryRead("YYYY") -> year = reader.tryReadInt(4) ?: return reportParse("YYYY") - //fmtReader.tryRead("YY") -> year = twoDigitBaseYear + (reader.tryReadInt(2) ?: return null) // @TODO: Kotlin compiler BUG? - fmtReader.tryRead("YY") -> { - val base = reader.tryReadInt(2) ?: return reportParse("YY") - year = twoDigitBaseYear + base - } - fmtReader.tryRead("MM") -> month = reader.tryReadInt(2) ?: return reportParse("MM") - fmtReader.tryRead("DD") -> dayOfMonth = reader.tryReadInt(2) ?: return reportParse("DD") - fmtReader.tryRead("DDD") -> dayOfYear = reader.tryReadInt(3) ?: return reportParse("DDD") - fmtReader.tryRead("ww") -> weekOfYear = reader.tryReadInt(2) ?: return reportParse("ww") - fmtReader.tryRead("D") -> dayOfWeek = reader.tryReadInt(1) ?: return reportParse("D") - - fmtReader.tryRead("hh") -> { - val nextComma = fmtReader.tryRead(',') - hours = if (nextComma || fmtReader.tryRead('.')) { - var count = 3 - while (fmtReader.tryRead('h')) count++ - reader.tryReadDouble(count) ?: return reportParse("incorrect hours") - } else { - reader.tryReadDouble(2) ?: return reportParse("incorrect hours") - } - } - fmtReader.tryRead("mm") -> { - val nextComma = fmtReader.tryRead(',') - minutes = if (nextComma || fmtReader.tryRead('.')) { - var count = 3 - while (fmtReader.tryRead('m')) count++ - reader.tryReadDouble(count) ?: return reportParse("incorrect minutes") - } else { - reader.tryReadDouble(2) ?: return reportParse("incorrect seconds") - } - } - fmtReader.tryRead("ss") -> { - val nextComma = fmtReader.tryRead(',') - seconds = if (nextComma || fmtReader.tryRead('.')) { - var count = 3 - while (fmtReader.tryRead('s')) count++ - reader.tryReadDouble(count) ?: return reportParse("incorrect seconds") - } else { - reader.tryReadDouble(2) ?: return reportParse("incorrect seconds") - } - } - fmtReader.tryRead("±") -> { - sign = when (reader.readChar()) { - '+' -> +1 - '-' -> -1 - else -> return reportParse("±") - } - } - else -> if (fmtReader.readChar() != reader.readChar()) return reportParse("separator") - } - } - if (reader.hasMore) return reportParse("uncomplete") - - val dateTime = when { - dayOfYear >= 0 -> DateTime(year, 1, 1) + (dayOfYear - 1).days - weekOfYear >= 0 -> { - val reference = Year(year).first(DayOfWeek.Thursday) - 3.days - val days = ((weekOfYear - 1) * 7 + (dayOfWeek - 1)) - reference + days.days - } - else -> DateTime(year, month, dayOfMonth) - } - - val baseDateTime = dateTime + hours.hours + minutes.minutes + seconds.seconds - return if (tzOffset != null) DateTimeTz.local(baseDateTime, TimezoneOffset(tzOffset)) else baseDateTime.local - } - - fun withTwoDigitBaseYear(twoDigitBaseYear: Int = 1900) = BaseIsoDateTimeFormat(format, twoDigitBaseYear) - } - - class IsoIntervalFormat(val format: String) : DateTimeSpanFormat { - override fun format(dd: DateTimeSpan): String = buildString { - val fmtReader = MicroStrReader(format) - var time = false - while (fmtReader.hasMore) { - when { - fmtReader.tryRead("T") -> append('T').also { time = true } - fmtReader.tryRead("nnY") -> append(dd.years).append('Y') - fmtReader.tryRead("nnM") -> append(if (time) dd.minutes else dd.months).append('M') - fmtReader.tryRead("nnD") -> append(dd.daysIncludingWeeks).append('D') - fmtReader.tryRead("nnH") -> append(dd.hours).append('H') - fmtReader.tryRead("nnS") -> append(dd.seconds).append('S') - else -> append(fmtReader.readChar()) - } - } - } - - override fun tryParse(str: String, doThrow: Boolean): DateTimeSpan? { - var time = false - var years = 0.0 - var months = 0.0 - var days = 0.0 - var hours = 0.0 - var minutes = 0.0 - var seconds = 0.0 - - val reader = MicroStrReader(str) - val fmtReader = MicroStrReader(format) - - while (fmtReader.hasMore) { - when { - fmtReader.tryRead("nn,nnY") || fmtReader.tryRead("nnY") -> { - years = reader.tryReadDouble() ?: return null - if (!reader.tryRead("Y")) return null - } - fmtReader.tryRead("nn,nnM") || fmtReader.tryRead("nnM") -> { - if (time) { - minutes = reader.tryReadDouble() ?: return null - } else { - months = reader.tryReadDouble() ?: return null - } - if (!reader.tryRead("M")) return null - } - fmtReader.tryRead("nn,nnD") || fmtReader.tryRead("nnD") -> { - days = reader.tryReadDouble() ?: return null - if (!reader.tryRead("D")) return null - } - fmtReader.tryRead("nn,nnH") || fmtReader.tryRead("nnH") -> { - hours = reader.tryReadDouble() ?: return null - if (!reader.tryRead("H")) return null - } - fmtReader.tryRead("nn,nnS") || fmtReader.tryRead("nnS") -> { - seconds = reader.tryReadDouble() ?: return null - if (!reader.tryRead("S")) return null - } - else -> { - val char = fmtReader.readChar() - if (char != reader.readChar()) return null - if (char == 'T') time = true - } - } - } - return ((years * 12) + months).toInt().months + (days.days + hours.hours + minutes.minutes + seconds.seconds) - } - } - - - data class IsoTimeFormat(val basicFormat: String?, val extendedFormat: String?) : TimeFormat { - val basic = BaseIsoTimeFormat(basicFormat ?: extendedFormat ?: TODO()) - val extended = BaseIsoTimeFormat(extendedFormat ?: basicFormat ?: TODO()) - - override fun format(dd: TimeSpan): String = extended.format(dd) - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): TimeSpan? = - basic.tryParse(str, false, doAdjust) ?: extended.tryParse(str, false, doAdjust) - ?: (if (doThrow) throw DateException("Invalid format $str") else null) - } - - data class IsoDateTimeFormat(val basicFormat: String?, val extendedFormat: String?) : DateFormat { - val basic = BaseIsoDateTimeFormat(basicFormat ?: extendedFormat ?: TODO()) - val extended = BaseIsoDateTimeFormat(extendedFormat ?: basicFormat ?: TODO()) - - override fun format(dd: DateTimeTz): String = extended.format(dd) - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): DateTimeTz? = null - ?: basic.tryParse(str, false, doAdjust) - ?: extended.tryParse(str, false, doAdjust) - ?: (if (doThrow) throw DateException("Invalid format $str") else null) - } - - // Date Calendar Variants - val DATE_CALENDAR_COMPLETE = IsoDateTimeFormat("YYYYMMDD", "YYYY-MM-DD") - val DATE_CALENDAR_REDUCED0 = IsoDateTimeFormat(null, "YYYY-MM") - val DATE_CALENDAR_REDUCED1 = IsoDateTimeFormat("YYYY", null) - val DATE_CALENDAR_REDUCED2 = IsoDateTimeFormat("YY", null) - val DATE_CALENDAR_EXPANDED0 = IsoDateTimeFormat("±YYYYYYMMDD", "±YYYYYY-MM-DD") - val DATE_CALENDAR_EXPANDED1 = IsoDateTimeFormat("±YYYYYYMM", "±YYYYYY-MM") - val DATE_CALENDAR_EXPANDED2 = IsoDateTimeFormat("±YYYYYY", null) - val DATE_CALENDAR_EXPANDED3 = IsoDateTimeFormat("±YYY", null) - - // Date Ordinal Variants - val DATE_ORDINAL_COMPLETE = IsoDateTimeFormat("YYYYDDD", "YYYY-DDD") - val DATE_ORDINAL_EXPANDED = IsoDateTimeFormat("±YYYYYYDDD", "±YYYYYY-DDD") - - // Date Week Variants - val DATE_WEEK_COMPLETE = IsoDateTimeFormat("YYYYWwwD", "YYYY-Www-D") - val DATE_WEEK_REDUCED = IsoDateTimeFormat("YYYYWww", "YYYY-Www") - val DATE_WEEK_EXPANDED0 = IsoDateTimeFormat("±YYYYYYWwwD", "±YYYYYY-Www-D") - val DATE_WEEK_EXPANDED1 = IsoDateTimeFormat("±YYYYYYWww", "±YYYYYY-Www") - - val DATE_ALL = listOf( - DATE_CALENDAR_COMPLETE, DATE_CALENDAR_REDUCED0, DATE_CALENDAR_REDUCED1, DATE_CALENDAR_REDUCED2, - DATE_CALENDAR_EXPANDED0, DATE_CALENDAR_EXPANDED1, DATE_CALENDAR_EXPANDED2, DATE_CALENDAR_EXPANDED3, - DATE_ORDINAL_COMPLETE, DATE_ORDINAL_EXPANDED, - DATE_WEEK_COMPLETE, DATE_WEEK_REDUCED, DATE_WEEK_EXPANDED0, DATE_WEEK_EXPANDED1 - ) - - // Time Variants - val TIME_LOCAL_COMPLETE = IsoTimeFormat("hhmmss", "hh:mm:ss") - val TIME_LOCAL_REDUCED0 = IsoTimeFormat("hhmm", "hh:mm") - val TIME_LOCAL_REDUCED1 = IsoTimeFormat("hh", null) - val TIME_LOCAL_FRACTION0 = IsoTimeFormat("hhmmss,ss", "hh:mm:ss,ss") - val TIME_LOCAL_FRACTION1 = IsoTimeFormat("hhmm,mm", "hh:mm,mm") - val TIME_LOCAL_FRACTION2 = IsoTimeFormat("hh,hh", null) - - // Time UTC Variants - val TIME_UTC_COMPLETE = IsoTimeFormat("hhmmssZ", "hh:mm:ssZ") - val TIME_UTC_REDUCED0 = IsoTimeFormat("hhmmZ", "hh:mmZ") - val TIME_UTC_REDUCED1 = IsoTimeFormat("hhZ", null) - val TIME_UTC_FRACTION0 = IsoTimeFormat("hhmmss,ssZ", "hh:mm:ss,ssZ") - val TIME_UTC_FRACTION1 = IsoTimeFormat("hhmm,mmZ", "hh:mm,mmZ") - val TIME_UTC_FRACTION2 = IsoTimeFormat("hh,hhZ", null) - - // Time Relative Variants - val TIME_RELATIVE0 = IsoTimeFormat("±hhmm", "±hh:mm") - val TIME_RELATIVE1 = IsoTimeFormat("±hh", null) - - val TIME_ALL = listOf( - TIME_LOCAL_COMPLETE, - TIME_LOCAL_REDUCED0, - TIME_LOCAL_REDUCED1, - TIME_LOCAL_FRACTION0, - TIME_LOCAL_FRACTION1, - TIME_LOCAL_FRACTION2, - TIME_UTC_COMPLETE, - TIME_UTC_REDUCED0, - TIME_UTC_REDUCED1, - TIME_UTC_FRACTION0, - TIME_UTC_FRACTION1, - TIME_UTC_FRACTION2, - TIME_RELATIVE0, - TIME_RELATIVE1 - ) - - // Date + Time Variants - val DATETIME_COMPLETE = IsoDateTimeFormat("YYYYMMDDThhmmss", "YYYY-MM-DDThh:mm:ss") - val DATETIME_UTC_COMPLETE = IsoDateTimeFormat("YYYYMMDDThhmmssZ", "YYYY-MM-DDThh:mm:ssZ") - val DATETIME_UTC_COMPLETE_FRACTION = IsoDateTimeFormat("YYYYMMDDThhmmss.sssZ", "YYYY-MM-DDThh:mm:ss.sssZ") - - // Interval Variants - val INTERVAL_COMPLETE0 = IsoIntervalFormat("PnnYnnMnnDTnnHnnMnnS") - val INTERVAL_COMPLETE1 = IsoIntervalFormat("PnnYnnW") - - val INTERVAL_REDUCED0 = IsoIntervalFormat("PnnYnnMnnDTnnHnnM") - val INTERVAL_REDUCED1 = IsoIntervalFormat("PnnYnnMnnDTnnH") - val INTERVAL_REDUCED2 = IsoIntervalFormat("PnnYnnMnnD") - val INTERVAL_REDUCED3 = IsoIntervalFormat("PnnYnnM") - val INTERVAL_REDUCED4 = IsoIntervalFormat("PnnY") - - val INTERVAL_DECIMAL0 = IsoIntervalFormat("PnnYnnMnnDTnnHnnMnn,nnS") - val INTERVAL_DECIMAL1 = IsoIntervalFormat("PnnYnnMnnDTnnHnn,nnM") - val INTERVAL_DECIMAL2 = IsoIntervalFormat("PnnYnnMnnDTnn,nnH") - val INTERVAL_DECIMAL3 = IsoIntervalFormat("PnnYnnMnn,nnD") - val INTERVAL_DECIMAL4 = IsoIntervalFormat("PnnYnn,nnM") - val INTERVAL_DECIMAL5 = IsoIntervalFormat("PnnYnn,nnW") - val INTERVAL_DECIMAL6 = IsoIntervalFormat("PnnY") - - val INTERVAL_ZERO_OMIT0 = IsoIntervalFormat("PnnYnnDTnnHnnMnnS") - val INTERVAL_ZERO_OMIT1 = IsoIntervalFormat("PnnYnnDTnnHnnM") - val INTERVAL_ZERO_OMIT2 = IsoIntervalFormat("PnnYnnDTnnH") - val INTERVAL_ZERO_OMIT3 = IsoIntervalFormat("PnnYnnD") - - val INTERVAL_ALL = listOf( - INTERVAL_COMPLETE0, INTERVAL_COMPLETE1, - INTERVAL_REDUCED0, INTERVAL_REDUCED1, INTERVAL_REDUCED2, INTERVAL_REDUCED3, INTERVAL_REDUCED4, - INTERVAL_DECIMAL0, INTERVAL_DECIMAL1, INTERVAL_DECIMAL2, INTERVAL_DECIMAL3, INTERVAL_DECIMAL4, - INTERVAL_DECIMAL5, INTERVAL_DECIMAL6, - INTERVAL_ZERO_OMIT0, INTERVAL_ZERO_OMIT1, INTERVAL_ZERO_OMIT2, INTERVAL_ZERO_OMIT3 - ) - - // Detects and parses all the variants - val DATE = object : DateFormat { - override fun format(dd: DateTimeTz): String = DATE_CALENDAR_COMPLETE.format(dd) - - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): DateTimeTz? { - DATE_ALL.fastForEach { format -> - val result = format.extended.tryParse(str, false, doAdjust) - if (result != null) return result - } - DATE_ALL.fastForEach { format -> - val result = format.basic.tryParse(str, false, doAdjust) - if (result != null) return result - } - return if (doThrow) throw DateException("Invalid format") else null - } - } - val TIME = object : TimeFormat { - override fun format(dd: TimeSpan): String = TIME_LOCAL_FRACTION0.format(dd) - - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): TimeSpan? { - TIME_ALL.fastForEach { format -> - val result = format.extended.tryParse(str, false, doAdjust) - if (result != null) return result - } - TIME_ALL.fastForEach { format -> - val result = format.basic.tryParse(str, false, doAdjust) - if (result != null) return result - } - return if (doThrow) throw DateException("Invalid format") else null - } - } - val INTERVAL = object : DateTimeSpanFormat { - override fun format(dd: DateTimeSpan): String = INTERVAL_DECIMAL0.format(dd) - - override fun tryParse(str: String, doThrow: Boolean): DateTimeSpan? { - INTERVAL_ALL.fastForEach { format -> - val result = format.tryParse(str, false) - if (result != null) return result - } - return if (doThrow) throw DateException("Invalid format") else null - } - } -} - -// ISO 8601 (first week is the one after 1 containing a thursday) -fun Year.first(dayOfWeek: DayOfWeek): DateTime { - val start = DateTime(this.year, 1, 1) - var n = 0 - while (true) { - val time = (start + n.days) - if (time.dayOfWeek == dayOfWeek) return time - n++ - } -} - -val DateTime.weekOfYear0: Int - get() { - val firstThursday = year.first(DayOfWeek.Thursday) - val offset = firstThursday.dayOfMonth - 3 - return (dayOfYear - offset) / 7 - } - -val DateTime.weekOfYear1: Int get() = weekOfYear0 + 1 -val DateTimeTz.weekOfYear0: Int get() = local.weekOfYear0 -val DateTimeTz.weekOfYear1: Int get() = local.weekOfYear1 diff --git a/klock/src/commonMain/kotlin/korlibs/time/KlockLocale.kt b/klock/src/commonMain/kotlin/korlibs/time/KlockLocale.kt deleted file mode 100644 index c20846a574e..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/KlockLocale.kt +++ /dev/null @@ -1,122 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.substr -import kotlin.native.concurrent.ThreadLocal - -private var KlockLocale_default: KlockLocale? = null - -abstract class KlockLocale { - abstract val ISO639_1: String - abstract val daysOfWeek: List - abstract val months: List - abstract val firstDayOfWeek: DayOfWeek - open val monthsShort: List get() = months.map { it.substr(0, 3) } - open val daysOfWeekShort: List get() = daysOfWeek.map { it.substr(0, 3) } - - //private val daysOfWeekWithLocaleList: Array = Array(7) { DayOfWeekWithLocale(DayOfWeek[it], this) } - - //fun localizedDayOfWeek(dayOfWeek: DayOfWeek) = daysOfWeekWithLocaleList[dayOfWeek.index0] - fun localizedDayOfWeek(dayOfWeek: DayOfWeek) = DayOfWeekWithLocale(DayOfWeek[dayOfWeek.index0], this) - - val daysOfWeekComparator get() = Comparator { a, b -> - a.index0Locale(this).compareTo(b.index0Locale(this)) - } - - open val ordinals get() = Array(32) { - if (it in 11..13) { - "${it}th" - } else { - when (it % 10) { - 1 -> "${it}st" - 2 -> "${it}nd" - 3 -> "${it}rd" - else -> "${it}th" - } - } - } - - open fun getOrdinalByDay(day: Int, context: KlockLocaleContext = KlockLocaleContext.Default): String = ordinals[day] - - open fun getDayByOrdinal(ordinal: String): Int = ordinals.indexOf(ordinal) - - //open val monthsShort: List by klockAtomicLazy { months.map { it.substr(0, 3) } } - //open val daysOfWeekShort: List by klockAtomicLazy { daysOfWeek.map { it.substr(0, 3) } } - /* - private val _lock = KlockLock() - private val _monthsShort = KlockAtomicRef?>(null) - private val _daysOfWeekShort = KlockAtomicRef?>(null) - //open val monthsShort by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { months.map { it.substr(0, 3) } } - open val monthsShort: List get() = _lock { - if (_monthsShort.value == null) { - _monthsShort.value = months.map { it.substr(0, 3) } - } - _monthsShort.value!! - } - open val daysOfWeekShort: List get() = _lock { - if (_daysOfWeekShort.value == null) { - _daysOfWeekShort.value = daysOfWeek.map { it.substr(0, 3) } - } - _daysOfWeekShort.value!! - } - */ - - open val h12Marker: List get() = listOf("am", "pm") - - // This might be required for some languages like chinese? - open fun intToString(value: Int) = "$value" - - open fun isWeekend(dayOfWeek: DayOfWeek): Boolean = dayOfWeek == DayOfWeek.Saturday || dayOfWeek == DayOfWeek.Sunday - - protected fun format(str: String) = PatternDateFormat(str, this) - - open val formatDateTimeMedium get() = format("MMM d, y h:mm:ss a") - open val formatDateTimeShort get() = format("M/d/yy h:mm a") - - open val formatDateFull get() = format("EEEE, MMMM d, y") - open val formatDateLong get() = format("MMMM d, y") - open val formatDateMedium get() = format("MMM d, y") - open val formatDateShort get() = format("M/d/yy") - - open val formatTimeMedium get() = format("HH:mm:ss") - open val formatTimeShort get() = format("HH:mm") - - companion object { - val english get() = English - - var default: KlockLocale - set(value) { KlockLocale_default = value } - get() = KlockLocale_default ?: English - - inline fun setTemporarily(locale: KlockLocale, callback: () -> R): R { - val old = default - default = locale - try { - return callback() - } finally { - default = old - } - } - } - - open class English : KlockLocale() { - companion object : English() - - override val ISO639_1 get() = "en" - - override val firstDayOfWeek: DayOfWeek get() = DayOfWeek.Sunday - - override val daysOfWeek: List get() = listOf( - "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" - ) - override val months: List get() = listOf( - "January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December" - ) - - override val formatTimeMedium get() = format("h:mm:ss a") - override val formatTimeShort get() = format("h:mm a") - } -} - -fun DateTime.format(format: String, locale: KlockLocale): String = DateFormat(format).withLocale(locale).format(this) -fun DateTimeTz.format(format: String, locale: KlockLocale): String = DateFormat(format).withLocale(locale).format(this) diff --git a/klock/src/commonMain/kotlin/korlibs/time/KlockLocaleContext.kt b/klock/src/commonMain/kotlin/korlibs/time/KlockLocaleContext.kt deleted file mode 100644 index 5f9cd9debf3..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/KlockLocaleContext.kt +++ /dev/null @@ -1,14 +0,0 @@ -package korlibs.time - -data class KlockLocaleContext(val gender: KlockLocaleGender = KlockLocaleGender.Neuter) { - - companion object { - - val Default = KlockLocaleContext() - } -} - -enum class KlockLocaleGender { - Neuter, - Masculine, -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/Measure.kt b/klock/src/commonMain/kotlin/korlibs/time/Measure.kt deleted file mode 100644 index 0461a58f023..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/Measure.kt +++ /dev/null @@ -1,37 +0,0 @@ -package korlibs.time - -/** - * Executes a [callback] and measure the time it takes to complete. - */ -inline fun measureTime(callback: () -> Unit): TimeSpan { - val start = PerformanceCounter.microseconds - callback() - val end = PerformanceCounter.microseconds - return (end - start).microseconds -} - -inline fun measureTime(callback: () -> T, handleTime: (TimeSpan) -> Unit): T { - val start = PerformanceCounter.microseconds - val result = callback() - val end = PerformanceCounter.microseconds - val elapsed = (end - start).microseconds - handleTime(elapsed) - return result -} - -/** - * Executes the [callback] measuring the time it takes to complete. - * Returns a [TimedResult] with the time and the return value of the callback. - */ -inline fun measureTimeWithResult(callback: () -> T): TimedResult { - val start = PerformanceCounter.microseconds - val result = callback() - val end = PerformanceCounter.microseconds - val elapsed = (end - start).microseconds - return TimedResult(result, elapsed) -} - -/** - * Represents a [result] associated to a [time]. - */ -data class TimedResult(val result: T, val time: TimeSpan) diff --git a/klock/src/commonMain/kotlin/korlibs/time/Month.kt b/klock/src/commonMain/kotlin/korlibs/time/Month.kt deleted file mode 100644 index ce84b33d8b5..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/Month.kt +++ /dev/null @@ -1,131 +0,0 @@ -package korlibs.time - -import korlibs.time.Month.* -import korlibs.time.internal.* -import kotlin.math.* - -/** Represents one of the twelve months of the year. */ -enum class Month( - /** 1: [January], 2: [February], 3: [March], 4: [April], 5: [May], 6: [June], 7: [July], 8: [August], 9: [September], 10: [October], 11: [November], 12: [December] */ - val index1: Int, - /** Number of days of this month in a common year */ - val daysCommon: Int, - /** Number of days of this month in a leap year */ - val daysLeap: Int = daysCommon -) : Serializable { - January(1, daysCommon = 31), - February(2, daysCommon = 28, daysLeap = 29), - March(3, daysCommon = 31), - April(4, daysCommon = 30), - May(5, daysCommon = 31), - June(6, daysCommon = 30), - July(7, daysCommon = 31), - August(8, daysCommon = 31), - September(9, daysCommon = 30), - October(10, daysCommon = 31), - November(11, daysCommon = 30), - December(12, daysCommon = 31); - - /** 0: [January], 1: [February], 2: [March], 3: [April], 4: [May], 5: [June], 6: [July], 7: [August], 8: [September], 9: [October], 10: [November], 11: [December] */ - val index0: Int get() = index1 - 1 - - /** Number of days in a specific month (28-31) depending whether the year is [leap] or not. */ - fun days(leap: Boolean): Int = if (leap) daysLeap else daysCommon - /** Number of days in a specific month (28-31) depending whether the [year] or not. */ - fun days(year: Int): Int = days(Year(year).isLeap) - /** Number of days in a specific month (28-31) depending whether the [year] or not. */ - fun days(year: Year): Int = days(year.isLeap) - - /** Number of days since the start of the [leap] year to reach this month. */ - fun daysToStart(leap: Boolean): Int = YEAR_DAYS(leap)[index0] - /** Number of days since the start of the [year] to reach this month. */ - fun daysToStart(year: Int): Int = daysToStart(Year(year).isLeap) - /** Number of days since the start of the [year] to reach this month. */ - fun daysToStart(year: Year): Int = daysToStart(year.isLeap) - - /** Number of days since the start of the [leap] year to reach next month. */ - fun daysToEnd(leap: Boolean): Int = YEAR_DAYS(leap)[index1] - /** Number of days since the start of the [year] to reach next month. */ - fun daysToEnd(year: Int): Int = daysToEnd(Year(year).isLeap) - /** Number of days since the start of the [year] to reach next month. */ - fun daysToEnd(year: Year): Int = daysToEnd(year.isLeap) - - /** Previous [Month]. */ - val previous: Month get() = this - 1 - /** Next [Month]. */ - val next: Month get() = this + 1 - - operator fun plus(delta: Int): Month = Month[index1 + delta] - operator fun minus(delta: Int): Month = Month[index1 - delta] - - operator fun minus(other: Month): Int = abs(this.index0 - other.index0) - - val localName get() = localName(KlockLocale.default) - fun localName(locale: KlockLocale) = locale.months[index0] - - val localShortName get() = localShortName(KlockLocale.default) - fun localShortName(locale: KlockLocale) = locale.monthsShort[index0] - - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - /** - * Number of months in a year (12). - */ - const val Count = 12 - - /** 1: [January], 2: [February], 3: [March], 4: [April], 5: [May], 6: [June], 7: [July], 8: [August], 9: [September], 10: [October], 11: [November], 12: [December] */ - operator fun invoke(index1: Int) = adjusted(index1) - /** 1: [January], 2: [February], 3: [March], 4: [April], 5: [May], 6: [June], 7: [July], 8: [August], 9: [September], 10: [October], 11: [November], 12: [December] */ - operator fun get(index1: Int) = adjusted(index1) - - /** - * Gets the [Month] from a month index where [January]=1 wrapping the index to valid values. - * - * For example 0 and 12=[December], 1 and 13=[January], -1 and 11=[November]. - */ - fun adjusted(index1: Int) = BY_INDEX0[(index1 - 1) umod 12] - - /** - * Gets the [Month] from a month index where [January]=1 checking that the provided [index1] is valid between 1..12. - */ - fun checked(index1: Int) = BY_INDEX0[index1.also { if (index1 !in 1..12) throw DateException("Month $index1 not in 1..12") } - 1] - - /** - * Gets the [Month] of a [dayOfYear] in a [leap] year. - * - * Returns null if the year doesn't contain that [dayOfYear]. - */ - fun fromDayOfYear(dayOfYear: Int, leap: Boolean): Month? { - val days = YEAR_DAYS(leap) - val day0 = dayOfYear - 1 - val guess = day0 / 32 - - if (guess in 0..11 && day0 in days[guess] until days[guess + 1]) return Month[guess + 1] - if (guess in 0..10 && day0 in days[guess + 1] until days[guess + 2]) return Month[guess + 2] - - return null - } - - /** - * Gets the [Month] of a [dayOfYear] in the specified [year]. - * - * Returns null if the year doesn't contain that [dayOfYear]. - */ - fun fromDayOfYear(dayOfYear: Int, year: Year): Month? = fromDayOfYear(dayOfYear, year.isLeap) - - private val BY_INDEX0 = values() - private fun YEAR_DAYS(isLeap: Boolean): IntArray = if (isLeap) YEAR_DAYS_LEAP else YEAR_DAYS_COMMON - private val YEAR_DAYS_LEAP = generateDaysToStart(leap = true) - private val YEAR_DAYS_COMMON = generateDaysToStart(leap = false) - - private fun generateDaysToStart(leap: Boolean): IntArray { - var total = 0 - return IntArray(13) { - total += if (it == 0) 0 else BY_INDEX0[it - 1].days(leap) - total - } - } - } -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/MonthSpan.kt b/klock/src/commonMain/kotlin/korlibs/time/MonthSpan.kt deleted file mode 100644 index 835de3777b0..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/MonthSpan.kt +++ /dev/null @@ -1,66 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.Serializable -import kotlin.jvm.JvmInline - -/** - * Creates a [MonthSpan] representing these years. - */ -inline val Int.years get() = MonthSpan(12 * this) - -/** - * Creates a [MonthSpan] representing these months. - */ -inline val Int.months get() = MonthSpan(this) - -/** - * Represents a number of years and months temporal distance. - */ -@JvmInline -value class MonthSpan( - /** Total months of this [MonthSpan] as integer */ - val totalMonths: Int -) : Comparable, Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - } - - operator fun unaryMinus() = MonthSpan(-totalMonths) - operator fun unaryPlus() = MonthSpan(+totalMonths) - - operator fun plus(other: TimeSpan) = DateTimeSpan(this, other) - operator fun plus(other: MonthSpan) = MonthSpan(totalMonths + other.totalMonths) - operator fun plus(other: DateTimeSpan) = DateTimeSpan(other.monthSpan + this, other.timeSpan) - - operator fun minus(other: TimeSpan) = this + -other - operator fun minus(other: MonthSpan) = this + -other - operator fun minus(other: DateTimeSpan) = this + -other - - operator fun times(times: Double) = MonthSpan((totalMonths * times).toInt()) - operator fun times(times: Int) = this * times.toDouble() - operator fun times(times: Float) = this * times.toDouble() - - operator fun div(times: Double) = MonthSpan((totalMonths / times).toInt()) - operator fun div(times: Int) = this / times.toDouble() - operator fun div(times: Float) = this / times.toDouble() - - override fun compareTo(other: MonthSpan): Int = this.totalMonths.compareTo(other.totalMonths) - - /** Converts this time to String formatting it like "20Y", "20Y 1M", "1M" or "0M". */ - override fun toString(): String { - val list = arrayListOf() - if (years != 0) list.add("${years}Y") - if (months != 0 || years == 0) list.add("${months}M") - return list.joinToString(" ") - } -} - -/** Total years of this [MonthSpan] as double (might contain decimals) */ -val MonthSpan.totalYears: Double get() = totalMonths.toDouble() / 12.0 - -/** Years part of this [MonthSpan] as integer */ -val MonthSpan.years: Int get() = totalMonths / 12 - -/** Months part of this [MonthSpan] as integer */ -val MonthSpan.months: Int get() = totalMonths % 12 diff --git a/klock/src/commonMain/kotlin/korlibs/time/NumberOfTimes.kt b/klock/src/commonMain/kotlin/korlibs/time/NumberOfTimes.kt deleted file mode 100644 index b94e6d9cebb..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/NumberOfTimes.kt +++ /dev/null @@ -1,28 +0,0 @@ -package korlibs.time - -import kotlin.jvm.JvmInline - -val infiniteTimes get() = NumberOfTimes.INFINITE -inline val Int.times get() = NumberOfTimes(this) - -@JvmInline -value class NumberOfTimes(val count: Int) { - companion object { - val ZERO = NumberOfTimes(0) - val ONE = NumberOfTimes(1) - val INFINITE = NumberOfTimes(Int.MIN_VALUE) - } - val isInfinite get() = this == INFINITE - val isFinite get() = !isInfinite - val hasMore get() = this != ZERO - val oneLess get() = if (this == INFINITE) INFINITE else NumberOfTimes(count - 1) - operator fun plus(other: NumberOfTimes) = if (this == INFINITE || other == INFINITE) INFINITE else NumberOfTimes(this.count + other.count) - operator fun minus(other: NumberOfTimes) = when { - this == other -> ZERO - this == INFINITE || other == INFINITE -> INFINITE - else -> NumberOfTimes(this.count - other.count) - } - operator fun times(other: Int) = if (this == INFINITE) INFINITE else NumberOfTimes(this.count * other) - operator fun div(other: Int) = if (this == INFINITE) INFINITE else NumberOfTimes(this.count / other) - override fun toString(): String = if (this == INFINITE) "$count times" else "Infinite times" -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/PatternDateFormat.kt b/klock/src/commonMain/kotlin/korlibs/time/PatternDateFormat.kt deleted file mode 100644 index 73ffb8e585d..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/PatternDateFormat.kt +++ /dev/null @@ -1,318 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.* -import korlibs.time.internal.MicroStrReader -import korlibs.time.internal.increment -import korlibs.time.internal.padded -import korlibs.time.internal.readTimeZoneOffset -import korlibs.time.internal.substr -import kotlin.jvm.JvmOverloads -import kotlin.math.absoluteValue -import kotlin.math.log10 -import kotlin.math.pow - -data class PatternDateFormat @JvmOverloads constructor( - val format: String, - val locale: KlockLocale? = null, - val tzNames: TimezoneNames = TimezoneNames.DEFAULT, - val options: Options = Options.DEFAULT -) : DateFormat, Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - } - - val realLocale get() = locale ?: KlockLocale.default - - data class Options(val optionalSupport: Boolean = false) : Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - val DEFAULT = Options(optionalSupport = false) - val WITH_OPTIONAL = Options(optionalSupport = true) - } - } - - fun withLocale(locale: KlockLocale?) = this.copy(locale = locale) - fun withTimezoneNames(tzNames: TimezoneNames) = this.copy(tzNames = this.tzNames + tzNames) - fun withOptions(options: Options) = this.copy(options = options) - fun withOptional() = this.copy(options = options.copy(optionalSupport = true)) - fun withNonOptional() = this.copy(options = options.copy(optionalSupport = false)) - - private val openOffsets = LinkedHashMap() - private val closeOffsets = LinkedHashMap() - - internal val chunks = arrayListOf().also { chunks -> - val s = MicroStrReader(format) - while (s.hasMore) { - if (s.peekChar() == '\'') { - val escapedChunk = s.readChunk { - s.tryRead('\'') - while (s.hasMore && s.readChar() != '\'') Unit - } - chunks.add(escapedChunk) - continue - } - if (options.optionalSupport) { - val offset = chunks.size - if (s.tryRead('[')) { - openOffsets.increment(offset) - continue - } - if (s.tryRead(']')) { - closeOffsets.increment(offset - 1) - continue - } - } - chunks.add(s.tryReadOrNull("do") ?: s.readRepeatedChar()) - } - }.toList() - - internal val regexChunks: List = chunks.map { - when (it) { - "E", "EE", "EEE", "EEEE", "EEEEE", "EEEEEE" -> """(\w+)""" - "z", "zzz" -> """([\w\s\-\+:]+)""" - "do" -> """(\d{1,2}\w+)""" - "d" -> """(\d{1,2})""" - "dd" -> """(\d{2})""" - "M" -> """(\d{1,5})""" - "MM" -> """(\d{2})""" - "MMM", "MMMM", "MMMMM" -> """(\w+)""" - "y" -> """(\d{1,5})""" - "yy" -> """(\d{2})""" - "yyy" -> """(\d{3})""" - "yyyy" -> """(\d{4})""" - "YYYY" -> """(\d{4})""" - "H", "k" -> """(\d{1,2})""" - "HH", "kk" -> """(\d{2})""" - "h", "K" -> """(\d{1,2})""" - "hh", "KK" -> """(\d{2})""" - "m" -> """(\d{1,2})""" - "mm" -> """(\d{2})""" - "s" -> """(\d{1,2})""" - "ss" -> """(\d{2})""" - "S" -> """(\d{1,9})""" - "SS" -> """(\d{2})""" - "SSS" -> """(\d{3})""" - "SSSS" -> """(\d{4})""" - "SSSSS" -> """(\d{5})""" - "SSSSSS" -> """(\d{6})""" - "SSSSSSS" -> """(\d{7})""" - "SSSSSSSS" -> """(\d{8})""" - "SSSSSSSSS" -> """(\d{9})""" - "X", "XX", "XXX", "x", "xx", "xxx", "Z" -> """([\w:\+\-]+)""" - "a" -> """(\w+)""" - " " -> """(\s+)""" - else -> when { - it.startsWith('\'') -> "(" + Regex.escape(it.substr(1, it.length - 2)) + ")" - else -> "(" + Regex.escape(it) + ")" - } - } - } - - /** - * @return the regular expression string used for matching this format, able to be composed into another regex - */ - fun matchingRegexString(): String = regexChunks.mapIndexed { index, it -> - if (options.optionalSupport) { - val opens = openOffsets.getOrElse(index) { 0 } - val closes = closeOffsets.getOrElse(index) { 0 } - buildString { - repeat(opens) { append("(?:") } - append(it) - repeat(closes) { append(")?") } - } - } else { - it - } - }.joinToString("") - - //val escapedFormat = Regex.escape(format) - internal val rx2: Regex = Regex("^" + matchingRegexString() + "$") - - - // EEE, dd MMM yyyy HH:mm:ss z -- > Sun, 06 Nov 1994 08:49:37 GMT - // YYYY-MM-dd HH:mm:ss - - override fun format(dd: DateTimeTz): String { - val utc = dd.local - var out = "" - for (name in chunks) { - val nlen = name.length - out += when (name) { - "E", "EE", "EEE" -> DayOfWeek[utc.dayOfWeek.index0].localShortName(realLocale) - "EEEE", "EEEEE", "EEEEEE" -> DayOfWeek[utc.dayOfWeek.index0].localName(realLocale) - "z", "zzz" -> dd.offset.timeZone - "d", "dd" -> utc.dayOfMonth.padded(nlen) - "do" -> realLocale.getOrdinalByDay(utc.dayOfMonth) - "M", "MM" -> utc.month1.padded(nlen) - "MMM" -> Month[utc.month1].localName(realLocale).substr(0, 3) - "MMMM" -> Month[utc.month1].localName(realLocale) - "MMMMM" -> Month[utc.month1].localName(realLocale).substr(0, 1) - "y" -> utc.yearInt - "yy" -> (utc.yearInt % 100).padded(2) - "yyy" -> (utc.yearInt % 1000).padded(3) - "yyyy" -> utc.yearInt.padded(4) - "YYYY" -> utc.yearInt.padded(4) - - "H", "HH" -> mconvertRangeZero(utc.hours, 24).padded(nlen) - "k", "kk" -> mconvertRangeNonZero(utc.hours, 24).padded(nlen) - - "h", "hh" -> mconvertRangeNonZero(utc.hours, 12).padded(nlen) - "K", "KK" -> mconvertRangeZero(utc.hours, 12).padded(nlen) - - "m", "mm" -> utc.minutes.padded(nlen) - "s", "ss" -> utc.seconds.padded(nlen) - - "S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS", "SSSSSSS", "SSSSSSSS", "SSSSSSSSS" -> { - val milli = utc.milliseconds - val base10length = log10(utc.milliseconds.toDouble()).toInt() + 1 - if (base10length > name.length) { - (milli.toDouble() * 10.0.pow(-1 * (base10length - name.length))).toInt() - } else { - "${milli.padded(3)}000000".substr(0, name.length) - } - } - "X", "XX", "XXX", "x", "xx", "xxx" -> { - when { - name.startsWith("X") && dd.offset.totalMinutesInt == 0 -> "Z" - else -> { - val p = if (dd.offset.totalMinutesInt >= 0) "+" else "-" - val hours = (dd.offset.totalMinutesInt / 60).absoluteValue - val minutes = (dd.offset.totalMinutesInt % 60).absoluteValue - when (name) { - "X", "x" -> "$p${hours.padded(2)}" - "XX", "xx" -> "$p${hours.padded(2)}${minutes.padded(2)}" - "XXX", "xxx" -> "$p${hours.padded(2)}:${minutes.padded(2)}" - else -> name - } - } - } - } - "a" -> realLocale.h12Marker[if (utc.hours < 12) 0 else 1] - else -> when { - name.startsWith('\'') -> name.substring(1, name.length - 1) - else -> name - } - } - } - return out - } - - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): DateTimeTz? { - var millisecond = 0 - var second = 0 - var minute = 0 - var hour = 0 - var day = 1 - var month = 1 - var fullYear = 1970 - var offset: TimeSpan? = null - var isPm = false - var is12HourFormat = false - val result = rx2.find(str) ?: return null //println("Parser error: Not match, $str, $rx2"); - for ((name, value) in chunks.zip(result.groupValues.drop(1))) { - if (value.isEmpty()) continue - - when (name) { - "E", "EE", "EEE", "EEEE", "EEEEE", "EEEEEE" -> Unit // day of week (Sun | Sunday) - "z", "zzz" -> { // timezone (GMT) - offset = MicroStrReader(value).readTimeZoneOffset(tzNames) - } - "d", "dd" -> day = value.toInt() - "do" -> day = realLocale.getDayByOrdinal(value) - "M", "MM" -> month = value.toInt() - "MMM" -> month = realLocale.monthsShort.indexOf(value) + 1 - "y", "yyyy", "YYYY" -> fullYear = value.toInt() - "yy" -> if (doThrow) throw RuntimeException("Not guessing years from two digits.") else return null - "yyy" -> fullYear = value.toInt() + if (value.toInt() < 800) 2000 else 1000 // guessing year... - "H", "HH", "k", "kk" -> hour = value.toInt() - "h", "hh", "K", "KK" -> { - hour = value.toInt() - is12HourFormat = true - } - "m", "mm" -> minute = value.toInt() - "s", "ss" -> second = value.toInt() - "S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS", "SSSSSSS", "SSSSSSSS", "SSSSSSSSS" -> { - val base10length = log10(value.toDouble()).toInt() + 1 - millisecond = if (base10length > 3) { - // only precision to millisecond supported, ignore the rest. ex: 9999999 => 999" - (value.toDouble() * 10.0.pow(-1 * (base10length - 3))).toInt() - } else { - value.toInt() - } - } - "X", "XX", "XXX", "x", "xx", "xxx" -> { - when { - name.startsWith("X") && value.first() == 'Z' -> offset = 0.hours - name.startsWith("x") && value.first() == 'Z' -> { - if (doThrow) throw RuntimeException("Zulu Time Zone is only accepted with X-XXX formats.") else return null - } - value.first() != 'Z' -> { - val valueUnsigned = value.replace(":", "").removePrefix("-").removePrefix("+") - val hours = when (name.length) { - 1 -> valueUnsigned.toInt() - else -> valueUnsigned.take(2).toInt() - } - val minutes = when (name.length) { - 1 -> 0 - else -> valueUnsigned.drop(2).toInt() - } - offset = hours.hours + minutes.minutes - if (value.first() == '-') { - offset = -offset - } - } - } - } - "MMMM" -> month = realLocale.months.indexOf(value) + 1 - "MMMMM" -> if (doThrow) throw RuntimeException("Not possible to get the month from one letter.") else return null - "a" -> isPm = value.equals("pm", ignoreCase = true) - else -> { - // ... - } - } - } - //return DateTime.createClamped(fullYear, month, day, hour, minute, second) - if (is12HourFormat) { - if (isPm) { - if (hour != 12) { - hour += 12 - } - } else { - if (hour == 12) { - hour = 0 - } - } - } - if (!doAdjust) { - if (month !in 1..12) if (doThrow) error("Invalid month $month") else return null - if (day !in 1..32) if (doThrow) error("Invalid day $day") else return null - if (hour !in 0..24) if (doThrow) error("Invalid hour $hour") else return null - if (minute !in 0..59) if (doThrow) error("Invalid minute $minute") else return null - if (second !in 0..59) if (doThrow) error("Invalid second $second") else return null - if (millisecond !in 0..999) if (doThrow) error("Invalid millisecond $millisecond") else return null - } - val dateTime = DateTime.createAdjusted(fullYear, month, day, hour umod 24, minute, second, millisecond) - return dateTime.toOffsetUnadjusted(offset ?: 0.hours) - } - - override fun toString(): String = format -} - -private fun mconvertRangeZero(value: Int, size: Int): Int { - return (value umod size) -} - -private fun mconvertRangeNonZero(value: Int, size: Int): Int { - val res = (value umod size) - return if (res == 0) size else res -} - -private fun MicroStrReader.readRepeatedChar(): String { - return readChunk { - val c = readChar() - while (hasMore && (tryRead(c))) Unit - } -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/PatternTimeFormat.kt b/klock/src/commonMain/kotlin/korlibs/time/PatternTimeFormat.kt deleted file mode 100644 index 48bf42663f0..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/PatternTimeFormat.kt +++ /dev/null @@ -1,183 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.* -import korlibs.time.internal.MicroStrReader -import korlibs.time.internal.increment -import korlibs.time.internal.padded -import korlibs.time.internal.substr -import kotlin.math.log10 -import kotlin.math.pow - -data class PatternTimeFormat( - val format: String, - val options: Options = Options.DEFAULT -) : TimeFormat, Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - } - - data class Options(val optionalSupport: Boolean = false) : Serializable { - companion object { - @Suppress("MayBeConstant", "unused") - private const val serialVersionUID = 1L - - val DEFAULT = Options(optionalSupport = false) - val WITH_OPTIONAL = Options(optionalSupport = true) - } - } - - fun withOptions(options: Options) = this.copy(options = options) - fun withOptional() = this.copy(options = options.copy(optionalSupport = true)) - fun withNonOptional() = this.copy(options = options.copy(optionalSupport = false)) - - private val openOffsets = LinkedHashMap() - private val closeOffsets = LinkedHashMap() - - internal val chunks = arrayListOf().also { chunks -> - val s = MicroStrReader(format) - while (s.hasMore) { - if (s.peekChar() == '\'') { - val escapedChunk = s.readChunk { - s.tryRead('\'') - while (s.hasMore && s.readChar() != '\'') Unit - } - chunks.add(escapedChunk) - continue - } - if (options.optionalSupport) { - val offset = chunks.size - if (s.tryRead('[')) { - openOffsets.increment(offset) - continue - } - if (s.tryRead(']')) { - closeOffsets.increment(offset - 1) - continue - } - } - val chunk = s.readChunk { - val c = s.readChar() - while (s.hasMore && s.tryRead(c)) Unit - } - chunks.add(chunk) - } - }.toList() - - private val regexChunks = chunks.map { - when (it) { - "H", "k" -> """(\d{1,})""" - "HH", "kk" -> """(\d{2,})""" - "h", "K" -> """(\d{1,2})""" - "hh", "KK" -> """(\d{2})""" - "m" -> """(\d{1,2})""" - "mm" -> """(\d{2})""" - "s" -> """(\d{1,2})""" - "ss" -> """(\d{2})""" - "S" -> """(\d{1,6})""" - "SS" -> """(\d{2})""" - "SSS" -> """(\d{3})""" - "SSSS" -> """(\d{4})""" - "SSSSS" -> """(\d{5})""" - "SSSSSS" -> """(\d{6})""" - "SSSSSSS" -> """(\d{7})""" - "SSSSSSSS" -> """(\d{8})""" - "a" -> """(\w+)""" - " " -> """(\s+)""" - else -> when { - it.startsWith('\'') -> "(" + Regex.escapeReplacement(it.substr(1, it.length - 2)) + ")" - else -> "(" + Regex.escapeReplacement(it) + ")" - } - } - } - - private val rx2: Regex = Regex("^" + regexChunks.mapIndexed { index, it -> - if (options.optionalSupport) { - val opens = openOffsets.getOrElse(index) { 0 } - val closes = closeOffsets.getOrElse(index) { 0 } - buildString { - repeat(opens) { append("(?:") } - append(it) - repeat(closes) { append(")?") } - } - } else { - it - } - }.joinToString("") + "$") - - private fun clampZero(value: Int, size: Int) = (value umod size) - - private fun clampNonZero(value: Int, size: Int) = (value umod size).let { if (it == 0) size else it } - - override fun format(dd: TimeSpan): String { - val time = Time(dd) - var out = "" - for (name in chunks) { - val nlen = name.length - out += when (name) { - "H", "HH" -> time.hour.padded(nlen) - "k", "kk" -> time.hour.padded(nlen) - - "h", "hh" -> clampNonZero(time.hour, 12).padded(nlen) - "K", "KK" -> clampZero(time.hour, 12).padded(nlen) - - "m", "mm" -> time.minute.padded(nlen) - "s", "ss" -> time.second.padded(nlen) - - "S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS", "SSSSSSS", "SSSSSSSS" -> { - val milli = time.millisecond - val numberLength = log10(time.millisecond.toDouble()).toInt() + 1 - if (numberLength > name.length) { - (milli.toDouble() / 10.0.pow(numberLength - name.length)).toInt() - } else { - "${milli.padded(3)}00000".substr(0, name.length) - } - } - "a" -> if (time.hour < 12) "am" else if (time.hour < 24) "pm" else "" - else -> if (name.startsWith('\'')) name.substring(1, name.length - 1) else name - } - } - return out - } - - override fun tryParse(str: String, doThrow: Boolean, doAdjust: Boolean): TimeSpan? { - var millisecond = 0 - var second = 0 - var minute = 0 - var hour = 0 - var isPm = false - var is12HourFormat = false - val result = rx2.find(str) ?: return null //println("Parser error: Not match, $str, $rx2"); - for ((name, value) in chunks.zip(result.groupValues.drop(1))) { - if (value.isEmpty()) continue - when (name) { - "H", "HH", "k", "kk" -> hour = value.toInt() - "h", "hh", "K", "KK" -> { - hour = value.toInt() umod 24 - is12HourFormat = true - } - "m", "mm" -> minute = value.toInt() - "s", "ss" -> second = value.toInt() - "S", "SS", "SSS", "SSSS", "SSSSS", "SSSSSS" -> { - val numberLength = log10(value.toDouble()).toInt() + 1 - millisecond = if (numberLength > 3) { - // only precision to millisecond supported, ignore the rest: 9999999 => 999 - (value.toDouble() * 10.0.pow(-1 * (numberLength - 3))).toInt() - } else { - value.toInt() - } - } - "a" -> isPm = value == "pm" - else -> { - // ... - } - } - } - if (is12HourFormat && isPm) { - hour += 12 - } - return hour.hours + minute.minutes + second.seconds + millisecond.milliseconds - } - - override fun toString(): String = format -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/PerformanceCounter.kt b/klock/src/commonMain/kotlin/korlibs/time/PerformanceCounter.kt deleted file mode 100644 index ccec5f32ec6..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/PerformanceCounter.kt +++ /dev/null @@ -1,29 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.* -import kotlin.time.* - -/** - * Class for measuring relative times with as much precision as possible. - */ -object PerformanceCounter { - /** - * Returns a performance counter measure in nanoseconds. - */ - val nanoseconds: Double get() = KlockInternal.now.nanoseconds - - /** - * Returns a performance counter measure in microseconds. - */ - val microseconds: Double get() = KlockInternal.now.microseconds - - /** - * Returns a performance counter measure in milliseconds. - */ - val milliseconds: Double get() = KlockInternal.now.milliseconds - - /** - * Returns a performance counter as a [Duration]. - */ - val reference: Duration get() = KlockInternal.now -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/RangesExt.kt b/klock/src/commonMain/kotlin/korlibs/time/RangesExt.kt deleted file mode 100644 index 1af9af9671a..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/RangesExt.kt +++ /dev/null @@ -1,4 +0,0 @@ -package korlibs.time - -public fun TimeSpan.convertRange(srcMin: TimeSpan, srcMax: TimeSpan, dstMin: TimeSpan, dstMax: TimeSpan): TimeSpan = (dstMin + (dstMax - dstMin) * ((this - srcMin) / (srcMax - srcMin))) -public fun DateTime.convertRange(srcMin: DateTime, srcMax: DateTime, dstMin: DateTime, dstMax: DateTime): DateTime = (dstMin + (dstMax - dstMin) * ((this - srcMin) / (srcMax - srcMin))) diff --git a/klock/src/commonMain/kotlin/korlibs/time/Sleep.kt b/klock/src/commonMain/kotlin/korlibs/time/Sleep.kt deleted file mode 100644 index 088abc31b47..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/Sleep.kt +++ /dev/null @@ -1,7 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.* - -/** Sleeps the thread during the specified time. Spinlocks on JS */ -@ExperimentalStdlibApi -fun blockingSleep(time: TimeSpan) = KlockInternal.sleep(time) diff --git a/klock/src/commonMain/kotlin/korlibs/time/Stopwatch.kt b/klock/src/commonMain/kotlin/korlibs/time/Stopwatch.kt deleted file mode 100644 index d3449a06d5d..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/Stopwatch.kt +++ /dev/null @@ -1,25 +0,0 @@ -package korlibs.time - -class Stopwatch(val nanosecondProvider: () -> Double = { PerformanceCounter.nanoseconds }) { - constructor(timeProvider: TimeProvider) : this({ timeProvider.now().unixMillis.milliseconds.nanoseconds }) - private var running = false - private var startNano = 0.0 - private val currentNano get() = nanosecondProvider() - private fun setStart() { startNano = currentNano } - init { - setStart() - } - fun start() = this.apply { - setStart() - running = true - } - fun restart() = start() - fun stop() = this.apply { - startNano = elapsedNanoseconds - running = false - } - val elapsedNanoseconds get() = if (running) currentNano - startNano else startNano - val elapsedMicroseconds get() = elapsedNanoseconds * 1000 - val elapsed: TimeSpan get() = elapsedNanoseconds.nanoseconds - fun getElapsedAndRestart(): TimeSpan = elapsed.also { restart() } -} diff --git a/klock/src/commonMain/kotlin/korlibs/time/Time.kt b/klock/src/commonMain/kotlin/korlibs/time/Time.kt deleted file mode 100644 index c8c08b0f168..00000000000 --- a/klock/src/commonMain/kotlin/korlibs/time/Time.kt +++ /dev/null @@ -1,50 +0,0 @@ -package korlibs.time - -import korlibs.time.internal.Serializable -import kotlin.jvm.JvmInline -import kotlin.math.abs - -/** - * Represents a union of [millisecond], [second], [minute] and [hour]. - */ -@JvmInline -value class Time(val encoded: TimeSpan) : Comparable