Giter Site home page Giter Site logo

insanusmokrassar / krontab Goto Github PK

View Code? Open in Web Editor NEW
94.0 3.0 5.0 1.79 MB

Library for using Crontab-like syntax in scheduling of some Kotlin Coroutines tasks to do from time to time

Home Page: https://docs.inmo.dev/krontab/index.html

License: Apache License 2.0

Kotlin 99.56% Shell 0.44%
kotlin kotlin-library kotlin-coroutines kotlin-multiplatform crontab-format kotlin-coroutine-flow kotlin-js kotlin-jvm scheduling kotlin-android

krontab's Introduction

krontab

Maven Central

Telegram Chat

JVM Android Js Linux x64

KDocs Tutorials

Library was created to give oppotunity to launch some things from time to time according to some schedule in runtime of applications.

How to use

There are several ways to configure and use this library:

  • From some string
  • From builder

Anyway, to start some action from time to time you will need to use one of extensions/functions:

val kronScheduler = /* creating of KronScheduler instance */;

kronScheduler.doWhile {
    // some action
    true // true - repeat on next time
}

Including in project

If you want to include krontab in your project, just add next line to your dependencies part:

implementation "dev.inmo:krontab:$krontab_version"

Next version is the latest currently for the library:

Maven Central

For old version of Gradle, instead of implementation word developers must use compile.

Config from string

Developers can use more simple way to configure repeat times is string. String configuring like a crontab, but with a little bit different meanings:

/--------------- Seconds
| /------------- Minutes
| | /----------- Hours
| | | /--------- Days of months
| | | | /------- Months
| | | | | /----- (optional) Year
| | | | | | /--- (optional) Timezone offset
| | | | | | |  / (optional) Week days
* * * * * * 0o *w

It is different with original crontab syntax for the reason, that expected that in practice developers will use seconds and minutes with more probability than months (for example) or even years. In fact, developers will use something like:

doWhile("/5 * * * *") {
    println("Called")
    true // true - repeat on next time
}

An other version:

doInfinity("/5 * * * *") {
    println("Called")
}

Both of examples will print Called message every five seconds.

Config via builder

Also, this library currently supports DSL for creating the same goals:

val kronScheduler = buildSchedule {
    seconds {
        from (0) every 5
    }
}
kronScheduler.doWhile {
    println("Called")
    true // true - repeat on next time
}

Or

val kronScheduler = buildSchedule {
    seconds {
        0 every 5
    }
}
kronScheduler.doWhile {
    println("Called")
    true // true - repeat on next time
}

Or

val kronScheduler = buildSchedule {
    seconds {
        0 every 5
    }
}
kronScheduler.doInfinity {
    println("Called")
}

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:

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:

doInfinityTz("/5 * * * * 0o") {
    println(it) // will print current date time in UTC
}

It is useful in cases when you need to get the time of calling and avoid extra calls to system time.

Helpful table for

No args Local DateTime Local DateTimeTz with offset of KronScheduler
Call only near time doOnce doOnceLocal doOnceTz
Call while condition is true doWhile doWhileLocal doWhileTz
Work infinity* doInfinity doInfinityLocal doInfinityTz

*Here there is an important notice, that Work infinity is not exactly infinity. Actually, that means that do while coroutine is alive and in fact executing will be stopped when coroutine became cancelled.

KronScheduler as a Flow

Any KronSchedulercan e converted to a Flow<DateTime using extension asFlow:

val kronScheduler = buildSchedule {
    seconds {
        0 every 1
    }
}

val flow = kronScheduler.asFlow()

So, in this case any operations related to flow are available and it is expected that they will work correctly. For example, it is possible to use this flow with takeWhile:

flow.takeWhile {
    condition()
}.collect {
    action()
}

Offsets

Offsets in this library works via passing parameter ending with o in any place after month config. Currently there is only one format supported for offsets: minutes of offsets. To use time zones you will need to call next method with DateTimeTz argument or nextTimeZoned method with any KronScheduler instance, but in case if this scheduler is not instance of KronSchedulerTz it will work like you passed just DateTime.

Besides, in case you wish to use time zones explicitly, you will need to get KronSchedulerTz. It is possible by:

  • Using createSimpleScheduler/buildSchedule/KrontabTemplate#toSchedule/KrontabTemplate#toKronScheduler methods with passing defaultOffset parameter
  • Using SchedulerBuilder#build/createSimpleScheduler/buildSchedule/KrontabTemplate#toSchedule/KrontabTemplate#toKronScheduler methods with casting to KronSchedulerTz in case you are pretty sure that it is timezoned KronScheduler
  • Creating your own implementation of KronSchedulerTz

Note about week days

Unlike original CRON, here week days:

  • Works as AND: cron date time will search first day which will pass requirement according all parameters including week days
  • You may use any related to numbers syntax with week days: 0-3w, 0,1,2,3w, etc.
  • Week days (like years and offsets) are optional and can be placed anywhere after month

krontab's People

Contributors

insanusmokrassar avatar renovate[bot] avatar tolsi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

krontab's Issues

Add template typealias

It is required to add some typealias like typealias KrontabTemplate = String for the krontab templates. It will give opportunity for more strict explanation of expected and incoming things for the extension functions and other places

Day of week schedules

The cron pattern supported is close to standard cron as mentioned in the docs but i can't see that day of week schedules are possible to configure, or maybe there are other ways / workarounds to support for instance monday - friday at 09:00 ?

Loop infinity

image image

At 2023-06-28 19:58 UTC, it can be easily reproduced with following code:

suspend fun main() {
  doInfinity("0 0 8 /3 *") {
    println("Hello")
  }
}

Make Kron Scheduler DSL more flexible

First of all, still enjoying this little library :)

So, i prefer using the builder DSL for creating schedules due to type safety and verboseness. Ie when building a time zone aware scheduler that should run a job every 5 minutes i would use something like this:


val scheduler = buildSchedule(0) {
            minutes {
                0 every 5
            }
        }

And if i run this like the following:

scheduler.asFlow().collect { ... }

That gives me a timeline of all the seconds on the 5th minute since this is a minute scheduler and makes my job run every second on the 5th minute. Not exactly what i want. And yes, i know i can use the schedule which takes a string cron expression as argument instead to control the data and give me exactly one DateTimeZone back. But as stated, i want to use the DSL builder.

So, i think in most cases you want to trigger your job only once with most schedulers so we need to control seconds, minutes and so on when we use schedulers other than the seconds scheduler. What i've ended up doing is writing a little extension functions like the following:


  @FlowPreview
  private fun KronScheduler.asSingleTzFlow(seconds: Int = 0): Flow<DateTimeTz> = channelFlow {
      doInfinityTz {
          send(it)
      }
  }
      .filter { it.seconds == seconds }

Which solves this problem without a lot of fuzz :) But i was thinking that maybe this is a function that should be included in your library. I think it would be great if we had api functions for all the other schedulers than seconds like this so we can override the seconds, minutes and so forth properties in an easy and concise way.

Thoughts ?

How to run serveral jobs

fun main() {
    runBlocking {
        doInfinity("*/3 * * * *") {
            println("3")
        }

        doInfinity("* * * * *") {
            println("1")
        }
    }
}

I thought two jobs would run like this:

1
1
1
3
1
1
1
3

but it was actually:

3
3

What should I do? btw thanks for the nice library!

`doInfinity` works only with seconds

Bug description

Hello, I noticed when I use seconds function for building SchedulerBuilder it works, but it fails in all other cases:

image

Cases

  1. Cron job expression ❌
  2. seconds { every(15) }
  3. minutes { every(1) }
  4. EveryMinuteScheduler
  5. EveryHourScheduler
  6. EveryDayOfMonthScheduler
  7. other shortcuts too from SchedulerShortcuts.kt

In all the cases other than seconds, it does not work and runs constantly

Version used

2.2.9 (latest)
https://github.com/InsanusMokrassar/krontab/releases/tag/v2.2.9

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.


Warning

Renovate failed to look up the following dependencies: Failed to look up maven package com.github.breadmoirai:github-release.

Files affected: gradle.properties


Other Branches

These updates are pending. To force PRs open, click the checkbox below.

  • Update actions/checkout action to v4
  • Update actions/labeler action to v5
  • Update actions/setup-java action to v4
  • Update peaceiris/actions-gh-pages action to v4

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/greetings.yml
  • actions/first-interaction v1
.github/workflows/kdocs.yml
  • actions/checkout v2
  • actions/setup-java v1
  • peaceiris/actions-gh-pages v3
.github/workflows/label.yml
  • actions/labeler v2
.github/workflows/publishing_packages.yml
  • actions/checkout v2
  • actions/setup-java v1
gradle
gradle.properties
  • org.jetbrains.kotlin:kotlin-gradle-plugin 1.9.23
  • org.jetbrains.kotlin:kotlin-serialization 1.9.23
  • com.github.breadmoirai:github-release 2.5.2
  • com.getkeepsafe.dexcount:dexcount-gradle-plugin 4.0.0
  • com.android.tools.build:gradle 8.1.4
  • org.jetbrains.kotlin.multiplatform 1.9.23
  • org.jetbrains.kotlin.plugin.serialization 1.9.23
  • org.jetbrains.dokka 1.9.20
  • org.jetbrains.kotlinx:kotlinx-coroutines-core 1.8.0
  • org.jetbrains.kotlinx:kotlinx-serialization-core 1.6.3
  • com.soywiz.korge:korlibs-time 5.4.0
  • androidx.work:work-runtime-ktx 2.9.0
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.8.0
dokka.gradle
github_release.gradle
publish.gradle
settings.gradle
build.gradle
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.6

  • Check this box to trigger a request for Renovate to run again on this repository

`next()` returns `null` on fixed date

I'm using this library with kotlinx-datetime. I would like to schedule a task once at a specified time, but when I execute the code that task runs immediately

import dev.inmo.krontab.KronScheduler
import dev.inmo.krontab.builder.buildSchedule
import dev.inmo.krontab.doOnce
import dev.inmo.krontab.nextOrNow
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import kotlin.time.Duration.Companion.seconds

suspend fun main() {
  ((Clock.System.now().also {
    println("now:\t\t\t${it.toString().replace("T", "\t\t ")}")
  }) + 6.seconds).toLocalDateTime(TimeZone.UTC)
    .also { println("should run at:\t${it.toString().replace("T", "\t\t ")}") }
    .toCron()
    .also {
      println("next:\t\t\t${it.next()?.toStringDefault()}")
      println("nextOrNow:\t\t${it.nextOrNow().toStringDefault()}")
    }
    .doOnce {
      println("Hello at\t\t${it.toStringDefault()}")
    }
}


fun LocalDateTime.toCron(): KronScheduler = buildSchedule {
  seconds { at(second) }
  minutes { at(minute) }
  hours { at(hour) }
  dayOfMonth { at(dayOfMonth) }
  months { at(monthNumber) }
  years { at(year) }
}

output:

now:			2022-05-29	 10:33:18.742564200Z
should run at:		2022-05-29	 10:33:24.742564200
next:			null
nextOrNow:		Sun, 29 May 2022 10:33:19 UTC
Hello at		Sun, 29 May 2022 10:33:19 UTC

If I comment out years { at(year) } or dayOfMonth { at(dayOfMonth) } I think there's a infinite loop
If I comment out months { at(monthNumber) } code works fine at scheduled time

Where is the problem?

`java.time.Duration` as a time period?

Thanks for the amazing library. Do you see value in providing an API to schedule the task with a java.time.Duration time period? I understand that it is not exactly a crontab format.

I could contribute if you agree to the proposal.

Time zone aware schedules

First of all, this looks like a really useful library that fits nicely into the current project i'm working on.

One of my needs is to have schedules on specific days in different time zones. I can't see anywhere to configure the time zone for the current schedule, isn't this supported as of now? If not, i see you are using the Klock library after a quick poke into the codebase. Klock supports time zone offsets, isn't it possible to use this feature?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.