mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2024-11-26 12:08:50 +00:00
add offset parameter in strings
This commit is contained in:
parent
93246cada7
commit
a07027399c
@ -21,9 +21,7 @@ interface KronScheduler {
|
|||||||
*/
|
*/
|
||||||
suspend fun next(relatively: DateTime = DateTime.now()): DateTime?
|
suspend fun next(relatively: DateTime = DateTime.now()): DateTime?
|
||||||
|
|
||||||
suspend fun next(relatively: DateTimeTz): DateTimeTz? {
|
suspend fun next(relatively: DateTimeTz): DateTimeTz?
|
||||||
return next(relatively.local) ?.toOffsetUnadjusted(relatively.offset)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -11,6 +11,7 @@ internal val anyCronDateTime by lazy {
|
|||||||
CronDateTime()
|
CronDateTime()
|
||||||
}
|
}
|
||||||
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
|
internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(relatively)!!
|
||||||
|
internal fun getAnyNext(relatively: DateTimeTz) = anyCronDateTime.toNearDateTime(relatively)!!
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
||||||
@ -18,7 +19,6 @@ internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(r
|
|||||||
val AnyTimeScheduler: KronScheduler by lazy {
|
val AnyTimeScheduler: KronScheduler by lazy {
|
||||||
CronDateTimeScheduler(listOf(anyCronDateTime))
|
CronDateTimeScheduler(listOf(anyCronDateTime))
|
||||||
}
|
}
|
||||||
internal suspend fun getAnyNext(relatively: DateTimeTz) = AnyTimeScheduler.next(relatively)!!
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one second
|
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now] + one second
|
||||||
|
@ -17,6 +17,7 @@ typealias KrontabTemplate = String
|
|||||||
* * dayOfMonth
|
* * dayOfMonth
|
||||||
* * month
|
* * month
|
||||||
* * (optional) year
|
* * (optional) year
|
||||||
|
* * (optional) (can be placed before year) offset
|
||||||
*
|
*
|
||||||
* And each one have next format:
|
* And each one have next format:
|
||||||
*
|
*
|
||||||
@ -39,6 +40,7 @@ typealias KrontabTemplate = String
|
|||||||
* * Days of month ranges can be found in [dayOfMonthRange]
|
* * Days of month ranges can be found in [dayOfMonthRange]
|
||||||
* * Months ranges can be found in [monthRange]
|
* * Months ranges can be found in [monthRange]
|
||||||
* * Years ranges can be found in [yearRange] (in fact - any [Int])
|
* * Years ranges can be found in [yearRange] (in fact - any [Int])
|
||||||
|
* * Offset (timezone) ranges can be found in [offsetRange]
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
*
|
*
|
||||||
@ -46,14 +48,31 @@ typealias KrontabTemplate = String
|
|||||||
* * "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
|
* * "1 2 3 F,4,L 5" for triggering in near first second of second minute of third hour of fourth day of may
|
||||||
|
* * "1 2 3 F,4,L 5 60o" for triggering in near first second of second minute of third hour of fourth day of may with timezone UTC+01:00
|
||||||
* * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year
|
* * "1 2 3 F,4,L 5 2021" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year
|
||||||
|
* * "1 2 3 F,4,L 5 2021 60o" for triggering in near first second of second minute of third hour of fourth day of may of 2021st year with timezone UTC+01:00
|
||||||
*
|
*
|
||||||
* @see dev.inmo.krontab.internal.createKronScheduler
|
* @see dev.inmo.krontab.internal.createKronScheduler
|
||||||
*/
|
*/
|
||||||
fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
||||||
val yearSource: String?
|
var offsetParsed: Int? = null
|
||||||
|
var yearParsed: Array<Int>? = null
|
||||||
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also {
|
val (secondsSource, minutesSource, hoursSource, dayOfMonthSource, monthSource) = incoming.split(" ").also {
|
||||||
yearSource = it.getOrNull(5)
|
listOfNotNull(
|
||||||
|
it.getOrNull(5),
|
||||||
|
it.getOrNull(6)
|
||||||
|
).forEach {
|
||||||
|
val parsedOffset = parseOffset(it)
|
||||||
|
offsetParsed = offsetParsed ?: parsedOffset
|
||||||
|
when {
|
||||||
|
parsedOffset == null && yearParsed == null -> {
|
||||||
|
yearParsed = parseYears(it)
|
||||||
|
}
|
||||||
|
parsedOffset != null && offsetParsed == null -> {
|
||||||
|
offsetParsed = parsedOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val secondsParsed = parseSeconds(secondsSource)
|
val secondsParsed = parseSeconds(secondsSource)
|
||||||
@ -61,10 +80,9 @@ fun createSimpleScheduler(incoming: KrontabTemplate): KronScheduler {
|
|||||||
val hoursParsed = parseHours(hoursSource)
|
val hoursParsed = parseHours(hoursSource)
|
||||||
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
val dayOfMonthParsed = parseDaysOfMonth(dayOfMonthSource)
|
||||||
val monthParsed = parseMonths(monthSource)
|
val monthParsed = parseMonths(monthSource)
|
||||||
val yearParsed = parseYears(yearSource)
|
|
||||||
|
|
||||||
return createKronScheduler(
|
return createKronScheduler(
|
||||||
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed
|
secondsParsed, minutesParsed, hoursParsed, dayOfMonthParsed, monthParsed, yearParsed, offsetParsed
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package dev.inmo.krontab.builder
|
package dev.inmo.krontab.builder
|
||||||
|
|
||||||
|
import com.soywiz.klock.TimeSpan
|
||||||
|
import com.soywiz.klock.TimezoneOffset
|
||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.KronScheduler
|
||||||
import dev.inmo.krontab.internal.createKronScheduler
|
import dev.inmo.krontab.internal.createKronScheduler
|
||||||
|
import dev.inmo.krontab.utils.Minutes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will help to create an instance of [KronScheduler]
|
* Will help to create an instance of [KronScheduler]
|
||||||
@ -22,7 +25,8 @@ class SchedulerBuilder(
|
|||||||
private var hours: Array<Byte>? = null,
|
private var hours: Array<Byte>? = null,
|
||||||
private var dayOfMonth: Array<Byte>? = null,
|
private var dayOfMonth: Array<Byte>? = null,
|
||||||
private var month: Array<Byte>? = null,
|
private var month: Array<Byte>? = null,
|
||||||
private var year: Array<Int>? = null
|
private var year: Array<Int>? = null,
|
||||||
|
var offset: Minutes? = null
|
||||||
) {
|
) {
|
||||||
private fun <I, T : TimeBuilder<I>> callAndReturn(
|
private fun <I, T : TimeBuilder<I>> callAndReturn(
|
||||||
initial: Array<I>?,
|
initial: Array<I>?,
|
||||||
@ -106,6 +110,27 @@ class SchedulerBuilder(
|
|||||||
) ?.toTypedArray()
|
) ?.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter of [offset] property
|
||||||
|
*/
|
||||||
|
fun offset(offset: Minutes?) {
|
||||||
|
this.offset = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter of [offset] property
|
||||||
|
*/
|
||||||
|
fun offset(offset: TimeSpan?) {
|
||||||
|
this.offset = offset ?.minutes ?.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter of [offset] property
|
||||||
|
*/
|
||||||
|
fun offset(offset: TimezoneOffset?) {
|
||||||
|
this.offset = offset ?.totalMinutesInt
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Completely built and independent [KronScheduler]
|
* @return Completely built and independent [KronScheduler]
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package dev.inmo.krontab.collection
|
package dev.inmo.krontab.collection
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
|
import com.soywiz.klock.DateTimeTz
|
||||||
import dev.inmo.krontab.*
|
import dev.inmo.krontab.*
|
||||||
import dev.inmo.krontab.internal.*
|
import dev.inmo.krontab.internal.*
|
||||||
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
import dev.inmo.krontab.internal.CronDateTimeScheduler
|
||||||
@ -48,4 +49,8 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
override suspend fun next(relatively: DateTime): DateTime {
|
override suspend fun next(relatively: DateTime): DateTime {
|
||||||
return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun next(relatively: DateTimeTz): DateTimeTz {
|
||||||
|
return schedulers.mapNotNull { it.next(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.*
|
||||||
import com.soywiz.klock.DateTimeSpan
|
|
||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.KronScheduler
|
||||||
|
import dev.inmo.krontab.utils.Minutes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param month 0-11
|
* @param month 0-11
|
||||||
@ -12,6 +12,7 @@ import dev.inmo.krontab.KronScheduler
|
|||||||
* @param seconds 0-59
|
* @param seconds 0-59
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTime(
|
internal data class CronDateTime(
|
||||||
|
val offset: Int? = null,
|
||||||
val year: Int? = null,
|
val year: Int? = null,
|
||||||
val month: Byte? = null,
|
val month: Byte? = null,
|
||||||
val dayOfMonth: Byte? = null,
|
val dayOfMonth: Byte? = null,
|
||||||
@ -29,9 +30,13 @@ internal data class CronDateTime(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val klockDayOfMonth = dayOfMonth ?.plus(1)
|
internal val klockDayOfMonth = dayOfMonth ?.plus(1)
|
||||||
|
internal val klockOffset = offset ?.minutes ?.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* THIS METHOD WILL <b>NOT</b> TAKE CARE ABOUT [offset] PARAMETER. It was decided due to the fact that we unable to get
|
||||||
|
* real timezone offset from simple [DateTime]
|
||||||
|
*
|
||||||
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
||||||
*/
|
*/
|
||||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
||||||
@ -77,6 +82,23 @@ internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()
|
|||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* THIS METHOD WILL TAKE CARE ABOUT [offset] PARAMETER. It was decided due to the fact that we unable to get
|
||||||
|
* real timezone offset from simple [DateTime]
|
||||||
|
*
|
||||||
|
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
||||||
|
*/
|
||||||
|
internal fun CronDateTime.toNearDateTime(
|
||||||
|
relativelyTo: DateTimeTz
|
||||||
|
): DateTimeTz? {
|
||||||
|
val klockOffset = klockOffset
|
||||||
|
return if (klockOffset != null) {
|
||||||
|
toNearDateTime(relativelyTo.toOffset(klockOffset).local) ?.toOffsetUnadjusted(klockOffset) ?.toOffset(relativelyTo.offset)
|
||||||
|
} else {
|
||||||
|
toNearDateTime(relativelyTo.local) ?.toOffsetUnadjusted(relativelyTo.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
|
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
|
||||||
*/
|
*/
|
||||||
@ -86,7 +108,8 @@ internal fun createKronScheduler(
|
|||||||
hours: Array<Byte>? = null,
|
hours: Array<Byte>? = null,
|
||||||
dayOfMonth: Array<Byte>? = null,
|
dayOfMonth: Array<Byte>? = null,
|
||||||
month: Array<Byte>? = null,
|
month: Array<Byte>? = null,
|
||||||
years: Array<Int>? = null
|
years: Array<Int>? = null,
|
||||||
|
offset: Minutes? = null
|
||||||
): KronScheduler {
|
): KronScheduler {
|
||||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
val resultCronDateTimes = mutableListOf(CronDateTime())
|
||||||
|
|
||||||
@ -114,5 +137,13 @@ internal fun createKronScheduler(
|
|||||||
previousCronDateTime.copy(year = currentTime)
|
previousCronDateTime.copy(year = currentTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int ->
|
||||||
|
previousCronDateTime.copy(year = currentTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int ->
|
||||||
|
previousCronDateTime.copy(year = currentTime)
|
||||||
|
}
|
||||||
|
|
||||||
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
return CronDateTimeScheduler(resultCronDateTimes.toList())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package dev.inmo.krontab.internal
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
|
import com.soywiz.klock.DateTimeTz
|
||||||
import dev.inmo.krontab.*
|
import dev.inmo.krontab.*
|
||||||
import dev.inmo.krontab.collection.plus
|
import dev.inmo.krontab.collection.plus
|
||||||
|
|
||||||
@ -29,6 +30,10 @@ internal data class CronDateTimeScheduler internal constructor(
|
|||||||
override suspend fun next(relatively: DateTime): DateTime {
|
override suspend fun next(relatively: DateTime): DateTime {
|
||||||
return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun next(relatively: DateTimeTz): DateTimeTz {
|
||||||
|
return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull() ?: getAnyNext(relatively)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun mergeCronDateTimeSchedulers(schedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
internal fun mergeCronDateTimeSchedulers(schedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
||||||
|
@ -38,6 +38,7 @@ private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataCon
|
|||||||
return results.map(dataConverter)
|
return results.map(dataConverter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun parseOffset(from: String?) = from ?.let { if (it.endsWith("o")) it.removeSuffix("o").toIntOrNull() else null }
|
||||||
internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() }
|
internal fun parseYears(from: String?) = from ?.let { createSimpleScheduler(from, yearRange, intToIntConverter) ?.toTypedArray() }
|
||||||
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray()
|
internal fun parseMonths(from: String) = createSimpleScheduler(from, monthRange, intToByteConverter) ?.toTypedArray()
|
||||||
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange, intToByteConverter) ?.toTypedArray()
|
internal fun parseDaysOfMonth(from: String) = createSimpleScheduler(from, dayOfMonthRange, intToByteConverter) ?.toTypedArray()
|
||||||
@ -60,3 +61,16 @@ internal fun <T> Array<T>.fillWith(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun <T> T.fillWith(
|
||||||
|
whereToPut: MutableList<CronDateTime>,
|
||||||
|
createFactory: (CronDateTime, T) -> CronDateTime
|
||||||
|
) {
|
||||||
|
val previousValues = whereToPut.toList()
|
||||||
|
|
||||||
|
whereToPut.clear()
|
||||||
|
|
||||||
|
previousValues.forEach { previousValue ->
|
||||||
|
whereToPut.add(createFactory(previousValue, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -6,3 +6,8 @@ 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From 0 - 1439 minutes (1440 == 24 hours, that is the same as 0 in terms of timezones)
|
||||||
|
*/
|
||||||
|
internal val offsetRange = 0 until (hoursRange.count() * minutesRange.count())
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
|
typealias Minutes = Int
|
@ -1,6 +1,8 @@
|
|||||||
package dev.inmo.krontab.utils
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
|
import com.soywiz.klock.DateTimeTz
|
||||||
import dev.inmo.krontab.buildSchedule
|
import dev.inmo.krontab.buildSchedule
|
||||||
|
import dev.inmo.krontab.internal.offsetRange
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.takeWhile
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
@ -76,4 +78,16 @@ class StringParseTest {
|
|||||||
assertEquals(expectedCollects, collected)
|
assertEquals(expectedCollects, collected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Test
|
||||||
|
fun testThatTimezoneCorrectlyDeserialized() {
|
||||||
|
val now = DateTimeTz.nowLocal()
|
||||||
|
|
||||||
|
runTest {
|
||||||
|
for (i in offsetRange) {
|
||||||
|
val kronScheduler = buildSchedule("* * 10 * * ${i}o")
|
||||||
|
val next = kronScheduler.next(now)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user