From 204bbe1f35545224a47a88af238127b35fe2884c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 23 May 2019 14:42:55 +0800 Subject: [PATCH] add KronTime entity and time calculation environment --- .../krontab/parts/KronTime.kt | 110 ++++++++++++++++++ .../krontab/parts/PeriodAdapters.kt | 93 +++++++++++++++ .../krontab/parts/TimeTypeAliases.kt | 11 ++ 3 files changed, 214 insertions(+) create mode 100644 src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/KronTime.kt create mode 100644 src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/PeriodAdapters.kt create mode 100644 src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/TimeTypeAliases.kt diff --git a/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/KronTime.kt b/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/KronTime.kt new file mode 100644 index 0000000..2017efa --- /dev/null +++ b/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/KronTime.kt @@ -0,0 +1,110 @@ +package com.github.insanusmokrassar.krontab.parts + +import kotlin.math.floor + +private const val millisCount = 1000 +private const val secondsCount = 60 +private const val minutesCount = 60 +private const val hoursCount = 24 +private const val weekDaysCount = 7 + +private const val secondsK = 1000 +private const val minutesK = 60 * secondsK +private const val hoursK = 60 * minutesK +private const val daysK = 24 * hoursK +private const val weekDaysK = 7 * daysK + +private fun List.notMaxOrNull(max: Int): List? = if (size >= max) { + null +} else { + this +} + +internal fun KronTimes( + timePattern: TimePattern +): List { + val reversedParts = timePattern.split(" ").toMutableList().also { + it.reverse() + } + val weekDaysParts = getTimes(reversedParts.removeAt(0), 0, weekDaysCount).notMaxOrNull(weekDaysCount) ?.map { + it.toByte() + } + val hoursParts = getTimes(reversedParts.removeAt(0), 0, hoursCount).let { + (weekDaysParts ?.let { _ -> + it + } ?: it.notMaxOrNull(hoursCount)) ?.map { + it.toByte() + } + } + val minutesParts = getTimes(reversedParts.removeAt(0), 0, minutesCount).let { + (hoursParts ?.let { _ -> + it + } ?: it.notMaxOrNull(minutesCount)) ?.map { + it.toByte() + } + } + val secondsParts = getTimes(reversedParts.removeAt(0), 0, secondsCount).let { + (minutesParts ?.let { _ -> + it + } ?: it.notMaxOrNull(secondsCount)) ?.map { + it.toByte() + } + } + val millisParts = if (reversedParts.isNotEmpty()) { + getTimes(reversedParts.removeAt(0), 0, millisCount).let { + secondsParts ?.let { _ -> + it + } ?: it.notMaxOrNull(millisCount) + } ?.map { + it.toShort() + } + } else { + null + } + + return millisParts ?.flatMap { millis -> + secondsParts ?.flatMap { seconds -> + minutesParts ?.flatMap { minutes -> + hoursParts ?.flatMap { hours -> + weekDaysParts ?.map { weekDay -> + KronTime(millis, seconds, minutes, hours, weekDay) + } ?: listOf(KronTime(millis, seconds, minutes, hours, null)) + } ?: listOf(KronTime(millis, seconds, minutes, null, null)) + } ?: listOf(KronTime(millis, seconds, null, null, null)) + } ?: listOf(KronTime(millis, null, null, null, null)) + } ?: listOf(KronTime(null, null, null, null, null)) +} + +internal fun KronTime( + milliseconds: Milliseconds +): KronTime { + val millis = (milliseconds % millisCount).toShort() + var currentDivided: Double = floor(milliseconds.toDouble() / millisCount) + + val seconds: Byte = (currentDivided % secondsCount).toByte() + currentDivided = floor(currentDivided / secondsCount) + + val minutes: Byte = (currentDivided % minutesCount).toByte() + currentDivided = floor(currentDivided / minutesCount) + + val hours: Byte = (currentDivided % hoursCount).toByte() + currentDivided = floor(currentDivided / hoursCount) + + val days: Byte = (currentDivided % weekDaysCount).toByte() + + return KronTime( + millis, + seconds, + minutes, + hours, + days + ) +} + +internal data class KronTime( + val milliseconds: Milliseconds?, + val seconds: Seconds?, + val minutes: Minutes?, + val hours: Hours?, + val weekDays: WeekDays? +) diff --git a/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/PeriodAdapters.kt b/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/PeriodAdapters.kt new file mode 100644 index 0000000..b7da5d5 --- /dev/null +++ b/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/PeriodAdapters.kt @@ -0,0 +1,93 @@ +package com.github.insanusmokrassar.krontab.parts + +internal inline fun buildPeriodRegex(periodNumbers: Int): Regex { + val periodRegex = "\\d{1,$periodNumbers}" + return Regex("(\\*(/$periodRegex)?)|($periodRegex(-$periodRegex)?)") +} + +private fun extractLongTermPeriod(period: String, minPeriodValue: Int, maxPeriodValue: Int): List? { + if (period.contains("-")) { + val (first, second) = period.split("-") + val firstNumber = first.toInt().let { + if (it < minPeriodValue) { + minPeriodValue + } else { + it + } + } + val secondNumber = second.toInt().let { + if (it > maxPeriodValue) { + maxPeriodValue + } else { + it + } + } + return (firstNumber .. secondNumber).toList() + } + return null +} + +private fun extractPeriods(period: String, minPeriodValue: Int, maxPeriodValue: Int): List? { + if (period.startsWith("*")) { + val splitted = period.split("/") + when { + splitted.size > 1 -> { + val repeatPeriod = splitted[1].toInt() + return (minPeriodValue .. maxPeriodValue step repeatPeriod).toList() + } + else -> { + (minPeriodValue .. maxPeriodValue).toList() + } + } + } + return null +} + +private val oneTimeRegex = Regex("^\\d*$") +private fun extractOneTime(period: String, minPeriodValue: Int, maxPeriodValue: Int): List? { + oneTimeRegex.find(period) ?.let { + val found = it.groupValues.firstOrNull() ?: return null + val foundAsInt = found.toInt() + val resultTime = when { + minPeriodValue > foundAsInt -> minPeriodValue + maxPeriodValue < foundAsInt -> maxPeriodValue + else -> foundAsInt + } + return listOf(resultTime) + } + return null +} + +private fun adaptPeriod(period: String, minPeriodValue: Int, maxPeriodValue: Int): List { + return extractLongTermPeriod( + period, + minPeriodValue, + maxPeriodValue + ) ?: extractPeriods( + period, + minPeriodValue, + maxPeriodValue + ) ?: extractOneTime( + period, + minPeriodValue, + maxPeriodValue + ) ?: emptyList() +} + +internal fun getTimes( + period: String, + minPeriodValue: Int, + maxPeriodValue: Int, + unconfinedVariants: Map = emptyMap() +): List { + return period.split(",").flatMap { + unconfinedVariants[it] ?.let { + listOf(it) + } ?: adaptPeriod( + it, + minPeriodValue, + maxPeriodValue + ) + } +} + diff --git a/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/TimeTypeAliases.kt b/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/TimeTypeAliases.kt new file mode 100644 index 0000000..4487423 --- /dev/null +++ b/src/commonMain/kotlin/com/github/insanusmokrassar/krontab/parts/TimeTypeAliases.kt @@ -0,0 +1,11 @@ +package com.github.insanusmokrassar.krontab.parts + +internal typealias Milliseconds = Short +internal typealias Seconds = Byte +internal typealias Minutes = Byte +internal typealias Hours = Byte +internal typealias WeekDays = Byte +internal typealias Weeks = Byte + +typealias TimePattern = String +typealias TimeSubPattern = String