Compare commits

...
Author SHA1 Message Date
renovate[bot] 2a4fa22bf4 Update dependency org.jetbrains.kotlinx:kotlinx-serialization-core to v1.12.0 2026-10-20 14:02:07 +00:00
InsanusMokrassar e4068ceee3 Merge pull request #173 from InsanusMokrassar/3.1.0
3.1.0
2026-10-09 18:44:08 +06:00
InsanusMokrassar fbf2d869c4 update dependencies 2026-09-25 01:59:37 +06:00
InsanusMokrassar bce1704aad start 3.1.0 2026-09-25 01:47:47 +06:00
InsanusMokrassar a97e05cbbe Merge pull request #170 from InsanusMokrassar/3.0.0
3.0.0
2026-08-31 14:36:45 +06:00
InsanusMokrassar 56e5c58137 migrate build toolchain 2026-08-31 14:27:39 +06:00
InsanusMokrassar 0bff6fe11e update dependencies 2026-08-31 13:51:06 +06:00
InsanusMokrassar 7e1fb011c8 start 3.0.0 2026-08-31 13:29:40 +06:00
InsanusMokrassar 8aaaa6306d Merge pull request #169 from InsanusMokrassar/2.10.0
2.10.0
2026-08-17 15:07:27 +06:00
InsanusMokrassar 27a8fba44f fix changelog 2026-08-17 12:01:22 +06:00
InsanusMokrassar e8f32eec7a update gradle/kotlin jvmargs 2026-08-17 12:00:26 +06:00
InsanusMokrassar 029739a90f update dependencies and gradle wrapper 2026-08-17 12:00:26 +06:00
InsanusMokrassar 950a8bccc3 start 2.10.0 2026-08-17 12:00:22 +06:00
InsanusMokrassar fc4e38cf10 update dependencies 2026-08-17 11:54:08 +06:00
InsanusMokrassar a3f507500b start 2.9.0 2026-08-17 11:54:08 +06:00
InsanusMokrassar ce087fa538 fix (?) of publishing 2026-08-17 11:54:08 +06:00
InsanusMokrassar 133f731000 fill changelog 2026-03-05 18:27:48 +06:00
InsanusMokrassar 20d44bd531 update dependencies 2026-03-05 18:24:32 +06:00
InsanusMokrassar 4ec9dacbae update dependencies
org.jetbrains.kotlinx:kotlinx-coroutines-core 1.10.1 -> 1.10.2
org.jetbrains.kotlinx:kotlinx-serialization-core 1.8.0 -> 1.8.1
org.jetbrains.dokka:dokka-gradle-plugin 2.0.0 -> 2.1.0
androidx.work:work-runtime-ktx 2.10.0 -> 2.11.1
com.android.tools.build:gradle 8.7.2 -> 8.10.1
junit:junit 4.12 -> 4.13.2
androidx.test.ext:junit 1.1.3 -> 1.3.0
androidx.test.espresso:espresso-core 3.4.0 -> 3.7.0
2026-02-10 17:19:15 +06:00
InsanusMokrassar ddab74152e start 2.8.0 2025-08-04 14:10:57 +06:00
InsanusMokrassar 2cce483dc8 update github_release to support environment variables 2025-05-16 12:44:14 +06:00
InsanusMokrassar fe5cdbefe8 Merge pull request #143 from InsanusMokrassar/2.7.2
2.7.2
2025-02-15 21:23:58 +06:00
InsanusMokrassar 4379635174 fixes 2025-02-15 21:07:13 +06:00
InsanusMokrassar d0c3bae000 update dependencies 2025-02-15 21:05:34 +06:00
InsanusMokrassar dfedd58b76 start 2.7.2 2025-02-15 21:03:08 +06:00
InsanusMokrassar b59c325641 Merge pull request #140 from InsanusMokrassar/2.7.1
2.7.1
2025-01-08 12:26:30 +06:00
InsanusMokrassar de64a4d806 __small__ optimization 2025-01-08 12:24:06 +06:00
InsanusMokrassar 409ca3770f fix parser 2025-01-08 12:22:29 +06:00
InsanusMokrassar 08ef7281e5 start 2.7.1 2025-01-08 12:17:21 +06:00
InsanusMokrassar c28bf17335 Merge pull request #138 from InsanusMokrassar/2.7.0
2.7.0
2024-12-30 08:53:12 +06:00
InsanusMokrassar bd590c3e3e update gradle wrapper version 2024-12-30 08:51:18 +06:00
InsanusMokrassar 9c11c0c7fc add support of wasm 2024-12-30 08:49:41 +06:00
InsanusMokrassar 79375f21e3 update dependencies 2024-12-30 08:35:20 +06:00
InsanusMokrassar 0b4dc1b328 start 2.7.0 2024-12-30 08:33:27 +06:00
InsanusMokrassar 4afebd8b7e Merge pull request #131 from InsanusMokrassar/2.6.1
2.6.1
2024-11-06 08:50:36 +06:00
InsanusMokrassar c76ca1798e fixes in parsing of ktontab templates 2024-11-06 08:46:10 +06:00
InsanusMokrassar 58917c6458 start 2.6.1 2024-11-06 07:38:24 +06:00
InsanusMokrassar 5ef6134d9f Merge pull request #129 from InsanusMokrassar/2.6.0
2.6.0
2024-11-03 21:52:59 +06:00
InsanusMokrassar b9237e13c9 fix changelog :) 2024-11-03 17:41:13 +06:00
InsanusMokrassar 17971ed8fb now doOnceTz works as it must 2024-11-03 17:40:42 +06:00
InsanusMokrassar 505108d2dd start 2.6.0 2024-11-03 17:40:12 +06:00
InsanusMokrassar 094284562d Merge pull request #128 from InsanusMokrassar/2.5.1
2.5.1
2024-11-03 13:38:07 +06:00
InsanusMokrassar 5ac1bc26e1 rewrite check onto fsm 2024-11-03 13:30:20 +06:00
InsanusMokrassar d82107b8b8 remove gitea publishing step from workflows 2024-11-03 12:59:35 +06:00
InsanusMokrassar 6b6b7a6fe0 remove redundant regexes 2024-11-03 12:58:26 +06:00
InsanusMokrassar aff50f0aec get back splitting by simple ' ' instead of regex in KrontabConfig 2024-11-03 12:57:32 +06:00
InsanusMokrassar e0f9db9c60 fix of changelog 2024-11-03 12:56:10 +06:00
InsanusMokrassar e4b3a5059a fix of #126 2024-11-03 12:54:51 +06:00
InsanusMokrassar ca5794726f update dependencies 2024-11-03 12:27:43 +06:00
InsanusMokrassar 72ef9317f9 start 2.5.1 2024-11-03 12:23:23 +06:00
InsanusMokrassar 4651a290d0 Merge pull request #122 from InsanusMokrassar/2.5.0
2.5.0
2024-09-20 16:30:52 +06:00
InsanusMokrassar 30df50c192 update depepndencies 2024-09-20 16:22:49 +06:00
InsanusMokrassar 05c6d74f7b start 2.5.0 2024-09-20 15:47:56 +06:00
InsanusMokrassar 7269833c6f Merge pull request #117 from InsanusMokrassar/2.4.0
2.4.0
2024-08-12 23:46:28 +06:00
InsanusMokrassar 6e97a365e5 remove redundant new functions 2024-08-12 23:40:30 +06:00
InsanusMokrassar 0c497f5dcb update gradle wrapper 2024-08-12 23:32:04 +06:00
InsanusMokrassar 552ec14d66 update result checker in KronSchedulerWork 2024-08-12 23:25:23 +06:00
InsanusMokrassar 3845f6e0c5 update dependencies 2024-08-12 23:21:57 +06:00
InsanusMokrassar 3bd4a7a245 start 2.4.0 2024-08-12 23:09:38 +06:00
InsanusMokrassar f90d8d03d9 Merge pull request #108 from InsanusMokrassar/2.3.0
2.3.0
2024-04-25 18:44:04 +06:00
InsanusMokrassar 36671cb379 potential fix of #107 2024-03-21 17:34:22 +06:00
InsanusMokrassar 68be768763 start 2.3.0 2024-03-21 17:14:59 +06:00
InsanusMokrassar 9546eb5aec Merge pull request #103 from InsanusMokrassar/2.2.9
2.2.9
2024-03-16 16:06:32 +06:00
InsanusMokrassar 2efd7e6157 fixes in built-in schedulers 2024-03-16 15:56:45 +06:00
InsanusMokrassar 6845537afd remove redundant change of KronSchedulerWork.kt 2024-03-16 10:31:12 +06:00
InsanusMokrassar af28fce6f5 update dependencies 2024-03-06 20:01:41 +06:00
InsanusMokrassar 237b42c2d3 start 2.2.9 2024-03-06 19:36:42 +06:00
InsanusMokrassar 49f852f27e Merge pull request #101 from InsanusMokrassar/renovate/dokka_version
Update plugin org.jetbrains.dokka to v1.9.20
2024-03-05 13:56:26 +06:00
renovate[bot] 470dc92f54 Update plugin org.jetbrains.dokka to v1.9.20 2024-03-05 02:59:24 +00:00
InsanusMokrassar b5dcdc185f Merge pull request #99 from InsanusMokrassar/2.2.8
2.2.8
2024-02-18 22:24:34 +06:00
InsanusMokrassar 8df9f9c086 update dependencies 2024-02-18 22:14:01 +06:00
InsanusMokrassar 484f46223f start 2.2.8 2024-02-18 21:56:06 +06:00
InsanusMokrassar 1ce654fa06 Merge pull request #94 from InsanusMokrassar/2.2.7
2.2.7
2024-01-31 17:56:23 +06:00
InsanusMokrassar 6da04f54da update dependencies 2024-01-31 17:40:15 +06:00
InsanusMokrassar e1d716688a start 2.2.7 2024-01-31 17:26:48 +06:00
InsanusMokrassar e247de9e24 Merge pull request #92 from InsanusMokrassar/2.2.6
2.2.6
2024-01-12 14:52:32 +06:00
InsanusMokrassar a153bc7996 update klock 2024-01-12 14:45:14 +06:00
InsanusMokrassar 9753cda961 start 2.2.6 2024-01-12 14:33:22 +06:00
InsanusMokrassar e8ef6e6935 update publication script 2023-12-28 15:51:33 +06:00
InsanusMokrassar b41bf0ea07 Merge pull request #91 from InsanusMokrassar/2.2.5
2.2.5
2023-12-28 15:47:39 +06:00
InsanusMokrassar 09d811e2c4 Update CHANGELOG.md 2023-12-28 15:47:27 +06:00
InsanusMokrassar 3faadefced update dependencies 2023-12-27 19:15:35 +06:00
InsanusMokrassar f3883a8678 start 2.2.5 2023-12-27 01:14:50 +06:00
InsanusMokrassar 5ff02a2a9d Merge pull request #85 from InsanusMokrassar/2.2.4
2.2.4
2023-11-26 21:23:18 +06:00
InsanusMokrassar f56215ee10 start 2.2.4 and update dependencies 2023-11-26 21:20:39 +06:00
InsanusMokrassar e66acb34de Update kdocs.yml 2023-11-03 00:31:23 +06:00
InsanusMokrassar 78ac446311 Update publishing_packages.yml 2023-11-03 00:31:12 +06:00
InsanusMokrassar 697b4e81b8 Merge pull request #81 from InsanusMokrassar/2.2.3
2.2.3
2023-11-02 23:28:22 +06:00
InsanusMokrassar d1288de032 build fixes 2023-11-02 23:27:53 +06:00
InsanusMokrassar eb867e7366 update dependencies and gradle environment 2023-11-02 23:22:19 +06:00
InsanusMokrassar 4aa10e00cf start 2.2.3 2023-11-02 23:19:24 +06:00
InsanusMokrassar 170d443517 Merge pull request #80 from InsanusMokrassar/2.2.2
2.2.2
2023-10-23 01:27:57 +06:00
InsanusMokrassar d1aab7ccd6 add arm target 2023-10-23 01:27:19 +06:00
InsanusMokrassar 58d758f141 Update gradle-wrapper.properties 2023-10-23 01:27:19 +06:00
InsanusMokrassar 71bd3f0eb4 update dependencies 2023-10-23 01:27:19 +06:00
InsanusMokrassar bd2a852b16 start 2.2.2 2023-10-23 01:27:19 +06:00
InsanusMokrassar 8e844b5875 update kotlinx serialization 2023-10-23 01:26:17 +06:00
InsanusMokrassar e0f7dddf42 update dependencies 2023-10-23 01:26:17 +06:00
InsanusMokrassar d64acc9806 potential fix of #65 2023-10-23 01:26:17 +06:00
InsanusMokrassar 58375452c7 start 2.2.1 2023-10-23 01:26:17 +06:00
InsanusMokrassar cbfbdc1b33 files replacements 2023-08-09 13:01:20 +06:00
InsanusMokrassar 715d414f51 update dependencies 2023-08-09 12:35:13 +06:00
InsanusMokrassar 2ecd8c4292 start 2.2.0 2023-08-09 12:24:41 +06:00
InsanusMokrassar 1be8d947f7 Merge pull request #67 from InsanusMokrassar/2.1.2
2.1.2
2023-07-05 20:07:15 +06:00
InsanusMokrassar f66c1c2477 remove redundant change in string parse change 2023-07-05 20:05:39 +06:00
InsanusMokrassar 69cf7afd4e make inline functions callbacks inline again 2023-07-02 16:41:52 +06:00
InsanusMokrassar 92df91edd3 update coroutines 2023-07-02 16:06:47 +06:00
InsanusMokrassar f7a388f438 start 2.1.2 2023-07-02 16:05:46 +06:00
InsanusMokrassar 9a10c7eb87 Merge pull request #64 from InsanusMokrassar/2.1.1
2.1.1
2023-06-20 11:44:13 +06:00
InsanusMokrassar d67be582af nullable since in asFlowWithoutDelays and asTzFlowWithoutDelays 2023-06-20 11:35:57 +06:00
InsanusMokrassar 171bb687ea start 2.1.1 2023-06-20 11:26:57 +06:00
InsanusMokrassar 7807d10121 Merge pull request #63 from InsanusMokrassar/2.1.0
2.1.0
2023-06-20 10:42:01 +06:00
InsanusMokrassar e5bbea84d9 update changelog and kdocs 2023-06-20 10:41:46 +06:00
InsanusMokrassar a01a18a6d1 KrontabTemplateWrapper deprecation, extensions like "daily" 2023-06-20 10:25:15 +06:00
InsanusMokrassar 132986d275 migration onto 2.1.0 2023-06-20 10:13:03 +06:00
InsanusMokrassar 04118902e8 KrontabConfig, KronScheduler.Companion and its builders extensions 2023-06-20 10:12:32 +06:00
InsanusMokrassar 96857aa7bc update dependencies 2023-06-20 09:45:07 +06:00
InsanusMokrassar f8b677406f start 2.0.1 2023-06-20 09:43:04 +06:00
InsanusMokrassar 53f34f0a27 Update README.md 2023-06-10 18:16:53 +06:00
InsanusMokrassar 989780243a update dokka 2023-06-09 12:10:41 +06:00
InsanusMokrassar 7b5417ccc5 Merge pull request #58 from InsanusMokrassar/2.0.0
2.0.0
2023-05-25 21:19:39 +06:00
InsanusMokrassar 2004a7dd05 update changelog 2023-05-25 20:53:58 +06:00
InsanusMokrassar 7c4217bda6 migration onto new klock and fixes in android manifest 2023-05-25 20:53:10 +06:00
InsanusMokrassar 3d6fee7257 migrate onto 2.0.0 due to changes in klock 2023-05-25 20:50:48 +06:00
InsanusMokrassar 6cec25eca0 update kotlin version 2023-05-25 20:41:33 +06:00
InsanusMokrassar a0972eaff9 start 1.1.0 2023-05-25 20:38:51 +06:00
InsanusMokrassar cc75501b04 Merge pull request #49 from InsanusMokrassar/1.0.0
1.0.0
2023-04-25 00:28:01 +06:00
InsanusMokrassar 89e500ff33 Merge pull request #48 from InsanusMokrassar/renovate/configure
Configure Renovate
2023-04-25 00:15:38 +06:00
InsanusMokrassar a61cd61602 update dependencies 2023-04-25 00:15:13 +06:00
renovate[bot] 5fdb2ea049 Add renovate.json 2023-04-24 18:11:35 +00:00
InsanusMokrassar 68ed562b19 start 1.0.0 2023-04-25 00:10:11 +06:00
InsanusMokrassar 87b5dfe1aa Update README.md 2023-04-03 16:18:18 +06:00
InsanusMokrassar 6a6bfe0552 Merge pull request #47 from InsanusMokrassar/0.10.0
0.10.0
2023-03-18 14:01:14 +06:00
InsanusMokrassar ede47ae664 remove redundant FlowPrevies on flows API 2023-03-18 12:34:06 +06:00
InsanusMokrassar 890ab5b15d fill changelogs 2023-03-18 12:24:25 +06:00
InsanusMokrassar 5a1ed2f933 start 0.10.0 + rework of flows 2023-03-18 12:23:53 +06:00
InsanusMokrassar 7da67386cf Update gradle.properties 2023-03-16 20:00:52 +06:00
InsanusMokrassar 7027719fe5 Update gradle-wrapper.properties 2023-03-16 20:00:20 +06:00
InsanusMokrassar a81c7c7c3f Merge pull request #46 from Tolsi/patch-1
typo
2023-03-16 18:20:18 +06:00
Sergey Tolmachev e5658998d4 typo 2023-03-16 15:16:35 +03:00
InsanusMokrassar 6b3cb981ab Merge pull request #45 from InsanusMokrassar/0.9.0
0.9.0
2023-02-28 12:48:51 +06:00
InsanusMokrassar f6518d4d25 add kotlin-js-store folder in gitignore 2023-02-28 12:25:23 +06:00
InsanusMokrassar 97a8f39826 update dependencies 2023-02-28 12:24:30 +06:00
InsanusMokrassar 469861f7e3 start 0.9.0 2023-02-28 12:23:12 +06:00
InsanusMokrassar 69305506ce Update README.md 2023-02-19 16:35:31 +06:00
InsanusMokrassar 63ea3f0f1a Merge pull request #44 from InsanusMokrassar/0.8.5
0.8.5
2022-12-15 08:38:54 +06:00
InsanusMokrassar 13a8bacb52 fix changelog 2022-12-15 08:37:54 +06:00
InsanusMokrassar 9c80e29e71 update autobuild script 2022-12-14 23:00:44 +06:00
InsanusMokrassar cfd2de8fe9 update dependencies 2022-12-14 22:45:34 +06:00
InsanusMokrassar e3490d432e fix in timezoned crontab scheduler 2022-12-14 22:44:27 +06:00
InsanusMokrassar 1c95636b71 start 0.8.5 2022-12-14 22:30:34 +06:00
InsanusMokrassar b31a6ab86a Merge pull request #43 from InsanusMokrassar/0.8.4
0.8.4
2022-12-08 09:32:25 +06:00
InsanusMokrassar c35a679827 update kotlin 2022-12-05 15:27:41 +06:00
InsanusMokrassar 06f56026db start 0.8.4 2022-12-05 15:26:31 +06:00
InsanusMokrassar 54be57d709 Merge pull request #42 from InsanusMokrassar/0.8.3
0.8.3
2022-11-15 10:04:46 +06:00
InsanusMokrassar fae297e4a6 Update CHANGELOG.md 2022-11-14 10:11:46 +06:00
InsanusMokrassar bd81298ac9 Update gradle.properties 2022-11-11 10:25:26 +06:00
InsanusMokrassar cfac5fcd1c Merge pull request #41 from InsanusMokrassar/0.8.2
0.8.2
2022-10-02 22:37:13 +06:00
InsanusMokrassar 93d38f8945 Update CHANGELOG.md 2022-10-02 22:26:24 +06:00
InsanusMokrassar 97dcd77a8a Update gradle.properties 2022-10-02 22:22:04 +06:00
InsanusMokrassar afa2521b5d Update gradle-wrapper.properties 2022-10-02 22:21:23 +06:00
InsanusMokrassar 37b0ec9fb7 start 0.8.2 2022-10-02 11:40:48 +06:00
InsanusMokrassar d36888173a Merge pull request #40 from InsanusMokrassar/0.8.1
0.8.1
2022-09-13 00:58:24 +06:00
InsanusMokrassar 30011cd309 Update CHANGELOG.md 2022-09-11 14:18:46 +06:00
InsanusMokrassar fd37a5ddc3 Update gradle.properties 2022-09-11 14:17:48 +06:00
InsanusMokrassar 3874f6a42c start 0.8.1 2022-09-11 14:16:52 +06:00
InsanusMokrassar 8a98ebd5f9 Merge pull request #39 from InsanusMokrassar/0.8.0
0.8.0
2022-08-06 10:01:18 +06:00
InsanusMokrassar 8d0c55129f Update CHANGELOG.md 2022-08-06 09:54:31 +06:00
InsanusMokrassar 54b06bf9e6 start 0.8.0 2022-08-06 09:50:59 +06:00
InsanusMokrassar da436ab432 Merge pull request #38 from InsanusMokrassar/0.7.5
0.7.5
2022-07-22 17:09:31 +06:00
InsanusMokrassar 154d211514 change kdocs link 2022-07-22 17:09:00 +06:00
InsanusMokrassar 0b843ada4f update yarn.lock 2022-07-22 17:03:20 +06:00
InsanusMokrassar 25f204a488 update dependencies 2022-07-22 17:00:41 +06:00
InsanusMokrassar 2e4ccb9253 start 0.7.5 2022-07-22 16:59:07 +06:00
InsanusMokrassar 411a52b85e Update github_release.gradle 2022-07-02 18:38:03 +06:00
InsanusMokrassar 5a5bde6f20 Merge pull request #37 from InsanusMokrassar/0.7.4
0.7.4
2022-07-02 12:20:28 +06:00
InsanusMokrassar 8f9b84ecc7 Update CHANGELOG.md 2022-07-02 12:20:04 +06:00
InsanusMokrassar 62c5a0f98d Update gradle.properties 2022-07-01 23:01:29 +06:00
InsanusMokrassar dfd1a5a909 Update gradle.properties 2022-07-01 23:00:34 +06:00
InsanusMokrassar 04390f0726 Update README.md 2022-06-15 15:18:41 +06:00
InsanusMokrassar d0f02e37ce Merge pull request #36 from InsanusMokrassar/0.7.3
0.7.3
2022-06-15 14:48:10 +06:00
InsanusMokrassar 681043db5a Update NearDateTimeCalculator.kt 2022-06-15 14:46:12 +06:00
InsanusMokrassar f63ed1a873 rename check infinity loop test class and fun 2022-06-15 14:43:28 +06:00
InsanusMokrassar 9ea8194d7d fill changelog 2022-06-15 13:46:37 +06:00
InsanusMokrassar 0c37ccc902 fix of #33 2022-06-15 13:37:48 +06:00
InsanusMokrassar 2abef58335 fix of #35 2022-06-15 13:31:26 +06:00
InsanusMokrassar 8ac6cd3e62 fix of #34 and upfix for #27 2022-06-15 13:27:21 +06:00
InsanusMokrassar 58f30aef15 fix of #27 2022-06-15 12:40:03 +06:00
InsanusMokrassar f163a64964 start 0.7.3 2022-06-14 00:14:15 +06:00
InsanusMokrassar 79ce455f5c Merge pull request #32 from InsanusMokrassar/0.7.2
0.7.2
2022-04-29 22:26:14 +06:00
InsanusMokrassar fd54b035fe update yarn.lock 2022-04-29 22:18:13 +06:00
InsanusMokrassar 4fab7a28ff fix of #30 2022-04-29 22:14:42 +06:00
InsanusMokrassar 28d5665a14 fix of #31 and several refactorings 2022-04-29 21:57:10 +06:00
InsanusMokrassar f98fd5ddb7 update dependencies 2022-04-29 20:44:51 +06:00
InsanusMokrassar 981327675a start 0.7.2 2022-04-29 20:43:33 +06:00
InsanusMokrassar 7649d20984 Merge pull request #28 from InsanusMokrassar/0.7.1
0.7.1
2022-02-27 22:08:03 +06:00
InsanusMokrassar ab274636b9 workflows fix 2022-02-27 20:47:10 +06:00
InsanusMokrassar 7f2f8a4a26 revert android sdk version 2022-02-27 20:37:48 +06:00
InsanusMokrassar f64171c0f0 update android compile sdk up to 33.0.0 2022-02-27 19:05:07 +06:00
InsanusMokrassar 8382108b01 start 0.7.1 2022-02-27 18:20:49 +06:00
InsanusMokrassar 32499d02fb Update kdocs.yml 2021-12-25 14:05:13 +06:00
InsanusMokrassar 2b06033c75 Merge pull request #25 from InsanusMokrassar/0.7.0
0.7.0
2021-12-25 00:25:16 +06:00
InsanusMokrassar 84f1063e3f Update gradle-wrapper.properties 2021-12-24 17:59:39 +06:00
InsanusMokrassar 347cf64cb2 Update CHANGELOG.md 2021-12-24 17:59:11 +06:00
InsanusMokrassar eb73da67eb Update gradle.properties 2021-12-23 13:03:16 +06:00
InsanusMokrassar 4aadf513aa Update gradle.properties 2021-12-23 13:02:47 +06:00
InsanusMokrassar 8c011ea1fb Update publishing_packages.yml 2021-12-22 12:55:22 +06:00
InsanusMokrassar aa9c2b8ea0 Update gradle.properties 2021-12-22 12:52:08 +06:00
InsanusMokrassar adf10301dc Update gradle.properties 2021-12-12 13:37:30 +06:00
InsanusMokrassar 7799077e44 Update gradle-wrapper.properties 2021-12-05 10:57:26 +06:00
InsanusMokrassar 83339dc095 Update gradle.properties 2021-12-05 10:51:49 +06:00
InsanusMokrassar 3040951f37 optimize imports 2021-11-22 19:09:33 +06:00
InsanusMokrassar 0bfe022476 update dependencies and remove deprecations 2021-11-22 19:08:24 +06:00
InsanusMokrassar 06107f75f2 start 0.7.0 2021-11-22 18:59:44 +06:00
InsanusMokrassar 039fe1cb5f Merge pull request #24 from InsanusMokrassar/0.6.6
0.6.6
2021-11-12 16:14:27 +06:00
InsanusMokrassar ea7fab6130 0.6.6 2021-11-12 16:06:08 +06:00
InsanusMokrassar 2f3796733d Hotfix for 0.6.5 flows 2021-09-26 14:17:01 +06:00
InsanusMokrassar 89a6a305f8 Merge pull request #23 from InsanusMokrassar/0.6.5
0.6.5
2021-09-26 14:08:01 +06:00
InsanusMokrassar 97752e1ac0 optimize imports 2021-09-26 13:06:22 +06:00
InsanusMokrassar 276ecaac27 overwrite do* extensions 2021-09-26 13:05:59 +06:00
InsanusMokrassar d36e406b59 now flows are using doInfinity 2021-09-26 11:49:01 +06:00
InsanusMokrassar 4dc65bf09a delay in doWhile and flows with doWhile instead their own logic 2021-09-25 19:28:49 +06:00
InsanusMokrassar 539b5fbcd8 0.6.5 2021-09-25 18:51:51 +06:00
InsanusMokrassar e4f417877d Update kdocs.yml 2021-09-23 13:22:27 +06:00
InsanusMokrassar 4f6054c0d9 Update build.gradle 2021-09-23 13:18:40 +06:00
InsanusMokrassar 88dbfee370 Update README.md 2021-09-22 20:57:21 +06:00
InsanusMokrassar 5af29c2246 Merge pull request #21 from InsanusMokrassar/0.6.4
0.6.4
2021-09-22 20:52:29 +06:00
61 changed files with 1812 additions and 541 deletions

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ 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: 17
- name: Build - name: Build
run: ./gradlew dokkaHtml run: ./gradlew dokkaHtml
- name: Publish KDocs - name: Publish KDocs
+2 -5
View File
@@ -7,10 +7,7 @@ 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: 17
- name: Fix android 31.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/31.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Update version - name: Update version
run: | run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`" branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -19,7 +16,7 @@ jobs:
run: ./gradlew clean build run: ./gradlew clean build
- name: Publish package - name: Publish package
continue-on-error: true continue-on-error: true
run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication -x signAndroidReleasePublication -x signAndroidDebugPublication -x signAndroidReleasePublication run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository
env: env:
GITHUBPACKAGES_USER: ${{ github.actor }} GITHUBPACKAGES_USER: ${{ github.actor }}
GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }} GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+3
View File
@@ -1,4 +1,5 @@
.idea .idea
.kotlin
out/* out/*
*.iml *.iml
target target
@@ -9,4 +10,6 @@ settings.xml
build/ build/
out/ out/
kotlin-js-store/
local.properties local.properties
+304
View File
@@ -1,5 +1,309 @@
# Changelog # Changelog
## 3.1.0
* `Version`:
* `Kotlin`: `2.4.10` -> `2.4.20`
## 3.0.0
* `Version`:
* `Kotlin`: `2.3.21` -> `2.4.10`
* `Gradle`: `9.3.0` -> `9.7.1`
* `Android Gradle Plugin`: `8.13.+` -> `9.3.2`
* `Android` (compileSdk): `36` -> `37`
* `Android Build Tools`: `36.0.0` -> `37.0.0`
* `NMCP`: `1.5.0` -> `1.6.1`
## 2.10.0
* `Version`:
* `Kotlin`: `2.3.21`
* `Coroutines`: `1.11.0`
## 2.9.0
* `Version`:
* `Kotlin`: `2.3.20`
* `Serialization`: `1.11.0`
* `Dokka`: `2.2.0`
* `AndroidXWork`: `2.11.2`
## 2.8.0
* `Version`:
* `Kotlin`: `2.3.10`
* `Coroutines`: `1.10.2`
* `Serialization`: `1.10.0`
* `AndroidXWork`: `2.11.1`
* `Android` (compileSdk): `36`
## 2.7.2
* `Version`:
* `Kotlin`: `2.1.10`
* `Serialization`: `1.8.0`
## 2.7.1
* Fixes in parser and checker
## 2.7.0
* `Version`:
* `Kotlin`: `2.1.0`
* `Coroutines`: `1.10.1`
* Add WASM support (solution of [#137](https://github.com/InsanusMokrassar/krontab/issues/137))
## 2.6.1
* Fixes in parsing of string parts
## 2.6.0
* Fix of `doOnceTz` behaviour: now it will use local time as utc time to get next waking up time
## 2.5.1
* Add cleaning up of incoming template, which must remove all malformed parts from string template (fix of [#126](https://github.com/InsanusMokrassar/krontab/issues/126))
* Add support of insufficient amount of arguments (fix of [#126](https://github.com/InsanusMokrassar/krontab/issues/126))
* `Version`:
* `Kotlin`: `2.0.21`
* `AndroidXWork`: `2.10.0`
## 2.5.0
* `Version`:
* `Kotlin`: `2.0.20`
* `Coroutines`: `1.9.0`
* `Serialization`: `1.7.3`
## 2.4.0
* `Version`:
* `Kotlin`: `2.0.10`
* `Coroutines`: `1.8.1`
* `Serialization`: `1.7.1`
* `Klock`: `5.4.0`
* `AndroidXWork`: `2.9.1`
## 2.3.0
* `doWhile` now will guarantee that it will not call `doOnce` more than once for time
## 2.2.9
* `Version`:
* `Kotlin`: `1.9.23`
* `Serialization`: `1.6.3`
* `Klock`: `5.3.2`
* Fixes in build-in schedulers
## 2.2.8
* `Version`:
* `Coroutines`: `1.8.0`
## 2.2.7
* `Version`:
* `Klock`: `5.3.1`
* `AndroidXWork`: `2.9.0`
## 2.2.6
**Since this version klock depdendency replaced with `com.soywiz.korge:korlibs-time`**
* `Version`:
* `Klock`: `5.3.0`
## 2.2.5
* `Versions`
* `Kotlin`: `1.9.22`
* `Serialization`: `1.6.2`
## 2.2.4
* `Versions`
* `Kotlin`: `1.9.21`
* `Serialization`: `1.6.1`
## 2.2.3
**THIS UPDATE CONTAINS UPGRADE OF USED JDK UP TO 17**
* Versions
* `Kotlin`: `1.9.20`
## 2.2.2
* Versions
* `Kotlin`: `1.9.20-RC`
* `Android` (min/target): `34`
## 2.2.1
* Potential fix of [#65](https://github.com/InsanusMokrassar/krontab/issues/65)
* Versions
* `Kotlin`: `1.9.20-Beta`
* `Klock`: `4.0.10`
* `Serialization`: `1.6.0`
## 2.2.0
* Versions
* `Kotlin`: `1.9.0`
* `Coroutines`: `1.7.3`
* `Klock`: `4.0.9`
## 2.1.2
* Versions
* `Coroutines`: `1.7.2`
* All callbacks for standard extensions like `doInfinity` now fully inline
## 2.1.1
* `asFlowWithoutDelays` and `asTzFlowWithoutDelays` will have nullable `since` parameters with default to `null`
to avoid any inconsistency of `Flow` idiom.
<details>
<summary>About the reason of changes</summary>
Cold flows should not contain some state by default. So, it was not right to save some `DateTime`/`DateTimeTz`
by default. Now it will not use some external state unless developers will set it manually
</details>
## 2.1.0
* Versions
* `Kotlin`: `1.8.22`
* `Klock`: `4.0.3`
* New value class `KrontabConfig`. It is preferable way to create `KronScheduler` instead of
`KrontabTemplate` since this update
* You may configure krontab with builders using simple `KronScheduler` invoke extension
* New useful extensions like `KronScheduler.daily`
* `KrontabTemplateWrapper` is obsolete in favor to `KrontabConfig`
## 2.0.0
* Versions
* `Kotlin`: `1.8.21`
* `Klock`: `4.0.1`
* Support of `mingwx64` platform
## 1.0.0
* Versions
* `Kotlin`: `1.8.20`
* `AndroidXWork`: `2.8.1`
## 0.10.0
* New extensions for `KronScheduler`:
* `asTzFlowWithoutDelays`/`asFlowWithoutDelays`
* `asTzFlowWithDelays`/`asFlowWithDelays`
* Old `KronScheduler.asFlow` and `KronScheduler.asTzFlow` temporarily marked as deprecated: after several versions their
behaviour will be changed to undelayed one
* All the flow extensions now use `cold` non-channel flows. Potentially it should increase performance and decrease memory usage
## 0.9.0
* Versions
* `Kotlin`: `1.8.10`
* `AndroidXWork`: `2.8.0`
## 0.8.5
* Project is now available in owner `Gitea`: https://git.inmo.dev/InsanusMokrassar/-/packages/maven/dev.inmo-krontab
* `KronSchedulerTz#next` with incoming `DateTime` now will use adjusted local time instead of unadjusted one
* `CronDateTimeSchedulerTz#next` with incoming `DateTime` will convert that parameter to internal offset directly
## 0.8.4
* Versions
* `Kotlin`: `1.7.22`
## 0.8.3
* Versions
* `Kotlin`: `1.7.21`
* `Klock`: `3.4.0`
## 0.8.2
**THIS VERSION HAS CHANGED COMPILE ANDROID SDK FROM 32 -> 33**
* Versions
* `Kotlin`: `1.7.20`
* `Klock`: `3.2.0`
## 0.8.1
* Versions
* `Klock`: `3.1.0`
## 0.8.0
**THIS VERSION HAS CHANGED MIN ANDROID SDK FROM 19 -> 21**
* Versions
* `Kotlin`: `1.7.10`
* `Klock`: `3.0.0`
## 0.7.5
* Versions
* `Coroutines`: `1.6.4`
## 0.7.4
* Versions
* `Coroutines`: `1.6.3`
## 0.7.3
* Versions
* `Kotlin`: `1.6.21`
* Several bug fixes ([#27](https://github.com/InsanusMokrassar/krontab/issues/27), [#34](https://github.com/InsanusMokrassar/krontab/issues/34))
* New factories for `KronScheduler` and `KronSchedulerTz` (fix of [#35](https://github.com/InsanusMokrassar/krontab/issues/35))
* Fill extensions for flow with time zones (fix of [#33](https://github.com/InsanusMokrassar/krontab/issues/33))
## 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 ## 0.6.4
* Versions * Versions
+47 -16
View File
@@ -1,22 +1,21 @@
# krontab # krontab
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/krontab)
[![Build Status](https://github.com/InsanusMokrassar/krontab/actions/workflows/regular_build.yml/badge.svg)](https://github.com/InsanusMokrassar/krontab/actions/workflows/regular_build.yml)
[![KDocs](https://raw.githubusercontent.com/InsanusMokrassar/badges/master/kdocs.svg)](https://krontab.inmo.dev/index.html) [![Telegram Chat](https://img.shields.io/badge/Telegram%20Chat-0288D1?style=for-the-badge&logo=telegram&logoColor=white)](https://inmodev_chat.t.me)
![JVM](https://img.shields.io/badge/JVM-red?style=for-the-badge&logo=openjdk&logoColor=white)
![Android](https://img.shields.io/badge/Android-green?style=for-the-badge&logo=android&logoColor=white)
![Js](https://img.shields.io/badge/JavaScript-323330?style=for-the-badge&logo=javascript&logoColor=F7DF1E)
![Linux x64](https://img.shields.io/badge/Linux%20x64-white?style=for-the-badge&logo=linux&logoColor=black)
![WASM](https://img.shields.io/badge/WASM-654FF0?style=for-the-badge&logo=webassembly&logoColor=white)
[![KDocs](https://img.shields.io/badge/KDocs-323330?style=for-the-badge&logo=Kotlin&logoColor=7F52FF)](https://insanusmokrassar.github.io/krontab/)
[![Tutorials](https://img.shields.io/badge/Tutorials-0288D1?style=for-the-badge&logo=mkdocs&logoColor=white)](https://docs.inmo.dev/krontab/index.html)
Library was created to give oppotunity to launch some things from time to time according to some schedule in Library was created to give oppotunity to launch some things from time to time according to some schedule in
runtime of applications. runtime of applications.
| Table of content |
|---|
| [ How to use ](#how-to-use) |
| [ How to use: Including in project ](#including-in-project) |
| [ How to use: Config from string ](#config-from-string) |
| [ How to use: Config via builder (DSL preview) ](#config-via-builder) |
| [ How to use: KronScheduler as a Flow ](#KronScheduler-as-a-Flow) |
| [ How to use: Offsets ](#Offsets) |
| [ How to use: Note about week days ](#Note-about-week-days) |
## How to use ## How to use
There are several ways to configure and use this library: There are several ways to configure and use this library:
@@ -29,7 +28,7 @@ Anyway, to start some action from time to time you will need to use one of exten
```kotlin ```kotlin
val kronScheduler = /* creating of KronScheduler instance */; val kronScheduler = /* creating of KronScheduler instance */;
kronScheuler.doWhile { kronScheduler.doWhile {
// some action // some action
true // true - repeat on next time true // true - repeat on next time
} }
@@ -133,6 +132,38 @@ kronScheduler.doInfinity {
All of these examples will do the same things: print `Called` message every five seconds. All of these examples will do the same things: print `Called` message every five seconds.
### do\* functions
With regular `doOnce`/`doWhile`/`doInfinity` there are two types of their variations: **local** and **timezoned**. Local
variations (`doOnceLocal`/`doWhileLocal`/`doInfinityLocal`) will pass `DateTime` as an argument into the block:
```kotlin
doInfinityLocal("/5 * * * *") {
println(it) // will print current date time
}
```
Timezoned variations (`doOnceTz`/`doWhileTz`/`doInfinityTz`) will do the same thing but pass as an argument `DateTimeTz`:
```kotlin
doInfinityTz("/5 * * * * 0o") {
println(it) // will print current date time in UTC
}
```
It is useful in cases when you need to get the time of calling and avoid extra calls to system time.
#### Helpful table for
| | No args | Local `DateTime` | Local `DateTimeTz` with offset of `KronScheduler` |
|---| ------- | ---------------- | ------------------------------------------------- |
| **Call only near time** | doOnce | doOnceLocal | doOnceTz |
| **Call while condition is true** | doWhile | doWhileLocal | doWhileTz |
| **Work infinity*** | doInfinity | doInfinityLocal | doInfinityTz |
*Here there is an important notice, that `Work infinity` is not exactly `infinity`. Actually, that means that `do while
coroutine is alive` and in fact executing will be stopped when coroutine became cancelled.
### KronScheduler as a Flow ### KronScheduler as a Flow
Any `KronScheduler`can e converted to a `Flow<DateTime` using extension `asFlow`: Any `KronScheduler`can e converted to a `Flow<DateTime` using extension `asFlow`:
@@ -163,7 +194,7 @@ flow.takeWhile {
Offsets in this library works via passing parameter ending with `o` in any place after `month` config. Currently Offsets in this library works via passing parameter ending with `o` in any place after `month` config. Currently
there is only one format supported for offsets: minutes of offsets. To use time zones you will need to call `next` there is only one format supported for offsets: minutes of offsets. To use time zones you will need to call `next`
method with `DateTimeTz` argument or `nextTimeZoned` method with any `KronScheduler` instance, but in case if this method with `DateTimeTz` argument or `nextTimeZoned` method with any `KronScheduler` instance, but in case if this
scheduler is not instance of `KronSchedulerTz` it will works like you passed just `DateTime`. scheduler is not instance of `KronSchedulerTz` it will work like you passed just `DateTime`.
Besides, in case you wish to use time zones explicitly, you will need to get `KronSchedulerTz`. It is possible by: Besides, in case you wish to use time zones explicitly, you will need to get `KronSchedulerTz`. It is possible by:
+43 -35
View File
@@ -8,15 +8,18 @@ buildscript {
dependencies { dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$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.2.2' classpath "com.android.tools.build:gradle:$android_gradle_version"
} }
} }
plugins { plugins {
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version" id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version"
id "org.jetbrains.dokka" version "$dokka_version" id "org.jetbrains.dokka" version "$dokka_version"
id "com.gradleup.nmcp.aggregation" version "$nmcp_version"
} }
// temporal crutch until legacy tests will be stabled or legacy target will be removed // temporal crutch until legacy tests will be stabled or legacy target will be removed
@@ -30,7 +33,7 @@ allprojects {
} }
} }
apply plugin: "com.android.library" apply plugin: "com.android.kotlin.multiplatform.library"
project.version = "$version" project.version = "$version"
project.group = "dev.inmo" project.group = "dev.inmo"
@@ -38,6 +41,20 @@ project.group = "dev.inmo"
apply from: "publish.gradle" apply from: "publish.gradle"
apply from: "github_release.gradle" apply from: "github_release.gradle"
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
nmcpAggregation {
centralPortal {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
validationTimeout = Duration.ofHours(4)
publishingType = System.getenv('PUBLISHING_TYPE') != "" ? System.getenv('PUBLISHING_TYPE') : "USER_MANAGED"
}
publishAllProjectsProbablyBreakingProjectIsolation()
}
}
repositories { repositories {
mavenLocal() mavenLocal()
// jcenter() // jcenter()
@@ -55,7 +72,22 @@ kotlin {
nodejs() nodejs()
} }
android { android {
publishAllLibraryVariants() compileSdk = "$android_compileSdkVersion".toInteger()
buildToolsVersion = "$android_buildToolsVersion"
minSdk = "$android_minSdkVersion".toInteger()
namespace = "${project.group}.${project.name}"
compilerOptions.jvmTarget.set(
compilerOptions.jvmTarget.get().class.fromTarget("17")
)
withHostTest {}
}
linuxX64()
mingwX64()
linuxArm64()
wasm {
browser()
nodejs()
d8()
} }
@@ -64,8 +96,9 @@ kotlin {
dependencies { dependencies {
implementation kotlin('stdlib') implementation kotlin('stdlib')
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlin_serialization_version"
api "com.soywiz.korlibs.klock:klock:$klockVersion" api "com.soywiz.korge:korlibs-time:$klockVersion"
} }
} }
@@ -79,6 +112,8 @@ kotlin {
dependencies { dependencies {
implementation kotlin('test-common') implementation kotlin('test-common')
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
implementation kotlin('test')
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
} }
} }
jvmTest { jvmTest {
@@ -91,7 +126,7 @@ kotlin {
implementation kotlin('test-js') implementation kotlin('test-js')
} }
} }
androidTest { androidHostTest {
dependencies { dependencies {
implementation kotlin('test-junit') implementation kotlin('test-junit')
} }
@@ -101,34 +136,7 @@ kotlin {
apply plugin: 'com.getkeepsafe.dexcount' apply plugin: 'com.getkeepsafe.dexcount'
android { java {
compileSdkVersion "$android_compileSdkVersion".toInteger() sourceCompatibility = JavaVersion.VERSION_17
buildToolsVersion "$android_buildToolsVersion" targetCompatibility = JavaVersion.VERSION_17
defaultConfig {
minSdkVersion "$android_minSdkVersion".toInteger()
targetSdkVersion "$android_compileSdkVersion".toInteger()
versionCode "${android_code_version}".toInteger()
versionName "$version"
}
buildTypes {
release {
minifyEnabled false
}
debug {
debuggable true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test {
java.srcDir file("src/jvmTest")
}
}
} }
Regular → Executable
View File
File mode changed.
+2 -2
View File
@@ -1,4 +1,4 @@
dokkaHtml { dokka {
String dokkaOutputDirectoryPath = null String dokkaOutputDirectoryPath = null
switch (true) { switch (true) {
case project.hasProperty("DOKKA_PATH"): case project.hasProperty("DOKKA_PATH"):
@@ -32,7 +32,7 @@ dokkaHtml {
sourceLink { sourceLink {
localDirectory.set(file("./")) localDirectory.set(file("./"))
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/krontab/blob/master/")) remoteUrl.set(new URI("https://github.com/InsanusMokrassar/krontab/blob/master/"))
remoteLineSuffix.set("#L") remoteLineSuffix.set("#L")
} }
} }
+26 -12
View File
@@ -1,30 +1,44 @@
interface InjectedExecOps {
@Inject //@javax.inject.Inject
ExecOperations getExecOps()
}
private String getCurrentVersionChangelog(String version) { private String getCurrentVersionChangelog(String version) {
OutputStream changelogDataOS = new ByteArrayOutputStream() OutputStream changelogDataOS = new ByteArrayOutputStream()
exec {
def injected = project.objects.newInstance(InjectedExecOps)
injected.execOps.exec {
commandLine 'chmod', "+x", './changelog_parser.sh' commandLine 'chmod', "+x", './changelog_parser.sh'
} }
exec { injected.execOps.exec {
standardOutput = changelogDataOS standardOutput = changelogDataOS
commandLine './changelog_parser.sh', "$version", 'CHANGELOG.md' commandLine './changelog_parser.sh', version, 'CHANGELOG.md'
} }
return changelogDataOS.toString().trim() return changelogDataOS.toString().trim()
} }
if (new File(projectDir, "secret.gradle").exists()) {
apply from: './secret.gradle' def githubTokenVariableName = "GITHUB_RELEASE_TOKEN"
def githubTokenVariableFromEnv = System.getenv(githubTokenVariableName)
def secretFile = new File(projectDir, "secret.gradle")
if (secretFile.exists() || project.hasProperty(githubTokenVariableName) || (githubTokenVariableFromEnv != "" && githubTokenVariableFromEnv != null)) {
if (secretFile.exists()) {
apply from: './secret.gradle'
}
apply plugin: "com.github.breadmoirai.github-release" apply plugin: "com.github.breadmoirai.github-release"
def githubReleaseToken = project.hasProperty(githubTokenVariableName) ? project.property(githubTokenVariableName).toString() : githubTokenVariableFromEnv
githubRelease { githubRelease {
token "${project.property('GITHUB_RELEASE_TOKEN')}" token githubReleaseToken
owner "InsanusMokrassar" owner = "InsanusMokrassar"
repo "${rootProject.name}" repo = "${rootProject.name}"
tagName "${project.version}" tagName = "v${project.version}"
releaseName "${project.version}" releaseName = "${project.version}"
targetCommitish "${project.version}" targetCommitish = "${project.version}"
body getCurrentVersionChangelog("${project.version}") body = getCurrentVersionChangelog("${project.version}")
} }
} }
+21 -17
View File
@@ -1,37 +1,41 @@
org.gradle.jvmargs=-Xmx1g -Xms500m
kotlin.daemon.jvmargs=-Xmx1g -Xms500m
kotlin.code.style=official kotlin.code.style=official
org.gradle.parallel=true org.gradle.parallel=true
kotlin.js.generate.externals=true
kotlin.incremental=true kotlin.incremental=true
kotlin.incremental.js=true kotlin.incremental.js=true
kotlin.incremental.multiplatform=true kotlin.incremental.multiplatform=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=false
kotlin_version=1.5.31 kotlin_version=2.4.20
kotlin_coroutines_version=1.5.2 kotlin_coroutines_version=1.11.0
kotlin_serialization_version=1.12.0
dokka_version=1.5.30 dokka_version=2.2.0
klockVersion=2.4.2 klockVersion=5.4.0
nmcp_version=1.6.2
## Github reease ## Github reease
github_release_plugin_version=2.2.12 github_release_plugin_version=2.5.2
## Android ## Android
android_minSdkVersion=19 android_gradle_version=9.3.2
android_compileSdkVersion=31 android_minSdkVersion=21
android_buildToolsVersion=31.0.0 android_compileSdkVersion=37
dexcount_version=3.0.0 android_buildToolsVersion=37.0.0
junit_version=4.12 dexcount_version=4.0.0
test_ext_junit_version=1.1.2 junit_version=4.13.2
espresso_core=3.3.0 test_ext_junit_version=1.3.0
espresso_core=3.7.0
androidx_work_version=2.6.0 androidx_work_version=2.11.2
## Common ## Common
version=0.6.4 version=3.1.0
android_code_version=8 android_code_version=53
+1 -1
View File
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
-1
View File
@@ -1 +0,0 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/krontab/blob/master/LICENSE"}],"mavenConfig":{"name":"Krontab","description":"It is an analog of crontab util for Kotlin Coroutines","url":"https://github.com/InsanusMokrassar/krontab","vcsUrl":"https://github.com/InsanusMokrassar/krontab.git","includeGpgSigning":true,"developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/krontab"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}]}}
+71 -22
View File
@@ -1,8 +1,7 @@
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
apply plugin: 'signing'
task javadocsJar(type: Jar) { task javadocsJar(type: Jar) {
classifier = 'javadoc' archiveClassifier = 'javadoc'
} }
publishing { publishing {
@@ -38,32 +37,82 @@ publishing {
} }
} }
repositories { }
if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) { repositories {
maven { if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) {
name = "GithubPackages" maven {
url = uri("https://maven.pkg.github.com/InsanusMokrassar/krontab") name = "GithubPackages"
credentials { url = uri("https://maven.pkg.github.com/InsanusMokrassar/krontab")
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') credentials {
} username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
} }
} }
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { }
maven { if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
name = "sonatype" maven {
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") name = "InmoNexus"
credentials { url = uri("https://nexus.inmo.dev/repository/maven-releases/")
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') credentials {
} username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER')
password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD')
} }
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
}
} }
} }
} }
} }
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)
}
}
signing { // Workaround to make android sign operations depend on signing tasks
useGpgCmd() project.getTasks().withType(AbstractPublishToMaven.class).configureEach {
sign publishing.publications def signingTasks = project.getTasks().withType(Sign.class)
mustRunAfter(signingTasks)
}
// Workaround to make test tasks use sign
project.getTasks().withType(Sign.class).configureEach { signTask ->
def withoutSign = (signTask.name.startsWith("sign") ? signTask.name.minus("sign") : signTask.name)
def pubName = withoutSign.endsWith("Publication") ? withoutSign.substring(0, withoutSign.length() - "Publication".length()) : withoutSign
// These tasks only exist for native targets, hence findByName() to avoid trying to find them for other targets
// Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def debugTestTask = tasks.findByName("linkDebugTest$pubName")
if (debugTestTask != null) {
signTask.mustRunAfter(debugTestTask)
}
// Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def testTask = tasks.findByName("compileTestKotlin$pubName")
if (testTask != null) {
signTask.mustRunAfter(testTask)
}
}
} }
+1
View File
@@ -0,0 +1 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/krontab/blob/master/LICENSE"}],"mavenConfig":{"name":"Krontab","description":"It is an analog of crontab util for Kotlin Coroutines","url":"https://github.com/InsanusMokrassar/krontab","vcsUrl":"https://github.com/InsanusMokrassar/krontab.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/krontab"},{"name":"InmoNexus","url":"https://nexus.inmo.dev/repository/maven-releases/"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}
+6
View File
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}
@@ -2,7 +2,8 @@ package dev.inmo.krontab
import android.content.Context import android.content.Context
import androidx.work.* import androidx.work.*
import com.soywiz.klock.DateTime import korlibs.time.DateTime
import korlibs.time.millisecondsLong
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/** /**
@@ -138,10 +139,14 @@ abstract class KronSchedulerWork(
} }
throw e throw e
} }
when (result) { result.checkResults(
is Result.Failure -> if (prolongOnFailure) prolong() onFailure = {
is Result.Success -> if (prolongOnSuccess) prolong() if (prolongOnFailure) prolong()
} },
onSuccess = {
if (prolongOnSuccess) prolong()
}
)
return result return result
} }
} }
+15
View File
@@ -0,0 +1,15 @@
package dev.inmo.krontab
import android.annotation.SuppressLint
import androidx.work.ListenableWorker
@SuppressLint("RestrictedApi")
internal inline fun ListenableWorker.Result.checkResults(
onFailure: () -> Unit,
onSuccess: () -> Unit
) {
when (this) {
is ListenableWorker.Result.Failure -> onFailure()
is ListenableWorker.Result.Success -> onSuccess()
}
}
+195
View File
@@ -0,0 +1,195 @@
package dev.inmo.krontab
import korlibs.time.DateTime
import korlibs.time.DateTimeTz
import korlibs.time.millisecondsLong
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.
*
* WARNING!!! If you want to launch it in parallel, you must do this explicitly.
*
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
*/
suspend inline fun <T> KronScheduler.doOnce(block: (DateTime) -> T): T {
val time = nextOrNow().also {
delay((it - DateTime.now()).millisecondsLong)
}
return block(time)
}
/**
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
*
* WARNING!!! If you want to launch it in parallel, you must do this explicitly.
*
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
*/
@Deprecated("Replaceable", ReplaceWith("doOnce", "dev.inmo.krontab.doOnce"))
suspend inline fun <T> KronScheduler.doOnceLocal(block: (DateTime) -> T): T = doOnce(block)
/**
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
*
* WARNING!!! This method will use local time instead of utc
*
* 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 -> nextTimeZoned() ?: DateTimeTz.nowLocal()
}
delay((time - DateTimeTz.nowLocal()).millisecondsLong)
return block(time)
}
/**
* Will [buildSchedule] using [scheduleConfig] and call [doOnce] on it
* @see buildSchedule
*/
suspend inline fun <T> doOnce(
scheduleConfig: String,
block: (DateTime) -> T
) = 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
*/
suspend inline fun KronScheduler.doWhile(block: (DateTime) -> Boolean) {
var latest: DateTime? = null
do {
delay(1L)
val result = doOnce {
if (latest != it) {
latest = it
block(it)
} else {
null
}
}
} while (result == null || result)
}
/**
* 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(block: (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) {
var latest: DateTimeTz? = null
do {
delay(1L)
val result = doOnceTz {
if (latest != it) {
latest = it
block(it)
} else {
null
}
}
} while (result == null || result)
}
/**
* Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
*
* @see buildSchedule
*/
suspend inline fun doWhile(
scheduleConfig: String,
block: (DateTime) -> Boolean
) = 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,
block: (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
*/
suspend inline fun KronScheduler.doInfinity(block: (DateTime) -> Unit) = doWhile {
block(it)
coroutineContext.isActive
}
/**
* Will execute [block] without any checking of result
*/
@Deprecated("Replaceable", ReplaceWith("doInfinity", "dev.inmo.krontab.doInfinity"))
suspend inline fun KronScheduler.doInfinityLocal(block: (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]
*
* @see buildSchedule
*/
suspend inline fun doInfinity(
scheduleConfig: String,
block: (DateTime) -> Unit
) = 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,
block: (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,6 +1,6 @@
package dev.inmo.krontab package dev.inmo.krontab
import com.soywiz.klock.DateTime import korlibs.time.DateTime
/** /**
* 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
@@ -18,6 +18,8 @@ interface KronScheduler {
* @see dev.inmo.krontab.internal.CronDateTimeScheduler.next * @see dev.inmo.krontab.internal.CronDateTimeScheduler.next
*/ */
suspend fun next(relatively: DateTime = DateTime.now()): DateTime? suspend fun next(relatively: DateTime = DateTime.now()): DateTime?
companion object
} }
suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively) suspend fun KronScheduler.nextOrRelative(relatively: DateTime = DateTime.now()): DateTime = next(relatively) ?: getAnyNext(relatively)
@@ -1,7 +1,7 @@
package dev.inmo.krontab package dev.inmo.krontab
import com.soywiz.klock.DateTime import korlibs.time.DateTime
import com.soywiz.klock.DateTimeTz import korlibs.time.DateTimeTz
/** /**
* This interface extending [KronScheduler] to use [DateTimeTz] with taking into account offset of incoming time for * This interface extending [KronScheduler] to use [DateTimeTz] with taking into account offset of incoming time for
@@ -13,7 +13,7 @@ import com.soywiz.klock.DateTimeTz
interface KronSchedulerTz : KronScheduler { interface KronSchedulerTz : KronScheduler {
suspend fun next(relatively: DateTimeTz): DateTimeTz? suspend fun next(relatively: DateTimeTz): DateTimeTz?
override suspend fun next(relatively: DateTime): DateTime? = next(relatively.localUnadjusted) ?.local override suspend fun next(relatively: DateTime): DateTime? = next(relatively.local) ?.local
} }
suspend fun KronSchedulerTz.nextOrRelative(relatively: DateTimeTz): DateTimeTz = next(relatively) ?: getAnyNext( suspend fun KronSchedulerTz.nextOrRelative(relatively: DateTimeTz): DateTimeTz = next(relatively) ?: getAnyNext(
@@ -31,4 +31,4 @@ suspend fun KronScheduler.next(relatively: DateTimeTz) = if (this is KronSchedul
this.next(relatively.local) ?.toOffsetUnadjusted(relatively.offset) this.next(relatively.local) ?.toOffsetUnadjusted(relatively.offset)
} }
suspend fun KronScheduler.nextTimeZoned() = next(DateTime.now().local) suspend fun KronScheduler.nextTimeZoned() = next(DateTime.now().local)
+176
View File
@@ -0,0 +1,176 @@
package dev.inmo.krontab
import dev.inmo.krontab.internal.*
import dev.inmo.krontab.internal.CronDateTimeScheduler
import dev.inmo.krontab.internal.CronDateTimeSchedulerTz
import dev.inmo.krontab.internal.createKronScheduler
import dev.inmo.krontab.internal.createKronSchedulerWithOffset
import dev.inmo.krontab.internal.millisecondsArrayDefault
import dev.inmo.krontab.internal.parseDaysOfMonth
import dev.inmo.krontab.internal.parseHours
import dev.inmo.krontab.internal.parseMilliseconds
import dev.inmo.krontab.internal.parseMinutes
import dev.inmo.krontab.internal.parseMonths
import dev.inmo.krontab.internal.parseOffset
import dev.inmo.krontab.internal.parseSeconds
import dev.inmo.krontab.internal.parseWeekDay
import dev.inmo.krontab.internal.parseYears
import dev.inmo.krontab.utils.Minutes
import korlibs.time.TimezoneOffset
import korlibs.time.minutes
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
/**
* This value class contains [KrontabTemplate]
*
* * **seconds**
* * **minutes**
* * **hours**
* * **dayOfMonth**
* * **month**
* * **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 (except of offsets) have next format:
*
* `{number}[,{number},...]` or `*`
*
* and {number} here is one of
*
* * {int}-{int}
* * {int}/{int}
* * *&#47;{int}
* * {int}
* * F
* * 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:
*
* * Seconds ranges can be found in [secondsRange]
* * Minutes ranges can be found in [minutesRange]
* * Hours ranges can be found in [hoursRange]
* * Days of month ranges can be found in [dayOfMonthRange]
* * Months ranges can be found in [monthRange]
* * 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:
*
* * "0/5 * * * *" for every five seconds triggering
* * "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 * * * 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 first, fifth and last days of may
* * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of first, fifth and last days 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 first, fifth and last days 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 first, fifth and last days 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 first, fifth and last days 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 first, fifth and last days 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 first, fifth and last days 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
*
* @see dev.inmo.krontab.internal.createKronScheduler
*/
@Serializable
@JvmInline
value class KrontabConfig(
@Suppress("MemberVisibilityCanBePrivate")
val template: KrontabTemplate
) {
/**
* Creates __new__ [KronScheduler] based on a [template]
*
* @return In case when offset parameter is absent in [template] will be used [createSimpleScheduler] method and
* returned [CronDateTimeScheduler]. In case when offset parameter there is in [template] [KrontabTemplate] will be used
* [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz]
*/
fun scheduler(): 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) = template
.split(" ")
.filter { checkIncomingPart(it) } // filter garbage from string
.let {
if (it.size < 5) { // reconstruction in case of insufficient arguments; 5 is amount of required arguments out of latest also code
it + (it.size until 5).map { "*" }
} else {
it
}
}
.also {
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 minutesParsed = parseMinutes(minutesSource)
val hoursParsed = parseHours(hoursSource)
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
val monthParsed = parseMonths(monthSource)
return offsetParsed ?.let { offset ->
createKronSchedulerWithOffset(
secondsParsed,
minutesParsed,
hoursParsed,
dayOfMonthParsed,
monthParsed,
yearParsed,
dayOfWeekParsed,
TimezoneOffset(offset.minutes),
millisecondsParsed ?: millisecondsArrayDefault
)
} ?: createKronScheduler(
secondsParsed,
minutesParsed,
hoursParsed,
dayOfMonthParsed,
monthParsed,
yearParsed,
dayOfWeekParsed,
millisecondsParsed ?: millisecondsArrayDefault
)
}
/**
* Creates base [KronScheduler] using [scheduler] function. In case when returned [KronScheduler] is [KronSchedulerTz],
* it will be returned as is. Otherwise, will be created new [CronDateTimeSchedulerTz] with [defaultOffset] as
* offset
*/
fun scheduler(defaultOffset: Minutes): KronSchedulerTz {
val scheduler = scheduler()
return if (scheduler is KronSchedulerTz) {
scheduler
} else {
CronDateTimeSchedulerTz(
(scheduler as CronDateTimeScheduler).cronDateTime,
TimezoneOffset(defaultOffset.minutes)
)
}
}
}
@@ -5,6 +5,10 @@ package dev.inmo.krontab
* [dev.inmo.krontab.internal.CronDateTimeScheduler] due to the fact that [toKronScheduler] will return it under the * [dev.inmo.krontab.internal.CronDateTimeScheduler] due to the fact that [toKronScheduler] will return it under the
* hood * hood
*/ */
@Deprecated(
"It is useless wrapper for KrontabTemplate. Use KrontabConfig instead",
ReplaceWith("KrontabConfig(template)", "dev.inmo.krontab.KrontabConfig")
)
data class KrontabTemplateWrapper( data class KrontabTemplateWrapper(
val template: KrontabTemplate val template: KrontabTemplate
) : KronScheduler by template.toKronScheduler() ) : KronScheduler by template.toKronScheduler()
@@ -15,4 +19,8 @@ data class KrontabTemplateWrapper(
* @see [toKronScheduler] * @see [toKronScheduler]
* @see [KrontabTemplateWrapper] * @see [KrontabTemplateWrapper]
*/ */
@Deprecated(
"Will be removed in near major update with KrontabTemplateWrapper",
ReplaceWith("this.krontabConfig", "dev.inmo.krontab.krontabConfig")
)
fun KrontabTemplate.wrapAsKronScheduler() = KrontabTemplateWrapper(this) fun KrontabTemplate.wrapAsKronScheduler() = KrontabTemplateWrapper(this)
@@ -0,0 +1,13 @@
package dev.inmo.krontab
import korlibs.time.DateTime
class LambdaKronScheduler(
private val onNext: suspend (DateTime) -> DateTime?
) : KronScheduler {
override suspend fun next(relatively: DateTime): DateTime? = onNext(relatively)
}
fun KronScheduler(
onNext: suspend (DateTime) -> DateTime?
) = LambdaKronScheduler(onNext)
@@ -0,0 +1,13 @@
package dev.inmo.krontab
import korlibs.time.DateTimeTz
class LambdaKronSchedulerTz(
private val onNext: suspend (DateTimeTz) -> DateTimeTz?
) : KronSchedulerTz {
override suspend fun next(relatively: DateTimeTz): DateTimeTz? = onNext(relatively)
}
fun KronSchedulerTz(
onNext: suspend (DateTimeTz) -> DateTimeTz?
) = LambdaKronSchedulerTz(onNext)
+133
View File
@@ -0,0 +1,133 @@
package dev.inmo.krontab
import korlibs.time.DateTime
import dev.inmo.krontab.builder.buildSchedule
import dev.inmo.krontab.internal.*
internal val anyCronDateTime by lazy {
CronDateTime()
}
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now]
*/
val AnyTimeScheduler: KronScheduler by lazy {
CronDateTimeScheduler(anyCronDateTime)
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one millisecond
*/
val EveryMillisecondScheduler: KronScheduler by lazy {
buildSchedule { milliseconds { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one second
*/
val EverySecondScheduler: KronScheduler by lazy {
buildSchedule { seconds { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one minute
*/
val EveryMinuteScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one hour
*/
val EveryHourScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one day
*/
val EveryDayOfMonthScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { at(0) }
dayOfMonth { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one month
*/
val EveryMonthScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { at(0) }
dayOfMonth { at(0) }
months { 0 every 1 }
}
}
/**
* [KronScheduler.next] will always return [korlibs.time.DateTime.now] + one year
*/
val EveryYearScheduler: KronScheduler by lazy {
buildSchedule {
seconds { at(0) }
minutes { at(0) }
hours { at(0) }
dayOfMonth { at(0) }
months { at(0) }
years { 0 every 1 }
}
}
/**
* Shortcut for [EveryMillisecondScheduler]
*/
inline val KronScheduler.Companion.everyMillisecond
get() = EveryMillisecondScheduler
/**
* Shortcut for [EverySecondScheduler]
*/
inline val KronScheduler.Companion.everySecond
get() = EverySecondScheduler
/**
* Shortcut for [EveryMinuteScheduler]
*/
inline val KronScheduler.Companion.everyMinute
get() = EveryMinuteScheduler
/**
* Shortcut for [EveryHourScheduler]
*/
inline val KronScheduler.Companion.hourly
get() = EveryHourScheduler
/**
* Shortcut for [EveryDayOfMonthScheduler]
*/
inline val KronScheduler.Companion.daily
get() = EveryDayOfMonthScheduler
/**
* Shortcut for [EveryMonthScheduler]
*/
inline val KronScheduler.Companion.monthly
get() = EveryMonthScheduler
/**
* Shortcut for [EveryYearScheduler]
*/
inline val KronScheduler.Companion.annually
get() = EveryYearScheduler
@@ -1,7 +1,5 @@
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 import dev.inmo.krontab.utils.Minutes
@@ -11,17 +9,20 @@ import dev.inmo.krontab.utils.Minutes
*/ */
typealias KrontabTemplate = String typealias KrontabTemplate = String
inline fun KrontabTemplate.krontabConfig() = KrontabConfig(this)
/** /**
* 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)
* * (optional) (can be placed anywhere after month) (must be marked with `o` at the end, for example: 60o == +01:00) offset * * **offset** (optional) (can be placed anywhere after month) (must be marked with `o` at the end, for example: 60o == +01:00)
* * (optional) (can be placed anywhere after month) dayOfWeek * * **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 (except of offsets) have next format: * And each one (except of offsets) have next format:
* *
@@ -48,79 +49,37 @@ typealias KrontabTemplate = String
* * 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] * * 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
* * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may * * "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 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" for triggering in near first second of second minute of third hour of first, fifth and last days of may
* * "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 60o" for triggering in near first second of second minute of third hour of first, fifth and last days of may 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 60o 0-2w" for triggering in near first second of second minute of third hour of first, fifth and last days 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 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" for triggering in near first second of second minute of third hour of first, fifth and last days of may of 2021st year
* * "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" for triggering in near first second of second minute of third hour of first, fifth and last days 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 first, fifth and last days 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 first, fifth and last days 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 * @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 * returned [CronDateTimeScheduler]. In case when offset parameter there is in [incoming] [KrontabTemplate] will be used
* [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz] * [createKronSchedulerWithOffset] and returned [CronDateTimeSchedulerTz]
* *
* @see dev.inmo.krontab.internal.createKronScheduler * @see dev.inmo.krontab.internal.createKronScheduler
* @see KrontabConfig.scheduler
*/ */
fun createSimpleScheduler( fun createSimpleScheduler(
incoming: KrontabTemplate incoming: KrontabTemplate
): KronScheduler { ): KronScheduler = KrontabConfig(incoming).scheduler()
var offsetParsed: Int? = null
var dayOfWeekParsed: Array<Byte>? = null
var yearParsed: Array<Int>? = null
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also {
listOfNotNull(
it.getOrNull(5),
it.getOrNull(6),
it.getOrNull(7)
).forEach {
val offsetFromString = parseOffset(it)
val dayOfWeekFromString = parseWeekDay(it)
offsetParsed = offsetParsed ?: offsetFromString
dayOfWeekParsed = dayOfWeekParsed ?: dayOfWeekFromString
when {
dayOfWeekFromString != null || offsetFromString != null -> return@forEach
yearParsed == null -> {
yearParsed = parseYears(it)
}
}
}
}
val secondsParsed = parseSeconds(secondsSource)
val minutesParsed = parseMinutes(minutesSource)
val hoursParsed = parseHours(hoursSource)
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
val monthParsed = parseMonths(monthSource)
return offsetParsed ?.let { offset ->
createKronSchedulerWithOffset(
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, dayOfWeekParsed, TimezoneOffset(offset.minutes)
)
} ?: createKronScheduler(
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, dayOfWeekParsed
)
}
fun createSimpleScheduler( fun createSimpleScheduler(
incoming: KrontabTemplate, incoming: KrontabTemplate,
defaultOffset: Minutes defaultOffset: Minutes
): KronSchedulerTz { ): KronSchedulerTz = KrontabConfig(incoming).scheduler(defaultOffset)
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]
@@ -1,9 +1,10 @@
package dev.inmo.krontab.builder package dev.inmo.krontab.builder
import com.soywiz.klock.TimezoneOffset import korlibs.time.TimezoneOffset
import com.soywiz.klock.minutes import korlibs.time.minutes
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.KronSchedulerTz 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.internal.createKronSchedulerWithOffset
import dev.inmo.krontab.utils.Minutes import dev.inmo.krontab.utils.Minutes
@@ -13,7 +14,7 @@ import dev.inmo.krontab.utils.Minutes
* *
* @see dev.inmo.krontab.createSimpleScheduler * @see dev.inmo.krontab.createSimpleScheduler
*/ */
fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler { inline fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
val builder = SchedulerBuilder() val builder = SchedulerBuilder()
builder.settingsBlock() builder.settingsBlock()
@@ -26,7 +27,7 @@ fun buildSchedule(settingsBlock: SchedulerBuilder.() -> Unit): KronScheduler {
* *
* @see dev.inmo.krontab.createSimpleScheduler * @see dev.inmo.krontab.createSimpleScheduler
*/ */
fun buildSchedule( inline fun buildSchedule(
offset: Minutes, offset: Minutes,
settingsBlock: SchedulerBuilder.() -> Unit settingsBlock: SchedulerBuilder.() -> Unit
): KronSchedulerTz { ): KronSchedulerTz {
@@ -37,6 +38,27 @@ fun buildSchedule(
return builder.build() as KronSchedulerTz return builder.build() as KronSchedulerTz
} }
/**
* Creates new [KronScheduler] with [settingsBlock]
*
* Since it is inline function, you may break execution of [settingsBlock]
* at any time
*/
inline operator fun KronScheduler.Companion.invoke(
offset: Minutes,
settingsBlock: SchedulerBuilder.() -> Unit
): KronSchedulerTz = buildSchedule(offset, settingsBlock)
/**
* Creates new [KronScheduler] with [settingsBlock]
*
* Since it is inline function, you may break execution of [settingsBlock]
* at any time
*/
inline operator fun KronScheduler.Companion.invoke(
settingsBlock: SchedulerBuilder.() -> Unit
): KronScheduler = buildSchedule(settingsBlock)
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,
@@ -45,7 +67,8 @@ class SchedulerBuilder(
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 var dayOfWeek: Array<Byte>? = null,
private val offset: Minutes? = 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>?,
@@ -63,6 +86,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
*/ */
@@ -147,6 +181,25 @@ class SchedulerBuilder(
* @see dev.inmo.krontab.internal.createKronScheduler * @see dev.inmo.krontab.internal.createKronScheduler
*/ */
fun build(): KronScheduler = offset ?.let { fun build(): KronScheduler = offset ?.let {
createKronSchedulerWithOffset(seconds, minutes, hours, dayOfMonth, month, year, dayOfWeek, TimezoneOffset(it.minutes)) createKronSchedulerWithOffset(
} ?: createKronScheduler(seconds, minutes, hours, dayOfMonth, month, year, dayOfWeek) seconds = seconds,
minutes = minutes,
hours = hours,
dayOfMonth = dayOfMonth,
month = month,
years = year,
weekDays = dayOfWeek,
offset = TimezoneOffset(it.minutes),
milliseconds = milliseconds ?: millisecondsArrayDefault
)
} ?: createKronScheduler(
seconds = seconds,
minutes = minutes,
hours = hours,
dayOfMonth = dayOfMonth,
month = month,
years = year,
weekDays = dayOfWeek,
milliseconds = milliseconds ?: millisecondsArrayDefault
)
} }
@@ -122,6 +122,7 @@ 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)
@@ -1,7 +1,7 @@
package dev.inmo.krontab.collection package dev.inmo.krontab.collection
import com.soywiz.klock.DateTime import korlibs.time.DateTime
import com.soywiz.klock.DateTimeTz import korlibs.time.DateTimeTz
import dev.inmo.krontab.* import dev.inmo.krontab.*
import dev.inmo.krontab.internal.* import dev.inmo.krontab.internal.*
@@ -1,61 +0,0 @@
package dev.inmo.krontab
import com.soywiz.klock.DateTime
import kotlinx.coroutines.delay
/**
* Execute [block] once at the [KronScheduler.next] time and return result of [block] calculation.
*
* WARNING!!! If you want to launch it in parallel, you must do this explicitly.
*
* WARNING!!! In case if [KronScheduler.next] of [this] instance will return null, [block] will be called immediately
*/
suspend inline fun <T> KronScheduler.doOnce(noinline block: suspend () -> T): T {
next() ?.let {
delay((it - DateTime.now()).millisecondsLong)
}
return block()
}
/**
* Will [buildSchedule] using [scheduleConfig] and call [doOnce] on it
* @see buildSchedule
*/
suspend inline fun <T> doOnce(
scheduleConfig: String,
noinline block: suspend () -> T
) = buildSchedule(scheduleConfig).doOnce(block)
/**
* Will execute [block] while it will return true as a result of its calculation
*/
suspend inline fun KronScheduler.doWhile(noinline block: suspend () -> Boolean) {
do { val doNext = doOnce(block) } while (doNext)
}
/**
* Will [buildSchedule] using [scheduleConfig] and call [doWhile] with [block]
*
* @see buildSchedule
*/
suspend inline fun doWhile(
scheduleConfig: String,
noinline block: suspend () -> Boolean
) = buildSchedule(scheduleConfig).doWhile(block)
/**
* Will execute [block] without any checking of result
*/
suspend inline fun KronScheduler.doInfinity(noinline block: suspend () -> Unit) = doWhile {
block()
true
}
/**
* Will [buildSchedule] using [scheduleConfig] and call [doInfinity] with [block]
*
* @see buildSchedule
*/
suspend inline fun doInfinity(
scheduleConfig: String,
noinline block: suspend () -> Unit
) = buildSchedule(scheduleConfig).doInfinity(block)
@@ -1,59 +0,0 @@
package dev.inmo.krontab
import com.soywiz.klock.DateTime
import dev.inmo.krontab.builder.buildSchedule
import dev.inmo.krontab.internal.*
internal val anyCronDateTime by lazy {
CronDateTime()
}
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
*/
val AnyTimeScheduler: KronScheduler by lazy {
CronDateTimeScheduler(anyCronDateTime)
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one second
*/
val EverySecondScheduler: KronScheduler by lazy {
buildSchedule { seconds { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one minute
*/
val EveryMinuteScheduler: KronScheduler by lazy {
buildSchedule { minutes { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one hour
*/
val EveryHourScheduler: KronScheduler by lazy {
buildSchedule { hours { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one day
*/
val EveryDayOfMonthScheduler: KronScheduler by lazy {
buildSchedule { dayOfMonth { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one month
*/
val EveryMonthScheduler: KronScheduler by lazy {
buildSchedule { months { 0 every 1 } }
}
/**
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one year
*/
val EveryYearScheduler: KronScheduler by lazy {
buildSchedule { years { 0 every 1 } }
}
@@ -1,58 +0,0 @@
package dev.inmo.krontab.utils
import com.soywiz.klock.DateTime
import com.soywiz.klock.DateTimeTz
import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.next
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
/**
* This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to
* time zones
*
* @see channelFlow
*/
@FlowPreview
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow {
var previousTime = DateTime.nowLocal()
while (isActive) {
val now = DateTime.nowLocal()
val nextTime = next(now) ?: break
if (previousTime == nextTime) {
delay(1L) // skip 1ms
continue
} else {
previousTime = nextTime
}
val sleepDelay = (nextTime - DateTime.now().local).millisecondsLong
delay(sleepDelay)
send(nextTime)
}
}
/**
* This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s
*/
@FlowPreview
fun KronScheduler.asFlow(): Flow<DateTime> = asTzFlow().map { it.local }
@Deprecated(
"It is not recommended to use this class in future. This functionality will be removed soon",
ReplaceWith("asFlow", "dev.inmo.krontab.utils.asFlow")
)
@FlowPreview
class SchedulerFlow(
private val scheduler: KronScheduler
) : AbstractFlow<DateTime>() {
@FlowPreview
override suspend fun collectSafely(collector: FlowCollector<DateTime>) {
while (true) {
val now = DateTime.now()
val nextTime = scheduler.next(now) ?: break
val sleepDelay = (nextTime - now).millisecondsLong
delay(sleepDelay)
collector.emit(nextTime)
}
}
}
@@ -1,14 +1,14 @@
package dev.inmo.krontab.internal package dev.inmo.krontab.internal
import com.soywiz.klock.* import korlibs.time.DateTime
import korlibs.time.TimezoneOffset
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.utils.copy
/** /**
* @param daysOfWeek 0-6 * @param daysOfWeek 0-6
* @param years any int * @param years any int
* @param months 0-11 * @param months 0-11
* @param daysOfMonth 0-31 * @param daysOfMonth 0-30
* @param hours 0-23 * @param hours 0-23
* @param minutes 0-59 * @param minutes 0-59
* @param seconds 0-59 * @param seconds 0-59
@@ -20,7 +20,8 @@ internal data class CronDateTime(
val daysOfMonth: Array<Byte>? = null, val daysOfMonth: Array<Byte>? = null,
val hours: Array<Byte>? = null, val hours: Array<Byte>? = null,
val minutes: Array<Byte>? = null, val minutes: Array<Byte>? = null,
val seconds: Array<Byte>? = null val seconds: Array<Byte>? = null,
val milliseconds: Array<Short>? = millisecondsArrayDefault
) { ) {
init { init {
check(daysOfWeek ?.all { it in dayOfWeekRange } ?: true) check(daysOfWeek ?.all { it in dayOfWeekRange } ?: true)
@@ -30,12 +31,13 @@ internal data class CronDateTime(
check(hours?.all { it in hoursRange } ?: true) check(hours?.all { it in hoursRange } ?: true)
check(minutes?.all { it in minutesRange } ?: true) check(minutes?.all { it in minutesRange } ?: true)
check(seconds?.all { it in secondsRange } ?: true) check(seconds?.all { it in secondsRange } ?: true)
check(milliseconds?.all { it in millisecondsRange } ?: true)
} }
internal val calculators = listOf( internal val calculators = listOf(
years ?.let { NearDateTimeCalculatorYears(it) }, years ?.let { NearDateTimeCalculatorYears(it) },
daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) }, daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) },
NearDateTimeCalculatorMillis(arrayOf(0)), milliseconds ?.let { NearDateTimeCalculatorMillis(it) },
seconds ?.let { NearDateTimeCalculatorSeconds(it) }, seconds ?.let { NearDateTimeCalculatorSeconds(it) },
minutes ?.let { NearDateTimeCalculatorMinutes(it) }, minutes ?.let { NearDateTimeCalculatorMinutes(it) },
hours ?.let { NearDateTimeCalculatorHours(it) }, hours ?.let { NearDateTimeCalculatorHours(it) },
@@ -65,9 +67,10 @@ internal fun createCronDateTime(
dayOfMonth: Array<Byte>? = null, dayOfMonth: Array<Byte>? = null,
month: Array<Byte>? = null, month: Array<Byte>? = null,
years: Array<Int>? = null, years: Array<Int>? = null,
weekDays: Array<Byte>? = null weekDays: Array<Byte>? = null,
milliseconds: Array<Short>? = millisecondsArrayDefault
): CronDateTime { ): CronDateTime {
return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds) return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds, milliseconds)
} }
/** /**
@@ -80,8 +83,20 @@ internal fun createKronScheduler(
dayOfMonth: Array<Byte>? = null, dayOfMonth: Array<Byte>? = null,
month: Array<Byte>? = null, month: Array<Byte>? = null,
years: Array<Int>? = null, years: Array<Int>? = null,
weekDays: Array<Byte>? = null weekDays: Array<Byte>? = null,
): KronScheduler = CronDateTimeScheduler(createCronDateTime(seconds, minutes, hours, dayOfMonth, month, years, weekDays)) milliseconds: Array<Short>? = millisecondsArrayDefault
): KronScheduler = CronDateTimeScheduler(
createCronDateTime(
seconds,
minutes,
hours,
dayOfMonth,
month,
years,
weekDays,
milliseconds
)
)
/** /**
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data * @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
*/ */
@@ -93,8 +108,21 @@ internal fun createKronSchedulerWithOffset(
month: Array<Byte>? = null, month: Array<Byte>? = null,
years: Array<Int>? = null, years: Array<Int>? = null,
weekDays: Array<Byte>? = null, weekDays: Array<Byte>? = null,
offset: TimezoneOffset offset: TimezoneOffset,
): KronScheduler = CronDateTimeSchedulerTz(createCronDateTime(seconds, minutes, hours, dayOfMonth, month, years, weekDays), offset) milliseconds: Array<Short>? = millisecondsArrayDefault
): KronScheduler = CronDateTimeSchedulerTz(
createCronDateTime(
seconds,
minutes,
hours,
dayOfMonth,
month,
years,
weekDays,
milliseconds
),
offset
)
internal fun List<CronDateTime>.merge() = CronDateTime( internal fun List<CronDateTime>.merge() = CronDateTime(
flatMap { it.daysOfWeek ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.daysOfWeek ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
@@ -104,4 +132,5 @@ internal fun List<CronDateTime>.merge() = CronDateTime(
flatMap { it.hours ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.hours ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
flatMap { it.minutes ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.minutes ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
flatMap { it.seconds ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() }, flatMap { it.seconds ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
flatMap { it.milliseconds ?.toList() ?: listOf(0) }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
) )
@@ -1,13 +1,13 @@
package dev.inmo.krontab.internal package dev.inmo.krontab.internal
import com.soywiz.klock.DateTime import korlibs.time.DateTime
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.collection.plus
/** /**
* Cron-oriented realisation of [KronScheduler] * Cron-oriented realisation of [KronScheduler]
* *
* @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
@@ -36,23 +36,3 @@ internal fun mergeCronDateTimeSchedulers(
): CronDateTimeScheduler = CronDateTimeScheduler( ): CronDateTimeScheduler = CronDateTimeScheduler(
schedulers.map { it.cronDateTime }.merge() 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
@@ -1,7 +1,8 @@
package dev.inmo.krontab.internal package dev.inmo.krontab.internal
import com.soywiz.klock.DateTimeTz import korlibs.time.DateTime
import com.soywiz.klock.TimezoneOffset import korlibs.time.DateTimeTz
import korlibs.time.TimezoneOffset
import dev.inmo.krontab.KronScheduler import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.KronSchedulerTz import dev.inmo.krontab.KronSchedulerTz
@@ -18,6 +19,10 @@ internal data class CronDateTimeSchedulerTz internal constructor(
val dateTimeWithActualOffset = relatively.toOffset(offset).local val dateTimeWithActualOffset = relatively.toOffset(offset).local
return cronDateTime.toNearDateTime(dateTimeWithActualOffset) ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset) return cronDateTime.toNearDateTime(dateTimeWithActualOffset) ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset)
} }
override suspend fun next(relatively: DateTime): DateTime? {
return next(relatively.toOffset(offset)) ?.utc
}
} }
internal fun mergeCronDateTimeSchedulers( internal fun mergeCronDateTimeSchedulers(
@@ -0,0 +1,4 @@
package dev.inmo.krontab.internal
internal const val millisecondsDefault: Short = 0
internal val millisecondsArrayDefault: Array<Short> = arrayOf(millisecondsDefault)
@@ -1,6 +1,6 @@
package dev.inmo.krontab.internal package dev.inmo.krontab.internal
import com.soywiz.klock.* import korlibs.time.*
import dev.inmo.krontab.utils.copy import dev.inmo.krontab.utils.copy
import kotlin.math.min import kotlin.math.min
@@ -101,19 +101,26 @@ internal fun NearDateTimeCalculatorDays(
times: Array<Byte> times: Array<Byte>
) = CommonNearDateTimeCalculator( ) = CommonNearDateTimeCalculator(
times, times,
{ it.dayOfMonth.toByte() }, { (it.dayOfMonth - 1).toByte() }, // index1, so, decrease
{ dateTime, newOne -> { dateTime, newOne ->
(if (newOne < dateTime.dayOfMonth) { val dateTime = (if (newOne < dateTime.dayOfMonth) {
dateTime.plus(1.months) dateTime.plus(1.months)
} else { } else {
dateTime dateTime
}).copy( })
val newDateTime = dateTime.copy(
dayOfMonth = min(dateTime.month.days(dateTime.year), newOne.toInt() + 1), // index1 dayOfMonth = min(dateTime.month.days(dateTime.year), newOne.toInt() + 1), // index1
hour = 0, hour = 0,
minute = 0, minute = 0,
second = 0, second = 0,
milliseconds = 0 milliseconds = 0
) )
// If day of month has not been changed, use old dateTime due to no changes required
if (newDateTime.dayOfMonth == dateTime.dayOfMonth) {
dateTime
} else {
newDateTime
}
} }
) )
@@ -121,7 +128,7 @@ internal fun NearDateTimeCalculatorMonths(
times: Array<Byte> times: Array<Byte>
) = CommonNearDateTimeCalculator( ) = CommonNearDateTimeCalculator(
times, times,
{ it.dayOfMonth.toByte() }, { it.month0.toByte() },
{ dateTime, newOne -> { dateTime, newOne ->
(if (newOne < dateTime.month0) { (if (newOne < dateTime.month0) {
dateTime.plus(1.years) dateTime.plus(1.years)
@@ -3,6 +3,7 @@ package dev.inmo.krontab.internal
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(",")
@@ -36,6 +37,106 @@ private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataCon
return results.map(dataConverter) return results.map(dataConverter)
} }
/**
* FSM for parsing of incoming data. If at the end of parsing it have non-null state and string is not empty, data passed check
*
* 1.
* * "\\d" -> 2
* * "\\*" -> 4
* * "F" -> 7
* * "f" -> 7
* * "L" -> 7
* * "l" -> 7
* * "/" -> 6
* 2.
* * "\\d" -> 2
* * "/" -> 6
* * "," -> 1
* * "-" -> 3
* * "m" -> 9
* * "o" -> 10
* * "w" -> 10
* 3.
* * "L" -> 7
* * "l" -> 7
* * "\\d" -> 8
* 4.
* * "/" -> 6
* * "," -> 1
* 5.
* * "/" -> 6
* 6.
* * "\\d" -> 8
* * "\\*" -> 7
* 7.
* * "," -> 1
* 8.
* * "\\d" -> 8
* * "," -> 1
* 9.
* * "s" -> 10 // end of ms
* 10. Empty, end of parse
*/
private val checkIncomingPartTransitionsMap = listOf(
listOf( // 0
Regex("\\d") to 1,
Regex("\\*") to 3,
Regex("[Ff]") to 6,
Regex("[Ll]") to 6,
Regex("/") to 5,
),
listOf( // 1
Regex("\\d") to 1,
Regex("/") to 5,
Regex(",") to 0,
Regex("-") to 2,
Regex("m") to 8,
Regex("o") to 9,
Regex("w") to 9,
),
listOf( // 2
Regex("[Ll]") to 6,
Regex("\\d") to 7,
),
listOf( // 3
Regex("/") to 5,
Regex(",") to 0,
),
listOf( // 4
Regex("/") to 5,
),
listOf( // 5
Regex("\\d") to 7,
Regex("\\*") to 6,
),
listOf( // 6
Regex(",") to 0,
),
listOf( // 7
Regex("\\d") to 7,
Regex(",") to 0,
),
listOf( // 8
Regex("s") to 9, // end of ms
),
listOf(), // empty state, end of parsing
)
internal fun checkIncomingPart(part: String): Boolean {
var i = 0
var state = checkIncomingPartTransitionsMap[0]
while (i < part.length) {
val char = part[i]
val nextState = state.firstNotNullOfOrNull {
it.second.takeIf { _ -> it.first.matches("$char") }
}
if (nextState == null) return false
state = checkIncomingPartTransitionsMap[nextState]
i++
}
return part.isNotEmpty()
}
internal fun parseWeekDay(from: String?) = from ?.let { if (it.endsWith("w")) createSimpleScheduler(it.removeSuffix("w"), dayOfWeekRange, intToByteConverter) ?.toTypedArray() else null } 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 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() }
@@ -44,6 +145,7 @@ internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfM
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>,
@@ -7,3 +7,4 @@ 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,6 +1,7 @@
package dev.inmo.krontab.utils package dev.inmo.krontab.utils
import com.soywiz.klock.* import korlibs.time.DateTime
import korlibs.time.Month
import kotlin.math.min import kotlin.math.min
fun DateTime.copy( fun DateTime.copy(
@@ -0,0 +1,94 @@
package dev.inmo.krontab.utils
import korlibs.time.DateTime
import korlibs.time.DateTimeTz
import korlibs.time.milliseconds
import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.next
import korlibs.time.millisecondsLong
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
/**
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
*
* Will emit all the [KronScheduler.next] as soon as possible. In case [KronScheduler.next] return null, flow will
* be completed
*
* @param since Will be used as the first parameter for [KronScheduler.next] fun. If passed null, `flow`
* will always start since the moment of collecting start
*/
fun KronScheduler.asTzFlowWithoutDelays(since: DateTimeTz? = null): Flow<DateTimeTz> = flow {
var previous = since ?: DateTime.nowLocal()
while (currentCoroutineContext().isActive) {
val next = next(previous) ?: break
emit(next)
previous = next + 1.milliseconds
}
}
/**
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
*
* This [Flow] will use [asTzFlowWithoutDelays], but stop on each time until this time will happen
*/
fun KronScheduler.asTzFlowWithDelays(): Flow<DateTimeTz> = asTzFlowWithoutDelays().onEach { futureHappenTime ->
val now = DateTime.nowLocal()
delay((futureHappenTime - now).millisecondsLong)
}
/**
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
*
* This [Flow] will use [asTzFlowWithoutDelays], but stop on each time until this time will happen
*/
@Deprecated(
"Behaviour will be changed. In some of near versions this flow will not delay executions",
ReplaceWith("this.asTzFlowWithDelays()", "dev.inmo.krontab.utils.asTzFlowWithDelays")
)
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = asTzFlowWithDelays()
/**
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
*
* Will emit all the [KronScheduler.next] as soon as possible. In case [KronScheduler.next] return null, flow will
* be completed
*
* @param since Will be used as the first parameter for [KronScheduler.next] fun. If passed null, `flow`
* will always start since the moment of collecting start
*/
fun KronScheduler.asFlowWithoutDelays(since: DateTime? = null): Flow<DateTime> = flow {
var previous = since ?: DateTime.now()
while (currentCoroutineContext().isActive) {
val next = next(previous) ?: break
emit(next)
previous = next + 1.milliseconds
}
}
/**
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
*
* This [Flow] will use [asFlowWithoutDelays], but stop on each time until this time will happen
*/
fun KronScheduler.asFlowWithDelays(): Flow<DateTime> = asFlowWithoutDelays().onEach { futureHappenTime ->
val now = DateTime.now()
delay((futureHappenTime - now).millisecondsLong)
}
/**
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
*
* This [Flow] will use [asFlowWithDelays], but stop on each time until this time will happen
*/
@Deprecated(
"Behaviour will be changed. In some of near versions this flow will not delay executions",
ReplaceWith("this.asFlowWithDelays()", "dev.inmo.krontab.utils.asFlowWithDelays")
)
fun KronScheduler.asFlow(): Flow<DateTime> = asFlowWithDelays()
@@ -0,0 +1,34 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
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 }
@@ -0,0 +1,34 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterSeconds(vararg seconds: Int) = filter { it.seconds in seconds }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterMinutes(vararg minutes: Int) = filter { it.minutes in minutes }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterHours(vararg hours: Int) = filter { it.hours in hours }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterDaysOfMonths(vararg daysOfMonths: Int) = filter { it.dayOfMonth in daysOfMonths }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterMonths(vararg months: Month) = filter { it.month in months }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterMonths0(vararg months: Int) = filter { it.month0 in months }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterMonths1(vararg months: Int) = filter { it.month1 in months }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterYears(year: Year) = filter { it.year == year }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterYears(vararg years: Int) = filter { it.yearInt in years }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterWeekDays(vararg weekDays: DayOfWeek) = filter { it.dayOfWeek in weekDays }
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.filterWeekDays(vararg weekDays: Int) = filter { it.dayOfWeekInt in weekDays }
@@ -0,0 +1,25 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
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()
@@ -0,0 +1,25 @@
package dev.inmo.krontab.utils.flows
import korlibs.time.*
import kotlinx.coroutines.flow.Flow
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.onlyStartsOfMinutes() = filterSeconds(0)
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.onlyStartsOfHours() = filterMinutes(0).onlyStartsOfMinutes()
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.onlyStartsOfDays() = filterHours(0).onlyStartsOfHours()
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.onlyStartsOfMondays() = filterWeekDays(DayOfWeek.Monday).onlyStartsOfDays()
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.onlyStartsOfSundays() = filterWeekDays(DayOfWeek.Sunday).onlyStartsOfDays()
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.onlyStartsOfMonths() = filterDaysOfMonths(1).onlyStartsOfDays()
@Suppress("NOTHING_TO_INLINE")
inline fun Flow<DateTimeTz>.onlyStartsOfYears() = filterMonths(Month.January).onlyStartsOfMonths()
+38
View File
@@ -0,0 +1,38 @@
package dev.inmo.krontab.utils
import dev.inmo.krontab.*
import korlibs.time.*
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
class BuildersTest {
@Test
fun presetsWorksCorrectly() {
val data = mapOf(
EverySecondScheduler to { it: DateTime -> if (it.milliseconds > 0 ) it + 1.seconds - it.milliseconds.milliseconds else it },
EveryMinuteScheduler to { it: DateTime -> if (it.seconds > 0 || it.milliseconds > 0 ) it + 1.minutes - it.seconds.seconds - it.milliseconds.milliseconds else it },
EveryHourScheduler to { it: DateTime -> if (it.minutes > 0 || it.seconds > 0 || it.milliseconds > 0 ) it + 1.hours - it.minutes.minutes - it.seconds.seconds - it.milliseconds.milliseconds else it },
EveryDayOfMonthScheduler to { it: DateTime -> if (it.hours > 0 || it.minutes > 0 || it.seconds > 0 || it.milliseconds > 0 ) it + 1.days - it.hours.hours - it.minutes.minutes - it.seconds.seconds - it.milliseconds.milliseconds else it },
EveryMonthScheduler to { it: DateTime -> if (it.dayOfMonth > 1 || it.hours > 0 || it.minutes > 0 || it.seconds > 0 || it.milliseconds > 0 ) (it + 1.months).copy(dayOfMonth = 1, hour = 0, minute = 0, second = 0, milliseconds = 0) else it },
)
val samples = 10000
runTest {
var now = DateTime.now()
for (i in 0 until samples) {
data.forEach { (scheduler, expectCalculator) ->
val expectValue = expectCalculator(now)
val newNow = scheduler.nextOrRelative(now)
assertEquals(expectValue, newNow, "For time ${now.toStringDefault()} calculated wrong value: ${newNow.toStringDefault()} is not equal to ${expectValue.toStringDefault()}")
now = newNow
}
}
}
}
}
@@ -0,0 +1,23 @@
package dev.inmo.krontab.utils
import korlibs.time.DateTime
import korlibs.time.days
import dev.inmo.krontab.buildSchedule
import kotlinx.coroutines.test.runTest
import kotlin.test.*
class CheckMonthsAndDaysCorrectWork {
@Test
fun checkMonthsAndDaysCorrectWork() {
val now = DateTime.now().startOfYear.startOfDay
for (i in 0 until now.year.days) {
val scheduleDateTime = (now + i.days)
runTest {
assertEquals(
scheduleDateTime,
buildSchedule("0 0 0 ${scheduleDateTime.dayOfMonth - 1} ${scheduleDateTime.month0}").next(now)
)
}
}
}
}
@@ -1,6 +1,8 @@
package dev.inmo.krontab.utils package dev.inmo.krontab.utils
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
fun CoroutineScope.createFailJob(forTimeMillis: Long) = launch { fun CoroutineScope.createFailJob(forTimeMillis: Long) = launch {
delay(forTimeMillis) delay(forTimeMillis)
@@ -2,8 +2,8 @@ package dev.inmo.krontab.utils
import dev.inmo.krontab.builder.buildSchedule import dev.inmo.krontab.builder.buildSchedule
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.test.runTest
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -18,7 +18,7 @@ class SchedulerFlowTests {
} }
} }
val flow = kronScheduler.asFlow() val flow = kronScheduler.asFlowWithoutDelays()
runTest { runTest {
val mustBeCollected = 10 val mustBeCollected = 10
@@ -40,14 +40,14 @@ class SchedulerFlowTests {
} }
} }
val flow = kronScheduler.asFlow() val flow = kronScheduler.asFlowWithoutDelays()
runTest { runTest {
val testsCount = 10 val testsCount = 10
val failJob = it.createFailJob((testsCount * 2) * 1000L) val failJob = createFailJob((testsCount * 2) * 1000L)
val mustBeCollected = 10 val mustBeCollected = 10
val answers = (0 until testsCount).map { _ -> val answers = (0 until testsCount).map { _ ->
it.async { async {
var collected = 0 var collected = 0
flow.takeWhile { flow.takeWhile {
collected < mustBeCollected collected < mustBeCollected
+173
View File
@@ -0,0 +1,173 @@
package dev.inmo.krontab.utils
import korlibs.time.*
import dev.inmo.krontab.KronSchedulerTz
import dev.inmo.krontab.KrontabTemplate
import dev.inmo.krontab.buildSchedule
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.test.runTest
import kotlin.test.*
@ExperimentalCoroutinesApi
@FlowPreview
class StringParseTest {
private fun makeSimpleEverySecondTest(template: KrontabTemplate) {
val kronScheduler = buildSchedule(template)
val flow = kronScheduler.asFlowWithoutDelays()
runTest {
val mustBeCollected = 10
var collected = 0
var previousTime: DateTime? = null
flow.takeWhile {
collected < mustBeCollected
}.collect {
previousTime ?.let { previousTime ->
assertEquals(previousTime + 1.seconds, it)
}
previousTime = it
collected++
}
assertEquals(mustBeCollected, collected)
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnString() {
makeSimpleEverySecondTest("*/1 * * * *")
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnStringWithWrongAmountOfSpaces() {
val templatesFirstReplacers = listOf(
"*/1",
"*",
"/1",
"f,/1",
)
templatesFirstReplacers.forEach { replacer ->
makeSimpleEverySecondTest("$replacer * * * * ")
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnStringWithGarbageInTemplate() {
makeSimpleEverySecondTest(" sdf */1 * * * oo * ")
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnStringWithInsufficientArgsInTemplate() {
makeSimpleEverySecondTest(" sdf */1 ")
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondWhenMillisIsHalfOfSecondBuiltOnString() {
val kronScheduler = buildSchedule("*/1 * * * * 500ms")
val flow = kronScheduler.asFlowWithoutDelays()
runTest {
val mustBeCollected = 10
var collected = 0
var previousTime: DateTime? = null
flow.takeWhile {
collected < mustBeCollected
}.collect {
previousTime ?.let { previousTime ->
assertEquals(previousTime.copy(milliseconds = 500) + 1.seconds, it)
}
previousTime = it
collected++
}
assertEquals(mustBeCollected, collected)
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmittersBuiltOnString() {
val kronScheduler = buildSchedule("*/1 * * * *")
val flow = kronScheduler.asFlowWithoutDelays()
runTest {
val testsCount = 10
val failJob = createFailJob((testsCount * 2) * 1000L)
val mustBeCollected = 10
val answers = (0 until testsCount).map { _ ->
async {
var collected = 0
flow.takeWhile {
collected < mustBeCollected
}.collect {
collected++
}
collected
}
}.awaitAll()
failJob.cancel()
answers.forEach {
assertEquals(mustBeCollected, it)
}
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySeveralSecondsRangeBuiltOnString() {
val rangesEnds = listOf(0 to 5, 30 to 35)
val kronScheduler = buildSchedule("${rangesEnds.joinToString(",") { "${it.first}-${it.second}" }} * * * *")
val flow = kronScheduler.asFlowWithoutDelays()
runTest {
val ranges = rangesEnds.map { it.first .. it.second }.flatten().distinct().toMutableList()
val expectedCollects = ranges.size
var collected = 0
flow.takeWhile { ranges.isNotEmpty() }.collect {
ranges.remove(it.seconds)
collected++
assertTrue(
collected <= expectedCollects,
"Expected value should be less than $expectedCollects, but was $collected. Ranges state: $ranges"
)
}
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)
}
}
}
}
@@ -1,8 +1,9 @@
package dev.inmo.krontab.utils package dev.inmo.krontab.utils
import com.soywiz.klock.* import korlibs.time.*
import dev.inmo.krontab.builder.buildSchedule import dev.inmo.krontab.builder.buildSchedule
import dev.inmo.krontab.next import dev.inmo.krontab.next
import kotlinx.coroutines.test.runTest
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,7 +1,8 @@
package dev.inmo.krontab.utils package dev.inmo.krontab.utils
import com.soywiz.klock.* import korlibs.time.*
import dev.inmo.krontab.builder.buildSchedule import dev.inmo.krontab.builder.buildSchedule
import kotlinx.coroutines.test.runTest
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.test.* import kotlin.test.*
@@ -1,8 +0,0 @@
package dev.inmo.krontab.utils
import kotlinx.coroutines.CoroutineScope
/**
* Workaround to use suspending functions in unit tests
*/
expect fun runTest(block: suspend (scope : CoroutineScope) -> Unit)
@@ -1,96 +0,0 @@
package dev.inmo.krontab.utils
import com.soywiz.klock.*
import dev.inmo.krontab.KronSchedulerTz
import dev.inmo.krontab.buildSchedule
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.takeWhile
import kotlin.test.Test
import kotlin.test.assertEquals
@ExperimentalCoroutinesApi
@FlowPreview
class StringParseTest {
@Test
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnString() {
val kronScheduler = buildSchedule("*/1 * * * *")
val flow = kronScheduler.asFlow()
runTest {
val mustBeCollected = 10
var collected = 0
flow.takeWhile {
collected < mustBeCollected
}.collect {
collected++
}
assertEquals(mustBeCollected, collected)
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmittersBuiltOnString() {
val kronScheduler = buildSchedule("*/1 * * * *")
val flow = kronScheduler.asFlow()
runTest {
val testsCount = 10
val failJob = it.createFailJob((testsCount * 2) * 1000L)
val mustBeCollected = 10
val answers = (0 until testsCount).map { _ ->
it.async {
var collected = 0
flow.takeWhile {
collected < mustBeCollected
}.collect {
collected++
}
collected
}
}.awaitAll()
failJob.cancel()
answers.forEach {
assertEquals(mustBeCollected, it)
}
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySeveralSecondsRangeBuiltOnString() {
val rangesEnds = listOf(0 to 5, 30 to 35)
val kronScheduler = buildSchedule("${rangesEnds.joinToString(",") { "${it.first}-${it.second}" }} * * * *")
val flow = kronScheduler.asFlow()
runTest {
val ranges = rangesEnds.map { it.first .. it.second }.flatten().distinct().toMutableList()
val expectedCollects = ranges.size
var collected = 0
flow.takeWhile { ranges.isNotEmpty() }.collect {
ranges.remove(it.seconds)
collected++
}
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)
}
}
}
}
@@ -1,5 +0,0 @@
package dev.inmo.krontab.utils
import kotlinx.coroutines.*
actual fun runTest(block: suspend (scope : CoroutineScope) -> Unit): dynamic = GlobalScope.promise { block(this) }
@@ -1,9 +0,0 @@
package dev.inmo.krontab.utils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
/**
* Workaround to use suspending functions in unit tests
*/
actual fun runTest(block: suspend (scope: CoroutineScope) -> Unit) = runBlocking(block = block)
-1
View File
@@ -1 +0,0 @@
<manifest package="dev.inmo.krontab"/>