Compare commits

...

72 Commits

Author SHA1 Message Date
7649d20984 Merge pull request #28 from InsanusMokrassar/0.7.1
0.7.1
2022-02-27 22:08:03 +06:00
ab274636b9 workflows fix 2022-02-27 20:47:10 +06:00
7f2f8a4a26 revert android sdk version 2022-02-27 20:37:48 +06:00
f64171c0f0 update android compile sdk up to 33.0.0 2022-02-27 19:05:07 +06:00
8382108b01 start 0.7.1 2022-02-27 18:20:49 +06:00
32499d02fb Update kdocs.yml 2021-12-25 14:05:13 +06:00
2b06033c75 Merge pull request #25 from InsanusMokrassar/0.7.0
0.7.0
2021-12-25 00:25:16 +06:00
84f1063e3f Update gradle-wrapper.properties 2021-12-24 17:59:39 +06:00
347cf64cb2 Update CHANGELOG.md 2021-12-24 17:59:11 +06:00
eb73da67eb Update gradle.properties 2021-12-23 13:03:16 +06:00
4aadf513aa Update gradle.properties 2021-12-23 13:02:47 +06:00
8c011ea1fb Update publishing_packages.yml 2021-12-22 12:55:22 +06:00
aa9c2b8ea0 Update gradle.properties 2021-12-22 12:52:08 +06:00
adf10301dc Update gradle.properties 2021-12-12 13:37:30 +06:00
7799077e44 Update gradle-wrapper.properties 2021-12-05 10:57:26 +06:00
83339dc095 Update gradle.properties 2021-12-05 10:51:49 +06:00
3040951f37 optimize imports 2021-11-22 19:09:33 +06:00
0bfe022476 update dependencies and remove deprecations 2021-11-22 19:08:24 +06:00
06107f75f2 start 0.7.0 2021-11-22 18:59:44 +06:00
039fe1cb5f Merge pull request #24 from InsanusMokrassar/0.6.6
0.6.6
2021-11-12 16:14:27 +06:00
ea7fab6130 0.6.6 2021-11-12 16:06:08 +06:00
2f3796733d Hotfix for 0.6.5 flows 2021-09-26 14:17:01 +06:00
89a6a305f8 Merge pull request #23 from InsanusMokrassar/0.6.5
0.6.5
2021-09-26 14:08:01 +06:00
97752e1ac0 optimize imports 2021-09-26 13:06:22 +06:00
276ecaac27 overwrite do* extensions 2021-09-26 13:05:59 +06:00
d36e406b59 now flows are using doInfinity 2021-09-26 11:49:01 +06:00
4dc65bf09a delay in doWhile and flows with doWhile instead their own logic 2021-09-25 19:28:49 +06:00
539b5fbcd8 0.6.5 2021-09-25 18:51:51 +06:00
e4f417877d Update kdocs.yml 2021-09-23 13:22:27 +06:00
4f6054c0d9 Update build.gradle 2021-09-23 13:18:40 +06:00
88dbfee370 Update README.md 2021-09-22 20:57:21 +06:00
5af29c2246 Merge pull request #21 from InsanusMokrassar/0.6.4
0.6.4
2021-09-22 20:52:29 +06:00
6aa40ad463 fix publication workflow script 2021-09-22 20:26:12 +06:00
3062f839af fix for publishing packages 2021-09-22 20:20:13 +06:00
001756ab9f add fix for android build 2021-09-22 20:12:50 +06:00
b7ebd0ac1c update workflows 2021-09-22 20:08:06 +06:00
ece767e4d0 update android compile sdk version and build tools 2021-09-22 20:04:08 +06:00
222542b38f update version of build gradle tool 2021-09-22 19:46:35 +06:00
5798774b2d update androidx work dependency 2021-09-22 19:39:18 +06:00
32bf5c0641 update dependencies 2021-09-22 19:34:04 +06:00
5916f3eebb start 0.6.4 2021-09-22 19:29:54 +06:00
df01a66b5a Merge pull request #20 from InsanusMokrassar/0.6.3
0.6.3
2021-08-30 20:26:10 +06:00
a622168297 update dependencies 2021-08-30 18:51:17 +06:00
9202eabf90 start 0.6.3 2021-08-30 18:28:12 +06:00
bc612604eb Update README.md 2021-08-07 17:11:10 +06:00
8808c00dc2 Update README.md 2021-08-07 16:40:24 +06:00
f3db5fd2e4 update kdocs yaml 2021-08-07 16:26:06 +06:00
b66284b6e5 update kdocs script 2021-08-07 16:25:35 +06:00
2a16f3c588 update kdocs pdenendency 2021-08-07 16:20:43 +06:00
6d055739fe Update dokka.gradle 2021-08-07 16:10:59 +06:00
92ae81d7a7 Create kdocs.yml 2021-08-07 16:07:40 +06:00
9e9c09e364 Merge pull request #19 from InsanusMokrassar/0.6.2
0.6.2
2021-07-30 11:02:45 +06:00
a8be548a72 Update build.gradle 2021-07-29 19:35:07 +06:00
6ec77b4e6f Remove bintray from repos 2021-07-29 19:33:35 +06:00
0ecb34709b update workflows 2021-07-28 14:41:27 +06:00
0f973adb9f fill changelog, remove legacy compiler for js 2021-07-28 14:31:30 +06:00
abc7a63d2e Update gradle.properties 2021-07-28 11:37:43 +06:00
fbab0e232f Update README.md 2021-07-28 11:29:31 +06:00
57fa3a30a6 Update dependencies 2021-07-28 11:27:17 +06:00
f50564abe2 start 0.6.2 2021-07-28 11:24:38 +06:00
8276ebc7ec Update README.md 2021-07-28 11:23:55 +06:00
5a6084c573 Merge pull request #18 from InsanusMokrassar/0.6.1
0.6.1
2021-06-03 13:40:06 +06:00
437206e915 remove old comments 2021-06-03 12:55:16 +06:00
0ecc6f0028 upfill of changelog 2021-06-03 12:52:39 +06:00
fec17f949f fixes in tests 2021-06-03 12:37:13 +06:00
c3ed1d024c fix tests 2021-06-03 12:16:39 +06:00
b4790b892a partial fix 2021-05-30 18:53:54 +06:00
28f84d4e3a rewrite mechanism of calculating of near time 2021-05-30 18:53:54 +06:00
22ef00fe8f Update publishing_packages.yml 2021-05-30 09:00:57 +06:00
c509d35b00 update klock 2021-05-29 17:30:28 +06:00
3de66a7274 start 0.6.1 2021-05-29 17:25:40 +06:00
1f0387363d Merge pull request #17 from InsanusMokrassar/0.6.0
0.6.0
2021-05-25 15:48:02 +06:00
23 changed files with 2518 additions and 255 deletions

24
.github/workflows/kdocs.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Publish KDocs
on:
push:
branches:
- master
jobs:
publishing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && cp d8 dx && cd lib && cp d8.jar dx.jar
- name: Build
run: ./gradlew dokkaHtml
- name: Publish KDocs
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build/dokka/html
publish_branch: kdocs

View File

@@ -1,4 +1,3 @@
name: Publish package to GitHub Packages name: Publish package to GitHub Packages
on: [push] on: [push]
jobs: jobs:
@@ -8,7 +7,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-java@v1 - uses: actions/setup-java@v1
with: with:
java-version: 1.8 java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && cp d8 dx && cd lib && cp d8.jar dx.jar
- name: Update version - name: Update version
run: | run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`" branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -16,7 +18,8 @@ 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 continue-on-error: true
run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signAndroidReleasePublication -x signAndroidDebugPublication -x signAndroidReleasePublication
env: env:
GITHUBPACKAGES_USER: ${{ github.actor }} GITHUBPACKAGES_USER: ${{ github.actor }}
GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,5 +1,61 @@
# Changelog # Changelog
## 0.7.1
* Versions
* `Klock`: `2.5.2`
## 0.7.0
**ALL DEPRECATIONS HAVE BEEN REMOVED**
* Versions
* `Kotlin`: `1.6.10`
* `Klock`: `2.4.10`
* `Coroutines`: `1.6.0`
* `Androidx Work`: `2.7.1`
## 0.6.6
* Versions
* `Klock`: `2.4.8`
* `Androidx Work`: `2.7.0`
## 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
* `Kotlin`: `1.5.31`
* `Coroutines`: `1.5.2`
* `Klock`: `2.4.2`
* `Androidx Work`: `2.6.0`
## 0.6.3
* Versions
* `Kotlin`: `1.5.30`
* `Klock`: `2.4.0`
## 0.6.2
**LEGACY COMPILER VARIANT FOR JS IS NOT AVAILABLE SINCE THIS UPDATE**
* Versions
* `Kotlin`: `1.5.21`
* `Coroutines`: `1.5.1`
* `Klock`: `2.3.1`
## 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

View File

@@ -1,22 +1,12 @@
# krontab # krontab
[![Download](https://api.bintray.com/packages/insanusmokrassar/InsanusMokrassar/krontab-mpp/images/download.svg) ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab-mpp/_latestVersion)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab)
[![Build Status](https://travis-ci.com/InsanusMokrassar/krontab.svg?branch=master)](https://travis-ci.com/InsanusMokrassar/krontab) [![Build Status](https://github.com/InsanusMokrassar/krontab/actions/workflows/publishing_packages.yml/badge.svg)](https://github.com/InsanusMokrassar/krontab/actions/workflows/publishing_packages.yml)
[![KDocs](https://raw.githubusercontent.com/InsanusMokrassar/badges/master/kdocs.svg)](https://krontab.inmo.dev/index.html)
Library was created to give oppotunity to launch some things from time to time according to some schedule in Library was created to give oppotunity to launch some things from time to time according to some schedule in
runtime of applications. 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 ## How to use
There are several ways to configure and use this library: There are several ways to configure and use this library:
@@ -46,7 +36,7 @@ implementation "dev.inmo:krontab:$krontab_version"
Next version is the latest currently for the library: Next version is the latest currently for the library:
[ ![Download](https://api.bintray.com/packages/insanusmokrassar/InsanusMokrassar/krontab-mpp/images/download.svg) ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab-mpp/_latestVersion) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab)
For old version of Gradle, instead of `implementation` word developers must use `compile`. For old version of Gradle, instead of `implementation` word developers must use `compile`.
@@ -133,6 +123,38 @@ kronScheduler.doInfinity {
All of these examples will do the same things: print `Called` message every five seconds. 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 ### KronScheduler as a Flow
Any `KronScheduler`can e converted to a `Flow<DateTime` using extension `asFlow`: 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 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` 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 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: Besides, in case you wish to use time zones explicitly, you will need to get `KronSchedulerTz`. It is possible by:

View File

@@ -1,7 +1,7 @@
buildscript { buildscript {
repositories { repositories {
mavenLocal() mavenLocal()
jcenter() // jcenter()
mavenCentral() mavenCentral()
google() google()
} }
@@ -10,7 +10,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version" classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version" classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
classpath 'com.android.tools.build:gradle:4.1.3' classpath 'com.android.tools.build:gradle:7.0.4'
} }
} }
@@ -40,9 +40,9 @@ apply from: "github_release.gradle"
repositories { repositories {
mavenLocal() mavenLocal()
jcenter() // jcenter()
mavenCentral() mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" } // maven { url "https://kotlin.bintray.com/kotlinx" }
google() google()
} }
@@ -50,7 +50,7 @@ apply from: './dokka.gradle'
kotlin { kotlin {
jvm() jvm()
js(BOTH) { js(IR) {
browser() browser()
nodejs() nodejs()
} }
@@ -132,3 +132,8 @@ android {
} }
} }
} }
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -15,31 +15,40 @@ dokkaHtml {
} }
moduleName.set("kdocs") moduleName.set("kdocs")
dokkaSourceSets { dokkaSourceSets {
configureEach { switch (true) {
skipDeprecated.set(true) case project.hasProperty("DOKKA_PATH"):
includeNonPublic.set(true) outputDirectory = new File(project.property("DOKKA_PATH").toString())
reportUndocumented.set(true) break
case System.getenv("DOKKA_PATH") != null:
outputDirectory = new File(System.getenv("DOKKA_PATH"))
break
}
sourceLink { dokkaSourceSets {
localDirectory.set(file("./")) configureEach {
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/krontab/blob/master/")) skipDeprecated.set(true)
remoteLineSuffix.set("#L")
sourceLink {
localDirectory.set(file("./"))
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/krontab/blob/master/"))
remoteLineSuffix.set("#L")
}
} }
}
named("commonMain") { named("commonMain") {
sourceRoot { path = "src/commonMain" } sourceRoots.setFrom(findSourcesWithName("commonMain"))
} }
// //
// named("jsMain") { // named("jsMain") {
// sourceRoot { path = "src/jsMain" } // sourceRoot { path = "src/jsMain" }
// } // }
// //
// named("jvmMain") { // named("jvmMain") {
// sourceRoot { path = "src/jvmMain" } // sourceRoot { path = "src/jvmMain" }
// } // }
}
} }
} }

View File

@@ -8,12 +8,12 @@ android.useAndroidX=true
android.enableJetifier=false android.enableJetifier=false
kotlin_version=1.5.10 kotlin_version=1.6.10
kotlin_coroutines_version=1.5.0 kotlin_coroutines_version=1.6.0
dokka_version=1.4.32 dokka_version=1.6.10
klockVersion=2.1.0 klockVersion=2.5.2
## Github reease ## Github reease
@@ -22,17 +22,16 @@ github_release_plugin_version=2.2.12
## Android ## Android
android_minSdkVersion=19 android_minSdkVersion=19
android_compileSdkVersion=30 android_compileSdkVersion=32
android_buildToolsVersion=30.0.3 android_buildToolsVersion=32.0.0
dexcount_version=2.0.0 dexcount_version=3.0.0
junit_version=4.12 junit_version=4.12
test_ext_junit_version=1.1.2 test_ext_junit_version=1.1.2
espresso_core=3.3.0 espresso_core=3.3.0
androidx_work_version=2.5.0 androidx_work_version=2.7.1
## Common ## Common
version=0.6.0 version=0.7.1
android_code_version=4 android_code_version=12

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip

1913
kotlin-js-store/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,10 @@
package dev.inmo.krontab package dev.inmo.krontab
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
import kotlinx.coroutines.delay 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. * 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 * 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 { suspend inline fun <T> KronScheduler.doOnceLocal(noinline block: suspend (DateTime) -> T): T {
next() ?.let { val time = nextOrNow().also {
delay((it - DateTime.now()).millisecondsLong) 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 * @see buildSchedule
*/ */
suspend inline fun <T> doOnce( suspend inline fun <T> doOnce(
scheduleConfig: String, scheduleConfig: String,
noinline block: suspend () -> T 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 * Will execute [block] while it will return true as a result of its calculation
*/ */
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) { suspend inline fun KronScheduler.doWhileLocal(noinline block: suspend (DateTime) -> Boolean) {
do { val doNext = doOnce(block) } while (doNext) 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] * 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( suspend inline fun doWhile(
scheduleConfig: String, scheduleConfig: String,
noinline block: suspend () -> Boolean 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 * Will execute [block] without any checking of result
*/ */
suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile { suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile {
block() 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] * Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
* *

View File

@@ -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))

View File

@@ -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 } }
} }

View File

@@ -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)
) )
} }

View File

@@ -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)

View File

@@ -1,106 +1,64 @@
package dev.inmo.krontab.internal 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.KronScheduler
/** /**
* @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() },
)

View File

@@ -2,7 +2,6 @@ package dev.inmo.krontab.internal
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.collection.plus
/** /**
* Cron-oriented realisation of [KronScheduler] * Cron-oriented realisation of [KronScheduler]
@@ -19,7 +18,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,30 +26,12 @@ 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()
) )
/**
* @return New instance of [CronDateTimeScheduler] with all unique [CronDateTimeScheduler.cronDateTimes] of
* [kronSchedulers] included
*/
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
fun merge(kronSchedulers: List<KronScheduler>) = kronSchedulers.apply { dev.inmo.krontab.merge() }
/**
* @return Vararg shortcut for [dev.inmo.krontab.merge]
*/
@Suppress("NOTHING_TO_INLINE")
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.apply { dev.inmo.krontab.merge() }
/**
* @return Vararg shortcut for [dev.inmo.krontab.merge]
*/
@Suppress("NOTHING_TO_INLINE")
@Deprecated("Will be removed in next major release", ReplaceWith("merge", "dev.inmo.krontab"))
inline fun KronScheduler.plus(other: KronScheduler) = this + other

View File

@@ -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
)
} }

View File

@@ -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
)
}
)

View File

@@ -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()

View File

@@ -0,0 +1,23 @@
package dev.inmo.krontab.utils
import com.soywiz.klock.DateTime
import com.soywiz.klock.Month
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
)

View File

@@ -2,50 +2,34 @@ package dev.inmo.krontab.utils
import com.soywiz.klock.DateTime import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz import com.soywiz.klock.DateTimeTz
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.*
import dev.inmo.krontab.next import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.channelFlow
/** /**
* This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to * This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to
* time zones * time zones
* *
* @see channelFlow * @see channelFlow
* @see KronSchedulerTz.doInfinityTz
*/ */
@FlowPreview @FlowPreview
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow { fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow {
while (isActive) { doInfinityTz {
val now = DateTime.now().local send(it)
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 * This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s
*
* @see channelFlow
* @see KronScheduler.doInfinityLocal
*/ */
@FlowPreview @FlowPreview
fun KronScheduler.asFlow(): Flow<DateTime> = asTzFlow().map { it.local } fun KronScheduler.asFlow(): Flow<DateTime> = channelFlow {
doInfinityLocal {
@Deprecated( send(it)
"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
) : AbstractFlow<DateTime>() {
@FlowPreview
override suspend fun collectSafely(collector: FlowCollector<DateTime>) {
while (true) {
val now = DateTime.now()
val nextTime = scheduler.next(now) ?: break
val sleepDelay = (nextTime - now).millisecondsLong
delay(sleepDelay)
collector.emit(nextTime)
}
} }
} }

View File

@@ -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) {

View File

@@ -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
) )
} }
} }
} }
} }
} }