mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2024-11-22 16:23:55 +00:00
rewrite mechanism of calculating of near time
This commit is contained in:
parent
22ef00fe8f
commit
28f84d4e3a
@ -3,7 +3,9 @@
|
|||||||
## 0.6.1
|
## 0.6.1
|
||||||
|
|
||||||
* Versions
|
* Versions
|
||||||
* `Klock`: `2.1.0` -> `2.1.2`
|
* `Klock`: `2.1.0` -> `2.1.2
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
## 0.6.0
|
## 0.6.0
|
||||||
|
|
||||||
|
@ -16,13 +16,13 @@ fun Iterator<KronScheduler>.merge(): CollectionKronScheduler {
|
|||||||
val collectionScheduler = CollectionKronScheduler()
|
val collectionScheduler = CollectionKronScheduler()
|
||||||
forEach {
|
forEach {
|
||||||
when (it) {
|
when (it) {
|
||||||
is CronDateTimeScheduler -> cronDateTimes.addAll(it.cronDateTimes)
|
is CronDateTimeScheduler -> cronDateTimes.add(it.cronDateTime)
|
||||||
is CronDateTimeSchedulerTz -> timezonedCronDateTimes.add(it)
|
is CronDateTimeSchedulerTz -> timezonedCronDateTimes.add(it)
|
||||||
else -> collectionScheduler.include(it)
|
else -> collectionScheduler.include(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cronDateTimes.isNotEmpty()) {
|
if (cronDateTimes.isNotEmpty()) {
|
||||||
collectionScheduler.include(CronDateTimeScheduler(cronDateTimes))
|
collectionScheduler.include(CronDateTimeScheduler(cronDateTimes.merge()))
|
||||||
}
|
}
|
||||||
if (timezonedCronDateTimes.isNotEmpty()) {
|
if (timezonedCronDateTimes.isNotEmpty()) {
|
||||||
collectionScheduler.includeAll(mergeCronDateTimeSchedulers(timezonedCronDateTimes))
|
collectionScheduler.includeAll(mergeCronDateTimeSchedulers(timezonedCronDateTimes))
|
||||||
|
@ -13,7 +13,7 @@ internal fun getAnyNext(relatively: DateTime) = anyCronDateTime.toNearDateTime(r
|
|||||||
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
* [KronScheduler.next] will always return [com.soywiz.klock.DateTime.now]
|
||||||
*/
|
*/
|
||||||
val AnyTimeScheduler: KronScheduler by lazy {
|
val AnyTimeScheduler: KronScheduler by lazy {
|
||||||
CronDateTimeScheduler(listOf(anyCronDateTime))
|
CronDateTimeScheduler(anyCronDateTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,4 +56,4 @@ val EveryMonthScheduler: KronScheduler by lazy {
|
|||||||
*/
|
*/
|
||||||
val EveryYearScheduler: KronScheduler by lazy {
|
val EveryYearScheduler: KronScheduler by lazy {
|
||||||
buildSchedule { years { 0 every 1 } }
|
buildSchedule { years { 0 every 1 } }
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ fun createSimpleScheduler(
|
|||||||
scheduler
|
scheduler
|
||||||
} else {
|
} else {
|
||||||
CronDateTimeSchedulerTz(
|
CronDateTimeSchedulerTz(
|
||||||
(scheduler as CronDateTimeScheduler).cronDateTimes,
|
(scheduler as CronDateTimeScheduler).cronDateTime,
|
||||||
TimezoneOffset(defaultOffset.minutes)
|
TimezoneOffset(defaultOffset.minutes)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -38,16 +38,16 @@ data class CollectionKronScheduler internal constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is CronDateTimeSchedulerTz -> {
|
is CronDateTimeSchedulerTz -> {
|
||||||
val newCronDateTimes = kronScheduler.cronDateTimes.toMutableList()
|
val newCronDateTimes = mutableListOf(kronScheduler.cronDateTime)
|
||||||
val cronDateTimes = schedulers.removeAll {
|
schedulers.removeAll {
|
||||||
if (it is CronDateTimeSchedulerTz && it.offset == kronScheduler.offset) {
|
if (it is CronDateTimeSchedulerTz && it.offset == kronScheduler.offset) {
|
||||||
newCronDateTimes.addAll(it.cronDateTimes)
|
newCronDateTimes.add(it.cronDateTime)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.toList(), kronScheduler.offset))
|
schedulers.add(CronDateTimeSchedulerTz(newCronDateTimes.merge(), kronScheduler.offset))
|
||||||
}
|
}
|
||||||
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
is CollectionKronScheduler -> kronScheduler.schedulers.forEach {
|
||||||
include(it)
|
include(it)
|
||||||
|
@ -4,103 +4,125 @@ import com.soywiz.klock.*
|
|||||||
import dev.inmo.krontab.KronScheduler
|
import dev.inmo.krontab.KronScheduler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param dayOfweek 0-6
|
* @param daysOfWeek 0-6
|
||||||
* @param year any int
|
* @param years any int
|
||||||
* @param month 0-11
|
* @param months 0-11
|
||||||
* @param dayOfMonth 0-31
|
* @param daysOfMonth 0-31
|
||||||
* @param hours 0-23
|
* @param hours 0-23
|
||||||
* @param minutes 0-59
|
* @param minutes 0-59
|
||||||
* @param seconds 0-59
|
* @param seconds 0-59
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTime(
|
internal data class CronDateTime(
|
||||||
val dayOfweek: Byte? = null,
|
val daysOfWeek: Array<Byte>? = null,
|
||||||
val year: Int? = null,
|
val years: Array<Int>? = null,
|
||||||
val month: Byte? = null,
|
val months: Array<Byte>? = null,
|
||||||
val dayOfMonth: Byte? = null,
|
val daysOfMonth: Array<Byte>? = null,
|
||||||
val hours: Byte? = null,
|
val hours: Array<Byte>? = null,
|
||||||
val minutes: Byte? = null,
|
val minutes: Array<Byte>? = null,
|
||||||
val seconds: Byte? = null
|
val seconds: Array<Byte>? = null
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
check(dayOfweek ?.let { it in dayOfWeekRange } ?: true)
|
check(daysOfWeek ?.all { it in dayOfWeekRange } ?: true)
|
||||||
check(year ?.let { it in yearRange } ?: true)
|
check(years?.all { it in yearRange } ?: true)
|
||||||
check(month ?.let { it in monthRange } ?: true)
|
check(months?.all { it in monthRange } ?: true)
|
||||||
check(dayOfMonth ?.let { it in dayOfMonthRange } ?: true)
|
check(daysOfMonth ?.all { it in dayOfMonthRange } ?: true)
|
||||||
check(hours?.let { it in hoursRange } ?: true)
|
check(hours?.all { it in hoursRange } ?: true)
|
||||||
check(minutes?.let { it in minutesRange } ?: true)
|
check(minutes?.all { it in minutesRange } ?: true)
|
||||||
check(seconds?.let { it in secondsRange } ?: true)
|
check(seconds?.all { it in secondsRange } ?: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val klockDayOfMonth = dayOfMonth ?.plus(1)
|
internal val calculators = listOf(
|
||||||
internal val dayOfWeekInt: Int? = dayOfweek ?.toInt()
|
NearDateTimeCalculatorMillis(arrayOf(0)),
|
||||||
}
|
seconds ?.let { NearDateTimeCalculatorSeconds(it) },
|
||||||
|
minutes ?.let { NearDateTimeCalculatorMinutes(it) },
|
||||||
|
hours ?.let { NearDateTimeCalculatorHours(it) },
|
||||||
|
daysOfMonth ?.let { NearDateTimeCalculatorDays(it) },
|
||||||
|
months ?.let { NearDateTimeCalculatorMonths(it) },
|
||||||
|
years ?.let { NearDateTimeCalculatorYears(it) },
|
||||||
|
daysOfWeek ?.let { NearDateTimeCalculatorWeekDays(it) },
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
internal fun toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
||||||
* THIS METHOD WILL <b>NOT</b> TAKE CARE ABOUT [offset] PARAMETER. It was decided due to the fact that we unable to get
|
var current = relativelyTo
|
||||||
* real timezone offset from simple [DateTime]
|
whileLoop@while (true) {
|
||||||
*
|
for (calculator in calculators) {
|
||||||
* @return The near [DateTime] which happens after [relativelyTo] or will be equal to [relativelyTo]
|
val (calculated, requireRecalculation) = (calculator ?: continue).calculateNearTime(current) ?: return null
|
||||||
*/
|
current = calculated
|
||||||
internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
if (requireRecalculation) {
|
||||||
var current = relativelyTo
|
continue@whileLoop
|
||||||
|
}
|
||||||
val weekDay = dayOfWeekInt
|
|
||||||
if (weekDay != null && current.dayOfWeek.index0 != weekDay) {
|
|
||||||
do {
|
|
||||||
var diff = weekDay - current.dayOfWeek.index0
|
|
||||||
if (diff < 0) {
|
|
||||||
diff += 7 /* days in week */
|
|
||||||
}
|
}
|
||||||
current = (current + diff.days).startOfDay
|
return current
|
||||||
|
|
||||||
val next = toNearDateTime(current)
|
|
||||||
if (next == null || next.dayOfWeek.index0 == weekDay) {
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
} while (true)
|
|
||||||
}
|
|
||||||
|
|
||||||
seconds?.let {
|
|
||||||
val left = it - current.seconds
|
|
||||||
current += DateTimeSpan(minutes = if (left <= 0) 1 else 0, seconds = left)
|
|
||||||
}
|
|
||||||
|
|
||||||
minutes?.let {
|
|
||||||
val left = it - current.minutes
|
|
||||||
current += DateTimeSpan(hours = if (left < 0) 1 else 0, minutes = left)
|
|
||||||
}
|
|
||||||
|
|
||||||
hours?.let {
|
|
||||||
val left = it - current.hours
|
|
||||||
current += DateTimeSpan(days = if (left < 0) 1 else 0, hours = left)
|
|
||||||
}
|
|
||||||
|
|
||||||
klockDayOfMonth ?.let {
|
|
||||||
val left = (it - current.dayOfMonth).let { diff ->
|
|
||||||
if (diff > 0 && current.endOfMonth.run { it > dayOfMonth && current.dayOfMonth == dayOfMonth }) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
diff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left)
|
|
||||||
}
|
|
||||||
|
|
||||||
month ?.let {
|
|
||||||
val left = it - current.month0
|
|
||||||
current += DateTimeSpan(years = if (left < 0) 1 else 0, months = left)
|
|
||||||
}
|
|
||||||
|
|
||||||
year ?.let {
|
|
||||||
if (current.yearInt != it) {
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return current
|
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * 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]
|
||||||
|
// */
|
||||||
|
//internal fun CronDateTime.toNearDateTime(relativelyTo: DateTime = DateTime.now()): DateTime? {
|
||||||
|
// var current = relativelyTo
|
||||||
|
//
|
||||||
|
// val weekDay = dayOfWeekInt
|
||||||
|
// if (weekDay != null && current.dayOfWeek.index0 != weekDay) {
|
||||||
|
// do {
|
||||||
|
// var diff = weekDay - current.dayOfWeek.index0
|
||||||
|
// if (diff < 0) {
|
||||||
|
// diff += 7 /* days in week */
|
||||||
|
// }
|
||||||
|
// current = (current + diff.days).startOfDay
|
||||||
|
//
|
||||||
|
// val next = toNearDateTime(current)
|
||||||
|
// if (next == null || next.dayOfWeek.index0 == weekDay) {
|
||||||
|
// return next
|
||||||
|
// }
|
||||||
|
// } while (true)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// seconds?.let {
|
||||||
|
// val left = it - current.seconds
|
||||||
|
// current += DateTimeSpan(minutes = if (left <= 0) 1 else 0, seconds = left)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// minutes?.let {
|
||||||
|
// val left = it - current.minutes
|
||||||
|
// current += DateTimeSpan(hours = if (left < 0) 1 else 0, minutes = left)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// hours?.let {
|
||||||
|
// val left = it - current.hours
|
||||||
|
// current += DateTimeSpan(days = if (left < 0) 1 else 0, hours = left)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// klockDayOfMonth ?.let {
|
||||||
|
// val left = (it - current.dayOfMonth).let { diff ->
|
||||||
|
// if (diff > 0 && current.endOfMonth.run { it > dayOfMonth && current.dayOfMonth == dayOfMonth }) {
|
||||||
|
// 0
|
||||||
|
// } else {
|
||||||
|
// diff
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// current += DateTimeSpan(months = if (left < 0) 1 else 0, days = left)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// months?.let {
|
||||||
|
// val left = it - current.month0
|
||||||
|
// current += DateTimeSpan(years = if (left < 0) 1 else 0, months = left)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// years?.let {
|
||||||
|
// if (current.yearInt != it) {
|
||||||
|
// return null
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return current
|
||||||
|
//}
|
||||||
|
|
||||||
internal fun createCronDateTimeList(
|
internal fun createCronDateTime(
|
||||||
seconds: Array<Byte>? = null,
|
seconds: Array<Byte>? = null,
|
||||||
minutes: Array<Byte>? = null,
|
minutes: Array<Byte>? = null,
|
||||||
hours: Array<Byte>? = null,
|
hours: Array<Byte>? = null,
|
||||||
@ -108,38 +130,39 @@ internal fun createCronDateTimeList(
|
|||||||
month: Array<Byte>? = null,
|
month: Array<Byte>? = null,
|
||||||
years: Array<Int>? = null,
|
years: Array<Int>? = null,
|
||||||
weekDays: Array<Byte>? = null
|
weekDays: Array<Byte>? = null
|
||||||
): List<CronDateTime> {
|
): CronDateTime {
|
||||||
val resultCronDateTimes = mutableListOf(CronDateTime())
|
return CronDateTime(weekDays, years, month, dayOfMonth, hours, minutes, seconds)
|
||||||
|
// val resultCronDateTimes = mutableListOf(CronDateTime())
|
||||||
seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
//
|
||||||
previousCronDateTime.copy(seconds = currentTime)
|
// seconds ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||||
}
|
// previousCronDateTime.copy(seconds = currentTime)
|
||||||
|
// }
|
||||||
minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
//
|
||||||
previousCronDateTime.copy(minutes = currentTime)
|
// minutes ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||||
}
|
// previousCronDateTime.copy(minutes = currentTime)
|
||||||
|
// }
|
||||||
hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
//
|
||||||
previousCronDateTime.copy(hours = currentTime)
|
// hours ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||||
}
|
// previousCronDateTime.copy(hours = currentTime)
|
||||||
|
// }
|
||||||
dayOfMonth ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
//
|
||||||
previousCronDateTime.copy(dayOfMonth = currentTime)
|
// dayOfMonth ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||||
}
|
// previousCronDateTime.copy(daysOfMonth = currentTime)
|
||||||
|
// }
|
||||||
month ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
//
|
||||||
previousCronDateTime.copy(month = currentTime)
|
// month ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||||
}
|
// previousCronDateTime.copy(months = currentTime)
|
||||||
|
// }
|
||||||
years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int ->
|
//
|
||||||
previousCronDateTime.copy(year = currentTime)
|
// years ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Int ->
|
||||||
}
|
// previousCronDateTime.copy(years = currentTime)
|
||||||
|
// }
|
||||||
weekDays ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
//
|
||||||
previousCronDateTime.copy(dayOfweek = currentTime)
|
// weekDays ?.fillWith(resultCronDateTimes) { previousCronDateTime: CronDateTime, currentTime: Byte ->
|
||||||
}
|
// previousCronDateTime.copy(daysOfWeek = currentTime)
|
||||||
|
// }
|
||||||
return resultCronDateTimes.toList()
|
//
|
||||||
|
// return resultCronDateTimes.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,7 +176,7 @@ internal fun createKronScheduler(
|
|||||||
month: Array<Byte>? = null,
|
month: Array<Byte>? = null,
|
||||||
years: Array<Int>? = null,
|
years: Array<Int>? = null,
|
||||||
weekDays: Array<Byte>? = null
|
weekDays: Array<Byte>? = null
|
||||||
): KronScheduler = CronDateTimeScheduler(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years, weekDays))
|
): KronScheduler = CronDateTimeScheduler(createCronDateTime(seconds, minutes, hours, dayOfMonth, month, years, weekDays))
|
||||||
/**
|
/**
|
||||||
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
|
* @return [KronScheduler] (in fact [CronDateTimeScheduler]) based on incoming data
|
||||||
*/
|
*/
|
||||||
@ -166,4 +189,14 @@ internal fun createKronSchedulerWithOffset(
|
|||||||
years: Array<Int>? = null,
|
years: Array<Int>? = null,
|
||||||
weekDays: Array<Byte>? = null,
|
weekDays: Array<Byte>? = null,
|
||||||
offset: TimezoneOffset
|
offset: TimezoneOffset
|
||||||
): KronScheduler = CronDateTimeSchedulerTz(createCronDateTimeList(seconds, minutes, hours, dayOfMonth, month, years, weekDays), offset)
|
): KronScheduler = CronDateTimeSchedulerTz(createCronDateTime(seconds, minutes, hours, dayOfMonth, month, years, weekDays), offset)
|
||||||
|
|
||||||
|
internal fun List<CronDateTime>.merge() = CronDateTime(
|
||||||
|
flatMap { it.daysOfWeek ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
|
flatMap { it.years ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
|
flatMap { it.months ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
|
flatMap { it.daysOfMonth ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
|
flatMap { it.hours ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
|
flatMap { it.minutes ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
|
flatMap { it.seconds ?.toList() ?: emptyList() }.distinct().toTypedArray().takeIf { it.isNotEmpty() },
|
||||||
|
)
|
||||||
|
@ -19,7 +19,7 @@ import dev.inmo.krontab.collection.plus
|
|||||||
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
* @see dev.inmo.krontab.builder.SchedulerBuilder
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTimeScheduler internal constructor(
|
internal data class CronDateTimeScheduler internal constructor(
|
||||||
internal val cronDateTimes: List<CronDateTime>
|
internal val cronDateTime: CronDateTime
|
||||||
) : KronScheduler {
|
) : KronScheduler {
|
||||||
/**
|
/**
|
||||||
* @return Near date using [cronDateTimes] list and getting the [Iterable.minByOrNull] one
|
* @return Near date using [cronDateTimes] list and getting the [Iterable.minByOrNull] one
|
||||||
@ -27,12 +27,14 @@ internal data class CronDateTimeScheduler internal constructor(
|
|||||||
* @see toNearDateTime
|
* @see toNearDateTime
|
||||||
*/
|
*/
|
||||||
override suspend fun next(relatively: DateTime): DateTime? {
|
override suspend fun next(relatively: DateTime): DateTime? {
|
||||||
return cronDateTimes.mapNotNull { it.toNearDateTime(relatively) }.minOrNull()
|
return cronDateTime.toNearDateTime(relatively)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun mergeCronDateTimeSchedulers(schedulers: List<CronDateTimeScheduler>) = CronDateTimeScheduler(
|
internal fun mergeCronDateTimeSchedulers(
|
||||||
schedulers.flatMap { it.cronDateTimes }
|
schedulers: List<CronDateTimeScheduler>
|
||||||
|
): CronDateTimeScheduler = CronDateTimeScheduler(
|
||||||
|
schedulers.map { it.cronDateTime }.merge()
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,14 +11,12 @@ import dev.inmo.krontab.KronSchedulerTz
|
|||||||
* @see CronDateTime
|
* @see CronDateTime
|
||||||
*/
|
*/
|
||||||
internal data class CronDateTimeSchedulerTz internal constructor(
|
internal data class CronDateTimeSchedulerTz internal constructor(
|
||||||
internal val cronDateTimes: List<CronDateTime>,
|
internal val cronDateTime: CronDateTime,
|
||||||
internal val offset: TimezoneOffset
|
internal val offset: TimezoneOffset
|
||||||
) : KronSchedulerTz {
|
) : KronSchedulerTz {
|
||||||
override suspend fun next(relatively: DateTimeTz): DateTimeTz? {
|
override suspend fun next(relatively: DateTimeTz): DateTimeTz? {
|
||||||
val dateTimeWithActualOffset = relatively.toOffset(offset).local
|
val dateTimeWithActualOffset = relatively.toOffset(offset).local
|
||||||
return cronDateTimes.mapNotNull {
|
return cronDateTime.toNearDateTime(dateTimeWithActualOffset) ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset)
|
||||||
it.toNearDateTime(dateTimeWithActualOffset)
|
|
||||||
}.minOrNull() ?.toOffsetUnadjusted(offset) ?.toOffset(relatively.offset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,5 +25,8 @@ internal fun mergeCronDateTimeSchedulers(
|
|||||||
) = schedulers.groupBy {
|
) = schedulers.groupBy {
|
||||||
it.offset
|
it.offset
|
||||||
}.map { (offset, schedulers) ->
|
}.map { (offset, schedulers) ->
|
||||||
CronDateTimeSchedulerTz(schedulers.flatMap { it.cronDateTimes }, offset)
|
CronDateTimeSchedulerTz(
|
||||||
|
schedulers.map { it.cronDateTime }.merge(),
|
||||||
|
offset
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,171 @@
|
|||||||
|
package dev.inmo.krontab.internal
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import dev.inmo.krontab.utils.copy
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
internal class NearDateTimeCalculator<T>(
|
||||||
|
private val times: Array<T>,
|
||||||
|
private val partGetter: (DateTime) -> T,
|
||||||
|
private val partSetter: (DateTime, T) -> DateTime?
|
||||||
|
) where T : Comparable<T>, T : Number {
|
||||||
|
/**
|
||||||
|
* @return pair of near [DateTime] for this checker and [Boolean] flag that all previous calculations must be
|
||||||
|
* recalculated
|
||||||
|
*/
|
||||||
|
fun calculateNearTime(
|
||||||
|
relativelyTo: DateTime
|
||||||
|
): Pair<DateTime, Boolean>? {
|
||||||
|
val currentData = partGetter(relativelyTo)
|
||||||
|
val greaterOrEquals = times.firstOrNull { it >= currentData }
|
||||||
|
val newDateTime = if (greaterOrEquals == null) {
|
||||||
|
partSetter(relativelyTo, times.first()) ?: return null
|
||||||
|
} else {
|
||||||
|
partSetter(relativelyTo, greaterOrEquals) ?: return null
|
||||||
|
}
|
||||||
|
return if (newDateTime == relativelyTo) {
|
||||||
|
relativelyTo to false
|
||||||
|
} else {
|
||||||
|
newDateTime to true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorMillis(
|
||||||
|
times: Array<Short>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.milliseconds.toShort() },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
(if (newOne < dateTime.milliseconds) {
|
||||||
|
dateTime.plus(1.seconds)
|
||||||
|
} else {
|
||||||
|
dateTime
|
||||||
|
}).copy(milliseconds = newOne.toInt())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorSeconds(
|
||||||
|
times: Array<Byte>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.seconds.toByte() },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
(if (newOne < dateTime.seconds) {
|
||||||
|
dateTime.plus(1.minutes)
|
||||||
|
} else {
|
||||||
|
dateTime
|
||||||
|
}).copy(second = newOne.toInt(), milliseconds = 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorMinutes(
|
||||||
|
times: Array<Byte>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.minutes.toByte() },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
(if (newOne < dateTime.minutes) {
|
||||||
|
dateTime.plus(1.hours)
|
||||||
|
} else {
|
||||||
|
dateTime
|
||||||
|
}).copy(minute = newOne.toInt(), second = 0, milliseconds = 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorHours(
|
||||||
|
times: Array<Byte>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.hours.toByte() },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
(if (newOne < dateTime.hours) {
|
||||||
|
dateTime.plus(1.days)
|
||||||
|
} else {
|
||||||
|
dateTime
|
||||||
|
}).copy(hour = newOne.toInt(), minute = 0, second = 0, milliseconds = 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorDays(
|
||||||
|
times: Array<Byte>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.dayOfMonth.toByte() },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
(if (newOne < dateTime.dayOfMonth) {
|
||||||
|
dateTime.plus(1.months)
|
||||||
|
} else {
|
||||||
|
dateTime
|
||||||
|
}).copy(
|
||||||
|
dayOfMonth = min(dateTime.month.days(dateTime.year), newOne.toInt() + 1), // index1
|
||||||
|
hour = 0,
|
||||||
|
minute = 0,
|
||||||
|
second = 0,
|
||||||
|
milliseconds = 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorMonths(
|
||||||
|
times: Array<Byte>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.dayOfMonth.toByte() },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
(if (newOne < dateTime.month0) {
|
||||||
|
dateTime.plus(1.years)
|
||||||
|
} else {
|
||||||
|
dateTime
|
||||||
|
}).copy(
|
||||||
|
month = newOne.toInt() + 1, // index1
|
||||||
|
dayOfMonth = 1, // index1
|
||||||
|
hour = 0,
|
||||||
|
minute = 0,
|
||||||
|
second = 0,
|
||||||
|
milliseconds = 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorWeekDays(
|
||||||
|
times: Array<Byte>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.dayOfWeek.index0.toByte() },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
val currentDayOfWeek = dateTime.dayOfWeek.index0
|
||||||
|
(if (newOne < currentDayOfWeek) {
|
||||||
|
dateTime.plus(7.days - (currentDayOfWeek - newOne).days)
|
||||||
|
} else {
|
||||||
|
dateTime.plus(newOne.toInt().days - currentDayOfWeek.days)
|
||||||
|
}).copy(
|
||||||
|
hour = 0,
|
||||||
|
minute = 0,
|
||||||
|
second = 0,
|
||||||
|
milliseconds = 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun NearDateTimeCalculatorYears(
|
||||||
|
times: Array<Int>
|
||||||
|
) = NearDateTimeCalculator(
|
||||||
|
times,
|
||||||
|
{ it.yearInt },
|
||||||
|
{ dateTime, newOne ->
|
||||||
|
val currentYear = dateTime.yearInt
|
||||||
|
(if (newOne < currentYear) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
dateTime.plus(newOne.years - currentYear.years)
|
||||||
|
}) ?.copy(
|
||||||
|
month = 1, // index1
|
||||||
|
dayOfMonth = 1, // index1
|
||||||
|
hour = 0,
|
||||||
|
minute = 0,
|
||||||
|
second = 0,
|
||||||
|
milliseconds = 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
@ -8,7 +8,7 @@ private fun <T> createSimpleScheduler(from: String, dataRange: IntRange, dataCon
|
|||||||
val things = from.split(",")
|
val things = from.split(",")
|
||||||
|
|
||||||
val results = things.flatMap {
|
val results = things.flatMap {
|
||||||
val currentToken = it.toLowerCase().replace(
|
val currentToken = it.lowercase().replace(
|
||||||
"f", dataRange.first.toString()
|
"f", dataRange.first.toString()
|
||||||
).replace(
|
).replace(
|
||||||
"l", dataRange.last.toString()
|
"l", dataRange.last.toString()
|
||||||
|
22
src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt
Normal file
22
src/commonMain/kotlin/dev/inmo/krontab/utils/DateTimeCopy.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
|
import com.soywiz.klock.*
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
fun DateTime.copy(
|
||||||
|
year: Int = yearInt,
|
||||||
|
month: Int = month1,
|
||||||
|
dayOfMonth: Int = this.dayOfMonth,
|
||||||
|
hour: Int = hours,
|
||||||
|
minute: Int = minutes,
|
||||||
|
second: Int = seconds,
|
||||||
|
milliseconds: Int = this.milliseconds
|
||||||
|
) = DateTime(
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
min(Month(month).days(yearInt), dayOfMonth),
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
milliseconds
|
||||||
|
)
|
@ -10,7 +10,8 @@ class TimeZoneTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testDifferentTimeZonesReturnsDifferentTimes() {
|
fun testDifferentTimeZonesReturnsDifferentTimes() {
|
||||||
val scheduler = buildSchedule { seconds { every(1) } }
|
val scheduler = buildSchedule { seconds { every(1) } }
|
||||||
val baseDate = DateTime.now().startOfWeek
|
val additionalMilliseconds = 100.milliseconds
|
||||||
|
val baseDate = DateTime.now().startOfWeek.copy(milliseconds = additionalMilliseconds.millisecondsInt)
|
||||||
runTest {
|
runTest {
|
||||||
for (i in 0 until 7) {
|
for (i in 0 until 7) {
|
||||||
val now = baseDate + i.days
|
val now = baseDate + i.days
|
||||||
@ -18,10 +19,10 @@ class TimeZoneTest {
|
|||||||
val nowTz = now.toOffset(j.hours)
|
val nowTz = now.toOffset(j.hours)
|
||||||
val next = scheduler.next(nowTz)!!
|
val next = scheduler.next(nowTz)!!
|
||||||
assertEquals(
|
assertEquals(
|
||||||
(nowTz + 1.seconds).utc.unixMillisLong, next.utc.unixMillisLong
|
(nowTz + 1.seconds - additionalMilliseconds).utc.unixMillisLong, next.utc.unixMillisLong
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user