From 3de66a72742048eb9ee7feeb499671ffb920de1c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 29 May 2021 17:25:40 +0600 Subject: [PATCH 1/9] start 0.6.1 --- CHANGELOG.md | 2 ++ gradle.properties | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bde7df..489b4e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.6.1 + ## 0.6.0 * Versions diff --git a/gradle.properties b/gradle.properties index d68cd69..b500d35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,6 +33,6 @@ androidx_work_version=2.5.0 ## Common -version=0.6.0 -android_code_version=4 +version=0.6.1 +android_code_version=5 From c509d35b0042d52bebc90e9d7b68cbeedf885743 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 29 May 2021 17:30:28 +0600 Subject: [PATCH 2/9] update klock --- CHANGELOG.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 489b4e2..ff0f03e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.6.1 +* Versions + * `Klock`: `2.1.0` -> `2.1.2` + ## 0.6.0 * Versions diff --git a/gradle.properties b/gradle.properties index b500d35..22a107f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ kotlin_coroutines_version=1.5.0 dokka_version=1.4.32 -klockVersion=2.1.0 +klockVersion=2.1.2 ## Github reease From 22ef00fe8fbbc5f1acf05f8639860b2c36cbe3d8 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 30 May 2021 09:00:57 +0600 Subject: [PATCH 3/9] Update publishing_packages.yml --- .github/workflows/publishing_packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publishing_packages.yml b/.github/workflows/publishing_packages.yml index 9af32ba..df9d39f 100644 --- a/.github/workflows/publishing_packages.yml +++ b/.github/workflows/publishing_packages.yml @@ -16,7 +16,7 @@ jobs: - name: prebuild run: ./gradlew clean build - name: Publish package - run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signMetadataPublication -x signAndroidDebugPublication -x signAndroidReleasePublication + run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signMetadataPublication -x signAndroidReleasePublication env: GITHUBPACKAGES_USER: ${{ github.actor }} GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} From 28f84d4e3abff339383147ff8ac1931c155183b6 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 30 May 2021 17:15:32 +0600 Subject: [PATCH 4/9] rewrite mechanism of calculating of near time --- CHANGELOG.md | 4 +- .../dev/inmo/krontab/KronSchedulersMerging.kt | 4 +- .../dev/inmo/krontab/SchedulerShortcuts.kt | 4 +- .../kotlin/dev/inmo/krontab/StringParser.kt | 2 +- .../collection/CollectionKronScheduler.kt | 8 +- .../dev/inmo/krontab/internal/CronDateTime.kt | 265 ++++++++++-------- .../krontab/internal/CronDateTimeScheduler.kt | 10 +- .../internal/CronDateTimeSchedulerTz.kt | 11 +- .../internal/NearDateTimeCalculator.kt | 171 +++++++++++ .../dev/inmo/krontab/internal/Parser.kt | 2 +- .../dev/inmo/krontab/utils/DateTimeCopy.kt | 22 ++ .../dev/inmo/krontab/utils/TimeZoneTest.kt | 7 +- 12 files changed, 371 insertions(+), 139 deletions(-) create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt create mode 100644 src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0f03e..272ce4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ ## 0.6.1 * Versions - * `Klock`: `2.1.0` -> `2.1.2` + * `Klock`: `2.1.0` -> `2.1.2 + +` ## 0.6.0 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt index e246e6b..b62b712 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/KronSchedulersMerging.kt @@ -16,13 +16,13 @@ fun Iterator.merge(): CollectionKronScheduler { val collectionScheduler = CollectionKronScheduler() forEach { when (it) { - is CronDateTimeScheduler -> cronDateTimes.addAll(it.cronDateTimes) + is CronDateTimeScheduler -> cronDateTimes.add(it.cronDateTime) is CronDateTimeSchedulerTz -> timezonedCronDateTimes.add(it) else -> collectionScheduler.include(it) } } if (cronDateTimes.isNotEmpty()) { - collectionScheduler.include(CronDateTimeScheduler(cronDateTimes)) + collectionScheduler.include(CronDateTimeScheduler(cronDateTimes.merge())) } if (timezonedCronDateTimes.isNotEmpty()) { collectionScheduler.includeAll(mergeCronDateTimeSchedulers(timezonedCronDateTimes)) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt index e05a29d..d351b61 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/SchedulerShortcuts.kt @@ -13,7 +13,7 @@ internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(r * [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] */ val AnyTimeScheduler: KronScheduler by lazy { - CronDateTimeScheduler(listOf(anyCronDateTime)) + CronDateTimeScheduler(anyCronDateTime) } /** @@ -56,4 +56,4 @@ val EveryMonthScheduler: KronScheduler by lazy { */ val EveryYearScheduler: KronScheduler by lazy { buildSchedule { years { 0 every 1 } } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt index b9f29ad..e2a1b99 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/StringParser.kt @@ -116,7 +116,7 @@ fun createSimpleScheduler( scheduler } else { CronDateTimeSchedulerTz( - (scheduler as CronDateTimeScheduler).cronDateTimes, + (scheduler as CronDateTimeScheduler).cronDateTime, TimezoneOffset(defaultOffset.minutes) ) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt index 97fe0c3..64fc73a 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/collection/CollectionKronScheduler.kt @@ -38,16 +38,16 @@ data class CollectionKronScheduler internal constructor( ) } is CronDateTimeSchedulerTz -> { - val newCronDateTimes = kronScheduler.cronDateTimes.toMutableList() - val cronDateTimes = schedulers.removeAll { + val newCronDateTimes = mutableListOf(kronScheduler.cronDateTime) + schedulers.removeAll { if (it is CronDateTimeSchedulerTz && it.offset == kronScheduler.offset) { - newCronDateTimes.addAll(it.cronDateTimes) + newCronDateTimes.add(it.cronDateTime) true } else { false } } - schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.toList(), kronScheduler.offset)) + schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.merge(), kronScheduler.offset)) } is CollectionKronScheduler -> kronScheduler.schedulers.forEach { include(it) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index 49cf462..802e765 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -4,103 +4,125 @@ import com.soywiz.klock.* import dev.inmo.krontab.KronScheduler /** - * @param dayOfweek 0-6 - * @param year any int - * @param month 0-11 - * @param dayOfMonth 0-31 + * @param daysOfWeek 0-6 + * @param years any int + * @param months 0-11 + * @param daysOfMonth 0-31 * @param hours 0-23 * @param minutes 0-59 * @param seconds 0-59 */ internal data class CronDateTime( - val dayOfweek: Byte? = null, - val year: Int? = null, - val month: Byte? = null, - val dayOfMonth: Byte? = null, - val hours: Byte? = null, - val minutes: Byte? = null, - val seconds: Byte? = null + val daysOfWeek: Array? = null, + val years: Array? = null, + val months: Array? = null, + val daysOfMonth: Array? = null, + val hours: Array? = null, + val minutes: Array? = null, + val seconds: Array? = 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) - check(hours?.let { it in hoursRange } ?: true) - check(minutes?.let { it in minutesRange } ?: true) - check(seconds?.let { it in secondsRange } ?: true) + check(daysOfWeek ?.all { it in dayOfWeekRange } ?: true) + check(years?.all { it in yearRange } ?: true) + check(months?.all { it in monthRange } ?: true) + check(daysOfMonth ?.all { it in dayOfMonthRange } ?: true) + check(hours?.all { it in hoursRange } ?: true) + check(minutes?.all { it in minutesRange } ?: true) + check(seconds?.all { it in secondsRange } ?: true) } - internal val klockDayOfMonth = dayOfMonth ?.plus(1) - internal val dayOfWeekInt: Int? = dayOfweek ?.toInt() -} + internal val calculators = listOf( + NearDateTimeCalculatorMillis(arrayOf(0)), + seconds ?.let { NearDateTimeCalculatorSeconds(it) }, + minutes ?.let { NearDateTimeCalculatorMinutes(it) }, + hours ?.let { NearDateTimeCalculatorHours(it) }, + daysOfMonth ?.let { NearDateTimeCalculatorDays(it) }, + months ?.let { NearDateTimeCalculatorMonths(it) }, + years ?.let { NearDateTimeCalculatorYears(it) }, + daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) }, + ) -/** - * 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? { - 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 */ + internal fun toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? { + var current = relativelyTo + whileLoop@while (true) { + for (calculator in calculators) { + val (calculated, requireRecalculation) = (calculator ?: continue).calculateNearTime(current) ?: return null + current = calculated + if (requireRecalculation) { + continue@whileLoop + } } - current = (current + diff.days).startOfDay - - val next = toNearDateTime(current) - if (next == null || 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) - } - - minutes?.let { - val left = it - current.minutes - current += DateTimeSpan(hours = if (left < 0) 1 else 0, minutes = left) - } - - hours?.let { - val left = it - current.hours - current += DateTimeSpan(days = if (left < 0) 1 else 0, hours = left) - } - - klockDayOfMonth ?.let { - val left = (it - current.dayOfMonth).let { diff -> - if (diff > 0 && current.endOfMonth.run { it > dayOfMonth && current.dayOfMonth == dayOfMonth }) { - 0 - } else { - diff - } - } - current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left) - } - - month ?.let { - val left = it - current.month0 - current += DateTimeSpan(years = if (left < 0) 1 else 0, months = left) - } - - year ?.let { - if (current.yearInt != it) { - return null + return current } } - - return current } +// +///** +// * 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? { +// 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 == null || 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) +// } +// +// minutes?.let { +// val left = it - current.minutes +// current += DateTimeSpan(hours = if (left < 0) 1 else 0, minutes = left) +// } +// +// hours?.let { +// val left = it - current.hours +// current += DateTimeSpan(days = if (left < 0) 1 else 0, hours = left) +// } +// +// klockDayOfMonth ?.let { +// val left = (it - current.dayOfMonth).let { diff -> +// if (diff > 0 && current.endOfMonth.run { it > dayOfMonth && current.dayOfMonth == dayOfMonth }) { +// 0 +// } else { +// diff +// } +// } +// current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left) +// } +// +// months?.let { +// val left = it - current.month0 +// current += DateTimeSpan(years = if (left < 0) 1 else 0, months = left) +// } +// +// years?.let { +// if (current.yearInt != it) { +// return null +// } +// } +// +// return current +//} -internal fun createCronDateTimeList( +internal fun createCronDateTime( seconds: Array? = null, minutes: Array? = null, hours: Array? = null, @@ -108,38 +130,39 @@ internal fun createCronDateTimeList( month: Array? = null, years: Array? = null, weekDays: Array? = null -): List { - val resultCronDateTimes = mutableListOf(CronDateTime()) - - seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> - previousCronDateTime.copy(seconds = currentTime) - } - - minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> - previousCronDateTime.copy(minutes = currentTime) - } - - hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> - previousCronDateTime.copy(hours = currentTime) - } - - dayOfMonth ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> - previousCronDateTime.copy(dayOfMonth = currentTime) - } - - month ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> - previousCronDateTime.copy(month = 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() +): CronDateTime { + return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds) +// val resultCronDateTimes = mutableListOf(CronDateTime()) +// +// seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> +// previousCronDateTime.copy(seconds = currentTime) +// } +// +// minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> +// previousCronDateTime.copy(minutes = currentTime) +// } +// +// hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> +// previousCronDateTime.copy(hours = currentTime) +// } +// +// dayOfMonth ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> +// previousCronDateTime.copy(daysOfMonth = currentTime) +// } +// +// month ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> +// previousCronDateTime.copy(months = currentTime) +// } +// +// years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int -> +// previousCronDateTime.copy(years = currentTime) +// } +// +// weekDays ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> +// previousCronDateTime.copy(daysOfWeek = currentTime) +// } +// +// return resultCronDateTimes.toList() } /** @@ -153,7 +176,7 @@ internal fun createKronScheduler( month: Array? = null, years: Array? = null, weekDays: Array? = null -): KronScheduler = CronDateTimeScheduler(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years, weekDays)) +): KronScheduler = CronDateTimeScheduler(createCronDateTime(seconds, minutes, hours, dayOfMonth, month, years, weekDays)) /** * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data */ @@ -166,4 +189,14 @@ internal fun createKronSchedulerWithOffset( years: Array? = null, weekDays: Array? = null, offset: TimezoneOffset -): KronScheduler = CronDateTimeSchedulerTz(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years, weekDays), offset) +): KronScheduler = CronDateTimeSchedulerTz(createCronDateTime(seconds, minutes, hours, dayOfMonth, month, years, weekDays), offset) + +internal fun List.merge() = CronDateTime( + flatMap { it.daysOfWeek ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, + flatMap { it.years ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, + flatMap { it.months ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, + flatMap { it.daysOfMonth ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, + flatMap { it.hours ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, + flatMap { it.minutes ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, + flatMap { it.seconds ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, +) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt index b4b3bcf..620a5de 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeScheduler.kt @@ -19,7 +19,7 @@ import dev.inmo.krontab.collection.plus * @see dev.inmo.krontab.builder.SchedulerBuilder */ internal data class CronDateTimeScheduler internal constructor( - internal val cronDateTimes: List + internal val cronDateTime: CronDateTime ) : KronScheduler { /** * @return Near date using [cronDateTimes] list and getting the [Iterable.minByOrNull] one @@ -27,12 +27,14 @@ internal data class CronDateTimeScheduler internal constructor( * @see toNearDateTime */ override suspend fun next(relatively: DateTime): DateTime? { - return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() + return cronDateTime.toNearDateTime(relatively) } } -internal fun mergeCronDateTimeSchedulers(schedulers: List) = CronDateTimeScheduler( - schedulers.flatMap { it.cronDateTimes } +internal fun mergeCronDateTimeSchedulers( + schedulers: List +): CronDateTimeScheduler = CronDateTimeScheduler( + schedulers.map { it.cronDateTime }.merge() ) /** diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt index adf74d4..8a6ca26 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTimeSchedulerTz.kt @@ -11,14 +11,12 @@ import dev.inmo.krontab.KronSchedulerTz * @see CronDateTime */ internal data class CronDateTimeSchedulerTz internal constructor( - internal val cronDateTimes: List, + internal val cronDateTime: CronDateTime, internal val offset: TimezoneOffset ) : KronSchedulerTz { override suspend fun next(relatively: DateTimeTz): DateTimeTz? { val dateTimeWithActualOffset = relatively.toOffset(offset).local - return cronDateTimes.mapNotNull { - it.toNearDateTime(dateTimeWithActualOffset) - }.minOrNull() ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset) + return cronDateTime.toNearDateTime(dateTimeWithActualOffset) ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset) } } @@ -27,5 +25,8 @@ internal fun mergeCronDateTimeSchedulers( ) = schedulers.groupBy { it.offset }.map { (offset, schedulers) -> - CronDateTimeSchedulerTz(schedulers.flatMap { it.cronDateTimes }, offset) + CronDateTimeSchedulerTz( + schedulers.map { it.cronDateTime }.merge(), + offset + ) } diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt new file mode 100644 index 0000000..7992ab4 --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt @@ -0,0 +1,171 @@ +package dev.inmo.krontab.internal + +import com.soywiz.klock.* +import dev.inmo.krontab.utils.copy +import kotlin.math.min + +internal class NearDateTimeCalculator( + private val times: Array, + private val partGetter: (DateTime) -> T, + private val partSetter: (DateTime, T) -> DateTime? +) where T : Comparable, T : Number { + /** + * @return pair of near [DateTime] for this checker and [Boolean] flag that all previous calculations must be + * recalculated + */ + fun calculateNearTime( + relativelyTo: DateTime + ): Pair? { + val currentData = partGetter(relativelyTo) + val greaterOrEquals = times.firstOrNull { it >= currentData } + val newDateTime = if (greaterOrEquals == null) { + partSetter(relativelyTo, times.first()) ?: return null + } else { + partSetter(relativelyTo, greaterOrEquals) ?: return null + } + return if (newDateTime == relativelyTo) { + relativelyTo to false + } else { + newDateTime to true + } + } +} + +internal fun NearDateTimeCalculatorMillis( + times: Array +) = NearDateTimeCalculator( + times, + { it.milliseconds.toShort() }, + { dateTime, newOne -> + (if (newOne < dateTime.milliseconds) { + dateTime.plus(1.seconds) + } else { + dateTime + }).copy(milliseconds = newOne.toInt()) + } +) + +internal fun NearDateTimeCalculatorSeconds( + times: Array +) = NearDateTimeCalculator( + times, + { it.seconds.toByte() }, + { dateTime, newOne -> + (if (newOne < dateTime.seconds) { + dateTime.plus(1.minutes) + } else { + dateTime + }).copy(second = newOne.toInt(), milliseconds = 0) + } +) + +internal fun NearDateTimeCalculatorMinutes( + times: Array +) = NearDateTimeCalculator( + times, + { it.minutes.toByte() }, + { dateTime, newOne -> + (if (newOne < dateTime.minutes) { + dateTime.plus(1.hours) + } else { + dateTime + }).copy(minute = newOne.toInt(), second = 0, milliseconds = 0) + } +) + +internal fun NearDateTimeCalculatorHours( + times: Array +) = NearDateTimeCalculator( + times, + { it.hours.toByte() }, + { dateTime, newOne -> + (if (newOne < dateTime.hours) { + dateTime.plus(1.days) + } else { + dateTime + }).copy(hour = newOne.toInt(), minute = 0, second = 0, milliseconds = 0) + } +) + +internal fun NearDateTimeCalculatorDays( + times: Array +) = NearDateTimeCalculator( + times, + { it.dayOfMonth.toByte() }, + { dateTime, newOne -> + (if (newOne < dateTime.dayOfMonth) { + dateTime.plus(1.months) + } else { + dateTime + }).copy( + dayOfMonth = min(dateTime.month.days(dateTime.year), newOne.toInt() + 1), // index1 + hour = 0, + minute = 0, + second = 0, + milliseconds = 0 + ) + } +) + +internal fun NearDateTimeCalculatorMonths( + times: Array +) = NearDateTimeCalculator( + times, + { it.dayOfMonth.toByte() }, + { dateTime, newOne -> + (if (newOne < dateTime.month0) { + dateTime.plus(1.years) + } else { + dateTime + }).copy( + month = newOne.toInt() + 1, // index1 + dayOfMonth = 1, // index1 + hour = 0, + minute = 0, + second = 0, + milliseconds = 0 + ) + } +) + +internal fun NearDateTimeCalculatorWeekDays( + times: Array +) = NearDateTimeCalculator( + times, + { it.dayOfWeek.index0.toByte() }, + { dateTime, newOne -> + val currentDayOfWeek = dateTime.dayOfWeek.index0 + (if (newOne < currentDayOfWeek) { + dateTime.plus(7.days - (currentDayOfWeek - newOne).days) + } else { + dateTime.plus(newOne.toInt().days - currentDayOfWeek.days) + }).copy( + hour = 0, + minute = 0, + second = 0, + milliseconds = 0 + ) + } +) + +internal fun NearDateTimeCalculatorYears( + times: Array +) = NearDateTimeCalculator( + times, + { it.yearInt }, + { dateTime, newOne -> + val currentYear = dateTime.yearInt + (if (newOne < currentYear) { + null + } else { + dateTime.plus(newOne.years - currentYear.years) + }) ?.copy( + month = 1, // index1 + dayOfMonth = 1, // index1 + hour = 0, + minute = 0, + second = 0, + milliseconds = 0 + ) + } +) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt index 17c9be2..a162d6c 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/Parser.kt @@ -8,7 +8,7 @@ private fun createSimpleScheduler(from: String, dataRange: IntRange, dataCon val things = from.split(",") val results = things.flatMap { - val currentToken = it.toLowerCase().replace( + val currentToken = it.lowercase().replace( "f", dataRange.first.toString() ).replace( "l", dataRange.last.toString() diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt new file mode 100644 index 0000000..c3666fb --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt @@ -0,0 +1,22 @@ +package dev.inmo.krontab.utils + +import com.soywiz.klock.* +import kotlin.math.min + +fun DateTime.copy( + year: Int = yearInt, + month: Int = month1, + dayOfMonth: Int = this.dayOfMonth, + hour: Int = hours, + minute: Int = minutes, + second: Int = seconds, + milliseconds: Int = this.milliseconds +) = DateTime( + year, + month, + min(Month(month).days(yearInt), dayOfMonth), + hour, + minute, + second, + milliseconds +) diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt index 4663174..97e03e9 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt @@ -10,7 +10,8 @@ class TimeZoneTest { @Test fun testDifferentTimeZonesReturnsDifferentTimes() { val scheduler = buildSchedule { seconds { every(1) } } - val baseDate = DateTime.now().startOfWeek + val additionalMilliseconds = 100.milliseconds + val baseDate = DateTime.now().startOfWeek.copy(milliseconds = additionalMilliseconds.millisecondsInt) runTest { for (i in 0 until 7) { val now = baseDate + i.days @@ -18,10 +19,10 @@ class TimeZoneTest { val nowTz = now.toOffset(j.hours) val next = scheduler.next(nowTz)!! assertEquals( - (nowTz + 1.seconds).utc.unixMillisLong, next.utc.unixMillisLong + (nowTz + 1.seconds - additionalMilliseconds).utc.unixMillisLong, next.utc.unixMillisLong ) } } } } -} \ No newline at end of file +} From b4790b892a91d2270500c83d91b0e8066bb5fc1d Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 30 May 2021 18:53:44 +0600 Subject: [PATCH 5/9] partial fix --- .../dev/inmo/krontab/internal/CronDateTime.kt | 5 ++- .../internal/NearDateTimeCalculator.kt | 38 ++++++++++++------- .../dev/inmo/krontab/utils/StringParseTest.kt | 4 +- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index 802e765..2200a06 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -2,6 +2,7 @@ package dev.inmo.krontab.internal import com.soywiz.klock.* import dev.inmo.krontab.KronScheduler +import dev.inmo.krontab.utils.copy /** * @param daysOfWeek 0-6 @@ -32,14 +33,14 @@ internal data class CronDateTime( } internal val calculators = listOf( + years ?.let { NearDateTimeCalculatorYears(it) }, + daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) }, NearDateTimeCalculatorMillis(arrayOf(0)), seconds ?.let { NearDateTimeCalculatorSeconds(it) }, minutes ?.let { NearDateTimeCalculatorMinutes(it) }, hours ?.let { NearDateTimeCalculatorHours(it) }, daysOfMonth ?.let { NearDateTimeCalculatorDays(it) }, months ?.let { NearDateTimeCalculatorMonths(it) }, - years ?.let { NearDateTimeCalculatorYears(it) }, - daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) }, ) internal fun toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? { diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt index 7992ab4..7983935 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt @@ -4,17 +4,27 @@ import com.soywiz.klock.* import dev.inmo.krontab.utils.copy import kotlin.math.min -internal class NearDateTimeCalculator( - private val times: Array, - private val partGetter: (DateTime) -> T, - private val partSetter: (DateTime, T) -> DateTime? -) where T : Comparable, T : Number { +fun interface NearDateTimeCalculator { /** * @return pair of near [DateTime] for this checker and [Boolean] flag that all previous calculations must be * recalculated */ fun calculateNearTime( relativelyTo: DateTime + ): Pair? +} + +internal class CommonNearDateTimeCalculator( + private val times: Array, + private val partGetter: (DateTime) -> T, + private val partSetter: (DateTime, T) -> DateTime? +) : NearDateTimeCalculator where T : Comparable, T : Number { + /** + * @return pair of near [DateTime] for this checker and [Boolean] flag that all previous calculations must be + * recalculated + */ + override fun calculateNearTime( + relativelyTo: DateTime ): Pair? { val currentData = partGetter(relativelyTo) val greaterOrEquals = times.firstOrNull { it >= currentData } @@ -33,7 +43,7 @@ internal class NearDateTimeCalculator( internal fun NearDateTimeCalculatorMillis( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.milliseconds.toShort() }, { dateTime, newOne -> @@ -47,7 +57,7 @@ internal fun NearDateTimeCalculatorMillis( internal fun NearDateTimeCalculatorSeconds( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.seconds.toByte() }, { dateTime, newOne -> @@ -61,7 +71,7 @@ internal fun NearDateTimeCalculatorSeconds( internal fun NearDateTimeCalculatorMinutes( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.minutes.toByte() }, { dateTime, newOne -> @@ -75,7 +85,7 @@ internal fun NearDateTimeCalculatorMinutes( internal fun NearDateTimeCalculatorHours( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.hours.toByte() }, { dateTime, newOne -> @@ -89,7 +99,7 @@ internal fun NearDateTimeCalculatorHours( internal fun NearDateTimeCalculatorDays( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.dayOfMonth.toByte() }, { dateTime, newOne -> @@ -109,7 +119,7 @@ internal fun NearDateTimeCalculatorDays( internal fun NearDateTimeCalculatorMonths( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.dayOfMonth.toByte() }, { dateTime, newOne -> @@ -130,11 +140,12 @@ internal fun NearDateTimeCalculatorMonths( internal fun NearDateTimeCalculatorWeekDays( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.dayOfWeek.index0.toByte() }, { dateTime, newOne -> val currentDayOfWeek = dateTime.dayOfWeek.index0 + if (newOne.toInt() == currentDayOfWeek) return@CommonNearDateTimeCalculator dateTime (if (newOne < currentDayOfWeek) { dateTime.plus(7.days - (currentDayOfWeek - newOne).days) } else { @@ -150,11 +161,12 @@ internal fun NearDateTimeCalculatorWeekDays( internal fun NearDateTimeCalculatorYears( times: Array -) = NearDateTimeCalculator( +) = CommonNearDateTimeCalculator( times, { it.yearInt }, { dateTime, newOne -> val currentYear = dateTime.yearInt + if (newOne == currentYear) return@CommonNearDateTimeCalculator dateTime (if (newOne < currentYear) { null } else { diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt index 0ddc638..7eae687 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt @@ -67,8 +67,8 @@ class StringParseTest { val flow = kronScheduler.asFlow() runTest { - val ranges = rangesEnds.map { it.first .. it.second }.flatten().toMutableList() - val expectedCollects = rangesEnds.sumOf { it.second - it.first + 1 } + val ranges = rangesEnds.map { it.first .. it.second }.flatten().distinct().toMutableList() + val expectedCollects = ranges.size var collected = 0 flow.takeWhile { ranges.isNotEmpty() }.collect { From c3ed1d024cbf8c04b4c5c18527ef94621a013e6a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 3 Jun 2021 12:16:39 +0600 Subject: [PATCH 6/9] fix tests --- CHANGELOG.md | 4 +--- .../kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt | 11 +++++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 272ce4f..ff0f03e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,7 @@ ## 0.6.1 * Versions - * `Klock`: `2.1.0` -> `2.1.2 - -` + * `Klock`: `2.1.0` -> `2.1.2` ## 0.6.0 diff --git a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt index 81738f4..1382d86 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/utils/SchedulerFlow.kt @@ -15,9 +15,16 @@ import kotlinx.coroutines.flow.* */ @FlowPreview fun KronScheduler.asTzFlow(): Flow = channelFlow { + var previousTime = DateTime.nowLocal() while (isActive) { - val now = DateTime.now().local + val now = DateTime.nowLocal() val nextTime = next(now) ?: break + if (previousTime == nextTime) { + delay(1L) // skip 1ms + continue + } else { + previousTime = nextTime + } val sleepDelay = (nextTime - DateTime.now().local).millisecondsLong delay(sleepDelay) send(nextTime) @@ -48,4 +55,4 @@ class SchedulerFlow( collector.emit(nextTime) } } -} \ No newline at end of file +} From fec17f949f4bf9c65a8dddd89a256cc30f73c433 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 3 Jun 2021 12:37:13 +0600 Subject: [PATCH 7/9] fixes in tests --- .../dev/inmo/krontab/internal/NearDateTimeCalculator.kt | 8 ++++---- .../kotlin/dev/inmo/krontab/utils/StringParseTest.kt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt index 7983935..714a3f6 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/NearDateTimeCalculator.kt @@ -28,10 +28,10 @@ internal class CommonNearDateTimeCalculator( ): Pair? { val currentData = partGetter(relativelyTo) val greaterOrEquals = times.firstOrNull { it >= currentData } - val newDateTime = if (greaterOrEquals == null) { - partSetter(relativelyTo, times.first()) ?: return null - } else { - partSetter(relativelyTo, greaterOrEquals) ?: return null + val newDateTime = when (greaterOrEquals) { + null -> partSetter(relativelyTo, times.first()) ?: return null + currentData -> relativelyTo + else -> partSetter(relativelyTo, greaterOrEquals) ?: return null } return if (newDateTime == relativelyTo) { relativelyTo to false diff --git a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt index 7eae687..64deafd 100644 --- a/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt +++ b/src/commonTest/kotlin/dev/inmo/krontab/utils/StringParseTest.kt @@ -80,7 +80,7 @@ class StringParseTest { } @Test fun testThatTimezoneCorrectlyDeserialized() { - val now = DateTimeTz.nowLocal() + val now = DateTime.now().copy(milliseconds = 0).local runTest { for (i in 0 .. 1339) { From 0ecc6f00285782e53f3fd0c4c1fb1219179fb634 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 3 Jun 2021 12:52:39 +0600 Subject: [PATCH 8/9] upfill of changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0f03e..671ed32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Versions * `Klock`: `2.1.0` -> `2.1.2` +* Rewriting of default mechanism of `KronScheduler`s ## 0.6.0 From 437206e915da1a74c30e3dfafefb02a193486400 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 3 Jun 2021 12:55:16 +0600 Subject: [PATCH 9/9] remove old comments --- .../dev/inmo/krontab/internal/CronDateTime.kt | 96 ------------------- 1 file changed, 96 deletions(-) diff --git a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt index 2200a06..350a066 100644 --- a/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt +++ b/src/commonMain/kotlin/dev/inmo/krontab/internal/CronDateTime.kt @@ -57,71 +57,6 @@ internal data class CronDateTime( } } } -// -///** -// * 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? { -// 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 == null || 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) -// } -// -// minutes?.let { -// val left = it - current.minutes -// current += DateTimeSpan(hours = if (left < 0) 1 else 0, minutes = left) -// } -// -// hours?.let { -// val left = it - current.hours -// current += DateTimeSpan(days = if (left < 0) 1 else 0, hours = left) -// } -// -// klockDayOfMonth ?.let { -// val left = (it - current.dayOfMonth).let { diff -> -// if (diff > 0 && current.endOfMonth.run { it > dayOfMonth && current.dayOfMonth == dayOfMonth }) { -// 0 -// } else { -// diff -// } -// } -// current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left) -// } -// -// months?.let { -// val left = it - current.month0 -// current += DateTimeSpan(years = if (left < 0) 1 else 0, months = left) -// } -// -// years?.let { -// if (current.yearInt != it) { -// return null -// } -// } -// -// return current -//} internal fun createCronDateTime( seconds: Array? = null, @@ -133,37 +68,6 @@ internal fun createCronDateTime( weekDays: Array? = null ): CronDateTime { return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds) -// val resultCronDateTimes = mutableListOf(CronDateTime()) -// -// seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> -// previousCronDateTime.copy(seconds = currentTime) -// } -// -// minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> -// previousCronDateTime.copy(minutes = currentTime) -// } -// -// hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> -// previousCronDateTime.copy(hours = currentTime) -// } -// -// dayOfMonth ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> -// previousCronDateTime.copy(daysOfMonth = currentTime) -// } -// -// month ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> -// previousCronDateTime.copy(months = currentTime) -// } -// -// years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int -> -// previousCronDateTime.copy(years = currentTime) -// } -// -// weekDays ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte -> -// previousCronDateTime.copy(daysOfWeek = currentTime) -// } -// -// return resultCronDateTimes.toList() } /**