mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2024-11-26 03:58:50 +00:00
commit
5a6084c573
2
.github/workflows/publishing_packages.yml
vendored
2
.github/workflows/publishing_packages.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: prebuild
|
- name: prebuild
|
||||||
run: ./gradlew clean build
|
run: ./gradlew clean build
|
||||||
- name: Publish package
|
- 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:
|
env:
|
||||||
GITHUBPACKAGES_USER: ${{ github.actor }}
|
GITHUBPACKAGES_USER: ${{ github.actor }}
|
||||||
GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
|
GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.6.1
|
||||||
|
|
||||||
|
* Versions
|
||||||
|
* `Klock`: `2.1.0` -> `2.1.2`
|
||||||
|
* Rewriting of default mechanism of `KronScheduler`s
|
||||||
|
|
||||||
## 0.6.0
|
## 0.6.0
|
||||||
|
|
||||||
* Versions
|
* Versions
|
||||||
|
@ -13,7 +13,7 @@ kotlin_coroutines_version=1.5.0
|
|||||||
|
|
||||||
dokka_version=1.4.32
|
dokka_version=1.4.32
|
||||||
|
|
||||||
klockVersion=2.1.0
|
klockVersion=2.1.2
|
||||||
|
|
||||||
## Github reease
|
## Github reease
|
||||||
|
|
||||||
@ -33,6 +33,6 @@ androidx_work_version=2.5.0
|
|||||||
|
|
||||||
## Common
|
## Common
|
||||||
|
|
||||||
version=0.6.0
|
version=0.6.1
|
||||||
android_code_version=4
|
android_code_version=5
|
||||||
|
|
||||||
|
@ -16,13 +16,13 @@ fun Iterator<KronScheduler>.merge(): CollectionKronScheduler {
|
|||||||
val collectionScheduler = CollectionKronScheduler()
|
val collectionScheduler = CollectionKronScheduler()
|
||||||
forEach {
|
forEach {
|
||||||
when (it) {
|
when (it) {
|
||||||
is CronDateTimeScheduler -> cronDateTimes.addAll(it.cronDateTimes)
|
is CronDateTimeScheduler -> cronDateTimes.add(it.cronDateTime)
|
||||||
is CronDateTimeSchedulerTz -> timezonedCronDateTimes.add(it)
|
is CronDateTimeSchedulerTz -> timezonedCronDateTimes.add(it)
|
||||||
else -> collectionScheduler.include(it)
|
else -> collectionScheduler.include(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cronDateTimes.isNotEmpty()) {
|
if (cronDateTimes.isNotEmpty()) {
|
||||||
collectionScheduler.include(CronDateTimeScheduler(cronDateTimes))
|
collectionScheduler.include(CronDateTimeScheduler(cronDateTimes.merge()))
|
||||||
}
|
}
|
||||||
if (timezonedCronDateTimes.isNotEmpty()) {
|
if (timezonedCronDateTimes.isNotEmpty()) {
|
||||||
collectionScheduler.includeAll(mergeCronDateTimeSchedulers(timezonedCronDateTimes))
|
collectionScheduler.includeAll(mergeCronDateTimeSchedulers(timezonedCronDateTimes))
|
||||||
|
@ -13,7 +13,7 @@ internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(r
|
|||||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
||||||
*/
|
*/
|
||||||
val AnyTimeScheduler: KronScheduler by lazy {
|
val AnyTimeScheduler: KronScheduler by lazy {
|
||||||
CronDateTimeScheduler(listOf(anyCronDateTime))
|
CronDateTimeScheduler(anyCronDateTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,4 +56,4 @@ val EveryMonthScheduler: KronScheduler by lazy {
|
|||||||
*/
|
*/
|
||||||
val EveryYearScheduler: KronScheduler by lazy {
|
val EveryYearScheduler: KronScheduler by lazy {
|
||||||
buildSchedule { years { 0 every 1 } }
|
buildSchedule { years { 0 every 1 } }
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ fun createSimpleScheduler(
|
|||||||
scheduler
|
scheduler
|
||||||
} else {
|
} else {
|
||||||
CronDateTimeSchedulerTz(
|
CronDateTimeSchedulerTz(
|
||||||
(scheduler as CronDateTimeScheduler).cronDateTimes,
|
(scheduler as CronDateTimeScheduler).cronDateTime,
|
||||||
TimezoneOffset(defaultOffset.minutes)
|
TimezoneOffset(defaultOffset.minutes)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -38,16 +38,16 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CronDateTimeSchedulerTz -> {
|
is CronDateTimeSchedulerTz -> {
|
||||||
val newCronDateTimes = kronScheduler.cronDateTimes.toMutableList()
|
val newCronDateTimes = mutableListOf(kronScheduler.cronDateTime)
|
||||||
val cronDateTimes = schedulers.removeAll {
|
schedulers.removeAll {
|
||||||
if (it is CronDateTimeSchedulerTz && it.offset == kronScheduler.offset) {
|
if (it is CronDateTimeSchedulerTz && it.offset == kronScheduler.offset) {
|
||||||
newCronDateTimes.addAll(it.cronDateTimes)
|
newCronDateTimes.add(it.cronDateTime)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.toList(), kronScheduler.offset))
|
schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.merge(), kronScheduler.offset))
|
||||||
}
|
}
|
||||||
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
||||||
include(it)
|
include(it)
|
||||||
|
@ -2,105 +2,63 @@ package dev.inmo.krontab.internal
|
|||||||
|
|
||||||
import com.soywiz.klock.*
|
import com.soywiz.klock.*
|
||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.KronScheduler
|
||||||
|
import dev.inmo.krontab.utils.copy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param dayOfweek 0-6
|
* @param daysOfWeek 0-6
|
||||||
* @param year any int
|
* @param years any int
|
||||||
* @param month 0-11
|
* @param months 0-11
|
||||||
* @param dayOfMonth 0-31
|
* @param daysOfMonth 0-31
|
||||||
* @param hours 0-23
|
* @param hours 0-23
|
||||||
* @param minutes 0-59
|
* @param minutes 0-59
|
||||||
* @param seconds 0-59
|
* @param seconds 0-59
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTime(
|
internal data class CronDateTime(
|
||||||
val dayOfweek: Byte? = null,
|
val daysOfWeek: Array<Byte>? = null,
|
||||||
val year: Int? = null,
|
val years: Array<Int>? = null,
|
||||||
val month: Byte? = null,
|
val months: Array<Byte>? = null,
|
||||||
val dayOfMonth: Byte? = null,
|
val daysOfMonth: Array<Byte>? = null,
|
||||||
val hours: Byte? = null,
|
val hours: Array<Byte>? = null,
|
||||||
val minutes: Byte? = null,
|
val minutes: Array<Byte>? = null,
|
||||||
val seconds: Byte? = null
|
val seconds: Array<Byte>? = null
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
check(dayOfweek ?.let { it in dayOfWeekRange } ?: true)
|
check(daysOfWeek ?.all { it in dayOfWeekRange } ?: true)
|
||||||
check(year ?.let { it in yearRange } ?: true)
|
check(years?.all { it in yearRange } ?: true)
|
||||||
check(month ?.let { it in monthRange } ?: true)
|
check(months?.all { it in monthRange } ?: true)
|
||||||
check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true)
|
check(daysOfMonth ?.all { it in dayOfMonthRange } ?: true)
|
||||||
check(hours?.let { it in hoursRange } ?: true)
|
check(hours?.all { it in hoursRange } ?: true)
|
||||||
check(minutes?.let { it in minutesRange } ?: true)
|
check(minutes?.all { it in minutesRange } ?: true)
|
||||||
check(seconds?.let { it in secondsRange } ?: true)
|
check(seconds?.all { it in secondsRange } ?: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val klockDayOfMonth = dayOfMonth ?.plus(1)
|
internal val calculators = listOf(
|
||||||
internal val dayOfWeekInt: Int? = dayOfweek ?.toInt()
|
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) },
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
internal fun toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
||||||
* THIS METHOD WILL <b>NOT</b> TAKE CARE ABOUT [offset] PARAMETER. It was decided due to the fact that we unable to get
|
var current = relativelyTo
|
||||||
* real timezone offset from simple [DateTime]
|
whileLoop@while (true) {
|
||||||
*
|
for (calculator in calculators) {
|
||||||
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
val (calculated, requireRecalculation) = (calculator ?: continue).calculateNearTime(current) ?: return null
|
||||||
*/
|
current = calculated
|
||||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
if (requireRecalculation) {
|
||||||
var current = relativelyTo
|
continue@whileLoop
|
||||||
|
}
|
||||||
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
|
return current
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun createCronDateTimeList(
|
internal fun createCronDateTime(
|
||||||
seconds: Array<Byte>? = null,
|
seconds: Array<Byte>? = null,
|
||||||
minutes: Array<Byte>? = null,
|
minutes: Array<Byte>? = null,
|
||||||
hours: Array<Byte>? = null,
|
hours: Array<Byte>? = null,
|
||||||
@ -108,38 +66,8 @@ internal fun createCronDateTimeList(
|
|||||||
month: Array<Byte>? = null,
|
month: Array<Byte>? = null,
|
||||||
years: Array<Int>? = null,
|
years: Array<Int>? = null,
|
||||||
weekDays: Array<Byte>? = null
|
weekDays: Array<Byte>? = null
|
||||||
): List<CronDateTime> {
|
): CronDateTime {
|
||||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds)
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,7 +81,7 @@ internal fun createKronScheduler(
|
|||||||
month: Array<Byte>? = null,
|
month: Array<Byte>? = null,
|
||||||
years: Array<Int>? = null,
|
years: Array<Int>? = null,
|
||||||
weekDays: Array<Byte>? = null
|
weekDays: Array<Byte>? = 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
|
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
|
||||||
*/
|
*/
|
||||||
@ -166,4 +94,14 @@ internal fun createKronSchedulerWithOffset(
|
|||||||
years: Array<Int>? = null,
|
years: Array<Int>? = null,
|
||||||
weekDays: Array<Byte>? = null,
|
weekDays: Array<Byte>? = null,
|
||||||
offset: TimezoneOffset
|
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<CronDateTime>.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() },
|
||||||
|
)
|
||||||
|
@ -19,7 +19,7 @@ import dev.inmo.krontab.collection.plus
|
|||||||
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTimeScheduler internal constructor(
|
internal data class CronDateTimeScheduler internal constructor(
|
||||||
internal val cronDateTimes: List<CronDateTime>
|
internal val cronDateTime: CronDateTime
|
||||||
) : KronScheduler {
|
) : KronScheduler {
|
||||||
/**
|
/**
|
||||||
* @return Near date using [cronDateTimes] list and getting the [Iterable.minByOrNull] one
|
* @return Near date using [cronDateTimes] list and getting the [Iterable.minByOrNull] one
|
||||||
@ -27,12 +27,14 @@ internal data class CronDateTimeScheduler internal constructor(
|
|||||||
* @see toNearDateTime
|
* @see toNearDateTime
|
||||||
*/
|
*/
|
||||||
override suspend fun next(relatively: DateTime): DateTime? {
|
override suspend fun next(relatively: DateTime): DateTime? {
|
||||||
return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull()
|
return cronDateTime.toNearDateTime(relatively)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun mergeCronDateTimeSchedulers(schedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
internal fun mergeCronDateTimeSchedulers(
|
||||||
schedulers.flatMap { it.cronDateTimes }
|
schedulers: List<CronDateTimeScheduler>
|
||||||
|
): CronDateTimeScheduler = CronDateTimeScheduler(
|
||||||
|
schedulers.map { it.cronDateTime }.merge()
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,14 +11,12 @@ import dev.inmo.krontab.KronSchedulerTz
|
|||||||
* @see CronDateTime
|
* @see CronDateTime
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTimeSchedulerTz internal constructor(
|
internal data class CronDateTimeSchedulerTz internal constructor(
|
||||||
internal val cronDateTimes: List<CronDateTime>,
|
internal val cronDateTime: CronDateTime,
|
||||||
internal val offset: TimezoneOffset
|
internal val offset: TimezoneOffset
|
||||||
) : KronSchedulerTz {
|
) : KronSchedulerTz {
|
||||||
override suspend fun next(relatively: DateTimeTz): DateTimeTz? {
|
override suspend fun next(relatively: DateTimeTz): DateTimeTz? {
|
||||||
val dateTimeWithActualOffset = relatively.toOffset(offset).local
|
val dateTimeWithActualOffset = relatively.toOffset(offset).local
|
||||||
return cronDateTimes.mapNotNull {
|
return cronDateTime.toNearDateTime(dateTimeWithActualOffset) ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset)
|
||||||
it.toNearDateTime(dateTimeWithActualOffset)
|
|
||||||
}.minOrNull() ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,5 +25,8 @@ internal fun mergeCronDateTimeSchedulers(
|
|||||||
) = schedulers.groupBy {
|
) = schedulers.groupBy {
|
||||||
it.offset
|
it.offset
|
||||||
}.map { (offset, schedulers) ->
|
}.map { (offset, schedulers) ->
|
||||||
CronDateTimeSchedulerTz(schedulers.flatMap { it.cronDateTimes }, offset)
|
CronDateTimeSchedulerTz(
|
||||||
|
schedulers.map { it.cronDateTime }.merge(),
|
||||||
|
offset
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,183 @@
|
|||||||
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import dev.inmo.krontab.utils.copy
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
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<DateTime, Boolean>?
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CommonNearDateTimeCalculator<T>(
|
||||||
|
private val times: Array<T>,
|
||||||
|
private val partGetter: (DateTime) -> T,
|
||||||
|
private val partSetter: (DateTime, T) -> DateTime?
|
||||||
|
) : NearDateTimeCalculator where T : Comparable<T>, 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<DateTime, Boolean>? {
|
||||||
|
val currentData = partGetter(relativelyTo)
|
||||||
|
val greaterOrEquals = times.firstOrNull { it >= currentData }
|
||||||
|
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
|
||||||
|
} else {
|
||||||
|
newDateTime to true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorMillis(
|
||||||
|
times: Array<Short>
|
||||||
|
) = CommonNearDateTimeCalculator(
|
||||||
|
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<Byte>
|
||||||
|
) = CommonNearDateTimeCalculator(
|
||||||
|
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<Byte>
|
||||||
|
) = CommonNearDateTimeCalculator(
|
||||||
|
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<Byte>
|
||||||
|
) = CommonNearDateTimeCalculator(
|
||||||
|
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<Byte>
|
||||||
|
) = CommonNearDateTimeCalculator(
|
||||||
|
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<Byte>
|
||||||
|
) = CommonNearDateTimeCalculator(
|
||||||
|
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<Byte>
|
||||||
|
) = 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 {
|
||||||
|
dateTime.plus(newOne.toInt().days - currentDayOfWeek.days)
|
||||||
|
}).copy(
|
||||||
|
hour = 0,
|
||||||
|
minute = 0,
|
||||||
|
second = 0,
|
||||||
|
milliseconds = 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorYears(
|
||||||
|
times: Array<Int>
|
||||||
|
) = CommonNearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.yearInt },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
val currentYear = dateTime.yearInt
|
||||||
|
if (newOne == currentYear) return@CommonNearDateTimeCalculator dateTime
|
||||||
|
(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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
@ -8,7 +8,7 @@ private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataCon
|
|||||||
val things = from.split(",")
|
val things = from.split(",")
|
||||||
|
|
||||||
val results = things.flatMap {
|
val results = things.flatMap {
|
||||||
val currentToken = it.toLowerCase().replace(
|
val currentToken = it.lowercase().replace(
|
||||||
"f", dataRange.first.toString()
|
"f", dataRange.first.toString()
|
||||||
).replace(
|
).replace(
|
||||||
"l", dataRange.last.toString()
|
"l", dataRange.last.toString()
|
||||||
|
22
src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt
Normal file
22
src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt
Normal file
@ -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
|
||||||
|
)
|
@ -15,9 +15,16 @@ import kotlinx.coroutines.flow.*
|
|||||||
*/
|
*/
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow {
|
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow {
|
||||||
|
var previousTime = DateTime.nowLocal()
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
val now = DateTime.now().local
|
val now = DateTime.nowLocal()
|
||||||
val nextTime = next(now) ?: break
|
val nextTime = next(now) ?: break
|
||||||
|
if (previousTime == nextTime) {
|
||||||
|
delay(1L) // skip 1ms
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
previousTime = nextTime
|
||||||
|
}
|
||||||
val sleepDelay = (nextTime - DateTime.now().local).millisecondsLong
|
val sleepDelay = (nextTime - DateTime.now().local).millisecondsLong
|
||||||
delay(sleepDelay)
|
delay(sleepDelay)
|
||||||
send(nextTime)
|
send(nextTime)
|
||||||
@ -48,4 +55,4 @@ class SchedulerFlow(
|
|||||||
collector.emit(nextTime)
|
collector.emit(nextTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,8 @@ class StringParseTest {
|
|||||||
val flow = kronScheduler.asFlow()
|
val flow = kronScheduler.asFlow()
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
val ranges = rangesEnds.map { it.first .. it.second }.flatten().toMutableList()
|
val ranges = rangesEnds.map { it.first .. it.second }.flatten().distinct().toMutableList()
|
||||||
val expectedCollects = rangesEnds.sumOf { it.second - it.first + 1 }
|
val expectedCollects = ranges.size
|
||||||
var collected = 0
|
var collected = 0
|
||||||
|
|
||||||
flow.takeWhile { ranges.isNotEmpty() }.collect {
|
flow.takeWhile { ranges.isNotEmpty() }.collect {
|
||||||
@ -80,7 +80,7 @@ class StringParseTest {
|
|||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
fun testThatTimezoneCorrectlyDeserialized() {
|
fun testThatTimezoneCorrectlyDeserialized() {
|
||||||
val now = DateTimeTz.nowLocal()
|
val now = DateTime.now().copy(milliseconds = 0).local
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
for (i in 0 .. 1339) {
|
for (i in 0 .. 1339) {
|
||||||
|
@ -10,7 +10,8 @@ class TimeZoneTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testDifferentTimeZonesReturnsDifferentTimes() {
|
fun testDifferentTimeZonesReturnsDifferentTimes() {
|
||||||
val scheduler = buildSchedule { seconds { every(1) } }
|
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 {
|
runTest {
|
||||||
for (i in 0 until 7) {
|
for (i in 0 until 7) {
|
||||||
val now = baseDate + i.days
|
val now = baseDate + i.days
|
||||||
@ -18,10 +19,10 @@ class TimeZoneTest {
|
|||||||
val nowTz = now.toOffset(j.hours)
|
val nowTz = now.toOffset(j.hours)
|
||||||
val next = scheduler.next(nowTz)!!
|
val next = scheduler.next(nowTz)!!
|
||||||
assertEquals(
|
assertEquals(
|
||||||
(nowTz + 1.seconds).utc.unixMillisLong, next.utc.unixMillisLong
|
(nowTz + 1.seconds - additionalMilliseconds).utc.unixMillisLong, next.utc.unixMillisLong
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user