mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2025-12-16 18:05:45 +00:00
Compare commits
106 Commits
0.5.1
...
79ce455f5c
| Author | SHA1 | Date | |
|---|---|---|---|
| 79ce455f5c | |||
| fd54b035fe | |||
| 4fab7a28ff | |||
| 28d5665a14 | |||
| f98fd5ddb7 | |||
| 981327675a | |||
| 7649d20984 | |||
| ab274636b9 | |||
| 7f2f8a4a26 | |||
| f64171c0f0 | |||
| 8382108b01 | |||
| 32499d02fb | |||
| 2b06033c75 | |||
| 84f1063e3f | |||
| 347cf64cb2 | |||
| eb73da67eb | |||
| 4aadf513aa | |||
| 8c011ea1fb | |||
| aa9c2b8ea0 | |||
| adf10301dc | |||
| 7799077e44 | |||
| 83339dc095 | |||
| 3040951f37 | |||
| 0bfe022476 | |||
| 06107f75f2 | |||
| 039fe1cb5f | |||
| ea7fab6130 | |||
| 2f3796733d | |||
| 89a6a305f8 | |||
| 97752e1ac0 | |||
| 276ecaac27 | |||
| d36e406b59 | |||
| 4dc65bf09a | |||
| 539b5fbcd8 | |||
| e4f417877d | |||
| 4f6054c0d9 | |||
| 88dbfee370 | |||
| 5af29c2246 | |||
| 6aa40ad463 | |||
| 3062f839af | |||
| 001756ab9f | |||
| b7ebd0ac1c | |||
| ece767e4d0 | |||
| 222542b38f | |||
| 5798774b2d | |||
| 32bf5c0641 | |||
| 5916f3eebb | |||
| df01a66b5a | |||
| a622168297 | |||
| 9202eabf90 | |||
| bc612604eb | |||
| 8808c00dc2 | |||
| f3db5fd2e4 | |||
| b66284b6e5 | |||
| 2a16f3c588 | |||
| 6d055739fe | |||
| 92ae81d7a7 | |||
| 9e9c09e364 | |||
| a8be548a72 | |||
| 6ec77b4e6f | |||
| 0ecb34709b | |||
| 0f973adb9f | |||
| abc7a63d2e | |||
| fbab0e232f | |||
| 57fa3a30a6 | |||
| f50564abe2 | |||
| 8276ebc7ec | |||
| 5a6084c573 | |||
| 437206e915 | |||
| 0ecc6f0028 | |||
| fec17f949f | |||
| c3ed1d024c | |||
| b4790b892a | |||
| 28f84d4e3a | |||
| 22ef00fe8f | |||
| c509d35b00 | |||
| 3de66a7274 | |||
| 1f0387363d | |||
| e816ad88bc | |||
| 404ea5e188 | |||
| 63e110dbc8 | |||
| 39323f3cd1 | |||
| 15e3430a55 | |||
| fde5d91375 | |||
| ba548969e1 | |||
| c6bfc5c8f8 | |||
| 301afb1ee0 | |||
| 7e58ec0873 | |||
| db1af7818e | |||
| 635a2d6a44 | |||
| da88d18d4d | |||
| 20f12f3f2d | |||
| 86cdda51a1 | |||
| bfa546ad1f | |||
| 0c7ceb03ee | |||
| b0889d0677 | |||
| 3408d58cb7 | |||
| 5c81eede9c | |||
| 5844859369 | |||
| a07027399c | |||
| 93246cada7 | |||
| 5311e651f4 | |||
| 68c49faa1d | |||
| 8ce351ecfe | |||
| b43bbea341 | |||
| fff010b890 |
24
.github/workflows/kdocs.yml
vendored
Normal file
24
.github/workflows/kdocs.yml
vendored
Normal 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
|
||||||
9
.github/workflows/publishing_packages.yml
vendored
9
.github/workflows/publishing_packages.yml
vendored
@@ -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 }}
|
||||||
|
|||||||
10
.travis.yml
10
.travis.yml
@@ -1,10 +0,0 @@
|
|||||||
language: java
|
|
||||||
install: true
|
|
||||||
|
|
||||||
os: linux
|
|
||||||
dist: trusty
|
|
||||||
jdk: oraclejdk8
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./gradlew build allTests -s
|
|
||||||
|
|
||||||
84
CHANGELOG.md
84
CHANGELOG.md
@@ -1,5 +1,89 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.7.2
|
||||||
|
|
||||||
|
* Versions
|
||||||
|
* `Coroutines`: `1.6.1`
|
||||||
|
* `Klock`: `2.7.0`
|
||||||
|
* Deprecate `do*Local` due to their redundancy (use `do*` instead)
|
||||||
|
* Add support of milliseconds as optional parameter after month (fix of [#31](https://github.com/InsanusMokrassar/krontab/issues/31))
|
||||||
|
* Add support of filters for `Flow`s with `DateTime` (fix of [#30](https://github.com/InsanusMokrassar/krontab/issues/30))
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
* Versions
|
||||||
|
* `Kotlin`: `1.4.32` -> `1.5.10`
|
||||||
|
* `Coroutines`: `1.4.3` -> `1.5.0`
|
||||||
|
* `Klock`: `2.0.7` -> `2.1.0`
|
||||||
|
|
||||||
|
## 0.5.2
|
||||||
|
|
||||||
|
* Versions
|
||||||
|
* `Kotlin`: `1.4.31` -> `1.4.32`
|
||||||
|
* Supporting of weekdays
|
||||||
|
* Supporting of timezones
|
||||||
|
* Any `KronScheduler` now can be used for calling `next` with `DateTimeTz`
|
||||||
|
* New type `KronSchedulerTz`
|
||||||
|
* `SchedulerFlow` has been deprecated
|
||||||
|
* New extension `asTzFlow` and small changes in `asFlow` logic
|
||||||
|
* `merge` extensions now return `CollectionKronScheduler` instead of just `KronScheduler`
|
||||||
|
|
||||||
## 0.5.1
|
## 0.5.1
|
||||||
|
|
||||||
* Versions
|
* Versions
|
||||||
|
|||||||
87
README.md
87
README.md
@@ -1,20 +1,12 @@
|
|||||||
# krontab
|
# krontab
|
||||||
|
|
||||||
[ ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab-mpp/_latestVersion)
|
|
||||||
[](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab)
|
[](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab)
|
||||||
[](https://travis-ci.com/InsanusMokrassar/krontab)
|
[](https://github.com/InsanusMokrassar/krontab/actions/workflows/publishing_packages.yml)
|
||||||
|
[](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
|
## How to use
|
||||||
|
|
||||||
There are several ways to configure and use this library:
|
There are several ways to configure and use this library:
|
||||||
@@ -44,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:
|
||||||
|
|
||||||
[  ](https://bintray.com/insanusmokrassar/InsanusMokrassar/krontab-mpp/_latestVersion)
|
[](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`.
|
||||||
|
|
||||||
@@ -52,14 +44,17 @@ For old version of Gradle, instead of `implementation` word developers must use
|
|||||||
|
|
||||||
Developers can use more simple way to configure repeat times is string. String configuring
|
Developers can use more simple way to configure repeat times is string. String configuring
|
||||||
like a `crontab`, but with a little bit different meanings:
|
like a `crontab`, but with a little bit different meanings:
|
||||||
|
|
||||||
```
|
```
|
||||||
/---------- Seconds
|
/--------------- Seconds
|
||||||
| /-------- Minutes
|
| /------------- Minutes
|
||||||
| | /------ Hours
|
| | /----------- Hours
|
||||||
| | | /---- Days of months
|
| | | /--------- Days of months
|
||||||
| | | | /-- Months
|
| | | | /------- Months
|
||||||
| | | | | / (optional) Year
|
| | | | | /----- (optional) Year
|
||||||
* * * * * *
|
| | | | | | /--- (optional) Timezone offset
|
||||||
|
| | | | | | | / (optional) Week days
|
||||||
|
* * * * * * 0o *w
|
||||||
```
|
```
|
||||||
|
|
||||||
It is different with original `crontab` syntax for the reason, that expected that in practice developers
|
It is different with original `crontab` syntax for the reason, that expected that in practice developers
|
||||||
@@ -128,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`:
|
||||||
@@ -152,3 +179,27 @@ flow.takeWhile {
|
|||||||
action()
|
action()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Offsets
|
||||||
|
|
||||||
|
Offsets in this library works via passing parameter ending with `o` in any place after `month` config. Currently
|
||||||
|
there is only one format supported for offsets: minutes of offsets. To use time zones you will need to call `next`
|
||||||
|
method with `DateTimeTz` argument or `nextTimeZoned` method with any `KronScheduler` instance, but in case if this
|
||||||
|
scheduler is not instance of `KronSchedulerTz` it will 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:
|
||||||
|
|
||||||
|
* Using `createSimpleScheduler`/`buildSchedule`/`KrontabTemplate#toSchedule`/`KrontabTemplate#toKronScheduler` methods
|
||||||
|
with passing `defaultOffset` parameter
|
||||||
|
* Using `SchedulerBuilder#build`/`createSimpleScheduler`/`buildSchedule`/`KrontabTemplate#toSchedule`/`KrontabTemplate#toKronScheduler`
|
||||||
|
methods with casting to `KronSchedulerTz` in case you are pretty sure that it is timezoned `KronScheduler`
|
||||||
|
* Creating your own implementation of `KronSchedulerTz`
|
||||||
|
|
||||||
|
### Note about week days
|
||||||
|
|
||||||
|
Unlike original CRON, here week days:
|
||||||
|
|
||||||
|
* Works as `AND`: cron date time will search first day which will pass requirement according all parameters including
|
||||||
|
week days
|
||||||
|
* You may use any related to numbers syntax with week days: `0-3w`, `0,1,2,3w`, etc.
|
||||||
|
* Week days (like years and offsets) are optional and can be placed anywhere after `month`
|
||||||
|
|||||||
31
build.gradle
31
build.gradle
@@ -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.0.2'
|
classpath 'com.android.tools.build:gradle:7.0.4'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +18,18 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
|
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
|
||||||
id "org.jetbrains.dokka" version "$dokka_version"
|
id "org.jetbrains.dokka" version "$dokka_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// temporal crutch until legacy tests will be stabled or legacy target will be removed
|
||||||
|
allprojects {
|
||||||
|
if (it != rootProject.findProject("docs")) {
|
||||||
|
tasks.whenTaskAdded { task ->
|
||||||
|
if(task.name == "jsLegacyBrowserTest" || task.name == "jsLegacyNodeTest") {
|
||||||
|
task.enabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
apply plugin: "com.android.library"
|
apply plugin: "com.android.library"
|
||||||
|
|
||||||
project.version = "$version"
|
project.version = "$version"
|
||||||
@@ -28,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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +50,7 @@ apply from: './dokka.gradle'
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm()
|
||||||
js(BOTH) {
|
js(IR) {
|
||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
@@ -113,10 +125,6 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
test {
|
test {
|
||||||
@@ -124,3 +132,8 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|||||||
47
dokka.gradle
47
dokka.gradle
@@ -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" }
|
||||||
// }
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,31 +8,30 @@ android.useAndroidX=true
|
|||||||
android.enableJetifier=false
|
android.enableJetifier=false
|
||||||
|
|
||||||
|
|
||||||
kotlin_version=1.4.31
|
kotlin_version=1.6.10
|
||||||
kotlin_coroutines_version=1.4.3
|
kotlin_coroutines_version=1.6.1
|
||||||
|
|
||||||
dokka_version=1.4.20
|
dokka_version=1.6.10
|
||||||
|
|
||||||
klockVersion=2.0.7
|
klockVersion=2.7.0
|
||||||
|
|
||||||
## Github reease
|
## Github reease
|
||||||
|
|
||||||
github_release_plugin_version=2.2.12
|
github_release_plugin_version=2.3.7
|
||||||
|
|
||||||
## Android
|
## Android
|
||||||
|
|
||||||
android_minSdkVersion=19
|
android_minSdkVersion=19
|
||||||
android_compileSdkVersion=30
|
android_compileSdkVersion=32
|
||||||
android_buildToolsVersion=30.0.2
|
android_buildToolsVersion=32.0.0
|
||||||
dexcount_version=2.0.0
|
dexcount_version=3.0.1
|
||||||
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.5.1
|
version=0.7.2
|
||||||
android_code_version=2
|
android_code_version=13
|
||||||
|
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
|
||||||
|
|||||||
1998
kotlin-js-store/yarn.lock
Normal file
1998
kotlin-js-store/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,11 @@
|
|||||||
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,11 +14,37 @@ 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.doOnce(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
@Deprecated("Replaceable", ReplaceWith("doOnce", "dev.inmo.krontab.doOnce"))
|
||||||
|
suspend inline fun <T> KronScheduler.doOnceLocal(noinline block: suspend (DateTime) -> T): T = doOnce(block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,14 +53,40 @@ suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T
|
|||||||
*/
|
*/
|
||||||
suspend inline fun <T> doOnce(
|
suspend inline fun <T> doOnce(
|
||||||
scheduleConfig: String,
|
scheduleConfig: String,
|
||||||
noinline block: suspend () -> T
|
noinline block: suspend (DateTime) -> T
|
||||||
) = buildSchedule(scheduleConfig).doOnce(block)
|
) = buildSchedule(scheduleConfig).doOnce(block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will [buildSchedule] using [scheduleConfig] and call [doOnce] on it
|
||||||
|
* @see buildSchedule
|
||||||
|
*/
|
||||||
|
suspend inline fun <T> doOnceTz(
|
||||||
|
scheduleConfig: String,
|
||||||
|
noinline block: suspend (DateTimeTz) -> T
|
||||||
|
) = buildSchedule(scheduleConfig).doOnceTz(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.doWhile(noinline block: suspend (DateTime) -> Boolean) {
|
||||||
do { val doNext = doOnce(block) } while (doNext)
|
do {
|
||||||
|
delay(1L)
|
||||||
|
} while (doOnce(block))
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Will execute [block] while it will return true as a result of its calculation
|
||||||
|
*/
|
||||||
|
@Deprecated("Replaceable", ReplaceWith("doWhile", "dev.inmo.krontab.doWhile"))
|
||||||
|
suspend inline fun KronScheduler.doWhileLocal(noinline block: suspend (DateTime) -> Boolean) = doWhile(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))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,16 +96,52 @@ 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 (DateTime) -> Boolean
|
||||||
) = buildSchedule(scheduleConfig).doWhile(block)
|
) = buildSchedule(scheduleConfig).doWhile(block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
|
||||||
|
*
|
||||||
|
* @see buildSchedule
|
||||||
|
*/
|
||||||
|
@Deprecated("Replaceable", ReplaceWith("doWhile", "dev.inmo.krontab.doWhile"))
|
||||||
|
suspend inline fun doWhileLocal(
|
||||||
|
scheduleConfig: String,
|
||||||
|
noinline block: suspend (DateTime) -> Boolean
|
||||||
|
) = doWhile(scheduleConfig, 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 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 (DateTime) -> Unit) = doWhile {
|
||||||
block()
|
block(it)
|
||||||
true
|
coroutineContext.isActive
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Will execute [block] without any checking of result
|
||||||
|
*/
|
||||||
|
@Deprecated("Replaceable", ReplaceWith("doInfinity", "dev.inmo.krontab.doInfinity"))
|
||||||
|
suspend inline fun KronScheduler.doInfinityLocal(noinline block: suspend (DateTime) -> Unit) = doInfinity(block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will execute [block] without any checking of result
|
||||||
|
*/
|
||||||
|
suspend inline fun KronScheduler.doInfinityTz(noinline block: suspend (DateTimeTz) -> Unit) = doWhileTz {
|
||||||
|
block(it)
|
||||||
|
coroutineContext.isActive
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
|
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
|
||||||
*
|
*
|
||||||
@@ -57,5 +149,26 @@ suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit)
|
|||||||
*/
|
*/
|
||||||
suspend inline fun doInfinity(
|
suspend inline fun doInfinity(
|
||||||
scheduleConfig: String,
|
scheduleConfig: String,
|
||||||
noinline block: suspend () -> Unit
|
noinline block: suspend (DateTime) -> Unit
|
||||||
) = buildSchedule(scheduleConfig).doInfinity(block)
|
) = buildSchedule(scheduleConfig).doInfinity(block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
|
||||||
|
*
|
||||||
|
* @see buildSchedule
|
||||||
|
*/
|
||||||
|
@Deprecated("Replaceable", ReplaceWith("doInfinity", "dev.inmo.krontab.doInfinity"))
|
||||||
|
suspend inline fun doInfinityLocal(
|
||||||
|
scheduleConfig: String,
|
||||||
|
noinline block: suspend (DateTime) -> Unit
|
||||||
|
) = doInfinity(scheduleConfig, 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)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package dev.inmo.krontab
|
package dev.inmo.krontab
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import dev.inmo.krontab.internal.toNearDateTime
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface was created for abstraction of [next] operation. Currently, there is only
|
* This interface was created for abstraction of [next] operation. Currently, there is only
|
||||||
|
|||||||
34
src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt
Normal file
34
src/commonMain/kotlin/dev/inmo/krontab/KronSchedulerTz.kt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package dev.inmo.krontab
|
||||||
|
|
||||||
|
import com.soywiz.klock.DateTime
|
||||||
|
import com.soywiz.klock.DateTimeTz
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface extending [KronScheduler] to use [DateTimeTz] with taking into account offset of incoming time for
|
||||||
|
* [next] operation.
|
||||||
|
*
|
||||||
|
* @see dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||||
|
* @see dev.inmo.krontab.KronScheduler
|
||||||
|
*/
|
||||||
|
interface KronSchedulerTz : KronScheduler {
|
||||||
|
suspend fun next(relatively: DateTimeTz): DateTimeTz?
|
||||||
|
|
||||||
|
override suspend fun next(relatively: DateTime): DateTime? = next(relatively.localUnadjusted) ?.local
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun KronSchedulerTz.nextOrRelative(relatively: DateTimeTz): DateTimeTz = next(relatively) ?: getAnyNext(
|
||||||
|
relatively.local
|
||||||
|
).toOffsetUnadjusted(relatively.offset)
|
||||||
|
suspend fun KronSchedulerTz.nextOrNowWithOffset(): DateTimeTz = DateTimeTz.nowLocal().let {
|
||||||
|
next(it) ?: getAnyNext(
|
||||||
|
it.local
|
||||||
|
).toOffsetUnadjusted(it.offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun KronScheduler.next(relatively: DateTimeTz) = if (this is KronSchedulerTz) {
|
||||||
|
this.next(relatively)
|
||||||
|
} else {
|
||||||
|
this.next(relatively.local) ?.toOffsetUnadjusted(relatively.offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun KronScheduler.nextTimeZoned() = next(DateTime.now().local)
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package dev.inmo.krontab
|
package dev.inmo.krontab
|
||||||
|
|
||||||
import dev.inmo.krontab.collection.CollectionKronScheduler
|
import dev.inmo.krontab.collection.CollectionKronScheduler
|
||||||
import dev.inmo.krontab.internal.CronDateTime
|
import dev.inmo.krontab.collection.includeAll
|
||||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
import dev.inmo.krontab.internal.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator]
|
* Create new one [CollectionKronScheduler] to include all [KronScheduler]s of [this] [Iterator]
|
||||||
@@ -10,17 +10,22 @@ import dev.inmo.krontab.internal.CronDateTimeScheduler
|
|||||||
* @see CollectionKronScheduler
|
* @see CollectionKronScheduler
|
||||||
* @see CollectionKronScheduler.include
|
* @see CollectionKronScheduler.include
|
||||||
*/
|
*/
|
||||||
fun Iterator<KronScheduler>.merge(): KronScheduler {
|
fun Iterator<KronScheduler>.merge(): CollectionKronScheduler {
|
||||||
val cronDateTimes = mutableListOf<CronDateTime>()
|
val cronDateTimes = mutableListOf<CronDateTime>()
|
||||||
|
val timezonedCronDateTimes = mutableListOf<CronDateTimeSchedulerTz>()
|
||||||
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)
|
||||||
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()) {
|
||||||
|
collectionScheduler.includeAll(mergeCronDateTimeSchedulers(timezonedCronDateTimes))
|
||||||
}
|
}
|
||||||
return collectionScheduler
|
return collectionScheduler
|
||||||
}
|
}
|
||||||
@@ -32,10 +37,10 @@ fun Iterator<KronScheduler>.merge(): KronScheduler {
|
|||||||
* @see CollectionKronScheduler.include
|
* @see CollectionKronScheduler.include
|
||||||
*/
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun Iterable<KronScheduler>.merge(): KronScheduler = iterator().merge()
|
inline fun Iterable<KronScheduler>.merge(): CollectionKronScheduler = iterator().merge()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Vararg shortcut for [merge]
|
* @return Vararg shortcut for [merge]
|
||||||
*/
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun merge(vararg kronDateTimeSchedulers: KronScheduler) = kronDateTimeSchedulers.iterator().merge()
|
inline fun merge(vararg kronDateTimeSchedulers: KronScheduler): CollectionKronScheduler = kronDateTimeSchedulers.iterator().merge()
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package dev.inmo.krontab
|
|||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import dev.inmo.krontab.builder.buildSchedule
|
import dev.inmo.krontab.builder.buildSchedule
|
||||||
import dev.inmo.krontab.internal.*
|
import dev.inmo.krontab.internal.*
|
||||||
import dev.inmo.krontab.internal.CronDateTime
|
|
||||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
|
||||||
|
|
||||||
internal val anyCronDateTime by lazy {
|
internal val anyCronDateTime by lazy {
|
||||||
CronDateTime()
|
CronDateTime()
|
||||||
@@ -15,7 +13,14 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one millisecond
|
||||||
|
*/
|
||||||
|
val EveryMillisecondScheduler: KronScheduler by lazy {
|
||||||
|
buildSchedule { milliseconds { 0 every 1 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,4 +63,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 } }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package dev.inmo.krontab
|
package dev.inmo.krontab
|
||||||
|
|
||||||
|
import com.soywiz.klock.TimezoneOffset
|
||||||
|
import com.soywiz.klock.minutes
|
||||||
import dev.inmo.krontab.internal.*
|
import dev.inmo.krontab.internal.*
|
||||||
|
import dev.inmo.krontab.utils.Minutes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see createSimpleScheduler
|
* @see createSimpleScheduler
|
||||||
@@ -11,14 +14,17 @@ typealias KrontabTemplate = String
|
|||||||
/**
|
/**
|
||||||
* Parse [incoming] string and adapt according to next format: "* * * * *" where order of things:
|
* Parse [incoming] string and adapt according to next format: "* * * * *" where order of things:
|
||||||
*
|
*
|
||||||
* * seconds
|
* * **seconds**
|
||||||
* * minutes
|
* * **minutes**
|
||||||
* * hours
|
* * **hours**
|
||||||
* * dayOfMonth
|
* * **dayOfMonth**
|
||||||
* * month
|
* * **month**
|
||||||
* * (optional) year
|
* * **year** (optional)
|
||||||
|
* * **offset** (optional) (can be placed anywhere after month) (must be marked with `o` at the end, for example: 60o == +01:00)
|
||||||
|
* * **dayOfWeek** (optional) (can be placed anywhere after month)
|
||||||
|
* * **milliseconds** (optional) (can be placed anywhere after month) (must be marked with `ms` at the end, for example: 500ms; 100-200ms)
|
||||||
*
|
*
|
||||||
* And each one have next format:
|
* And each one (except of offsets) have next format:
|
||||||
*
|
*
|
||||||
* `{number}[,{number},...]` or `*`
|
* `{number}[,{number},...]` or `*`
|
||||||
*
|
*
|
||||||
@@ -31,6 +37,9 @@ typealias KrontabTemplate = String
|
|||||||
* * F
|
* * F
|
||||||
* * L
|
* * L
|
||||||
*
|
*
|
||||||
|
* Week days must be marked with `w` at the end, and starts with 0 which means Sunday. For example, 0w == Sunday. With
|
||||||
|
* weeks you can use syntax like with any number like seconds, for example: 0-2w means Sunday-Tuesday
|
||||||
|
*
|
||||||
* Additional info about ranges can be found in follow accordance:
|
* Additional info about ranges can be found in follow accordance:
|
||||||
*
|
*
|
||||||
* * Seconds ranges can be found in [secondsRange]
|
* * Seconds ranges can be found in [secondsRange]
|
||||||
@@ -39,21 +48,56 @@ typealias KrontabTemplate = String
|
|||||||
* * Days of month ranges can be found in [dayOfMonthRange]
|
* * Days of month ranges can be found in [dayOfMonthRange]
|
||||||
* * Months ranges can be found in [monthRange]
|
* * Months ranges can be found in [monthRange]
|
||||||
* * Years ranges can be found in [yearRange] (in fact - any [Int])
|
* * Years ranges can be found in [yearRange] (in fact - any [Int])
|
||||||
|
* * WeekDay (timezone) ranges can be found in [dayOfWeekRange]
|
||||||
|
* * Milliseconds ranges can be found in [millisecondsRange]
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
*
|
*
|
||||||
* * "0/5 * * * *" for every five seconds triggering
|
* * "0/5 * * * *" for every five seconds triggering
|
||||||
* * "0/5,L * * * *" for every five seconds triggering and on 59 second
|
* * "0/5,L * * * *" for every five seconds triggering and on 59 second
|
||||||
* * "0/15 30 * * *" for every 15th seconds in a half of each hour
|
* * "0/15 30 * * *" for every 15th seconds in a half of each hour
|
||||||
|
* * "0/15 30 * * * 500ms" for every 15th seconds in a half of each hour when milliseconds equal to 500
|
||||||
* * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may
|
* * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may
|
||||||
|
* * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of fourth day of may with timezone UTC+01:00
|
||||||
|
* * "1 2 3 F,4,L 5 60o 0-2w" for triggering in near first second of second minute of third hour of fourth day of may in case if it will be in Sunday-Tuesday week days with timezone UTC+01:00
|
||||||
* * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year
|
* * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year
|
||||||
|
* * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year with timezone UTC+01:00
|
||||||
|
* * "1 2 3 F,4,L 5 2021 60o 0-2w" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00
|
||||||
|
* * "1 2 3 F,4,L 5 2021 60o 0-2w 500ms" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year if it will be in Sunday-Tuesday week days with timezone UTC+01:00 when milliseconds will be equal to 500
|
||||||
|
*
|
||||||
|
* @return In case when offset parameter is absent in [incoming] will be used [createSimpleScheduler] method and
|
||||||
|
* returned [CronDateTimeScheduler]. In case when offset parameter there is in [incoming] [KrontabTemplate] will be used
|
||||||
|
* [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz]
|
||||||
*
|
*
|
||||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||||
*/
|
*/
|
||||||
fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
fun createSimpleScheduler(
|
||||||
val yearSource: String?
|
incoming: KrontabTemplate
|
||||||
|
): KronScheduler {
|
||||||
|
var offsetParsed: Int? = null
|
||||||
|
var dayOfWeekParsed: Array<Byte>? = null
|
||||||
|
var yearParsed: Array<Int>? = null
|
||||||
|
var millisecondsParsed: Array<Short>? = null
|
||||||
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also {
|
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also {
|
||||||
yearSource = it.getOrNull(5)
|
listOfNotNull(
|
||||||
|
it.getOrNull(5),
|
||||||
|
it.getOrNull(6),
|
||||||
|
it.getOrNull(7),
|
||||||
|
it.getOrNull(8)
|
||||||
|
).forEach {
|
||||||
|
val offsetFromString = parseOffset(it)
|
||||||
|
val dayOfWeekFromString = parseWeekDay(it)
|
||||||
|
val millisecondsFromString = parseMilliseconds(it)
|
||||||
|
offsetParsed = offsetParsed ?: offsetFromString
|
||||||
|
dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString
|
||||||
|
millisecondsParsed = millisecondsParsed ?: millisecondsFromString
|
||||||
|
when {
|
||||||
|
dayOfWeekFromString != null || offsetFromString != null || millisecondsFromString != null -> return@forEach
|
||||||
|
yearParsed == null -> {
|
||||||
|
yearParsed = parseYears(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val secondsParsed = parseSeconds(secondsSource)
|
val secondsParsed = parseSeconds(secondsSource)
|
||||||
@@ -61,24 +105,69 @@ fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
|||||||
val hoursParsed = parseHours(hoursSource)
|
val hoursParsed = parseHours(hoursSource)
|
||||||
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
||||||
val monthParsed = parseMonths(monthSource)
|
val monthParsed = parseMonths(monthSource)
|
||||||
val yearParsed = parseYears(yearSource)
|
|
||||||
|
|
||||||
return createKronScheduler(
|
return offsetParsed ?.let { offset ->
|
||||||
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed
|
createKronSchedulerWithOffset(
|
||||||
|
secondsParsed,
|
||||||
|
minutesParsed,
|
||||||
|
hoursParsed,
|
||||||
|
dayOfMonthParsed,
|
||||||
|
monthParsed,
|
||||||
|
yearParsed,
|
||||||
|
dayOfWeekParsed,
|
||||||
|
TimezoneOffset(offset.minutes),
|
||||||
|
millisecondsParsed ?: millisecondsArrayDefault
|
||||||
|
)
|
||||||
|
} ?: createKronScheduler(
|
||||||
|
secondsParsed,
|
||||||
|
minutesParsed,
|
||||||
|
hoursParsed,
|
||||||
|
dayOfMonthParsed,
|
||||||
|
monthParsed,
|
||||||
|
yearParsed,
|
||||||
|
dayOfWeekParsed,
|
||||||
|
millisecondsParsed ?: millisecondsArrayDefault
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createSimpleScheduler(
|
||||||
|
incoming: KrontabTemplate,
|
||||||
|
defaultOffset: Minutes
|
||||||
|
): KronSchedulerTz {
|
||||||
|
val scheduler = createSimpleScheduler(incoming)
|
||||||
|
return if (scheduler is KronSchedulerTz) {
|
||||||
|
scheduler
|
||||||
|
} else {
|
||||||
|
CronDateTimeSchedulerTz(
|
||||||
|
(scheduler as CronDateTimeScheduler).cronDateTime,
|
||||||
|
TimezoneOffset(defaultOffset.minutes)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut for [createSimpleScheduler]
|
* Shortcut for [createSimpleScheduler]
|
||||||
*/
|
*/
|
||||||
fun buildSchedule(incoming: KrontabTemplate): KronScheduler = createSimpleScheduler(incoming)
|
fun buildSchedule(incoming: KrontabTemplate): KronScheduler = createSimpleScheduler(incoming)
|
||||||
|
/**
|
||||||
|
* Shortcut for [createSimpleScheduler]
|
||||||
|
*/
|
||||||
|
fun buildSchedule(incoming: KrontabTemplate, defaultOffset: Minutes): KronSchedulerTz = createSimpleScheduler(incoming, defaultOffset)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut for [buildSchedule]
|
* Shortcut for [buildSchedule]
|
||||||
*/
|
*/
|
||||||
fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this)
|
fun KrontabTemplate.toSchedule(): KronScheduler = buildSchedule(this)
|
||||||
|
/**
|
||||||
|
* Shortcut for [buildSchedule]
|
||||||
|
*/
|
||||||
|
fun KrontabTemplate.toSchedule(defaultOffset: Minutes): KronSchedulerTz = buildSchedule(this, defaultOffset)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut for [buildSchedule]
|
* Shortcut for [buildSchedule]
|
||||||
*/
|
*/
|
||||||
fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this)
|
fun KrontabTemplate.toKronScheduler(): KronScheduler = buildSchedule(this)
|
||||||
|
/**
|
||||||
|
* Shortcut for [buildSchedule]
|
||||||
|
*/
|
||||||
|
fun KrontabTemplate.toKronScheduler(defaultOffset: Minutes): KronSchedulerTz = buildSchedule(this, defaultOffset)
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
package dev.inmo.krontab.builder
|
package dev.inmo.krontab.builder
|
||||||
|
|
||||||
|
import com.soywiz.klock.TimezoneOffset
|
||||||
|
import com.soywiz.klock.minutes
|
||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.KronScheduler
|
||||||
|
import dev.inmo.krontab.KronSchedulerTz
|
||||||
|
import dev.inmo.krontab.internal.*
|
||||||
import dev.inmo.krontab.internal.createKronScheduler
|
import dev.inmo.krontab.internal.createKronScheduler
|
||||||
|
import dev.inmo.krontab.internal.createKronSchedulerWithOffset
|
||||||
|
import dev.inmo.krontab.utils.Minutes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will help to create an instance of [KronScheduler]
|
* Will help to create an instance of [KronScheduler]
|
||||||
@@ -16,13 +22,32 @@ fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
|
|||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will help to create an instance of [KronScheduler]
|
||||||
|
*
|
||||||
|
* @see dev.inmo.krontab.createSimpleScheduler
|
||||||
|
*/
|
||||||
|
fun buildSchedule(
|
||||||
|
offset: Minutes,
|
||||||
|
settingsBlock: SchedulerBuilder.() -> Unit
|
||||||
|
): KronSchedulerTz {
|
||||||
|
val builder = SchedulerBuilder(offset = offset)
|
||||||
|
|
||||||
|
builder.settingsBlock()
|
||||||
|
|
||||||
|
return builder.build() as KronSchedulerTz
|
||||||
|
}
|
||||||
|
|
||||||
class SchedulerBuilder(
|
class SchedulerBuilder(
|
||||||
private var seconds: Array<Byte>? = null,
|
private var seconds: Array<Byte>? = null,
|
||||||
private var minutes: Array<Byte>? = null,
|
private var minutes: Array<Byte>? = null,
|
||||||
private var hours: Array<Byte>? = null,
|
private var hours: Array<Byte>? = null,
|
||||||
private var dayOfMonth: Array<Byte>? = null,
|
private var dayOfMonth: Array<Byte>? = null,
|
||||||
private var month: Array<Byte>? = null,
|
private var month: Array<Byte>? = null,
|
||||||
private var year: Array<Int>? = null
|
private var year: Array<Int>? = null,
|
||||||
|
private var dayOfWeek: Array<Byte>? = null,
|
||||||
|
private val offset: Minutes? = null,
|
||||||
|
private var milliseconds: Array<Short>? = null
|
||||||
) {
|
) {
|
||||||
private fun <I, T : TimeBuilder<I>> callAndReturn(
|
private fun <I, T : TimeBuilder<I>> callAndReturn(
|
||||||
initial: Array<I>?,
|
initial: Array<I>?,
|
||||||
@@ -40,6 +65,17 @@ class SchedulerBuilder(
|
|||||||
} ?: builderValue
|
} ?: builderValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an milliseconds block
|
||||||
|
*/
|
||||||
|
fun milliseconds(block: MillisecondsBuilder.() -> Unit) {
|
||||||
|
milliseconds = callAndReturn(
|
||||||
|
milliseconds,
|
||||||
|
MillisecondsBuilder(),
|
||||||
|
block
|
||||||
|
) ?.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts an seconds block
|
* Starts an seconds block
|
||||||
*/
|
*/
|
||||||
@@ -84,6 +120,17 @@ class SchedulerBuilder(
|
|||||||
) ?.toTypedArray()
|
) ?.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an hours block
|
||||||
|
*/
|
||||||
|
fun dayOfWeek(block: WeekDaysBuilder.() -> Unit) {
|
||||||
|
dayOfWeek = callAndReturn(
|
||||||
|
dayOfWeek,
|
||||||
|
WeekDaysBuilder(),
|
||||||
|
block
|
||||||
|
) ?.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts an months block
|
* Starts an months block
|
||||||
*/
|
*/
|
||||||
@@ -112,5 +159,17 @@ class SchedulerBuilder(
|
|||||||
* @see dev.inmo.krontab.createSimpleScheduler
|
* @see dev.inmo.krontab.createSimpleScheduler
|
||||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||||
*/
|
*/
|
||||||
fun build(): KronScheduler = createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year)
|
fun build(): KronScheduler = offset ?.let {
|
||||||
|
createKronSchedulerWithOffset(
|
||||||
|
seconds,
|
||||||
|
minutes,
|
||||||
|
hours,
|
||||||
|
dayOfMonth,
|
||||||
|
month,
|
||||||
|
year,
|
||||||
|
dayOfWeek,
|
||||||
|
TimezoneOffset(it.minutes),
|
||||||
|
milliseconds ?: millisecondsArrayDefault
|
||||||
|
)
|
||||||
|
} ?: createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year, dayOfWeek, milliseconds ?: millisecondsArrayDefault)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package dev.inmo.krontab.builder
|
package dev.inmo.krontab.builder
|
||||||
|
|
||||||
import dev.inmo.krontab.internal.*
|
import dev.inmo.krontab.internal.*
|
||||||
import dev.inmo.krontab.utils.clamp
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class was created for incapsulation of builder work with specified [restrictionsRange]. For example,
|
* This class was created for incapsulation of builder work with specified [restrictionsRange]. For example,
|
||||||
* [include] function of [TimeBuilder] will always [clamp] incoming data using its [restrictionsRange]
|
* [include] function of [TimeBuilder] will always [coerceIn] incoming data using its [restrictionsRange]
|
||||||
*/
|
*/
|
||||||
sealed class TimeBuilder<T : Number> (
|
sealed class TimeBuilder<T : Number> (
|
||||||
private val restrictionsRange: IntRange,
|
private val restrictionsRange: IntRange,
|
||||||
@@ -37,7 +36,7 @@ sealed class TimeBuilder<T : Number> (
|
|||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
infix fun include(array: Array<Int>) {
|
infix fun include(array: Array<Int>) {
|
||||||
val clamped = array.map { it.clamp(restrictionsRange) } + (result ?: emptySet())
|
val clamped = array.map { it.coerceIn(restrictionsRange) } + (result ?: emptySet())
|
||||||
result = clamped.toSet()
|
result = clamped.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ sealed class TimeBuilder<T : Number> (
|
|||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
infix fun at(value: Int) {
|
infix fun at(value: Int) {
|
||||||
result = (result ?: emptySet()) + value.clamp(restrictionsRange)
|
result = (result ?: emptySet()) + value.coerceIn(restrictionsRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +69,7 @@ sealed class TimeBuilder<T : Number> (
|
|||||||
* @see [from]
|
* @see [from]
|
||||||
*/
|
*/
|
||||||
infix fun Int.every(delay: Int): Array<Int> {
|
infix fun Int.every(delay: Int): Array<Int> {
|
||||||
val progression = clamp(restrictionsRange) .. restrictionsRange.last step delay
|
val progression = coerceIn(restrictionsRange) .. restrictionsRange.last step delay
|
||||||
val result = progression.toSet().toTypedArray()
|
val result = progression.toSet().toTypedArray()
|
||||||
|
|
||||||
this@TimeBuilder include result
|
this@TimeBuilder include result
|
||||||
@@ -88,7 +87,7 @@ sealed class TimeBuilder<T : Number> (
|
|||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
infix fun Int.upTo(endIncluding: Int): Array<Int> {
|
infix fun Int.upTo(endIncluding: Int): Array<Int> {
|
||||||
val progression = clamp(restrictionsRange) .. endIncluding.clamp(restrictionsRange)
|
val progression = coerceIn(restrictionsRange) .. endIncluding.coerceIn(restrictionsRange)
|
||||||
val result = progression.toSet().toTypedArray()
|
val result = progression.toSet().toTypedArray()
|
||||||
|
|
||||||
this@TimeBuilder include result
|
this@TimeBuilder include result
|
||||||
@@ -123,9 +122,11 @@ sealed class TimeBuilder<T : Number> (
|
|||||||
internal fun build() = result ?.map(converter)
|
internal fun build() = result ?.map(converter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MillisecondsBuilder : TimeBuilder<Short>(millisecondsRange, intToShortConverter)
|
||||||
class SecondsBuilder : TimeBuilder<Byte>(secondsRange, intToByteConverter)
|
class SecondsBuilder : TimeBuilder<Byte>(secondsRange, intToByteConverter)
|
||||||
class MinutesBuilder : TimeBuilder<Byte>(minutesRange, intToByteConverter)
|
class MinutesBuilder : TimeBuilder<Byte>(minutesRange, intToByteConverter)
|
||||||
class HoursBuilder : TimeBuilder<Byte>(hoursRange, intToByteConverter)
|
class HoursBuilder : TimeBuilder<Byte>(hoursRange, intToByteConverter)
|
||||||
class DaysOfMonthBuilder : TimeBuilder<Byte>(dayOfMonthRange, intToByteConverter)
|
class DaysOfMonthBuilder : TimeBuilder<Byte>(dayOfMonthRange, intToByteConverter)
|
||||||
class MonthsBuilder : TimeBuilder<Byte>(monthRange, intToByteConverter)
|
class MonthsBuilder : TimeBuilder<Byte>(monthRange, intToByteConverter)
|
||||||
class YearsBuilder : TimeBuilder<Int>(yearRange, intToIntConverter)
|
class YearsBuilder : TimeBuilder<Int>(yearRange, intToIntConverter)
|
||||||
|
class WeekDaysBuilder : TimeBuilder<Byte>(dayOfWeekRange, intToByteConverter)
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
package dev.inmo.krontab.collection
|
package dev.inmo.krontab.collection
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
|
import com.soywiz.klock.DateTimeTz
|
||||||
import dev.inmo.krontab.*
|
import dev.inmo.krontab.*
|
||||||
import dev.inmo.krontab.internal.*
|
import dev.inmo.krontab.internal.*
|
||||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
|
||||||
import dev.inmo.krontab.internal.toNearDateTime
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This scheduler will be useful in case you want to unite several different [KronScheduler]s
|
* This scheduler will be useful in case you want to unite several different [KronScheduler]s
|
||||||
*/
|
*/
|
||||||
data class CollectionKronScheduler internal constructor(
|
data class CollectionKronScheduler internal constructor(
|
||||||
internal val schedulers: MutableList<KronScheduler>
|
internal val schedulers: MutableList<KronScheduler>
|
||||||
) : KronScheduler {
|
) : KronSchedulerTz {
|
||||||
internal constructor() : this(mutableListOf())
|
internal constructor() : this(mutableListOf())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,6 +37,18 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
mergeCronDateTimeSchedulers(resultCronDateTimes)
|
mergeCronDateTimeSchedulers(resultCronDateTimes)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is CronDateTimeSchedulerTz -> {
|
||||||
|
val newCronDateTimes = mutableListOf(kronScheduler.cronDateTime)
|
||||||
|
schedulers.removeAll {
|
||||||
|
if (it is CronDateTimeSchedulerTz && it.offset == kronScheduler.offset) {
|
||||||
|
newCronDateTimes.add(it.cronDateTime)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.merge(), kronScheduler.offset))
|
||||||
|
}
|
||||||
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
||||||
include(it)
|
include(it)
|
||||||
}
|
}
|
||||||
@@ -48,4 +59,8 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
override suspend fun next(relatively: DateTime): DateTime {
|
override suspend fun next(relatively: DateTime): DateTime {
|
||||||
return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun next(relatively: DateTimeTz): DateTimeTz {
|
||||||
|
return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively.local).toOffsetUnadjusted(relatively.offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +1,76 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import com.soywiz.klock.DateTimeSpan
|
import com.soywiz.klock.TimezoneOffset
|
||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.KronScheduler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param month 0-11
|
* @param daysOfWeek 0-6
|
||||||
* @param dayOfMonth 0-31
|
* @param years any int
|
||||||
|
* @param months 0-11
|
||||||
|
* @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 year: Int? = null,
|
val daysOfWeek: Array<Byte>? = null,
|
||||||
val month: Byte? = null,
|
val years: Array<Int>? = null,
|
||||||
val dayOfMonth: Byte? = null,
|
val months: Array<Byte>? = null,
|
||||||
val hours: Byte? = null,
|
val daysOfMonth: Array<Byte>? = null,
|
||||||
val minutes: Byte? = null,
|
val hours: Array<Byte>? = null,
|
||||||
val seconds: Byte? = null
|
val minutes: Array<Byte>? = null,
|
||||||
|
val seconds: Array<Byte>? = null,
|
||||||
|
val milliseconds: Array<Short>? = millisecondsArrayDefault
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
check(year ?.let { it in yearRange } ?: true)
|
check(daysOfWeek ?.all { it in dayOfWeekRange } ?: true)
|
||||||
check(month ?.let { it in monthRange } ?: true)
|
check(years?.all { it in yearRange } ?: true)
|
||||||
check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true)
|
check(months?.all { it in monthRange } ?: true)
|
||||||
check(hours?.let { it in hoursRange } ?: true)
|
check(daysOfMonth ?.all { it in dayOfMonthRange } ?: true)
|
||||||
check(minutes?.let { it in minutesRange } ?: true)
|
check(hours?.all { it in hoursRange } ?: true)
|
||||||
check(seconds?.let { it in secondsRange } ?: true)
|
check(minutes?.all { it in minutesRange } ?: true)
|
||||||
|
check(seconds?.all { it in secondsRange } ?: true)
|
||||||
|
check(milliseconds?.all { it in millisecondsRange } ?: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val klockDayOfMonth = dayOfMonth ?.plus(1)
|
internal val calculators = listOf(
|
||||||
|
years ?.let { NearDateTimeCalculatorYears(it) },
|
||||||
|
daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) },
|
||||||
|
milliseconds ?.let { NearDateTimeCalculatorMillis(it) },
|
||||||
|
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? {
|
||||||
|
var current = relativelyTo
|
||||||
|
whileLoop@while (true) {
|
||||||
|
for (calculator in calculators) {
|
||||||
|
val (calculated, requireRecalculation) = (calculator ?: continue).calculateNearTime(current) ?: return null
|
||||||
|
current = calculated
|
||||||
|
if (requireRecalculation) {
|
||||||
|
continue@whileLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
internal fun createCronDateTime(
|
||||||
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
seconds: Array<Byte>? = null,
|
||||||
*/
|
minutes: Array<Byte>? = null,
|
||||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
hours: Array<Byte>? = null,
|
||||||
var current = relativelyTo
|
dayOfMonth: Array<Byte>? = null,
|
||||||
|
month: Array<Byte>? = null,
|
||||||
seconds?.let {
|
years: Array<Int>? = null,
|
||||||
val left = it - current.seconds
|
weekDays: Array<Byte>? = null,
|
||||||
current += DateTimeSpan(minutes = if (left <= 0) 1 else 0, seconds = left)
|
milliseconds: Array<Short>? = millisecondsArrayDefault
|
||||||
}
|
): CronDateTime {
|
||||||
|
return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds, milliseconds)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,33 +82,55 @@ internal fun createKronScheduler(
|
|||||||
hours: Array<Byte>? = null,
|
hours: Array<Byte>? = null,
|
||||||
dayOfMonth: Array<Byte>? = null,
|
dayOfMonth: Array<Byte>? = null,
|
||||||
month: Array<Byte>? = null,
|
month: Array<Byte>? = null,
|
||||||
years: Array<Int>? = null
|
years: Array<Int>? = null,
|
||||||
): KronScheduler {
|
weekDays: Array<Byte>? = null,
|
||||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
milliseconds: Array<Short>? = millisecondsArrayDefault
|
||||||
|
): KronScheduler = CronDateTimeScheduler(
|
||||||
|
createCronDateTime(
|
||||||
|
seconds,
|
||||||
|
minutes,
|
||||||
|
hours,
|
||||||
|
dayOfMonth,
|
||||||
|
month,
|
||||||
|
years,
|
||||||
|
weekDays,
|
||||||
|
milliseconds
|
||||||
|
)
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
|
||||||
|
*/
|
||||||
|
internal fun createKronSchedulerWithOffset(
|
||||||
|
seconds: Array<Byte>? = null,
|
||||||
|
minutes: Array<Byte>? = null,
|
||||||
|
hours: Array<Byte>? = null,
|
||||||
|
dayOfMonth: Array<Byte>? = null,
|
||||||
|
month: Array<Byte>? = null,
|
||||||
|
years: Array<Int>? = null,
|
||||||
|
weekDays: Array<Byte>? = null,
|
||||||
|
offset: TimezoneOffset,
|
||||||
|
milliseconds: Array<Short>? = millisecondsArrayDefault
|
||||||
|
): KronScheduler = CronDateTimeSchedulerTz(
|
||||||
|
createCronDateTime(
|
||||||
|
seconds,
|
||||||
|
minutes,
|
||||||
|
hours,
|
||||||
|
dayOfMonth,
|
||||||
|
month,
|
||||||
|
years,
|
||||||
|
weekDays,
|
||||||
|
milliseconds
|
||||||
|
),
|
||||||
|
offset
|
||||||
|
)
|
||||||
|
|
||||||
seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
internal fun List<CronDateTime>.merge() = CronDateTime(
|
||||||
previousCronDateTime.copy(seconds = currentTime)
|
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() },
|
||||||
minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
flatMap { it.daysOfMonth ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
previousCronDateTime.copy(minutes = currentTime)
|
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() },
|
||||||
hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
flatMap { it.milliseconds ?.toList() ?: listOf(0) }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import dev.inmo.krontab.*
|
import dev.inmo.krontab.KronScheduler
|
||||||
import dev.inmo.krontab.collection.plus
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cron-oriented realisation of [KronScheduler]
|
* Cron-oriented realisation of [KronScheduler]
|
||||||
*
|
*
|
||||||
* @see dev.inmo.krontab.AnyTimeScheduler
|
* @see dev.inmo.krontab.AnyTimeScheduler
|
||||||
|
* @see dev.inmo.krontab.EveryMillisecondScheduler
|
||||||
* @see dev.inmo.krontab.EverySecondScheduler
|
* @see dev.inmo.krontab.EverySecondScheduler
|
||||||
* @see dev.inmo.krontab.EveryMinuteScheduler
|
* @see dev.inmo.krontab.EveryMinuteScheduler
|
||||||
* @see dev.inmo.krontab.EveryHourScheduler
|
* @see dev.inmo.krontab.EveryHourScheduler
|
||||||
@@ -19,38 +19,20 @@ 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
|
||||||
*
|
*
|
||||||
* @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() ?: getAnyNext(relatively)
|
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
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
|
import com.soywiz.klock.DateTimeTz
|
||||||
|
import com.soywiz.klock.TimezoneOffset
|
||||||
|
import dev.inmo.krontab.KronScheduler
|
||||||
|
import dev.inmo.krontab.KronSchedulerTz
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cron-oriented realisation of [KronScheduler] with taking into account [offset] for list of [cronDateTimes]
|
||||||
|
*
|
||||||
|
* @see CronDateTime
|
||||||
|
*/
|
||||||
|
internal data class CronDateTimeSchedulerTz internal constructor(
|
||||||
|
internal val cronDateTime: CronDateTime,
|
||||||
|
internal val offset: TimezoneOffset
|
||||||
|
) : KronSchedulerTz {
|
||||||
|
override suspend fun next(relatively: DateTimeTz): DateTimeTz? {
|
||||||
|
val dateTimeWithActualOffset = relatively.toOffset(offset).local
|
||||||
|
return cronDateTime.toNearDateTime(dateTimeWithActualOffset) ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun mergeCronDateTimeSchedulers(
|
||||||
|
schedulers: List<CronDateTimeSchedulerTz>
|
||||||
|
) = schedulers.groupBy {
|
||||||
|
it.offset
|
||||||
|
}.map { (offset, schedulers) ->
|
||||||
|
CronDateTimeSchedulerTz(
|
||||||
|
schedulers.map { it.cronDateTime }.merge(),
|
||||||
|
offset
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
|
internal const val millisecondsDefault: Short = 0
|
||||||
|
internal val millisecondsArrayDefault: Array<Short> = arrayOf(millisecondsDefault)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
import dev.inmo.krontab.utils.clamp
|
|
||||||
|
|
||||||
typealias Converter<T> = (Int) -> T
|
typealias Converter<T> = (Int) -> T
|
||||||
|
|
||||||
internal val intToByteConverter: Converter<Byte> = { it: Int -> it.toByte() }
|
internal val intToByteConverter: Converter<Byte> = { it: Int -> it.toByte() }
|
||||||
|
internal val intToShortConverter: Converter<Short> = { it: Int -> it.toShort() }
|
||||||
internal val intToIntConverter: Converter<Int> = { it: Int -> it }
|
internal val intToIntConverter: Converter<Int> = { it: Int -> it }
|
||||||
private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataConverter: Converter<T>): List<T>? {
|
private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataConverter: Converter<T>): List<T>? {
|
||||||
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()
|
||||||
@@ -18,7 +17,7 @@ private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataCon
|
|||||||
when {
|
when {
|
||||||
currentToken.contains("-") -> {
|
currentToken.contains("-") -> {
|
||||||
val splitted = currentToken.split("-")
|
val splitted = currentToken.split("-")
|
||||||
(splitted.first().toInt().clamp(dataRange) .. splitted[1].toInt().clamp(dataRange)).toList()
|
(splitted.first().toInt().coerceIn(dataRange) .. splitted[1].toInt().coerceIn(dataRange)).toList()
|
||||||
}
|
}
|
||||||
currentToken.contains("/") -> {
|
currentToken.contains("/") -> {
|
||||||
val (start, step) = currentToken.split("/")
|
val (start, step) = currentToken.split("/")
|
||||||
@@ -26,24 +25,27 @@ private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataCon
|
|||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
start.toInt()
|
start.toInt()
|
||||||
}).clamp(dataRange)
|
}).coerceIn(dataRange)
|
||||||
val stepNum = step.toInt().clamp(dataRange)
|
val stepNum = step.toInt().coerceIn(dataRange)
|
||||||
(startNum .. dataRange.last step stepNum).map { it }
|
(startNum .. dataRange.last step stepNum).map { it }
|
||||||
}
|
}
|
||||||
currentToken == "*" -> return null
|
currentToken == "*" -> return null
|
||||||
else -> listOf(currentToken.toInt().clamp(dataRange))
|
else -> listOf(currentToken.toInt().coerceIn(dataRange))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results.map(dataConverter)
|
return results.map(dataConverter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun parseWeekDay(from: String?) = from ?.let { if (it.endsWith("w")) createSimpleScheduler(it.removeSuffix("w"), dayOfWeekRange, intToByteConverter) ?.toTypedArray() else null }
|
||||||
|
internal fun parseOffset(from: String?) = from ?.let { if (it.endsWith("o")) it.removeSuffix("o").toIntOrNull() else null }
|
||||||
internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() }
|
internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() }
|
||||||
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray()
|
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange, intToByteConverter) ?.toTypedArray()
|
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseHours(from: String) = createSimpleScheduler(from, hoursRange, intToByteConverter) ?.toTypedArray()
|
internal fun parseHours(from: String) = createSimpleScheduler(from, hoursRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseMinutes(from: String) = createSimpleScheduler(from, minutesRange, intToByteConverter) ?.toTypedArray()
|
internal fun parseMinutes(from: String) = createSimpleScheduler(from, minutesRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseSeconds(from: String) = createSimpleScheduler(from, secondsRange, intToByteConverter) ?.toTypedArray()
|
internal fun parseSeconds(from: String) = createSimpleScheduler(from, secondsRange, intToByteConverter) ?.toTypedArray()
|
||||||
|
internal fun parseMilliseconds(from: String?) = from ?.let { if (it.endsWith("ms")) createSimpleScheduler(from.removeSuffix("ms"), millisecondsRange, intToShortConverter) ?.toTypedArray() else null }
|
||||||
|
|
||||||
internal fun <T> Array<T>.fillWith(
|
internal fun <T> Array<T>.fillWith(
|
||||||
whereToPut: MutableList<CronDateTime>,
|
whereToPut: MutableList<CronDateTime>,
|
||||||
@@ -60,3 +62,16 @@ internal fun <T> Array<T>.fillWith(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun <T> T.fillWith(
|
||||||
|
whereToPut: MutableList<CronDateTime>,
|
||||||
|
createFactory: (CronDateTime, T) -> CronDateTime
|
||||||
|
) {
|
||||||
|
val previousValues = whereToPut.toList()
|
||||||
|
|
||||||
|
whereToPut.clear()
|
||||||
|
|
||||||
|
previousValues.forEach { previousValue ->
|
||||||
|
whereToPut.add(createFactory(previousValue, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
|
internal val dayOfWeekRange = 0 .. 6
|
||||||
internal val yearRange = Int.MIN_VALUE .. Int.MAX_VALUE
|
internal val yearRange = Int.MIN_VALUE .. Int.MAX_VALUE
|
||||||
internal val monthRange = 0 .. 11
|
internal val monthRange = 0 .. 11
|
||||||
internal val dayOfMonthRange = 0 .. 30
|
internal val dayOfMonthRange = 0 .. 30
|
||||||
internal val hoursRange = 0 .. 23
|
internal val hoursRange = 0 .. 23
|
||||||
internal val minutesRange = 0 .. 59
|
internal val minutesRange = 0 .. 59
|
||||||
internal val secondsRange = minutesRange
|
internal val secondsRange = minutesRange
|
||||||
|
internal val millisecondsRange = 0 .. 999
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package dev.inmo.krontab.utils
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return [min] in case if [this] less than [min]. Otherwise will check that [max] grant than [this] and return [this]
|
|
||||||
* if so or [max] otherwise
|
|
||||||
*/
|
|
||||||
internal fun Int.clamp(min: Int, max: Int): Int = if (this < min) min else if (this > max) max else this
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper function for [clamp] extension
|
|
||||||
*/
|
|
||||||
internal fun Int.clamp(range: IntRange): Int = clamp(range.first, range.last)
|
|
||||||
23
src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt
Normal file
23
src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt
Normal 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
|
||||||
|
)
|
||||||
@@ -1,26 +1,36 @@
|
|||||||
package dev.inmo.krontab.utils
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import dev.inmo.krontab.KronScheduler
|
import com.soywiz.klock.DateTimeTz
|
||||||
|
import dev.inmo.krontab.*
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.channelFlow
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to
|
||||||
|
* time zones
|
||||||
|
*
|
||||||
|
* @see channelFlow
|
||||||
|
* @see KronSchedulerTz.doInfinityTz
|
||||||
|
*/
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
fun KronScheduler.asFlow(): Flow<DateTime> = SchedulerFlow(this)
|
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow {
|
||||||
|
doInfinityTz {
|
||||||
@FlowPreview
|
send(it)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s
|
||||||
|
*
|
||||||
|
* @see channelFlow
|
||||||
|
* @see KronScheduler.doInfinity
|
||||||
|
*/
|
||||||
|
@FlowPreview
|
||||||
|
fun KronScheduler.asFlow(): Flow<DateTime> = channelFlow {
|
||||||
|
doInfinity {
|
||||||
|
send(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
|
typealias Minutes = Int
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package dev.inmo.krontab.utils.flows
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterSeconds(vararg seconds: Int) = filter { it.seconds in seconds }
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterMinutes(vararg minutes: Int) = filter { it.minutes in minutes }
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterHours(vararg hours: Int) = filter { it.hours in hours }
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterDaysOfMonths(vararg daysOfMonths: Int) = filter { it.dayOfMonth in daysOfMonths }
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterMonths(vararg months: Month) = filter { it.month in months }
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterMonths0(vararg months: Int) = filter { it.month0 in months }
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterMonths1(vararg months: Int) = filter { it.month1 in months }
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterYears(year: Year) = filter { it.year == year }
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterYears(vararg years: Int) = filter { it.yearInt in years }
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterWeekDays(vararg weekDays: DayOfWeek) = filter { it.dayOfWeek in weekDays }
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.filterWeekDays(vararg weekDays: Int) = filter { it.dayOfWeekInt in weekDays }
|
||||||
25
src/commonMain/kotlin/dev/inmo/krontab/utils/flows/When.kt
Normal file
25
src/commonMain/kotlin/dev/inmo/krontab/utils/flows/When.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package dev.inmo.krontab.utils.flows
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.onlyStartsOfMinutes() = filterSeconds(0)
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.onlyStartsOfHours() = filterMinutes(0).onlyStartsOfMinutes()
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.onlyStartsOfDays() = filterHours(0).onlyStartsOfHours()
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.onlyStartsOfMondays() = filterWeekDays(DayOfWeek.Monday).onlyStartsOfDays()
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.onlyStartsOfSundays() = filterWeekDays(DayOfWeek.Sunday).onlyStartsOfDays()
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.onlyStartsOfMonths() = filterDaysOfMonths(1).onlyStartsOfDays()
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Flow<DateTime>.onlyStartsOfYears() = filterMonths(Month.January).onlyStartsOfMonths()
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package dev.inmo.krontab.utils
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import dev.inmo.krontab.KronSchedulerTz
|
||||||
import dev.inmo.krontab.buildSchedule
|
import dev.inmo.krontab.buildSchedule
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.takeWhile
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
import kotlin.test.Test
|
import kotlin.math.floor
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.*
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
@@ -27,6 +29,23 @@ class StringParseTest {
|
|||||||
assertEquals(mustBeCollected, collected)
|
assertEquals(mustBeCollected, collected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Test
|
||||||
|
fun testThatFlowIsCorrectlyWorkEverySecondWhenMillisIsHalfOfSecondBuiltOnString() {
|
||||||
|
val kronScheduler = buildSchedule("*/1 * * * * 500ms")
|
||||||
|
|
||||||
|
val flow = kronScheduler.asFlow()
|
||||||
|
|
||||||
|
runTest {
|
||||||
|
val mustBeCollected = 10
|
||||||
|
var collected = 0
|
||||||
|
flow.takeWhile {
|
||||||
|
collected < mustBeCollected
|
||||||
|
}.collect {
|
||||||
|
collected++
|
||||||
|
}
|
||||||
|
assertEquals(mustBeCollected, collected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmittersBuiltOnString() {
|
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmittersBuiltOnString() {
|
||||||
@@ -65,15 +84,53 @@ 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.sumBy { 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 {
|
||||||
ranges.remove(it.seconds)
|
ranges.remove(it.seconds)
|
||||||
collected++
|
collected++
|
||||||
|
assertTrue(collected <= expectedCollects)
|
||||||
}
|
}
|
||||||
assertEquals(expectedCollects, collected)
|
assertEquals(expectedCollects, collected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Test
|
||||||
|
fun testNextIsCorrectlyWorkEverySeveralMillisecondsRangeBuiltOnString() {
|
||||||
|
val rangesEnds = listOf(0, 200, 500, 750)
|
||||||
|
val kronScheduler = buildSchedule("* * * * * ${rangesEnds.joinToString(",") { "$it" }}ms")
|
||||||
|
|
||||||
|
runTest {
|
||||||
|
val ranges = rangesEnds.toMutableList()
|
||||||
|
val expectedCollects = ranges.size
|
||||||
|
var collected = 0
|
||||||
|
|
||||||
|
var currentTime = DateTime.now()
|
||||||
|
while (ranges.isNotEmpty()) {
|
||||||
|
val nextTrigger = kronScheduler.next(currentTime) ?: error("Strangely unable to get next time")
|
||||||
|
|
||||||
|
ranges.remove(nextTrigger.milliseconds)
|
||||||
|
collected++
|
||||||
|
|
||||||
|
currentTime = nextTrigger + 1.milliseconds
|
||||||
|
}
|
||||||
|
assertEquals(expectedCollects, collected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
fun testThatTimezoneCorrectlyDeserialized() {
|
||||||
|
val now = DateTime.now().copy(milliseconds = 0).local
|
||||||
|
|
||||||
|
runTest {
|
||||||
|
for (i in 0 .. 1339) {
|
||||||
|
val expectedInCurrentOffset = now.toOffset(TimezoneOffset(i.minutes)) + 1.hours
|
||||||
|
val kronScheduler = buildSchedule(
|
||||||
|
"${expectedInCurrentOffset.seconds} ${expectedInCurrentOffset.minutes} ${expectedInCurrentOffset.hours} * * ${i}o"
|
||||||
|
) as KronSchedulerTz
|
||||||
|
val next = kronScheduler.next(now)
|
||||||
|
assertEquals(expectedInCurrentOffset.toOffset(now.offset), next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt
Normal file
28
src/commonTest/kotlin/dev/inmo/krontab/utils/TimeZoneTest.kt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import dev.inmo.krontab.builder.buildSchedule
|
||||||
|
import dev.inmo.krontab.next
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class TimeZoneTest {
|
||||||
|
@Test
|
||||||
|
fun testDifferentTimeZonesReturnsDifferentTimes() {
|
||||||
|
val scheduler = buildSchedule { seconds { every(1) } }
|
||||||
|
val additionalMilliseconds = 100.milliseconds
|
||||||
|
val baseDate = DateTime.now().startOfWeek.copy(milliseconds = additionalMilliseconds.millisecondsInt)
|
||||||
|
runTest {
|
||||||
|
for (i in 0 until 7) {
|
||||||
|
val now = baseDate + i.days
|
||||||
|
for (j in 0 .. 24) {
|
||||||
|
val nowTz = now.toOffset(j.hours)
|
||||||
|
val next = scheduler.next(nowTz)!!
|
||||||
|
assertEquals(
|
||||||
|
(nowTz + 1.seconds - additionalMilliseconds).utc.unixMillisLong, next.utc.unixMillisLong
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/commonTest/kotlin/dev/inmo/krontab/utils/WeekDaysTest.kt
Normal file
38
src/commonTest/kotlin/dev/inmo/krontab/utils/WeekDaysTest.kt
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import dev.inmo.krontab.builder.buildSchedule
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
class WeekDaysTest {
|
||||||
|
@Test
|
||||||
|
fun testThatWeekDaysSchedulingWorks() {
|
||||||
|
val startDateTime = DateTime.now().startOfDay
|
||||||
|
val weekDay = startDateTime.dayOfWeek.index0
|
||||||
|
val testDays = 400
|
||||||
|
val scheduler = buildSchedule {
|
||||||
|
dayOfWeek {
|
||||||
|
at(weekDay)
|
||||||
|
}
|
||||||
|
years {
|
||||||
|
at(startDateTime.yearInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runTest {
|
||||||
|
for (day in 0 until testDays) {
|
||||||
|
val currentDateTime = startDateTime + day.days
|
||||||
|
val next = scheduler.next(currentDateTime)
|
||||||
|
val expected = when {
|
||||||
|
day % 7 == 0 -> currentDateTime
|
||||||
|
else -> startDateTime + ceil(day.toFloat() / 7).weeks
|
||||||
|
}
|
||||||
|
if (expected.yearInt != startDateTime.yearInt) {
|
||||||
|
assertNull(next)
|
||||||
|
} else {
|
||||||
|
assertEquals(expected, next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user