mirror of
				https://github.com/InsanusMokrassar/krontab.git
				synced 2025-11-04 05:20:41 +00:00 
			
		
		
		
	@@ -1,5 +1,11 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## 0.6.5
 | 
			
		||||
 | 
			
		||||
* Flows now use `doWhile` functions
 | 
			
		||||
* `doWhile` now use additional delay (for 1 ms) for cases when `block` executing too fast
 | 
			
		||||
* New extensions for `KronScheduler`: `do(Once/While/Infinity)Local`/`do(Once/While/Infinity)Tz`
 | 
			
		||||
 | 
			
		||||
## 0.6.4
 | 
			
		||||
 | 
			
		||||
* Versions
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								README.md
									
									
									
									
									
								
							@@ -7,16 +7,6 @@
 | 
			
		||||
Library was created to give oppotunity to launch some things from time to time according to some schedule in
 | 
			
		||||
runtime of applications.
 | 
			
		||||
 | 
			
		||||
| Table of content |
 | 
			
		||||
|---|
 | 
			
		||||
| [ How to use ](#how-to-use) |
 | 
			
		||||
| [ How to use: Including in project ](#including-in-project) |
 | 
			
		||||
| [ 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) |
 | 
			
		||||
| [ How to use: Offsets ](#Offsets) |
 | 
			
		||||
| [ How to use: Note about week days ](#Note-about-week-days) |
 | 
			
		||||
 | 
			
		||||
## How to use
 | 
			
		||||
 | 
			
		||||
There are several ways to configure and use this library:
 | 
			
		||||
@@ -133,6 +123,38 @@ kronScheduler.doInfinity {
 | 
			
		||||
 | 
			
		||||
All of these examples will do the same things: print `Called` message every five seconds.
 | 
			
		||||
 | 
			
		||||
### do\* functions
 | 
			
		||||
 | 
			
		||||
With regular `doOnce`/`doWhile`/`doInfinity` there are two types of their variations: **local** and **timezoned**. Local
 | 
			
		||||
variations (`doOnceLocal`/`doWhileLocal`/`doInfinityLocal`) will pass `DateTime` as an argument into the block:
 | 
			
		||||
 | 
			
		||||
```kotlin
 | 
			
		||||
doInfinityLocal("/5 * * * *") {
 | 
			
		||||
    println(it) // will print current date time
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Timezoned variations (`doOnceTz`/`doWhileTz`/`doInfinityTz`) will do the same thing but pass as an argument `DateTimeTz`:
 | 
			
		||||
 | 
			
		||||
```kotlin
 | 
			
		||||
doInfinityTz("/5 * * * * 0o") {
 | 
			
		||||
    println(it) // will print current date time in UTC
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
It is useful in cases when you need to get the time of calling and avoid extra calls to system time.
 | 
			
		||||
 | 
			
		||||
#### Helpful table for 
 | 
			
		||||
 | 
			
		||||
|  | No args | Local `DateTime` | Local `DateTimeTz` with offset of `KronScheduler` |
 | 
			
		||||
|---| ------- | ---------------- | ------------------------------------------------- |
 | 
			
		||||
| **Call only near time** | doOnce | doOnceLocal | doOnceTz |
 | 
			
		||||
| **Call while condition is true** | doWhile | doWhileLocal | doWhileTz |
 | 
			
		||||
| **Work infinity*** | doInfinity | doInfinityLocal | doInfinityTz |
 | 
			
		||||
 | 
			
		||||
*Here there is an important notice, that `Work infinity` is not exactly `infinity`. Actually, that means that `do while
 | 
			
		||||
coroutine is alive` and in fact executing will be stopped when coroutine became cancelled.
 | 
			
		||||
 | 
			
		||||
### KronScheduler as a Flow
 | 
			
		||||
 | 
			
		||||
Any `KronScheduler`can e converted to a `Flow<DateTime` using extension `asFlow`:
 | 
			
		||||
@@ -163,7 +185,7 @@ flow.takeWhile {
 | 
			
		||||
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`.
 | 
			
		||||
scheduler is not instance of `KronSchedulerTz` it will work 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:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,5 +33,5 @@ androidx_work_version=2.6.0
 | 
			
		||||
 | 
			
		||||
## Common
 | 
			
		||||
 | 
			
		||||
version=0.6.4
 | 
			
		||||
android_code_version=8
 | 
			
		||||
version=0.6.5
 | 
			
		||||
android_code_version=9
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,10 @@
 | 
			
		||||
package dev.inmo.krontab
 | 
			
		||||
 | 
			
		||||
import com.soywiz.klock.DateTime
 | 
			
		||||
import com.soywiz.klock.DateTimeTz
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlinx.coroutines.isActive
 | 
			
		||||
import kotlin.coroutines.coroutineContext
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
 | 
			
		||||
@@ -10,29 +13,109 @@ import kotlinx.coroutines.delay
 | 
			
		||||
 *
 | 
			
		||||
 * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T {
 | 
			
		||||
    next() ?.let {
 | 
			
		||||
suspend inline fun <T> KronScheduler.doOnceLocal(noinline block: suspend (DateTime) -> T): T {
 | 
			
		||||
    val time = nextOrNow().also {
 | 
			
		||||
        delay((it - DateTime.now()).millisecondsLong)
 | 
			
		||||
    }
 | 
			
		||||
    return block()
 | 
			
		||||
    return block(time)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doOnce] on it
 | 
			
		||||
 * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
 | 
			
		||||
 *
 | 
			
		||||
 * WARNING!!! If you want to launch it in parallel, you must do this explicitly.
 | 
			
		||||
 *
 | 
			
		||||
 * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediatelly
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun <T> KronScheduler.doOnceTz(noinline block: suspend (DateTimeTz) -> T): T {
 | 
			
		||||
    val time = when (this) {
 | 
			
		||||
        is KronSchedulerTz -> nextOrNowWithOffset()
 | 
			
		||||
        else -> nextOrNow().local
 | 
			
		||||
    }
 | 
			
		||||
    delay((time - DateTimeTz.nowLocal()).millisecondsLong)
 | 
			
		||||
    return block(time)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
 | 
			
		||||
 *
 | 
			
		||||
 * WARNING!!! If you want to launch it in parallel, you must do this explicitly.
 | 
			
		||||
 *
 | 
			
		||||
 * WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T = doOnceLocal { _ -> block() }
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it
 | 
			
		||||
 * @see buildSchedule
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun <T> doOnce(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend (DateTime) -> T
 | 
			
		||||
) = buildSchedule(scheduleConfig).doOnceLocal(block)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it
 | 
			
		||||
 * @see buildSchedule
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun <T> doOnceTz(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend (DateTimeTz) -> T
 | 
			
		||||
) = buildSchedule(scheduleConfig).doOnceTz(block)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doOnceLocal] on it
 | 
			
		||||
 * @see buildSchedule
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun <T> doOnce(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend () -> T
 | 
			
		||||
) = buildSchedule(scheduleConfig).doOnce(block)
 | 
			
		||||
) = doOnce(scheduleConfig) { _ -> block() }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will execute [block] while it will return true as a result of its calculation
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) {
 | 
			
		||||
    do { val doNext = doOnce(block) } while (doNext)
 | 
			
		||||
suspend inline fun KronScheduler.doWhileLocal(noinline block: suspend (DateTime) -> Boolean) {
 | 
			
		||||
    do {
 | 
			
		||||
        delay(1L)
 | 
			
		||||
    } while (doOnceLocal(block))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will execute [block] while it will return true as a result of its calculation
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun KronScheduler.doWhileTz(noinline block: suspend (DateTimeTz) -> Boolean) {
 | 
			
		||||
    do {
 | 
			
		||||
        delay(1L)
 | 
			
		||||
    } while (doOnceTz(block))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will execute [block] while it will return true as a result of its calculation
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) = doWhileLocal { block() }
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
 | 
			
		||||
 *
 | 
			
		||||
 * @see buildSchedule
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun doWhileLocal(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend (DateTime) -> Boolean
 | 
			
		||||
) = buildSchedule(scheduleConfig).doWhileLocal(block)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
 | 
			
		||||
 *
 | 
			
		||||
 * @see buildSchedule
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun doWhileTz(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend (DateTimeTz) -> Boolean
 | 
			
		||||
) = buildSchedule(scheduleConfig).doWhileTz(block)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
 | 
			
		||||
 *
 | 
			
		||||
@@ -41,15 +124,53 @@ suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean)
 | 
			
		||||
suspend inline fun doWhile(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend () -> Boolean
 | 
			
		||||
) = buildSchedule(scheduleConfig).doWhile(block)
 | 
			
		||||
) = doWhileLocal(scheduleConfig) { block() }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will execute [block] without any checking of result
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun KronScheduler.doInfinityLocal(noinline block: suspend (DateTime) -> Unit) = doWhileLocal {
 | 
			
		||||
    block(it)
 | 
			
		||||
    coroutineContext.isActive
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will execute [block] without any checking of result
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun KronScheduler.doInfinityTz(noinline block: suspend (DateTimeTz) -> Unit) = doWhileTz {
 | 
			
		||||
    block(it)
 | 
			
		||||
    coroutineContext.isActive
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will execute [block] without any checking of result
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile {
 | 
			
		||||
    block()
 | 
			
		||||
    true
 | 
			
		||||
    coroutineContext.isActive
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
 | 
			
		||||
 *
 | 
			
		||||
 * @see buildSchedule
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun doInfinityLocal(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend (DateTime) -> Unit
 | 
			
		||||
) = buildSchedule(scheduleConfig).doInfinityLocal(block)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
 | 
			
		||||
 *
 | 
			
		||||
 * @see buildSchedule
 | 
			
		||||
 */
 | 
			
		||||
suspend inline fun doInfinityTz(
 | 
			
		||||
    scheduleConfig: String,
 | 
			
		||||
    noinline block: suspend (DateTimeTz) -> Unit
 | 
			
		||||
) = buildSchedule(scheduleConfig).doInfinityTz(block)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
package dev.inmo.krontab.internal
 | 
			
		||||
 | 
			
		||||
import com.soywiz.klock.*
 | 
			
		||||
import com.soywiz.klock.DateTime
 | 
			
		||||
import com.soywiz.klock.TimezoneOffset
 | 
			
		||||
import dev.inmo.krontab.KronScheduler
 | 
			
		||||
import dev.inmo.krontab.utils.copy
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param daysOfWeek 0-6
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package dev.inmo.krontab.utils
 | 
			
		||||
 | 
			
		||||
import com.soywiz.klock.*
 | 
			
		||||
import com.soywiz.klock.DateTime
 | 
			
		||||
import com.soywiz.klock.Month
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
 | 
			
		||||
fun DateTime.copy(
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@ package dev.inmo.krontab.utils
 | 
			
		||||
 | 
			
		||||
import com.soywiz.klock.DateTime
 | 
			
		||||
import com.soywiz.klock.DateTimeTz
 | 
			
		||||
import dev.inmo.krontab.KronScheduler
 | 
			
		||||
import dev.inmo.krontab.next
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import dev.inmo.krontab.*
 | 
			
		||||
import kotlinx.coroutines.FlowPreview
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlinx.coroutines.flow.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -12,30 +12,41 @@ import kotlinx.coroutines.flow.*
 | 
			
		||||
 * time zones
 | 
			
		||||
 *
 | 
			
		||||
 * @see channelFlow
 | 
			
		||||
 * @see KronSchedulerTz.doWhile
 | 
			
		||||
 */
 | 
			
		||||
@FlowPreview
 | 
			
		||||
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow {
 | 
			
		||||
    var previousTime = DateTime.nowLocal()
 | 
			
		||||
    while (isActive) {
 | 
			
		||||
        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)
 | 
			
		||||
fun KronSchedulerTz.asTzFlow(): Flow<DateTimeTz> = channelFlow {
 | 
			
		||||
    doInfinity {
 | 
			
		||||
        send(DateTime.nowLocal())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s
 | 
			
		||||
 *
 | 
			
		||||
 * @see channelFlow
 | 
			
		||||
 * @see KronScheduler.doWhile
 | 
			
		||||
 */
 | 
			
		||||
@FlowPreview
 | 
			
		||||
fun KronScheduler.asFlow(): Flow<DateTime> = asTzFlow().map { it.local }
 | 
			
		||||
fun KronScheduler.asFlow(): Flow<DateTime> = channelFlow {
 | 
			
		||||
    doInfinity {
 | 
			
		||||
        send(DateTime.now())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to
 | 
			
		||||
 * time zones
 | 
			
		||||
 *
 | 
			
		||||
 * @see channelFlow
 | 
			
		||||
 * @see KronScheduler.asFlow
 | 
			
		||||
 * @see KronSchedulerTz.asTzFlow
 | 
			
		||||
 */
 | 
			
		||||
@FlowPreview
 | 
			
		||||
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = when (this) {
 | 
			
		||||
    is KronSchedulerTz -> asTzFlow()
 | 
			
		||||
    else -> asFlow().map { it.local }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Deprecated(
 | 
			
		||||
    "It is not recommended to use this class in future. This functionality will be removed soon",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user