From 8ce351ecfe329aed1bd650e961c444a6dbbce17f Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 29 Mar 2021 21:30:24 +0600 Subject: [PATCH 01/19] start 0.5.2 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3400205..32ef077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.5.2 + ## 0.5.1 * Versions diff --git a/gradle.properties b/gradle.properties index 06a6ce4..bdb17da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,6 +33,6 @@ androidx_work_version=2.5.0 ## Common -version=0.5.1 -android_code_version=2 +version=0.5.2 +android_code_version=3 From 68c49faa1d74c1a2f47b6daa9be58474c6d753c2 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 29 Mar 2021 21:32:39 +0600 Subject: [PATCH 02/19] update kotlin --- CHANGELOG.md | 3 +++ gradle.properties | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32ef077..4d6a029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.5.2 +* Versions + * `Kotlin`: `1.4.31` -> `1.4.32` + ## 0.5.1 * Versions diff --git a/gradle.properties b/gradle.properties index bdb17da..3a825fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,10 +8,10 @@ android.useAndroidX=true android.enableJetifier=false -kotlin_version=1.4.31 +kotlin_version=1.4.32 kotlin_coroutines_version=1.4.3 -dokka_version=1.4.20 +dokka_version=1.4.30 klockVersion=2.0.7 From 5311e651f44f9dbd9ea104fc657d66a9e257acb5 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 29 Mar 2021 22:14:49 +0600 Subject: [PATCH 03/19] add work with time zones --- .../kotlin/dev/inmo/krontab/KronScheduler.kt | 9 ++++++++ .../dev/inmo/krontab/SchedulerShortcuts.kt | 2 ++ .../dev/inmo/krontab/utils/TimeZoneTest.kt | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index b7e214a..53af1c5 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime +import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.internal.toNearDateTime /** @@ -19,9 +20,17 @@ interface KronScheduler { * @see dev.inmo.krontab.internal.CronDateTimeScheduler.next */ suspend fun next(relatively: DateTime = DateTime.now()): DateTime? + + suspend fun next(relatively: DateTimeTz): DateTimeTz? { + return next(relatively.utc) ?.toOffset(relatively.offset) + } } suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) +suspend fun KronScheduler.nextOrRelative(relatively: DateTimeTz): DateTimeTz = next(relatively) ?: getAnyNext(relatively) suspend fun KronScheduler.nextOrNow(): DateTime = DateTime.now().let { next(it) ?: getAnyNext(it) } +suspend fun KronScheduler.nextOrNowWithOffset(): DateTimeTz = DateTimeTz.nowLocal().let { + next(it) ?: getAnyNext(it) +} diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index ed641e6..4b7945c 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime +import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.builder.buildSchedule import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.CronDateTime @@ -17,6 +18,7 @@ internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(r val AnyTimeScheduler: KronScheduler by lazy { CronDateTimeScheduler(listOf(anyCronDateTime)) } +internal suspend fun getAnyNext(relatively: DateTimeTz) = AnyTimeScheduler.next(relatively)!! /** * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one second diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt new file mode 100644 index 0000000..0b3bc2b --- /dev/null +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt @@ -0,0 +1,23 @@ +package dev.inmo.krontab.utils + +import com.soywiz.klock.* +import dev.inmo.krontab.builder.buildSchedule +import kotlin.test.Test +import kotlin.test.assertEquals + +class TimeZoneTest { + @Test + fun testDifferentTimeZonesReturnsDifferentTimes() { + val scheduler = buildSchedule { seconds { every(1) } } + val now = DateTime.now() + runTest { + for (i in 0 .. 24) { + val nowTz = now.toOffset(i.hours) + val next = scheduler.next(nowTz)!! + assertEquals( + (nowTz + 1.seconds).utc.unixMillisLong, next.utc.unixMillisLong + ) + } + } + } +} \ No newline at end of file From 93246cada7f97de4275c226e8c157e61eab2ce3c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 29 Mar 2021 22:29:10 +0600 Subject: [PATCH 04/19] fix of next with DateTimeTz --- src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index 53af1c5..dcd2eef 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -22,7 +22,7 @@ interface KronScheduler { suspend fun next(relatively: DateTime = DateTime.now()): DateTime? suspend fun next(relatively: DateTimeTz): DateTimeTz? { - return next(relatively.utc) ?.toOffset(relatively.offset) + return next(relatively.local) ?.toOffsetUnadjusted(relatively.offset) } } From a07027399cd5fa7afa0feb1a16134a025181cce6 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 30 Mar 2021 14:07:39 +0600 Subject: [PATCH 05/19] add offset parameter in strings --- .../kotlin/dev/inmo/krontab/KronScheduler.kt | 4 +- .../dev/inmo/krontab/SchedulerShortcuts.kt | 2 +- .../kotlin/dev/inmo/krontab/StringParser.kt | 26 +++++++++++-- .../inmo/krontab/builder/SchedulerBuilder.kt | 27 +++++++++++++- .../collection/CollectionKronScheduler.kt | 5 +++ .../dev/inmo/krontab/internal/CronDateTime.kt | 37 +++++++++++++++++-- .../krontab/internal/CronDateTimeScheduler.kt | 5 +++ .../dev/inmo/krontab/internal/Parser.kt | 14 +++++++ .../dev/inmo/krontab/internal/Ranges.kt | 5 +++ .../dev/inmo/krontab/utils/Typealiases.kt | 3 ++ .../dev/inmo/krontab/utils/StringParseTest.kt | 14 +++++++ 11 files changed, 130 insertions(+), 12 deletions(-) create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/utils/Typealiases.kt diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index dcd2eef..efd89ef 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -21,9 +21,7 @@ interface KronScheduler { */ suspend fun next(relatively: DateTime = DateTime.now()): DateTime? - suspend fun next(relatively: DateTimeTz): DateTimeTz? { - return next(relatively.local) ?.toOffsetUnadjusted(relatively.offset) - } + suspend fun next(relatively: DateTimeTz): DateTimeTz? } suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index 4b7945c..aa5500b 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -11,6 +11,7 @@ internal val anyCronDateTime by lazy { CronDateTime() } internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!! +internal fun getAnyNext(relatively: DateTimeTz) = anyCronDateTime.toNearDateTime(relatively)!! /** * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] @@ -18,7 +19,6 @@ internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(r val AnyTimeScheduler: KronScheduler by lazy { CronDateTimeScheduler(listOf(anyCronDateTime)) } -internal suspend fun getAnyNext(relatively: DateTimeTz) = AnyTimeScheduler.next(relatively)!! /** * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one second diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index d41c5b4..0eb868f 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -17,6 +17,7 @@ typealias KrontabTemplate = String * * dayOfMonth * * month * * (optional) year + * * (optional) (can be placed before year) offset * * And each one have next format: * @@ -39,6 +40,7 @@ typealias KrontabTemplate = String * * Days of month ranges can be found in [dayOfMonthRange] * * Months ranges can be found in [monthRange] * * Years ranges can be found in [yearRange] (in fact - any [Int]) + * * Offset (timezone) ranges can be found in [offsetRange] * * Examples: * @@ -46,14 +48,31 @@ typealias KrontabTemplate = String * * "0/5,L * * * *" for every five seconds triggering and on 59 second * * "0/15 30 * * *" for every 15th seconds in a half of each hour * * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may + * * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of fourth day of may with timezone UTC+01:00 * * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year + * * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year with timezone UTC+01:00 * * @see dev.inmo.krontab.internal.createKronScheduler */ fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler { - val yearSource: String? + var offsetParsed: Int? = null + var yearParsed: Array? = null val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also { - yearSource = it.getOrNull(5) + listOfNotNull( + it.getOrNull(5), + it.getOrNull(6) + ).forEach { + val parsedOffset = parseOffset(it) + offsetParsed = offsetParsed ?: parsedOffset + when { + parsedOffset == null && yearParsed == null -> { + yearParsed = parseYears(it) + } + parsedOffset != null && offsetParsed == null -> { + offsetParsed = parsedOffset + } + } + } } val secondsParsed = parseSeconds(secondsSource) @@ -61,10 +80,9 @@ fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler { val hoursParsed = parseHours(hoursSource) val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource) val monthParsed = parseMonths(monthSource) - val yearParsed = parseYears(yearSource) return createKronScheduler( - secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed + secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, offsetParsed ) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt index acc1334..2f477d0 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt @@ -1,7 +1,10 @@ package dev.inmo.krontab.builder +import com.soywiz.klock.TimeSpan +import com.soywiz.klock.TimezoneOffset import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.internal.createKronScheduler +import dev.inmo.krontab.utils.Minutes /** * Will help to create an instance of [KronScheduler] @@ -22,7 +25,8 @@ class SchedulerBuilder( private var hours: Array? = null, private var dayOfMonth: Array? = null, private var month: Array? = null, - private var year: Array? = null + private var year: Array? = null, + var offset: Minutes? = null ) { private fun > callAndReturn( initial: Array?, @@ -106,6 +110,27 @@ class SchedulerBuilder( ) ?.toTypedArray() } + /** + * Setter of [offset] property + */ + fun offset(offset: Minutes?) { + this.offset = offset + } + + /** + * Setter of [offset] property + */ + fun offset(offset: TimeSpan?) { + this.offset = offset ?.minutes ?.toInt() + } + + /** + * Setter of [offset] property + */ + fun offset(offset: TimezoneOffset?) { + this.offset = offset ?.totalMinutesInt + } + /** * @return Completely built and independent [KronScheduler] * diff --git a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt index 1f552b2..62fbf8c 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab.collection import com.soywiz.klock.DateTime +import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.* import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.CronDateTimeScheduler @@ -48,4 +49,8 @@ data class CollectionKronScheduler internal constructor( override suspend fun next(relatively: DateTime): DateTime { return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively) } + + override suspend fun next(relatively: DateTimeTz): DateTimeTz { + return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively) + } } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index 269cd9b..4b1d312 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -1,8 +1,8 @@ package dev.inmo.krontab.internal -import com.soywiz.klock.DateTime -import com.soywiz.klock.DateTimeSpan +import com.soywiz.klock.* import dev.inmo.krontab.KronScheduler +import dev.inmo.krontab.utils.Minutes /** * @param month 0-11 @@ -12,6 +12,7 @@ import dev.inmo.krontab.KronScheduler * @param seconds 0-59 */ internal data class CronDateTime( + val offset: Int? = null, val year: Int? = null, val month: Byte? = null, val dayOfMonth: Byte? = null, @@ -29,9 +30,13 @@ internal data class CronDateTime( } internal val klockDayOfMonth = dayOfMonth ?.plus(1) + internal val klockOffset = offset ?.minutes ?.offset } /** + * THIS METHOD WILL NOT TAKE CARE ABOUT [offset] PARAMETER. It was decided due to the fact that we unable to get + * real timezone offset from simple [DateTime] + * * @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo] */ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? { @@ -77,6 +82,23 @@ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now() return current } +/** + * THIS METHOD WILL TAKE CARE ABOUT [offset] PARAMETER. It was decided due to the fact that we unable to get + * real timezone offset from simple [DateTime] + * + * @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo] + */ +internal fun CronDateTime.toNearDateTime( + relativelyTo: DateTimeTz +): DateTimeTz? { + val klockOffset = klockOffset + return if (klockOffset != null) { + toNearDateTime(relativelyTo.toOffset(klockOffset).local) ?.toOffsetUnadjusted(klockOffset) ?.toOffset(relativelyTo.offset) + } else { + toNearDateTime(relativelyTo.local) ?.toOffsetUnadjusted(relativelyTo.offset) + } +} + /** * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data */ @@ -86,7 +108,8 @@ internal fun createKronScheduler( hours: Array? = null, dayOfMonth: Array? = null, month: Array? = null, - years: Array? = null + years: Array? = null, + offset: Minutes? = null ): KronScheduler { val resultCronDateTimes = mutableListOf(CronDateTime()) @@ -114,5 +137,13 @@ internal fun createKronScheduler( previousCronDateTime.copy(year = currentTime) } + years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int -> + previousCronDateTime.copy(year = currentTime) + } + + offset ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int -> + previousCronDateTime.copy(year = currentTime) + } + return CronDateTimeScheduler(resultCronDateTimes.toList()) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index 23de605..ede3fca 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab.internal import com.soywiz.klock.DateTime +import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.* import dev.inmo.krontab.collection.plus @@ -29,6 +30,10 @@ internal data class CronDateTimeScheduler internal constructor( override suspend fun next(relatively: DateTime): DateTime { return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively) } + + override suspend fun next(relatively: DateTimeTz): DateTimeTz { + return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively) + } } internal fun mergeCronDateTimeSchedulers(schedulers: List) = CronDateTimeScheduler( diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt index f2f8648..871a60f 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt @@ -38,6 +38,7 @@ private fun createSimpleScheduler(from: String, dataRange: IntRange, dataCon return results.map(dataConverter) } +internal fun parseOffset(from: String?) = from ?.let { if (it.endsWith("o")) it.removeSuffix("o").toIntOrNull() else null } internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() } internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray() internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange, intToByteConverter) ?.toTypedArray() @@ -60,3 +61,16 @@ internal fun Array.fillWith( } } +internal fun T.fillWith( + whereToPut: MutableList, + createFactory: (CronDateTime, T) -> CronDateTime +) { + val previousValues = whereToPut.toList() + + whereToPut.clear() + + previousValues.forEach { previousValue -> + whereToPut.add(createFactory(previousValue, this)) + } +} + diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt index ec4359b..232cf13 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt @@ -6,3 +6,8 @@ internal val dayOfMonthRange = 0 .. 30 internal val hoursRange = 0 .. 23 internal val minutesRange = 0 .. 59 internal val secondsRange = minutesRange + +/** + * From 0 - 1439 minutes (1440 == 24 hours, that is the same as 0 in terms of timezones) + */ +internal val offsetRange = 0 until (hoursRange.count() * minutesRange.count()) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/Typealiases.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/Typealiases.kt new file mode 100644 index 0000000..5cb9dc9 --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/Typealiases.kt @@ -0,0 +1,3 @@ +package dev.inmo.krontab.utils + +typealias Minutes = Int diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt index fe37fc2..fdc0146 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt @@ -1,6 +1,8 @@ package dev.inmo.krontab.utils +import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.buildSchedule +import dev.inmo.krontab.internal.offsetRange import kotlinx.coroutines.* import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.takeWhile @@ -76,4 +78,16 @@ class StringParseTest { assertEquals(expectedCollects, collected) } } + @Test + fun testThatTimezoneCorrectlyDeserialized() { + val now = DateTimeTz.nowLocal() + + runTest { + for (i in offsetRange) { + val kronScheduler = buildSchedule("* * 10 * * ${i}o") + val next = kronScheduler.next(now) + + } + } + } } From 5844859369f8b476ea19df0d5c4bab56b9fab142 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 10 Apr 2021 00:28:08 +0600 Subject: [PATCH 06/19] rewrite system with offsets --- .../kotlin/dev/inmo/krontab/KronScheduler.kt | 7 --- .../dev/inmo/krontab/KronSchedulerTz.kt | 32 ++++++++++ .../dev/inmo/krontab/KronSchedulersMerging.kt | 15 ++++- .../dev/inmo/krontab/SchedulerShortcuts.kt | 1 - .../kotlin/dev/inmo/krontab/StringParser.kt | 47 +++++++++++++-- .../inmo/krontab/builder/SchedulerBuilder.kt | 48 ++++++++------- .../collection/CollectionKronScheduler.kt | 4 +- .../dev/inmo/krontab/internal/CronDateTime.kt | 59 +++++++++---------- .../krontab/internal/CronDateTimeScheduler.kt | 7 +-- .../internal/CronDateTimeSchedulerTz.kt | 30 ++++++++++ .../dev/inmo/krontab/internal/Ranges.kt | 5 -- .../dev/inmo/krontab/utils/StringParseTest.kt | 4 +- .../dev/inmo/krontab/utils/TimeZoneTest.kt | 1 + 13 files changed, 174 insertions(+), 86 deletions(-) create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index efd89ef..172536b 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -13,22 +13,15 @@ import dev.inmo.krontab.internal.toNearDateTime * @see dev.inmo.krontab.internal.CronDateTimeScheduler */ interface KronScheduler { - /** * @return Next [DateTime] when some action must be triggered according to settings of this instance * * @see dev.inmo.krontab.internal.CronDateTimeScheduler.next */ suspend fun next(relatively: DateTime = DateTime.now()): DateTime? - - suspend fun next(relatively: DateTimeTz): DateTimeTz? } suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) -suspend fun KronScheduler.nextOrRelative(relatively: DateTimeTz): DateTimeTz = next(relatively) ?: getAnyNext(relatively) suspend fun KronScheduler.nextOrNow(): DateTime = DateTime.now().let { next(it) ?: getAnyNext(it) } -suspend fun KronScheduler.nextOrNowWithOffset(): DateTimeTz = DateTimeTz.nowLocal().let { - next(it) ?: getAnyNext(it) -} diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt new file mode 100644 index 0000000..65d812f --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt @@ -0,0 +1,32 @@ +package dev.inmo.krontab + +import com.soywiz.klock.DateTime +import com.soywiz.klock.DateTimeTz + +/** + * This interface extending [KronScheduler] to use [DateTimeTz] with taking into account offset of incoming time for + * [next] operation. + * + * @see dev.inmo.krontab.internal.CronDateTimeScheduler + * @see dev.inmo.krontab.KronScheduler + */ +interface KronSchedulerTz : KronScheduler { + suspend fun next(relatively: DateTimeTz): DateTimeTz? + + override suspend fun next(relatively: DateTime): DateTime? = next(relatively.localUnadjusted) ?.local +} + +suspend fun KronSchedulerTz.nextOrRelative(relatively: DateTimeTz): DateTimeTz = next(relatively) ?: getAnyNext( + relatively.local +).toOffsetUnadjusted(relatively.offset) +suspend fun KronSchedulerTz.nextOrNowWithOffset(): DateTimeTz = DateTimeTz.nowLocal().let { + next(it) ?: getAnyNext( + it.local + ).toOffsetUnadjusted(it.offset) +} + +suspend fun KronScheduler.next(relatively: DateTimeTz) = if (this is KronSchedulerTz) { + this.next(relatively) +} else { + this.next(relatively.local) ?.toOffsetUnadjusted(relatively.offset) +} \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt index f5a682f..3e27445 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt @@ -1,8 +1,12 @@ package dev.inmo.krontab +import com.soywiz.klock.TimezoneOffset import dev.inmo.krontab.collection.CollectionKronScheduler +import dev.inmo.krontab.collection.includeAll +import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.CronDateTime import dev.inmo.krontab.internal.CronDateTimeScheduler +import dev.inmo.krontab.internal.CronDateTimeSchedulerTz /** * Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator] @@ -10,18 +14,23 @@ import dev.inmo.krontab.internal.CronDateTimeScheduler * @see CollectionKronScheduler * @see CollectionKronScheduler.include */ -fun Iterator.merge(): KronScheduler { +fun Iterator.merge(): CollectionKronScheduler { val cronDateTimes = mutableListOf() + val timezonedCronDateTimes = mutableListOf() val collectionScheduler = CollectionKronScheduler() forEach { when (it) { is CronDateTimeScheduler -> cronDateTimes.addAll(it.cronDateTimes) + is CronDateTimeSchedulerTz -> timezonedCronDateTimes.add(it) else -> collectionScheduler.include(it) } } if (cronDateTimes.isNotEmpty()) { collectionScheduler.include(CronDateTimeScheduler(cronDateTimes)) } + if (timezonedCronDateTimes.isNotEmpty()) { + collectionScheduler.includeAll(mergeCronDateTimeSchedulers(timezonedCronDateTimes)) + } return collectionScheduler } @@ -32,10 +41,10 @@ fun Iterator.merge(): KronScheduler { * @see CollectionKronScheduler.include */ @Suppress("NOTHING_TO_INLINE") -inline fun Iterable.merge(): KronScheduler = iterator().merge() +inline fun Iterable.merge(): CollectionKronScheduler = iterator().merge() /** * @return Vararg shortcut for [merge] */ @Suppress("NOTHING_TO_INLINE") -inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.iterator().merge() +inline fun merge(vararg kronDateTimeSchedulers: KronScheduler): CollectionKronScheduler = kronDateTimeSchedulers.iterator().merge() diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index aa5500b..f313d40 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -11,7 +11,6 @@ internal val anyCronDateTime by lazy { CronDateTime() } internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!! -internal fun getAnyNext(relatively: DateTimeTz) = anyCronDateTime.toNearDateTime(relatively)!! /** * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index 0eb868f..287761f 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -1,6 +1,8 @@ package dev.inmo.krontab +import com.soywiz.klock.* import dev.inmo.krontab.internal.* +import dev.inmo.krontab.utils.Minutes /** * @see createSimpleScheduler @@ -52,9 +54,15 @@ typealias KrontabTemplate = String * * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year * * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year with timezone UTC+01:00 * + * @return In case when offset parameter is absent in [incoming] will be used [createSimpleScheduler] method and + * returned [CronDateTimeScheduler]. In case when offset parameter there is in [incoming] [KrontabTemplate] will be used + * [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz] + * * @see dev.inmo.krontab.internal.createKronScheduler */ -fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler { +fun createSimpleScheduler( + incoming: KrontabTemplate +): KronScheduler { var offsetParsed: Int? = null var yearParsed: Array? = null val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also { @@ -81,22 +89,53 @@ fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler { val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource) val monthParsed = parseMonths(monthSource) - return createKronScheduler( - secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, offsetParsed + return offsetParsed ?.let { offset -> + createKronSchedulerWithOffset( + secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, TimezoneOffset(offset.minutes) + ) + } ?: createKronScheduler( + secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed ) } +fun createSimpleScheduler( + incoming: KrontabTemplate, + defaultOffset: Minutes +): KronSchedulerTz { + val scheduler = createSimpleScheduler(incoming) + return if (scheduler is KronSchedulerTz) { + scheduler + } else { + CronDateTimeSchedulerTz( + (scheduler as CronDateTimeScheduler).cronDateTimes, + TimezoneOffset(defaultOffset.minutes) + ) + } +} + /** * Shortcut for [createSimpleScheduler] */ fun buildSchedule(incoming: KrontabTemplate): KronScheduler = createSimpleScheduler(incoming) +/** + * Shortcut for [createSimpleScheduler] + */ +fun buildSchedule(incoming: KrontabTemplate, defaultOffset: Minutes): KronSchedulerTz = createSimpleScheduler(incoming, defaultOffset) /** * Shortcut for [buildSchedule] */ fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this) +/** + * Shortcut for [buildSchedule] + */ +fun KrontabTemplate.toSchedule(defaultOffset: Minutes): KronSchedulerTz = buildSchedule(this, defaultOffset) /** * Shortcut for [buildSchedule] */ -fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this) \ No newline at end of file +fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this) +/** + * Shortcut for [buildSchedule] + */ +fun KrontabTemplate.toKronScheduler(defaultOffset: Minutes): KronSchedulerTz = buildSchedule(this, defaultOffset) \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt index 2f477d0..84dcfec 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt @@ -1,9 +1,10 @@ package dev.inmo.krontab.builder -import com.soywiz.klock.TimeSpan -import com.soywiz.klock.TimezoneOffset +import com.soywiz.klock.* import dev.inmo.krontab.KronScheduler +import dev.inmo.krontab.KronSchedulerTz import dev.inmo.krontab.internal.createKronScheduler +import dev.inmo.krontab.internal.createKronSchedulerWithOffset import dev.inmo.krontab.utils.Minutes /** @@ -19,6 +20,22 @@ fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler { return builder.build() } +/** + * Will help to create an instance of [KronScheduler] + * + * @see dev.inmo.krontab.createSimpleScheduler + */ +fun buildSchedule( + offset: Minutes, + settingsBlock: SchedulerBuilder.() -> Unit +): KronSchedulerTz { + val builder = SchedulerBuilder(offset = offset) + + builder.settingsBlock() + + return builder.build() as KronSchedulerTz +} + class SchedulerBuilder( private var seconds: Array? = null, private var minutes: Array? = null, @@ -26,7 +43,7 @@ class SchedulerBuilder( private var dayOfMonth: Array? = null, private var month: Array? = null, private var year: Array? = null, - var offset: Minutes? = null + private val offset: Minutes? = null ) { private fun > callAndReturn( initial: Array?, @@ -110,32 +127,13 @@ class SchedulerBuilder( ) ?.toTypedArray() } - /** - * Setter of [offset] property - */ - fun offset(offset: Minutes?) { - this.offset = offset - } - - /** - * Setter of [offset] property - */ - fun offset(offset: TimeSpan?) { - this.offset = offset ?.minutes ?.toInt() - } - - /** - * Setter of [offset] property - */ - fun offset(offset: TimezoneOffset?) { - this.offset = offset ?.totalMinutesInt - } - /** * @return Completely built and independent [KronScheduler] * * @see dev.inmo.krontab.createSimpleScheduler * @see dev.inmo.krontab.internal.createKronScheduler */ - fun build(): KronScheduler = createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year) + fun build(): KronScheduler = offset ?.let { + createKronSchedulerWithOffset(seconds, minutes, hours, dayOfMonth, month, year, TimezoneOffset(it.minutes)) + } ?: createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt index 62fbf8c..a4ab265 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt @@ -12,7 +12,7 @@ import dev.inmo.krontab.internal.toNearDateTime */ data class CollectionKronScheduler internal constructor( internal val schedulers: MutableList -) : KronScheduler { +) : KronSchedulerTz { internal constructor() : this(mutableListOf()) /** @@ -51,6 +51,6 @@ data class CollectionKronScheduler internal constructor( } override suspend fun next(relatively: DateTimeTz): DateTimeTz { - return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively) + return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively.local).toOffsetUnadjusted(relatively.offset) } } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index 4b1d312..ae47a95 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -12,7 +12,6 @@ import dev.inmo.krontab.utils.Minutes * @param seconds 0-59 */ internal data class CronDateTime( - val offset: Int? = null, val year: Int? = null, val month: Byte? = null, val dayOfMonth: Byte? = null, @@ -30,7 +29,6 @@ internal data class CronDateTime( } internal val klockDayOfMonth = dayOfMonth ?.plus(1) - internal val klockOffset = offset ?.minutes ?.offset } /** @@ -82,35 +80,14 @@ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now() return current } -/** - * THIS METHOD WILL TAKE CARE ABOUT [offset] PARAMETER. It was decided due to the fact that we unable to get - * real timezone offset from simple [DateTime] - * - * @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo] - */ -internal fun CronDateTime.toNearDateTime( - relativelyTo: DateTimeTz -): DateTimeTz? { - val klockOffset = klockOffset - return if (klockOffset != null) { - toNearDateTime(relativelyTo.toOffset(klockOffset).local) ?.toOffsetUnadjusted(klockOffset) ?.toOffset(relativelyTo.offset) - } else { - toNearDateTime(relativelyTo.local) ?.toOffsetUnadjusted(relativelyTo.offset) - } -} - -/** - * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data - */ -internal fun createKronScheduler( +internal fun createCronDateTimeList( seconds: Array? = null, minutes: Array? = null, hours: Array? = null, dayOfMonth: Array? = null, month: Array? = null, - years: Array? = null, - offset: Minutes? = null -): KronScheduler { + years: Array? = null +): List { val resultCronDateTimes = mutableListOf(CronDateTime()) seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> @@ -141,9 +118,29 @@ internal fun createKronScheduler( previousCronDateTime.copy(year = currentTime) } - offset ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int -> - previousCronDateTime.copy(year = currentTime) - } - - return CronDateTimeScheduler(resultCronDateTimes.toList()) + return resultCronDateTimes.toList() } + +/** + * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data + */ +internal fun createKronScheduler( + seconds: Array? = null, + minutes: Array? = null, + hours: Array? = null, + dayOfMonth: Array? = null, + month: Array? = null, + years: Array? = null +): KronScheduler = CronDateTimeScheduler(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years)) +/** + * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data + */ +internal fun createKronSchedulerWithOffset( + seconds: Array? = null, + minutes: Array? = null, + hours: Array? = null, + dayOfMonth: Array? = null, + month: Array? = null, + years: Array? = null, + offset: TimezoneOffset +): KronScheduler = CronDateTimeSchedulerTz(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years), offset) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index ede3fca..f4ae554 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -1,7 +1,6 @@ package dev.inmo.krontab.internal -import com.soywiz.klock.DateTime -import com.soywiz.klock.DateTimeTz +import com.soywiz.klock.* import dev.inmo.krontab.* import dev.inmo.krontab.collection.plus @@ -30,10 +29,6 @@ internal data class CronDateTimeScheduler internal constructor( override suspend fun next(relatively: DateTime): DateTime { return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively) } - - override suspend fun next(relatively: DateTimeTz): DateTimeTz { - return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively) - } } internal fun mergeCronDateTimeSchedulers(schedulers: List) = CronDateTimeScheduler( diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt new file mode 100644 index 0000000..86c71c5 --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt @@ -0,0 +1,30 @@ +package dev.inmo.krontab.internal + +import com.soywiz.klock.* +import dev.inmo.krontab.* +import dev.inmo.krontab.collection.plus + +/** + * Cron-oriented realisation of [KronScheduler] with taking into account [offset] for list of [cronDateTimes] + * + * @see CronDateTime + */ +internal data class CronDateTimeSchedulerTz internal constructor( + internal val cronDateTimes: List, + internal val offset: TimezoneOffset +) : KronSchedulerTz { + override suspend fun next(relatively: DateTimeTz): DateTimeTz? { + val dateTimeWithActualOffset = relatively.utc.toOffset(offset).local + return cronDateTimes.mapNotNull { + it.toNearDateTime(dateTimeWithActualOffset) + }.minOrNull() ?.toOffset(relatively.offset) + } +} + +internal fun mergeCronDateTimeSchedulers( + schedulers: List +) = schedulers.groupBy { + it.offset +}.map { (offset, schedulers) -> + CronDateTimeSchedulerTz(schedulers.flatMap { it.cronDateTimes }, offset) +} diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt index 232cf13..ec4359b 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt @@ -6,8 +6,3 @@ internal val dayOfMonthRange = 0 .. 30 internal val hoursRange = 0 .. 23 internal val minutesRange = 0 .. 59 internal val secondsRange = minutesRange - -/** - * From 0 - 1439 minutes (1440 == 24 hours, that is the same as 0 in terms of timezones) - */ -internal val offsetRange = 0 until (hoursRange.count() * minutesRange.count()) diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt index fdc0146..4333187 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab.utils import com.soywiz.klock.DateTimeTz +import dev.inmo.krontab.KronSchedulerTz import dev.inmo.krontab.buildSchedule import dev.inmo.krontab.internal.offsetRange import kotlinx.coroutines.* @@ -84,9 +85,8 @@ class StringParseTest { runTest { for (i in offsetRange) { - val kronScheduler = buildSchedule("* * 10 * * ${i}o") + val kronScheduler = buildSchedule("* * 10 * * ${i}o") as KronSchedulerTz val next = kronScheduler.next(now) - } } } diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt index 0b3bc2b..481e86a 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt @@ -2,6 +2,7 @@ package dev.inmo.krontab.utils import com.soywiz.klock.* import dev.inmo.krontab.builder.buildSchedule +import dev.inmo.krontab.next import kotlin.test.Test import kotlin.test.assertEquals From 5c81eede9c49a167b3391f84e85ceff55c65f49c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 10 Apr 2021 00:33:05 +0600 Subject: [PATCH 07/19] add CronDateTimeSchedulerTz variant in CollectionKronScheduler --- .../krontab/collection/CollectionKronScheduler.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt index a4ab265..10d3b70 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt @@ -39,6 +39,18 @@ data class CollectionKronScheduler internal constructor( mergeCronDateTimeSchedulers(resultCronDateTimes) ) } + is CronDateTimeSchedulerTz -> { + val newCronDateTimes = kronScheduler.cronDateTimes.toMutableList() + val cronDateTimes = schedulers.removeAll { + if (it is CronDateTimeSchedulerTz && it.offset == kronScheduler.offset) { + newCronDateTimes.addAll(it.cronDateTimes) + true + } else { + false + } + } + schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.toList(), kronScheduler.offset)) + } is CollectionKronScheduler -> kronScheduler.schedulers.forEach { include(it) } From 3408d58cb76e304f6797446bbd21394126587454 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 10 Apr 2021 15:29:55 +0600 Subject: [PATCH 08/19] fixes for Tz --- .../kotlin/dev/inmo/krontab/KronScheduler.kt | 2 +- .../kotlin/dev/inmo/krontab/SchedulerShortcuts.kt | 1 - .../inmo/krontab/internal/CronDateTimeSchedulerTz.kt | 4 ++-- .../kotlin/dev/inmo/krontab/utils/StringParseTest.kt | 11 +++++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index 172536b..b7e214a 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -1,7 +1,6 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime -import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.internal.toNearDateTime /** @@ -13,6 +12,7 @@ import dev.inmo.krontab.internal.toNearDateTime * @see dev.inmo.krontab.internal.CronDateTimeScheduler */ interface KronScheduler { + /** * @return Next [DateTime] when some action must be triggered according to settings of this instance * diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index f313d40..ed641e6 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -1,7 +1,6 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime -import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.builder.buildSchedule import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.CronDateTime diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt index 86c71c5..c6f0e1f 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt @@ -14,10 +14,10 @@ internal data class CronDateTimeSchedulerTz internal constructor( internal val offset: TimezoneOffset ) : KronSchedulerTz { override suspend fun next(relatively: DateTimeTz): DateTimeTz? { - val dateTimeWithActualOffset = relatively.utc.toOffset(offset).local + val dateTimeWithActualOffset = relatively.toOffset(offset).local return cronDateTimes.mapNotNull { it.toNearDateTime(dateTimeWithActualOffset) - }.minOrNull() ?.toOffset(relatively.offset) + }.minOrNull() ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset) } } diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt index 4333187..108c278 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt @@ -1,9 +1,8 @@ package dev.inmo.krontab.utils -import com.soywiz.klock.DateTimeTz +import com.soywiz.klock.* import dev.inmo.krontab.KronSchedulerTz import dev.inmo.krontab.buildSchedule -import dev.inmo.krontab.internal.offsetRange import kotlinx.coroutines.* import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.takeWhile @@ -84,9 +83,13 @@ class StringParseTest { val now = DateTimeTz.nowLocal() runTest { - for (i in offsetRange) { - val kronScheduler = buildSchedule("* * 10 * * ${i}o") as KronSchedulerTz + for (i in 0 .. 1339) { + val expectedInCurrentOffset = now.toOffset(TimezoneOffset(i.minutes)) + 1.hours + val kronScheduler = buildSchedule( + "${expectedInCurrentOffset.seconds} ${expectedInCurrentOffset.minutes} ${expectedInCurrentOffset.hours} * * ${i}o" + ) as KronSchedulerTz val next = kronScheduler.next(now) + assertEquals(expectedInCurrentOffset.toOffset(now.offset), next) } } } From b0889d06778f24c35a94ce1a762412eb22de7079 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 10 Apr 2021 15:38:20 +0600 Subject: [PATCH 09/19] remove redundant changes --- src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt | 10 +++++----- .../dev/inmo/krontab/internal/CronDateTimeScheduler.kt | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index 287761f..a20bf93 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -70,14 +70,14 @@ fun createSimpleScheduler( it.getOrNull(5), it.getOrNull(6) ).forEach { - val parsedOffset = parseOffset(it) - offsetParsed = offsetParsed ?: parsedOffset + val offsetFromString = parseOffset(it) + offsetParsed = offsetParsed ?: offsetFromString when { - parsedOffset == null && yearParsed == null -> { + offsetFromString == null && yearParsed == null -> { yearParsed = parseYears(it) } - parsedOffset != null && offsetParsed == null -> { - offsetParsed = parsedOffset + offsetFromString != null && offsetParsed == null -> { + offsetParsed = offsetFromString } } } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index f4ae554..23de605 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -1,6 +1,6 @@ package dev.inmo.krontab.internal -import com.soywiz.klock.* +import com.soywiz.klock.DateTime import dev.inmo.krontab.* import dev.inmo.krontab.collection.plus From 0c7ceb03ee9c8fc57874fde2f773ac5abf283be0 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 20 Apr 2021 17:24:33 +0600 Subject: [PATCH 10/19] temporarily add nodejs crutch --- build.gradle | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/build.gradle b/build.gradle index 44f9d63..6ab9cc0 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,18 @@ plugins { id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version" id "org.jetbrains.dokka" version "$dokka_version" } + +// temporal crutch until legacy tests will be stabled or legacy target will be removed +allprojects { + if (it != rootProject.findProject("docs")) { + tasks.whenTaskAdded { task -> + if(task.name == "jsLegacyBrowserTest" || task.name == "jsLegacyNodeTest") { + task.enabled = false + } + } + } +} + apply plugin: "com.android.library" project.version = "$version" From bfa546ad1ffc28c22c653a11e41dad35afd14763 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 22 Apr 2021 11:58:19 +0600 Subject: [PATCH 11/19] add support of week days --- .../kotlin/dev/inmo/krontab/KronScheduler.kt | 1 - .../dev/inmo/krontab/KronSchedulersMerging.kt | 4 -- .../dev/inmo/krontab/SchedulerShortcuts.kt | 2 - .../kotlin/dev/inmo/krontab/StringParser.kt | 26 +++++++++---- .../inmo/krontab/builder/SchedulerBuilder.kt | 19 ++++++++-- .../dev/inmo/krontab/builder/TimeBuilder.kt | 12 +++--- .../collection/CollectionKronScheduler.kt | 2 - .../dev/inmo/krontab/internal/CronDateTime.kt | 37 +++++++++++++++---- .../krontab/internal/CronDateTimeScheduler.kt | 3 +- .../internal/CronDateTimeSchedulerTz.kt | 7 ++-- .../dev/inmo/krontab/internal/Parser.kt | 11 +++--- .../dev/inmo/krontab/internal/Ranges.kt | 1 + .../kotlin/dev/inmo/krontab/utils/Data.kt | 12 ------ 13 files changed, 83 insertions(+), 54 deletions(-) delete mode 100644 src/commonMain/kotlin/dev/inmo/krontab/utils/Data.kt diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt index b7e214a..5dc31c7 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronScheduler.kt @@ -1,7 +1,6 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime -import dev.inmo.krontab.internal.toNearDateTime /** * This interface was created for abstraction of [next] operation. Currently, there is only diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt index 3e27445..e246e6b 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt @@ -1,12 +1,8 @@ package dev.inmo.krontab -import com.soywiz.klock.TimezoneOffset import dev.inmo.krontab.collection.CollectionKronScheduler import dev.inmo.krontab.collection.includeAll import dev.inmo.krontab.internal.* -import dev.inmo.krontab.internal.CronDateTime -import dev.inmo.krontab.internal.CronDateTimeScheduler -import dev.inmo.krontab.internal.CronDateTimeSchedulerTz /** * Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator] diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index ed641e6..e05a29d 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -3,8 +3,6 @@ package dev.inmo.krontab import com.soywiz.klock.DateTime import dev.inmo.krontab.builder.buildSchedule import dev.inmo.krontab.internal.* -import dev.inmo.krontab.internal.CronDateTime -import dev.inmo.krontab.internal.CronDateTimeScheduler internal val anyCronDateTime by lazy { CronDateTime() diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index a20bf93..fef6714 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab -import com.soywiz.klock.* +import com.soywiz.klock.TimezoneOffset +import com.soywiz.klock.minutes import dev.inmo.krontab.internal.* import dev.inmo.krontab.utils.Minutes @@ -19,9 +20,10 @@ typealias KrontabTemplate = String * * dayOfMonth * * month * * (optional) year - * * (optional) (can be placed before year) offset + * * (optional) (can be placed anywhere after month) (must be marked with `o` at the end, for example: 60o == +01:00) offset + * * (optional) (can be placed anywhere after month) dayOfWeek * - * And each one have next format: + * And each one (except of offsets) have next format: * * `{number}[,{number},...]` or `*` * @@ -34,6 +36,9 @@ typealias KrontabTemplate = String * * F * * L * + * Week days must be marked with `w` at the end, and starts with 0 which means Sunday. For example, 0w == Sunday. With + * weeks you can use syntax like with any number like seconds, for example: 0-2w means Sunday-Tuesday + * * Additional info about ranges can be found in follow accordance: * * * Seconds ranges can be found in [secondsRange] @@ -42,7 +47,7 @@ typealias KrontabTemplate = String * * Days of month ranges can be found in [dayOfMonthRange] * * Months ranges can be found in [monthRange] * * Years ranges can be found in [yearRange] (in fact - any [Int]) - * * Offset (timezone) ranges can be found in [offsetRange] + * * WeekDay (timezone) ranges can be found in [dayOfWeekRange] * * Examples: * @@ -51,8 +56,10 @@ typealias KrontabTemplate = String * * "0/15 30 * * *" for every 15th seconds in a half of each hour * * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may * * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of fourth day of may with timezone UTC+01:00 + * * "1 2 3 F,4,L 5 60o 0-2w" for triggering in near first second of second minute of third hour of fourth day of may in case if it will be in Sunday-Tuesday week days with timezone UTC+01:00 * * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year * * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year with timezone UTC+01:00 + * * "1 2 3 F,4,L 5 2021 60o 0-2w" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00 * * @return In case when offset parameter is absent in [incoming] will be used [createSimpleScheduler] method and * returned [CronDateTimeScheduler]. In case when offset parameter there is in [incoming] [KrontabTemplate] will be used @@ -64,15 +71,20 @@ fun createSimpleScheduler( incoming: KrontabTemplate ): KronScheduler { var offsetParsed: Int? = null + var dayOfWeekParsed: Array? = null var yearParsed: Array? = null val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also { listOfNotNull( it.getOrNull(5), - it.getOrNull(6) + it.getOrNull(6), + it.getOrNull(7) ).forEach { val offsetFromString = parseOffset(it) + val dayOfWeekFromString = parseWeekDay(it) offsetParsed = offsetParsed ?: offsetFromString + dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString when { + dayOfWeekFromString != null -> return@forEach offsetFromString == null && yearParsed == null -> { yearParsed = parseYears(it) } @@ -91,10 +103,10 @@ fun createSimpleScheduler( return offsetParsed ?.let { offset -> createKronSchedulerWithOffset( - secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, TimezoneOffset(offset.minutes) + secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, dayOfWeekParsed, TimezoneOffset(offset.minutes) ) } ?: createKronScheduler( - secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed + secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, dayOfWeekParsed ) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt index 84dcfec..768a127 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/builder/SchedulerBuilder.kt @@ -1,6 +1,7 @@ package dev.inmo.krontab.builder -import com.soywiz.klock.* +import com.soywiz.klock.TimezoneOffset +import com.soywiz.klock.minutes import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.KronSchedulerTz import dev.inmo.krontab.internal.createKronScheduler @@ -43,6 +44,7 @@ class SchedulerBuilder( private var dayOfMonth: Array? = null, private var month: Array? = null, private var year: Array? = null, + private var dayOfWeek: Array? = null, private val offset: Minutes? = null ) { private fun > callAndReturn( @@ -105,6 +107,17 @@ class SchedulerBuilder( ) ?.toTypedArray() } + /** + * Starts an hours block + */ + fun dayOfWeek(block: WeekDaysBuilder.() -> Unit) { + dayOfWeek = callAndReturn( + dayOfWeek, + WeekDaysBuilder(), + block + ) ?.toTypedArray() + } + /** * Starts an months block */ @@ -134,6 +147,6 @@ class SchedulerBuilder( * @see dev.inmo.krontab.internal.createKronScheduler */ fun build(): KronScheduler = offset ?.let { - createKronSchedulerWithOffset(seconds, minutes, hours, dayOfMonth, month, year, TimezoneOffset(it.minutes)) - } ?: createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year) + createKronSchedulerWithOffset(seconds, minutes, hours, dayOfMonth, month, year, dayOfWeek, TimezoneOffset(it.minutes)) + } ?: createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year, dayOfWeek) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt b/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt index c4cc8a4..ad21c6d 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/builder/TimeBuilder.kt @@ -1,11 +1,10 @@ package dev.inmo.krontab.builder import dev.inmo.krontab.internal.* -import dev.inmo.krontab.utils.clamp /** * This class was created for incapsulation of builder work with specified [restrictionsRange]. For example, - * [include] function of [TimeBuilder] will always [clamp] incoming data using its [restrictionsRange] + * [include] function of [TimeBuilder] will always [coerceIn] incoming data using its [restrictionsRange] */ sealed class TimeBuilder ( private val restrictionsRange: IntRange, @@ -37,7 +36,7 @@ sealed class TimeBuilder ( */ @Suppress("MemberVisibilityCanBePrivate") infix fun include(array: Array) { - val clamped = array.map { it.clamp(restrictionsRange) } + (result ?: emptySet()) + val clamped = array.map { it.coerceIn(restrictionsRange) } + (result ?: emptySet()) result = clamped.toSet() } @@ -46,7 +45,7 @@ sealed class TimeBuilder ( */ @Suppress("unused") infix fun at(value: Int) { - result = (result ?: emptySet()) + value.clamp(restrictionsRange) + result = (result ?: emptySet()) + value.coerceIn(restrictionsRange) } @@ -70,7 +69,7 @@ sealed class TimeBuilder ( * @see [from] */ infix fun Int.every(delay: Int): Array { - val progression = clamp(restrictionsRange) .. restrictionsRange.last step delay + val progression = coerceIn(restrictionsRange) .. restrictionsRange.last step delay val result = progression.toSet().toTypedArray() this@TimeBuilder include result @@ -88,7 +87,7 @@ sealed class TimeBuilder ( */ @Suppress("MemberVisibilityCanBePrivate") infix fun Int.upTo(endIncluding: Int): Array { - val progression = clamp(restrictionsRange) .. endIncluding.clamp(restrictionsRange) + val progression = coerceIn(restrictionsRange) .. endIncluding.coerceIn(restrictionsRange) val result = progression.toSet().toTypedArray() this@TimeBuilder include result @@ -129,3 +128,4 @@ class HoursBuilder : TimeBuilder(hoursRange, intToByteConverter) class DaysOfMonthBuilder : TimeBuilder(dayOfMonthRange, intToByteConverter) class MonthsBuilder : TimeBuilder(monthRange, intToByteConverter) class YearsBuilder : TimeBuilder(yearRange, intToIntConverter) +class WeekDaysBuilder : TimeBuilder(dayOfWeekRange, intToByteConverter) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt index 10d3b70..97fe0c3 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt @@ -4,8 +4,6 @@ import com.soywiz.klock.DateTime import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.* import dev.inmo.krontab.internal.* -import dev.inmo.krontab.internal.CronDateTimeScheduler -import dev.inmo.krontab.internal.toNearDateTime /** * This scheduler will be useful in case you want to unite several different [KronScheduler]s diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index ae47a95..cc3667f 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -2,9 +2,10 @@ package dev.inmo.krontab.internal import com.soywiz.klock.* import dev.inmo.krontab.KronScheduler -import dev.inmo.krontab.utils.Minutes /** + * @param dayOfweek 0-6 + * @param year any int * @param month 0-11 * @param dayOfMonth 0-31 * @param hours 0-23 @@ -12,6 +13,7 @@ import dev.inmo.krontab.utils.Minutes * @param seconds 0-59 */ internal data class CronDateTime( + val dayOfweek: Byte? = null, val year: Int? = null, val month: Byte? = null, val dayOfMonth: Byte? = null, @@ -20,6 +22,7 @@ internal data class CronDateTime( val seconds: Byte? = null ) { init { + check(dayOfweek ?.let { it in dayOfWeekRange } ?: true) check(year ?.let { it in yearRange } ?: true) check(month ?.let { it in monthRange } ?: true) check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true) @@ -29,6 +32,7 @@ internal data class CronDateTime( } internal val klockDayOfMonth = dayOfMonth ?.plus(1) + internal val dayOfWeekInt: Int? = dayOfweek ?.toInt() } /** @@ -40,6 +44,22 @@ internal data class CronDateTime( internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? { var current = relativelyTo + val weekDay = dayOfWeekInt + if (weekDay != null && current.dayOfWeek.index0 != weekDay) { + do { + var diff = weekDay - current.dayOfWeek.index0 + if (diff < 0) { + diff += 7 /* days in week */ + } + current = (current + diff.days).startOfDay + + val next = toNearDateTime(current) + if (next ?.dayOfWeek ?.index0 == weekDay) { + return next + } + } while (true) + } + seconds?.let { val left = it - current.seconds current += DateTimeSpan(minutes = if (left <= 0) 1 else 0, seconds = left) @@ -86,7 +106,8 @@ internal fun createCronDateTimeList( hours: Array? = null, dayOfMonth: Array? = null, month: Array? = null, - years: Array? = null + years: Array? = null, + weekDays: Array? = null ): List { val resultCronDateTimes = mutableListOf(CronDateTime()) @@ -114,8 +135,8 @@ internal fun createCronDateTimeList( previousCronDateTime.copy(year = currentTime) } - years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int -> - previousCronDateTime.copy(year = currentTime) + weekDays ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> + previousCronDateTime.copy(dayOfweek = currentTime) } return resultCronDateTimes.toList() @@ -130,8 +151,9 @@ internal fun createKronScheduler( hours: Array? = null, dayOfMonth: Array? = null, month: Array? = null, - years: Array? = null -): KronScheduler = CronDateTimeScheduler(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years)) + years: Array? = null, + weekDays: Array? = null +): KronScheduler = CronDateTimeScheduler(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years, weekDays)) /** * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data */ @@ -142,5 +164,6 @@ internal fun createKronSchedulerWithOffset( dayOfMonth: Array? = null, month: Array? = null, years: Array? = null, + weekDays: Array? = null, offset: TimezoneOffset -): KronScheduler = CronDateTimeSchedulerTz(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years), offset) +): KronScheduler = CronDateTimeSchedulerTz(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years, weekDays), offset) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index 23de605..751c179 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -1,8 +1,9 @@ package dev.inmo.krontab.internal import com.soywiz.klock.DateTime -import dev.inmo.krontab.* +import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.collection.plus +import dev.inmo.krontab.getAnyNext /** * Cron-oriented realisation of [KronScheduler] diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt index c6f0e1f..adf74d4 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt @@ -1,8 +1,9 @@ package dev.inmo.krontab.internal -import com.soywiz.klock.* -import dev.inmo.krontab.* -import dev.inmo.krontab.collection.plus +import com.soywiz.klock.DateTimeTz +import com.soywiz.klock.TimezoneOffset +import dev.inmo.krontab.KronScheduler +import dev.inmo.krontab.KronSchedulerTz /** * Cron-oriented realisation of [KronScheduler] with taking into account [offset] for list of [cronDateTimes] diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt index 871a60f..17c9be2 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt @@ -1,7 +1,5 @@ package dev.inmo.krontab.internal -import dev.inmo.krontab.utils.clamp - typealias Converter = (Int) -> T internal val intToByteConverter: Converter = { it: Int -> it.toByte() } @@ -18,7 +16,7 @@ private fun createSimpleScheduler(from: String, dataRange: IntRange, dataCon when { currentToken.contains("-") -> { val splitted = currentToken.split("-") - (splitted.first().toInt().clamp(dataRange) .. splitted[1].toInt().clamp(dataRange)).toList() + (splitted.first().toInt().coerceIn(dataRange) .. splitted[1].toInt().coerceIn(dataRange)).toList() } currentToken.contains("/") -> { val (start, step) = currentToken.split("/") @@ -26,18 +24,19 @@ private fun createSimpleScheduler(from: String, dataRange: IntRange, dataCon 0 } else { start.toInt() - }).clamp(dataRange) - val stepNum = step.toInt().clamp(dataRange) + }).coerceIn(dataRange) + val stepNum = step.toInt().coerceIn(dataRange) (startNum .. dataRange.last step stepNum).map { it } } currentToken == "*" -> return null - else -> listOf(currentToken.toInt().clamp(dataRange)) + else -> listOf(currentToken.toInt().coerceIn(dataRange)) } } return results.map(dataConverter) } +internal fun parseWeekDay(from: String?) = from ?.let { if (it.endsWith("w")) createSimpleScheduler(it.removeSuffix("w"), dayOfWeekRange, intToByteConverter) ?.toTypedArray() else null } internal fun parseOffset(from: String?) = from ?.let { if (it.endsWith("o")) it.removeSuffix("o").toIntOrNull() else null } internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() } internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray() diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt index ec4359b..e0a03b7 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Ranges.kt @@ -1,5 +1,6 @@ package dev.inmo.krontab.internal +internal val dayOfWeekRange = 0 .. 6 internal val yearRange = Int.MIN_VALUE .. Int.MAX_VALUE internal val monthRange = 0 .. 11 internal val dayOfMonthRange = 0 .. 30 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/Data.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/Data.kt deleted file mode 100644 index f442496..0000000 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/Data.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.inmo.krontab.utils - -/** - * @return [min] in case if [this] less than [min]. Otherwise will check that [max] grant than [this] and return [this] - * if so or [max] otherwise - */ -internal fun Int.clamp(min: Int, max: Int): Int = if (this < min) min else if (this > max) max else this - -/** - * Wrapper function for [clamp] extension - */ -internal fun Int.clamp(range: IntRange): Int = clamp(range.first, range.last) From 86cdda51a1f7562db6e0926e0f487c97999c18e7 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 22 Apr 2021 12:24:26 +0600 Subject: [PATCH 12/19] tests and fixes in week work --- .../dev/inmo/krontab/internal/CronDateTime.kt | 2 +- .../krontab/internal/CronDateTimeScheduler.kt | 4 +- .../dev/inmo/krontab/utils/WeekDaysTest.kt | 38 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/commonTest/kotlin/dev/inmo/krontab/utils/WeekDaysTest.kt diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index cc3667f..49cf462 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -54,7 +54,7 @@ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now() current = (current + diff.days).startOfDay val next = toNearDateTime(current) - if (next ?.dayOfWeek ?.index0 == weekDay) { + if (next == null || next.dayOfWeek.index0 == weekDay) { return next } } while (true) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index 751c179..d522dd4 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -27,8 +27,8 @@ internal data class CronDateTimeScheduler internal constructor( * * @see toNearDateTime */ - override suspend fun next(relatively: DateTime): DateTime { - return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively) + override suspend fun next(relatively: DateTime): DateTime? { + return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() } } diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/WeekDaysTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/WeekDaysTest.kt new file mode 100644 index 0000000..9b8d8b7 --- /dev/null +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/WeekDaysTest.kt @@ -0,0 +1,38 @@ +package dev.inmo.krontab.utils + +import com.soywiz.klock.* +import dev.inmo.krontab.builder.buildSchedule +import kotlin.math.ceil +import kotlin.test.* + +class WeekDaysTest { + @Test + fun testThatWeekDaysSchedulingWorks() { + val startDateTime = DateTime.now().startOfDay + val weekDay = startDateTime.dayOfWeek.index0 + val testDays = 400 + val scheduler = buildSchedule { + dayOfWeek { + at(weekDay) + } + years { + at(startDateTime.yearInt) + } + } + runTest { + for (day in 0 until testDays) { + val currentDateTime = startDateTime + day.days + val next = scheduler.next(currentDateTime) + val expected = when { + day % 7 == 0 -> currentDateTime + else -> startDateTime + ceil(day.toFloat() / 7).weeks + } + if (expected.yearInt != startDateTime.yearInt) { + assertNull(next) + } else { + assertEquals(expected, next) + } + } + } + } +} From 20f12f3f2d03f75a09f2a743ca2fc6268b2cf932 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 22 Apr 2021 12:32:45 +0600 Subject: [PATCH 13/19] update readme to include week days info --- README.md | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 376cb2e..5edc2bb 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ runtime of applications. | [ How to use: Config from string ](#config-from-string) | | [ How to use: Config via builder (DSL preview) ](#config-via-builder) | | [ How to use: KronScheduler as a Flow ](#KronScheduler-as-a-Flow) | +| [ Note about week days ](#Note-about-week-days) | ## How to use @@ -52,14 +53,17 @@ For old version of Gradle, instead of `implementation` word developers must use Developers can use more simple way to configure repeat times is string. String configuring like a `crontab`, but with a little bit different meanings: + ``` -/---------- Seconds -| /-------- Minutes -| | /------ Hours -| | | /---- Days of months -| | | | /-- Months -| | | | | / (optional) Year -* * * * * * +/--------------- Seconds +| /------------- Minutes +| | /----------- Hours +| | | /--------- Days of months +| | | | /------- Months +| | | | | /----- (optional) Year +| | | | | | /--- (optional) Timezone offset +| | | | | | | / (optional) Week days +* * * * * * 0o *w ``` It is different with original `crontab` syntax for the reason, that expected that in practice developers @@ -152,3 +156,12 @@ flow.takeWhile { action() } ``` + +### Note about week days + +Unlike original CRON, here week days: + +* Works as `AND`: cron date time will search first day which will pass requirement according all parameters including +week days +* You may use any related to numbers syntax with week days: `0-3w`, `0,1,2,3w`, etc. +* Week days (like years and offsets) are optional and can be placed anywhere after `month` From da88d18d4d4fa3a414e8d2dfd625b1c6c7c2f27a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 22 Apr 2021 12:44:22 +0600 Subject: [PATCH 14/19] add note about offsets --- README.md | 18 +++++++++++++++++- .../kotlin/dev/inmo/krontab/KronSchedulerTz.kt | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5edc2bb..ee5a487 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ runtime of applications. | [ How to use: Config from string ](#config-from-string) | | [ How to use: Config via builder (DSL preview) ](#config-via-builder) | | [ How to use: KronScheduler as a Flow ](#KronScheduler-as-a-Flow) | -| [ Note about week days ](#Note-about-week-days) | +| [ How to use: Offsets ](#Offsets) | +| [ How to use: Note about week days ](#Note-about-week-days) | ## How to use @@ -157,6 +158,21 @@ flow.takeWhile { } ``` +### Offsets + +Offsets in this library works via passing parameter ending with `o` in any place after `month` config. Currently +there is only one format supported for offsets: minutes of offsets. To use time zones you will need to call `next` +method with `DateTimeTz` argument or `nextTimeZoned` method with any `KronScheduler` instance, but in case if this +scheduler is not instance of `KronSchedulerTz` it will works like you passed just `DateTime`. + +Besides, in case you wish to use time zones explicitly, you will need to get `KronSchedulerTz`. It is possible by: + +* Using `createSimpleScheduler`/`buildSchedule`/`KrontabTemplate#toSchedule`/`KrontabTemplate#toKronScheduler` methods +with passing `defaultOffset` parameter +* Using `SchedulerBuilder#build`/`createSimpleScheduler`/`buildSchedule`/`KrontabTemplate#toSchedule`/`KrontabTemplate#toKronScheduler` +methods with casting to `KronSchedulerTz` in case you are pretty sure that it is timezoned `KronScheduler` +* Creating your own implementation of `KronSchedulerTz` + ### Note about week days Unlike original CRON, here week days: diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt index 65d812f..16867bb 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt @@ -29,4 +29,6 @@ suspend fun KronScheduler.next(relatively: DateTimeTz) = if (this is KronSchedul this.next(relatively) } else { this.next(relatively.local) ?.toOffsetUnadjusted(relatively.offset) -} \ No newline at end of file +} + +suspend fun KronScheduler.nextTimeZoned() = next(DateTime.now().local) \ No newline at end of file From 635a2d6a44a1cf9bd51d99221acf56f93c94b223 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 24 Apr 2021 01:47:29 +0600 Subject: [PATCH 15/19] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d6a029..fe9005d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ * Versions * `Kotlin`: `1.4.31` -> `1.4.32` +* Supporting of weekdays +* Supporting of timezones + * Any `KronScheduler` now can be used for calling `next` with `DateTimeTz` ## 0.5.1 From db1af7818e86f3ceb9cbfac9b1fa9dcbf614cd1f Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 24 Apr 2021 01:53:57 +0600 Subject: [PATCH 16/19] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe9005d..5388174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Supporting of weekdays * Supporting of timezones * Any `KronScheduler` now can be used for calling `next` with `DateTimeTz` + * New type `KronSchedulerTz` ## 0.5.1 From 7e58ec08739c3523add496c92829cd4c3f3de22b Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 24 Apr 2021 16:58:25 +0600 Subject: [PATCH 17/19] fixes in flow according to time zones --- CHANGELOG.md | 2 ++ .../dev/inmo/krontab/utils/SchedulerFlow.kt | 31 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5388174..fce6dba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * Supporting of timezones * Any `KronScheduler` now can be used for calling `next` with `DateTimeTz` * New type `KronSchedulerTz` +* `SchedulerFlow` has been deprecated +* New extension `asTzFlow` and small changes in `asFlow` logic ## 0.5.1 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt index 14cc761..81738f4 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt @@ -1,14 +1,39 @@ package dev.inmo.krontab.utils import com.soywiz.klock.DateTime +import com.soywiz.klock.DateTimeTz import dev.inmo.krontab.KronScheduler -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.delay +import dev.inmo.krontab.next +import kotlinx.coroutines.* import kotlinx.coroutines.flow.* +/** + * This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to + * time zones + * + * @see channelFlow + */ @FlowPreview -fun KronScheduler.asFlow(): Flow = SchedulerFlow(this) +fun KronScheduler.asTzFlow(): Flow = channelFlow { + while (isActive) { + val now = DateTime.now().local + val nextTime = next(now) ?: break + val sleepDelay = (nextTime - DateTime.now().local).millisecondsLong + delay(sleepDelay) + send(nextTime) + } +} +/** + * This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s + */ +@FlowPreview +fun KronScheduler.asFlow(): Flow = asTzFlow().map { it.local } + +@Deprecated( + "It is not recommended to use this class in future. This functionality will be removed soon", + ReplaceWith("asFlow", "dev.inmo.krontab.utils.asFlow") +) @FlowPreview class SchedulerFlow( private val scheduler: KronScheduler From 301afb1ee0f6c9b8b680164b95662a423295501c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 24 Apr 2021 18:33:37 +0600 Subject: [PATCH 18/19] updates and fixes --- CHANGELOG.md | 1 + .../kotlin/dev/inmo/krontab/StringParser.kt | 9 +++------ .../dev/inmo/krontab/utils/TimeZoneTest.kt | 17 ++++++++++------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fce6dba..24fe066 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * New type `KronSchedulerTz` * `SchedulerFlow` has been deprecated * New extension `asTzFlow` and small changes in `asFlow` logic +* `merge` extensions now return `CollectionKronScheduler` instead of just `KronScheduler` ## 0.5.1 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index fef6714..b9f29ad 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -84,13 +84,10 @@ fun createSimpleScheduler( offsetParsed = offsetParsed ?: offsetFromString dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString when { - dayOfWeekFromString != null -> return@forEach - offsetFromString == null && yearParsed == null -> { + dayOfWeekFromString != null || offsetFromString != null -> return@forEach + yearParsed == null -> { yearParsed = parseYears(it) } - offsetFromString != null && offsetParsed == null -> { - offsetParsed = offsetFromString - } } } } @@ -150,4 +147,4 @@ fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this) /** * Shortcut for [buildSchedule] */ -fun KrontabTemplate.toKronScheduler(defaultOffset: Minutes): KronSchedulerTz = buildSchedule(this, defaultOffset) \ No newline at end of file +fun KrontabTemplate.toKronScheduler(defaultOffset: Minutes): KronSchedulerTz = buildSchedule(this, defaultOffset) diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt index 481e86a..4663174 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt @@ -10,14 +10,17 @@ class TimeZoneTest { @Test fun testDifferentTimeZonesReturnsDifferentTimes() { val scheduler = buildSchedule { seconds { every(1) } } - val now = DateTime.now() + val baseDate = DateTime.now().startOfWeek runTest { - for (i in 0 .. 24) { - val nowTz = now.toOffset(i.hours) - val next = scheduler.next(nowTz)!! - assertEquals( - (nowTz + 1.seconds).utc.unixMillisLong, next.utc.unixMillisLong - ) + for (i in 0 until 7) { + val now = baseDate + i.days + for (j in 0 .. 24) { + val nowTz = now.toOffset(j.hours) + val next = scheduler.next(nowTz)!! + assertEquals( + (nowTz + 1.seconds).utc.unixMillisLong, next.utc.unixMillisLong + ) + } } } } From c6bfc5c8f8f85d58896aa2cd421df219987866de Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 24 Apr 2021 18:33:59 +0600 Subject: [PATCH 19/19] optimize imports before merge --- .../kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index d522dd4..b4b3bcf 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -3,7 +3,6 @@ package dev.inmo.krontab.internal import com.soywiz.klock.DateTime import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.collection.plus -import dev.inmo.krontab.getAnyNext /** * Cron-oriented realisation of [KronScheduler]