diff --git a/README.md b/README.md
index d18e540..9b15891 100644
--- a/README.md
+++ b/README.md
@@ -10,18 +10,18 @@ A parser that converts natural (English) language to a cron expression in Kotlin
 You can add the library to your project using gradle:
 
 ```kotlin
-implementation("io.github.yamilmedina:natural-kron:0.0.1")
+implementation("io.github.yamilmedina:natural-kron:0.1.0")
 ```
 
 ## Usage ##
 
 ```kotlin
-import io.github.yamilmedina.naturalkron.NaturalKron
+import io.github.yamilmedina.kron.NaturalKronParser
 
 val expression = "every day at 9am"
-val parsed = NaturalKronExpressionParser().parse(expression)
+val parsed = NaturalKronParser().parse(expression)
 
-val expectedKronExpressionEveryDayAt9am = "0 0 9 * * *"
+val expectedKronExpressionEveryDayAt9am = "0 9 * * *"
 assertEquals("*", parsed.dayOfWeek) // --> every day cron expression
 assertEquals(expectedKronExpressionEveryDayAt9am, parsed.toString())  // --> TRUE
 ```
diff --git a/build.gradle.kts b/build.gradle.kts
index cf35f96..fd4638b 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,6 +16,7 @@ repositories {
 
 dependencies {
     testImplementation(libs.kotlin.test)
+    testImplementation(libs.junit.params)
 }
 
 tasks.test {
diff --git a/gradle.properties b/gradle.properties
index 571c7f1..4a13ab4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
 kotlin.code.style=official
-VERSION_NAME=0.0.3
+VERSION_NAME=0.1.0
 
 SONATYPE_HOST=CENTRAL_PORTAL
 RELEASE_SIGNING_ENABLED=false
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 166732e..00bea95 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,6 +2,7 @@
 kotlin = "1.9.23"
 maven-publish = "0.28.0"
 kover = "0.7.4"
+junit = "5.10.2"
 
 [plugins]
 kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
@@ -10,3 +11,4 @@ kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
 
 [libraries]
 kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" }
+junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit" }
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/ExpressionElementProvider.kt b/src/main/kotlin/io/github/yamilmedina/kron/ExpressionElementProvider.kt
new file mode 100644
index 0000000..eec9453
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/ExpressionElementProvider.kt
@@ -0,0 +1,86 @@
+package  io.github.yamilmedina.kron
+
+internal interface ExpressionElementProvider {
+    /**
+     * @param string
+     * @return Boolean
+     */
+    fun matches(string: String): Boolean
+
+    /**
+     * @return Boolean
+     */
+    fun canProvideMinute(): Boolean
+
+    /**
+     * @return String
+     */
+    fun getMinuteElement(): String?
+
+    /**
+     * @return Boolean
+     */
+    fun isMinuteElementLocked(): Boolean
+
+    /**
+     * @return Boolean
+     */
+    fun canProvideHour(): Boolean
+
+    /**
+     * @return String
+     */
+    fun getHourElement(): String?
+
+    /**
+     * @return Boolean
+     */
+    fun isHourElementLocked(): Boolean
+
+    /**
+     * @return Boolean
+     */
+    fun canProvideDayNumber(): Boolean
+
+    /**
+     * @return String
+     */
+    fun getDayNumberElement(): String?
+
+    /**
+     * @return Boolean
+     */
+    fun isDayNumberElementLocked(): Boolean
+
+    /**
+     * @return Boolean
+     */
+    fun canProvideMonth(): Boolean
+
+    /**
+     * @return String
+     */
+    fun getMonthElement(): String?
+
+    /**
+     * @return Boolean
+     */
+    fun isMonthElementLocked(): Boolean
+
+    /**
+     * @return Boolean
+     */
+    fun canProvideDayOfWeek(): Boolean
+
+    /**
+     * @return String
+     */
+    fun getDayOfWeekElement(): String?
+
+    /**
+     * @return Boolean
+     */
+    fun isDayOfWeekElementLocked(): Boolean
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/KronExpression.kt b/src/main/kotlin/io/github/yamilmedina/kron/KronExpression.kt
new file mode 100644
index 0000000..1fa1c79
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/KronExpression.kt
@@ -0,0 +1,57 @@
+package io.github.yamilmedina.kron
+
+class KronExpression(
+    var minute: String? = null,
+    var hour: String? = null,
+    var dayNumber: String? = null,
+    var month: String? = null,
+    var dayOfWeek: String? = null
+) {
+
+    fun hasMinute(): Boolean = minute != null
+
+    fun setMinute(minute: String): KronExpression {
+        this.minute = minute
+        return this
+    }
+
+    fun hasHour(): Boolean = hour != null
+
+    fun setHour(hour: String): KronExpression {
+        this.hour = hour
+        return this
+    }
+
+    fun hasDayNumber(): Boolean = dayNumber != null
+
+    fun setDayNumber(dayNumber: String): KronExpression {
+        this.dayNumber = dayNumber
+        return this
+    }
+
+    fun hasMonth(): Boolean = month != null
+
+    fun setMonth(month: String): KronExpression {
+        this.month = month
+        return this
+    }
+
+    fun hasDayOfWeek(): Boolean = dayOfWeek != null
+
+    fun setDayOfWeek(dayOfWeek: String): KronExpression {
+        this.dayOfWeek = dayOfWeek
+        return this
+    }
+
+    fun hasNothing(): Boolean = !hasMinute() && !hasHour() && !hasDayNumber() && !hasMonth() && !hasDayOfWeek()
+
+    override fun toString(): String = "%s %s %s %s %s".format(
+        if (hasMinute()) minute else "0",
+        if (hasHour()) hour else "0",
+        if (hasDayNumber()) dayNumber else "*",
+        if (hasMonth()) month else "*",
+        if (hasDayOfWeek()) dayOfWeek else "*"
+    )
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/NaturalKronParser.kt b/src/main/kotlin/io/github/yamilmedina/kron/NaturalKronParser.kt
new file mode 100644
index 0000000..9c6135e
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/NaturalKronParser.kt
@@ -0,0 +1,172 @@
+package io.github.yamilmedina.kron
+
+import io.github.yamilmedina.kron.elementprovider.DayNumber
+import io.github.yamilmedina.kron.elementprovider.hour.Base12Hour
+import io.github.yamilmedina.kron.elementprovider.hour.Base12HourShort
+import io.github.yamilmedina.kron.elementprovider.hour.Base24Hour
+import io.github.yamilmedina.kron.elementprovider.hour.Midnight
+import io.github.yamilmedina.kron.elementprovider.hour.Noon
+import io.github.yamilmedina.kron.elementprovider.recurring.EveryDay
+import io.github.yamilmedina.kron.elementprovider.recurring.EveryDayNumber
+import io.github.yamilmedina.kron.elementprovider.recurring.EveryHour
+import io.github.yamilmedina.kron.elementprovider.recurring.EveryMinute
+import io.github.yamilmedina.kron.elementprovider.recurring.EveryMonth
+import io.github.yamilmedina.kron.elementprovider.recurring.EveryWeek
+import io.github.yamilmedina.kron.elementprovider.recurring.EveryYear
+import java.text.ParseException
+import java.util.regex.Pattern
+
+class NaturalKronParser {
+
+    companion object {
+        const val VALID_PATTERN =
+            "^(@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly|((?:[1-9]?\\d|\\*)\\s*(?:(?:[\\/\\-][1-9]?\\d)|(?:,[1-9]?\\d)+)?\\s*){5})$"
+    }
+
+    private val elementProviders: List<ExpressionElementProvider> = listOf(
+        EveryYear(),
+        EveryMonth(),
+        EveryWeek(),
+        EveryDayNumber(),
+        EveryDay(),
+        EveryHour(),
+        EveryMinute(),
+        DayNumber(),
+        Noon(),
+        Midnight(),
+        Base12Hour(),
+        Base12HourShort(),
+        Base24Hour()
+    )
+
+    /**
+     * Parses a string to a KronExpression
+     *
+     * @param string the string to parse
+     * @throws ParseException if the string is not a valid cron expression
+     * @return a [KronExpression] instance
+     */
+    fun parse(string: String): KronExpression {
+        val lowercaseString = string.toLowerCase()
+        val mappings = mapOf(
+            "@yearly" to KronExpression("0", "0", "1", "1", "*"),
+            "@annually" to KronExpression("0", "0", "1", "1", "*"),
+            "@monthly" to KronExpression("0", "0", "1", "*", "*"),
+            "@weekly" to KronExpression("0", "0", "*", "*", "0"),
+            "@midnight" to KronExpression("0", "0", "*", "*", "*"),
+            "@daily" to KronExpression("0", "0", "*", "*", "*"),
+            "@hourly" to KronExpression("0", "*", "*", "*", "*")
+        )
+
+        if (mappings.containsKey(lowercaseString)) {
+            return mappings[lowercaseString]!!
+        }
+
+        val expression = KronExpression()
+        var isMinuteElementLocked = false
+        var isHourElementLocked = false
+        var isDayNumberElementLocked = false
+        var isMonthElementLocked = false
+        var isDayOfWeekElementLocked = false
+
+        val shouldUpdateMinute: (KronExpression, ExpressionElementProvider) -> Boolean = { expression, subParser ->
+            subParser.canProvideMinute() && !isMinuteElementLocked
+        }
+
+        val shouldUpdateHour: (KronExpression, ExpressionElementProvider) -> Boolean = { expression, subParser ->
+            subParser.canProvideHour() && !isHourElementLocked
+        }
+
+        val shouldUpdateDayNumber: (KronExpression, ExpressionElementProvider) -> Boolean = { expression, subParser ->
+            subParser.canProvideDayNumber() && !isDayNumberElementLocked
+        }
+
+        val shouldUpdateMonth: (KronExpression, ExpressionElementProvider) -> Boolean = { expression, subParser ->
+            subParser.canProvideMonth() && !isMonthElementLocked
+        }
+
+        val shouldUpdateDayOfWeek: (KronExpression, ExpressionElementProvider) -> Boolean = { expression, subParser ->
+            subParser.canProvideDayOfWeek() && !isDayOfWeekElementLocked
+        }
+
+        for (elementProvider in elementProviders) {
+            if (elementProvider.matches(lowercaseString)) {
+                if (shouldUpdateMinute(expression, elementProvider)) {
+                    expression.minute = elementProvider.getMinuteElement()
+                }
+
+                if (shouldUpdateHour(expression, elementProvider)) {
+                    expression.hour = elementProvider.getHourElement()
+                }
+
+                if (shouldUpdateDayNumber(expression, elementProvider)) {
+                    expression.dayNumber = elementProvider.getDayNumberElement()
+                }
+
+                if (shouldUpdateMonth(expression, elementProvider)) {
+                    expression.month = elementProvider.getMonthElement()
+                }
+
+                if (shouldUpdateDayOfWeek(expression, elementProvider)) {
+                    expression.dayOfWeek = elementProvider.getDayOfWeekElement()
+                }
+
+                if (elementProvider.isMinuteElementLocked()) {
+                    isMinuteElementLocked = true
+                }
+
+                if (elementProvider.isHourElementLocked()) {
+                    isHourElementLocked = true
+                }
+
+                if (elementProvider.isDayNumberElementLocked()) {
+                    isDayNumberElementLocked = true
+                }
+
+                if (elementProvider.isMonthElementLocked()) {
+                    isMonthElementLocked = true
+                }
+
+                if (elementProvider.isDayOfWeekElementLocked()) {
+                    isDayOfWeekElementLocked = true
+                }
+            }
+        }
+
+        val validPattern = Pattern.compile(VALID_PATTERN)
+        if (expression.hasNothing() || !validPattern.matcher(expression.toString()).matches()) {
+            throw ParseException("Unable to parse \"$string\", expression is: $expression", Int.MIN_VALUE)
+        }
+
+        return expression
+    }
+
+    /**
+     * Converts a string to a valid cron expression
+     *
+     * @param string the string to convert
+     * @throws ParseException if the string is not a valid cron expression
+     * @return a valid cron expression in string format
+     */
+    fun fromString(string: String): String {
+        val parser = NaturalKronParser()
+        return parser.parse(string).toString()
+    }
+
+    /**
+     * Checks if a string is a valid cron expression
+     */
+    fun isValid(string: String): Boolean {
+        if (string == "@reboot") {
+            return true
+        }
+        return try {
+            fromString(string)
+            true
+        } catch (e: ParseException) {
+            false
+        }
+    }
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/DayNumber.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/DayNumber.kt
new file mode 100644
index 0000000..e0872e7
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/DayNumber.kt
@@ -0,0 +1,84 @@
+package io.github.yamilmedina.kron.elementprovider
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+import java.util.regex.Pattern
+
+internal class DayNumber : ExpressionElementProvider {
+    companion object {
+        private const val PATTERN = "([0-9]?[0-9])(st|nd|rd|th)"
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val regex = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
+        val matcher = regex.matcher(string)
+        if (matcher.find()) {
+            segments = (1..matcher.groupCount()).map { matcher.group(it) }
+            return true
+        }
+        return false
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return true
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return true
+    }
+
+    override fun getHourElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return true
+    }
+
+    override fun getDayNumberElement(): String {
+        return segments[0]
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return segments.isNotEmpty()
+    }
+
+    override fun getMonthElement(): String {
+        return "*"
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return true
+    }
+
+    override fun getDayOfWeekElement(): String {
+        return "*"
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return true
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base12Hour.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base12Hour.kt
new file mode 100644
index 0000000..b682f37
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base12Hour.kt
@@ -0,0 +1,25 @@
+package io.github.yamilmedina.kron.elementprovider.hour
+
+
+internal open class Base12Hour : Base24Hour() {
+
+    companion object {
+        const val PATTERN = "(1[012]|[1-9]):([0-5][0-9])?(?i)\\s?(am|pm)"
+    }
+
+    override fun matches(string: String): Boolean {
+        val regex = Regex(PATTERN, RegexOption.IGNORE_CASE)
+        val matches = regex.find(string)
+        this.segments = matches?.groupValues?.toList() ?: emptyList()
+        return this.segments.isNotEmpty()
+    }
+
+    override fun getHourElement(): String {
+        println("> 12 Hour segments: $segments")
+        return when {
+            segments[3].toLowerCase() == "pm" && segments[1].toInt() < 12 -> (segments[1].toInt() + 12).toString()
+            segments[3].toLowerCase() == "am" && segments[1].toInt() == 12 -> 0.toString()
+            else -> segments[1]
+        }
+    }
+}
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base12HourShort.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base12HourShort.kt
new file mode 100644
index 0000000..8cf5047
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base12HourShort.kt
@@ -0,0 +1,33 @@
+package io.github.yamilmedina.kron.elementprovider.hour
+
+internal open class Base12HourShort : Base12Hour() {
+
+    companion object {
+        const val PATTERN = "(1[012]|[1-9])\\s?(?i)\\s?(am|pm)"
+    }
+
+    override fun matches(string: String): Boolean {
+        val regex = Regex(PATTERN, RegexOption.IGNORE_CASE)
+        val matches = regex.find(string)
+        this.segments = matches?.groupValues?.toList() ?: emptyList()
+        return this.segments.isNotEmpty()
+    }
+
+    override fun getHourElement(): String {
+        return when {
+            segments[2].toLowerCase() == "pm" && segments[1].toInt() < 12 -> (segments[1].toInt() + 12).toString()
+            segments[2].toLowerCase() == "am" && segments[1].toInt() == 12 -> 0.toString()
+            else -> segments[1]
+        }
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return segments.size > 1
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base24Hour.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base24Hour.kt
new file mode 100644
index 0000000..7bf1cde
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Base24Hour.kt
@@ -0,0 +1,80 @@
+package io.github.yamilmedina.kron.elementprovider.hour
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+
+internal open class Base24Hour : ExpressionElementProvider {
+
+    companion object {
+        const val PATTERN = "(2[0-3]|[01]?[0-9]):([0-5]?[0-9])"
+    }
+
+    protected var segments: List<String> = listOf()
+
+    override fun matches(string: String): Boolean {
+        val regex = Regex(PATTERN, RegexOption.IGNORE_CASE)
+        val matches = regex.find(string)
+        this.segments = matches?.groupValues?.toList() ?: emptyList()
+        return this.segments.isNotEmpty()
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return segments.size > 2
+    }
+
+    override fun getMinuteElement(): String {
+        return segments[2].toUInt().toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return segments.size > 1
+    }
+
+    override fun getHourElement(): String {
+        return segments[1].toUInt().toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return false
+    }
+
+    override fun getDayNumberElement(): String? {
+        return null
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return false
+    }
+
+    override fun getMonthElement(): String? {
+        return null
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return false
+    }
+
+    override fun getDayOfWeekElement(): String? {
+        return null
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return canProvideMinute()
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return canProvideHour()
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Midnight.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Midnight.kt
new file mode 100644
index 0000000..495d0b1
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Midnight.kt
@@ -0,0 +1,44 @@
+package io.github.yamilmedina.kron.elementprovider.hour
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+
+internal class Midnight : ExpressionElementProvider {
+    private var match: Boolean = false
+
+    override fun matches(string: String): Boolean {
+        match = string.contains("midnight")
+        return match
+    }
+
+    override fun canProvideMinute(): Boolean = match
+
+    override fun getMinuteElement(): String = 0.toString()
+
+    override fun canProvideHour(): Boolean = match
+
+    override fun getHourElement(): String = 0.toString()
+
+    override fun canProvideDayNumber(): Boolean = false
+
+    override fun getDayNumberElement(): String? = null
+
+    override fun canProvideMonth(): Boolean = false
+
+    override fun getMonthElement(): String? = null
+
+    override fun canProvideDayOfWeek(): Boolean = false
+
+    override fun getDayOfWeekElement(): String? = null
+
+    override fun isMinuteElementLocked(): Boolean = canProvideMinute()
+
+    override fun isHourElementLocked(): Boolean = canProvideHour()
+
+    override fun isDayNumberElementLocked(): Boolean = false
+
+    override fun isMonthElementLocked(): Boolean = false
+
+    override fun isDayOfWeekElementLocked(): Boolean = false
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Noon.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Noon.kt
new file mode 100644
index 0000000..26c3f85
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/hour/Noon.kt
@@ -0,0 +1,74 @@
+package io.github.yamilmedina.kron.elementprovider.hour
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+
+internal class Noon : ExpressionElementProvider {
+    private var match: Boolean = false
+
+    override fun matches(string: String): Boolean {
+        match = string.contains("noon")
+        return match
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return match
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return match
+    }
+
+    override fun getHourElement(): String {
+        return 12.toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return false
+    }
+
+    override fun getDayNumberElement(): String? {
+        return null
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return false
+    }
+
+    override fun getMonthElement(): String? {
+        return null
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return false
+    }
+
+    override fun getDayOfWeekElement(): String? {
+        return null
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return canProvideMinute()
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return canProvideHour()
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryDay.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryDay.kt
new file mode 100644
index 0000000..3ff4baa
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryDay.kt
@@ -0,0 +1,96 @@
+package io.github.yamilmedina.kron.elementprovider.recurring
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+import java.util.regex.Pattern
+
+internal class EveryDay : ExpressionElementProvider {
+    companion object {
+        private const val PATTERN =
+            "(daily|(every|each|on)\\s(day|monday|tuesday|wednesday|thursday|friday|saturday|sunday)(?s))"
+        private val DAY_MAP = mapOf(
+            "sunday" to 0,
+            "monday" to 1,
+            "tuesday" to 2,
+            "wednesday" to 3,
+            "thursday" to 4,
+            "friday" to 5,
+            "saturday" to 6
+        )
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val regex = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
+        val matcher = regex.matcher(string)
+        if (matcher.find()) {
+            segments = (0..matcher.groupCount()).map { matcher.group(it) }
+            return true
+        }
+        return false
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return true
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return true
+    }
+
+    override fun getHourElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return true
+    }
+
+    override fun getDayNumberElement(): String {
+        return "*"
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return true
+    }
+
+    override fun getMonthElement(): String {
+        return "*"
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return segments.isNotEmpty()
+    }
+
+    override fun getDayOfWeekElement(): String? {
+        return if (segments[0] == "daily" || (segments.size >= 4 && segments[3] == "day")) {
+            null
+        } else {
+            DAY_MAP[segments.getOrNull(3)]?.toString()
+        }
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return true
+    }
+}
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryDayNumber.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryDayNumber.kt
new file mode 100644
index 0000000..c9627c4
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryDayNumber.kt
@@ -0,0 +1,98 @@
+package io.github.yamilmedina.kron.elementprovider.recurring
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+import java.util.regex.Pattern
+
+internal class EveryDayNumber : ExpressionElementProvider {
+    companion object {
+        private const val PATTERN =
+            "(every|each)\\s([0-9]?[0-9])(st|nd|rd|th)\\sof\\s(month|january|february|march|april|may|june|july|august|september|october|november|december)"
+        private val MONTH_MAP = mapOf(
+            "january" to 1,
+            "february" to 2,
+            "march" to 3,
+            "april" to 4,
+            "may" to 5,
+            "june" to 6,
+            "july" to 7,
+            "august" to 8,
+            "september" to 9,
+            "october" to 10,
+            "november" to 11,
+            "december" to 12
+        )
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
+        val matcher = pattern.matcher(string)
+        if (matcher.find()) {
+            segments = (0..matcher.groupCount()).map { matcher.group(it) }
+            return true
+        }
+        return false
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return true
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return true
+    }
+
+    override fun getHourElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return true
+    }
+
+    override fun getDayNumberElement(): String {
+        return segments[2]
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return segments.size > 4
+    }
+
+    override fun getMonthElement(): String? {
+        return MONTH_MAP[segments.getOrNull(4)]?.toString()
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return true
+    }
+
+    override fun getDayOfWeekElement(): String {
+        return "*"
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return canProvideMonth()
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryHour.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryHour.kt
new file mode 100644
index 0000000..c49fd9e
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryHour.kt
@@ -0,0 +1,88 @@
+package io.github.yamilmedina.kron.elementprovider.recurring
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+import java.util.regex.Pattern
+
+internal class EveryHour : ExpressionElementProvider {
+    companion object {
+        private const val PATTERN = "(hourly|(every|each)?\\s?([0-9]+)?\\shour)"
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val regex = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
+        val matcher = regex.matcher(string)
+        if (matcher.find()) {
+            segments = (0..matcher.groupCount()).map { matcher.group(it) }
+            return true
+        }
+        return false
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return false
+    }
+
+    override fun getMinuteElement(): String? {
+        return null
+    }
+
+    override fun canProvideHour(): Boolean {
+        return segments.isNotEmpty()
+    }
+
+    override fun getHourElement(): String? {
+        return if (segments.isNotEmpty()) {
+            segments[3]?.let { "*/$it" } ?: "*"
+        } else {
+            null
+        }
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return false
+    }
+
+    override fun getDayNumberElement(): String? {
+        return null
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return false
+    }
+
+    override fun getMonthElement(): String? {
+        return null
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return false
+    }
+
+    override fun getDayOfWeekElement(): String? {
+        return null
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return true
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryMinute.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryMinute.kt
new file mode 100644
index 0000000..ed69de0
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryMinute.kt
@@ -0,0 +1,88 @@
+package io.github.yamilmedina.kron.elementprovider.recurring
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+import java.util.regex.Pattern
+
+internal class EveryMinute : ExpressionElementProvider {
+    companion object {
+        private const val PATTERN = "((every|each)?\\s?([0-9]+)?\\s?minute)"
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val regex = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
+        val matcher = regex.matcher(string)
+        if (matcher.find()) {
+            segments = (0..matcher.groupCount()).map { matcher.group(it) }
+            return true
+        }
+        return false
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return segments.isNotEmpty()
+    }
+
+    override fun getMinuteElement(): String? {
+        return if (segments.isNotEmpty()) {
+            segments[3]?.let { "*/$it" } ?: "*"
+        } else {
+            null
+        }
+    }
+
+    override fun canProvideHour(): Boolean {
+        return canProvideMinute()
+    }
+
+    override fun getHourElement(): String? {
+        return "*"
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return false
+    }
+
+    override fun getDayNumberElement(): String? {
+        return null
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return false
+    }
+
+    override fun getMonthElement(): String? {
+        return null
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return false
+    }
+
+    override fun getDayOfWeekElement(): String? {
+        return null
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return true
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryMonth.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryMonth.kt
new file mode 100644
index 0000000..3e65321
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryMonth.kt
@@ -0,0 +1,98 @@
+package io.github.yamilmedina.kron.elementprovider.recurring
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+
+internal class EveryMonth : ExpressionElementProvider {
+
+    companion object {
+        private const val PATTERN =
+            "(monthly|(every|each)\\s(month|january|february|march|april|may|june|july|august|september|october|november|december))"
+        private val MONTH_MAP = mapOf(
+            "january" to 1,
+            "february" to 2,
+            "march" to 3,
+            "april" to 4,
+            "may" to 5,
+            "june" to 6,
+            "july" to 7,
+            "august" to 8,
+            "september" to 9,
+            "october" to 10,
+            "november" to 11,
+            "december" to 12
+        )
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val regex = Regex(PATTERN, RegexOption.IGNORE_CASE)
+        segments = regex.find(string)?.groupValues?.drop(1) ?: emptyList()
+        return segments.isNotEmpty()
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return true
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return true
+    }
+
+    override fun getHourElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return true
+    }
+
+    override fun getDayNumberElement(): String {
+        return "1"
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return segments.isNotEmpty()
+    }
+
+    override fun getMonthElement(): String? {
+        return if (segments[0] == "monthly" || (segments.size == 4 && segments[3] == "month")) {
+            "*"
+        } else {
+            MONTH_MAP[segments.getOrNull(2)]?.toString()
+        }
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return true
+    }
+
+    override fun getDayOfWeekElement(): String {
+        return "*"
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryWeek.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryWeek.kt
new file mode 100644
index 0000000..074c944
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryWeek.kt
@@ -0,0 +1,81 @@
+package io.github.yamilmedina.kron.elementprovider.recurring
+
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+
+internal class EveryWeek : ExpressionElementProvider {
+
+    companion object {
+        private const val PATTERN = "(weekly|(every|each)\\sweek)"
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val regex = Regex(PATTERN, RegexOption.IGNORE_CASE)
+        segments = regex.find(string)?.groupValues?.drop(1) ?: emptyList()
+        return segments.isNotEmpty()
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return true
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return true
+    }
+
+    override fun getHourElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return true
+    }
+
+    override fun getDayNumberElement(): String {
+        return "*"
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return true
+    }
+
+    override fun getMonthElement(): String {
+        return "*"
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return segments.isNotEmpty()
+    }
+
+    override fun getDayOfWeekElement(): String {
+        return "0"
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
+
diff --git a/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryYear.kt b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryYear.kt
new file mode 100644
index 0000000..9c40b99
--- /dev/null
+++ b/src/main/kotlin/io/github/yamilmedina/kron/elementprovider/recurring/EveryYear.kt
@@ -0,0 +1,79 @@
+package io.github.yamilmedina.kron.elementprovider.recurring
+
+import io.github.yamilmedina.kron.ExpressionElementProvider
+
+internal class EveryYear : ExpressionElementProvider {
+
+    companion object {
+        private const val PATTERN = "(yearly|annually|(every|each) ?([0-9]+)?year)"
+    }
+
+    private var segments: List<String> = emptyList()
+
+    override fun matches(string: String): Boolean {
+        val regex = Regex(PATTERN, RegexOption.IGNORE_CASE)
+        segments = regex.find(string)?.groupValues ?: emptyList()
+        return segments.isNotEmpty()
+    }
+
+    override fun canProvideMinute(): Boolean {
+        return true
+    }
+
+    override fun getMinuteElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideHour(): Boolean {
+        return true
+    }
+
+    override fun getHourElement(): String {
+        return 0.toString()
+    }
+
+    override fun canProvideDayNumber(): Boolean {
+        return true
+    }
+
+    override fun getDayNumberElement(): String {
+        return 1.toString()
+    }
+
+    override fun canProvideMonth(): Boolean {
+        return true
+    }
+
+    override fun getMonthElement(): String {
+        return 1.toString()
+    }
+
+    override fun canProvideDayOfWeek(): Boolean {
+        return true
+    }
+
+    override fun getDayOfWeekElement(): String {
+        return "*"
+    }
+
+    override fun isMinuteElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isHourElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayNumberElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isMonthElementLocked(): Boolean {
+        return false
+    }
+
+    override fun isDayOfWeekElementLocked(): Boolean {
+        return false
+    }
+}
+
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/ExpressionElementProvider.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/ExpressionElementProvider.kt
deleted file mode 100644
index bee448d..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/ExpressionElementProvider.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package  io.github.yamilmedina.naturalkron
-
-internal interface ExpressionElementProvider {
-
-    fun matches(value: String): Boolean
-
-    fun canProvideSecond(): Boolean {
-        return false
-    }
-
-    val secondElement: String?
-        get() = null
-
-    fun canProvideMinute(): Boolean
-
-    val minuteElement: String?
-
-    fun canProvideHour(): Boolean
-
-    val hourElement: String?
-
-    fun canProvideDayNumber(): Boolean
-
-    val dayNumberElement: String?
-
-    fun canProvideMonth(): Boolean
-
-    val monthElement: String?
-
-    fun canProvideDayOfWeek(): Boolean
-
-    val dayOfWeekElement: String?
-
-    val isSecondElementLocked: Boolean
-        get() = false
-
-    val isMinuteElementLocked: Boolean
-
-    val isHourElementLocked: Boolean
-
-    val isDayNumberElementLocked: Boolean
-
-    val isMonthElementLocked: Boolean
-
-    val isDayOfWeekElementLocked: Boolean
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/KronExpression.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/KronExpression.kt
deleted file mode 100644
index 4146a1b..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/KronExpression.kt
+++ /dev/null
@@ -1,292 +0,0 @@
-package  io.github.yamilmedina.naturalkron
-
-import java.text.SimpleDateFormat
-import java.util.*
-import java.util.regex.Pattern
-
-class KronExpression {
-    var second: String? = null
-    var minute: String? = null
-    var hour: String? = null
-    var dayNumber: String? = null
-    var month: String? = null
-    var dayOfWeek: String? = null
-
-    internal constructor()
-
-    constructor(
-        second: String?,
-        minute: String?,
-        hour: String?,
-        dayNumber: String?,
-        month: String?,
-        dayOfWeek: String?
-    ) : super() {
-        this.second = second
-        this.minute = minute
-        this.hour = hour
-        this.dayNumber = dayNumber
-        this.month = month
-        this.dayOfWeek = dayOfWeek
-    }
-
-    fun hasSecond(): Boolean {
-        return second != null
-    }
-
-    fun setSecond(second: String?): KronExpression {
-        this.second = second
-        return this
-    }
-
-    fun setSecond(second: Int): KronExpression {
-        return setSecond(second.toString())
-    }
-
-    fun hasMinute(): Boolean {
-        return minute != null
-    }
-
-    fun setMinute(minute: String?): KronExpression {
-        this.minute = minute
-        return this
-    }
-
-    fun setMinute(minute: Int): KronExpression {
-        return setMinute(minute.toString())
-    }
-
-    fun hasHour(): Boolean {
-        return hour != null
-    }
-
-    fun setHour(hour: String?): KronExpression {
-        this.hour = hour
-        return this
-    }
-
-    fun setHour(minute: Int): KronExpression {
-        return setHour(minute.toString())
-    }
-
-    fun hasDayNumber(): Boolean {
-        return dayNumber != null
-    }
-
-    fun setDayNumber(dayNumber: String?): KronExpression {
-        this.dayNumber = dayNumber
-        return this
-    }
-
-    fun setDayNumber(minute: Int): KronExpression {
-        return setDayNumber(minute.toString())
-    }
-
-    fun hasMonth(): Boolean {
-        return month != null
-    }
-
-    fun setMonth(month: String?): KronExpression {
-        this.month = month
-        return this
-    }
-
-    fun setMonth(minute: Int): KronExpression {
-        return setMonth(minute.toString())
-    }
-
-    fun hasDayOfWeek(): Boolean {
-        return dayOfWeek != null
-    }
-
-    fun setDayOfWeek(dayOfWeek: String?): KronExpression {
-        this.dayOfWeek = dayOfWeek
-        return this
-    }
-
-    fun setDayOfWeek(minute: Int): KronExpression {
-        return setDayOfWeek(minute.toString())
-    }
-
-    fun hasNothing(): Boolean {
-        return !hasMinute() && !hasHour() && !hasDayNumber() && !hasMonth() && !hasDayOfWeek()
-    }
-
-    fun toNaturalLanguage(): String? {
-        if (!hasNothing()) {
-            // check if it's one of our static expressions
-            val expression = toString()
-            if (expression == "0 0 0 1 1 *") return "yearly"
-            if (expression == "0 0 0 1 * *") return "monthly"
-            if (expression == "0 0 0 * * 0") return "weekly"
-            if (expression == "0 0 12 * * ?" || expression == "0 0 12 * * *") return "midday"
-            if (expression == "* * * * * ?" || expression == "* * * * * *") return "every second"
-            if (expression == "0 * * * * ?" || expression == "0 * * * * *") return "every minute"
-            if (expression == "0 0 * * * ?" || expression == "0 0 * * * *") return "hourly"
-            if (expression == "0 0 0 * * *") return "daily"
-
-            val b = StringBuilder()
-            b.append("every ")
-
-            val timeSetPattern = Pattern.compile("^([1-9]|[0-5][0-9])$")
-            val timeStepSetPattern = Pattern.compile("^\\*\\/([1-9]|[0-5][0-9])$")
-
-            // minutes
-            if (timeSetPattern.matcher(minute).matches()) {
-                if (hour == "*") {
-                    b.append(minute).append(" past the hour")
-                }
-                // let hour capture the minute if set
-            }
-
-            if (timeStepSetPattern.matcher(minute).matches()) {
-                if (hour == "*") {
-                    b.append(minute!!.replace("[^0-9]".toRegex(), "")).append(" minutes")
-                }
-            }
-
-            // hours
-            if (timeSetPattern.matcher(hour).matches()) {
-                val h = hour!!.toInt()
-
-                if (minute == "*") {
-                    b.append("minute past ")
-                        .append(if (h >= 12) h - 12 else h)
-                        .append(if (h >= 12) "pm" else "am")
-                } else if (timeStepSetPattern.matcher(minute).matches()) {
-                    val m = minute!!.replace("[^0-9]".toRegex(), "").toInt()
-                    b.append(m)
-                        .append(" minutes past ")
-                        .append(if (h >= 12) h - 12 else h)
-                        .append(if (h >= 12) "pm" else "am")
-                } else if (timeSetPattern.matcher(minute).matches()) {
-                    val m = minute!!.replace("[^0-9]".toRegex(), "").toInt()
-                    if (dayNumber == "*") {
-                        b.append("day at ")
-                    }
-                    b.append(if (h >= 12) h - 12 else h)
-                        .append(":")
-                        .append(if (m < 10) "0$m" else m)
-                        .append(if (h >= 12) "pm" else "am")
-                } else if (minute == "0") {
-                    if (dayNumber == "*") {
-                        b.append("day at ")
-                    }
-                    b.append(if (h >= 12) h - 12 else h)
-                        .append(if (h >= 12) "pm" else "am")
-                }
-            }
-
-            if (timeStepSetPattern.matcher(hour).matches()) {
-                b.append(hour!!.replace("[^0-9]".toRegex(), "")).append(" hours")
-            }
-
-            // day of month
-            if (dayNumber != "*") {
-                val dayOfMonthSetPattern = Pattern.compile("^([1-9]|[1-2][0-9]|3[0-1])$")
-                if (dayOfMonthSetPattern.matcher(dayNumber).matches()) {
-                    b.append(
-                        String.format(
-                            "%s%s", dayNumber, KronExpression.Companion.getDayNumberSuffix(
-                                dayNumber!!.toInt()
-                            )
-                        )
-                    )
-                }
-            }
-
-            // month
-            if (month != "*") {
-                val monthSetPattern = Pattern.compile("^([1-9]|1[0-2])$")
-                if (monthSetPattern.matcher(month).matches()) {
-                    val c = Calendar.getInstance()
-                    c[Calendar.MONTH] = month!!.toInt() - 1
-                    val sdf = SimpleDateFormat("MMMM")
-                    if (dayNumber != "*") {
-                        b.append(" of ")
-                    }
-                    b.append(sdf.format(c.time))
-                }
-            }
-
-            // day of week
-            val daySetPattern = Pattern.compile("[0-6]")
-            if (daySetPattern.matcher(dayOfWeek).matches()) {
-                val c = Calendar.getInstance()
-                c[Calendar.DAY_OF_WEEK] = dayOfWeek!!.toInt() + 1
-                val sdf = SimpleDateFormat("EEEE")
-
-                if (b.toString().endsWith("minutes")) {
-                    b.append(" on ")
-                        .append(sdf.format(c.time))
-                        .append("s")
-                } else {
-                    b.append(sdf.format(c.time))
-                }
-            }
-
-            return b.toString()
-        }
-
-        return null
-    }
-
-    override fun toString(): String {
-        return String.format(
-            "%s %s %s %s %s %s",
-            if (hasSecond()) second else 0,
-            if (hasMinute()) minute else 0,
-            if (hasHour()) hour else 0,
-            if (hasDayNumber()) dayNumber else '*',
-            if (hasMonth()) month else '*',
-            if (hasDayOfWeek()) dayOfWeek else '*'
-        )
-    }
-
-    companion object {
-        /**
-         * Returns a CronExpression for the specified cron expression string.
-         *
-         * @param expression A cron string
-         * @return A CronExpression
-         */
-        fun fromExpression(expression: String?): KronExpression? {
-            if (expression != null) {
-                val parts = expression.split("\\s".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
-                if (parts.size != 6) {
-                    throw KronParserException("Unexpected expression, expected 5 parts, e.g. * * * * * *.")
-                }
-
-                val kronExpression = KronExpression()
-                kronExpression.setSecond(parts[0])
-                kronExpression.setMinute(parts[1])
-                kronExpression.setHour(parts[2])
-                kronExpression.setDayNumber(parts[3])
-                kronExpression.setMonth(parts[4])
-                kronExpression.setDayOfWeek(parts[5])
-
-                return kronExpression
-            }
-
-            return null
-        }
-
-        /**
-         * Return a valid suffix for a positional number
-         *
-         * @param day
-         * @return
-         */
-        private fun getDayNumberSuffix(day: Int): String {
-            if (day in 11..13) {
-                return "th"
-            }
-            return when (day % 10) {
-                1 -> "st"
-                2 -> "nd"
-                3 -> "rd"
-                else -> "th"
-            }
-        }
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/KronParserException.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/KronParserException.kt
deleted file mode 100644
index 21f7585..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/KronParserException.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package  io.github.yamilmedina.naturalkron
-
-data class KronParserException(override val message: String) : IllegalArgumentException(message)
\ No newline at end of file
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/NaturalKronExpressionParser.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/NaturalKronExpressionParser.kt
deleted file mode 100644
index c281718..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/NaturalKronExpressionParser.kt
+++ /dev/null
@@ -1,168 +0,0 @@
-package io.github.yamilmedina.naturalkron
-
-import io.github.yamilmedina.naturalkron.elementprovider.DayNumber
-import io.github.yamilmedina.naturalkron.elementprovider.hour.Base12Hour
-import io.github.yamilmedina.naturalkron.elementprovider.hour.Base12HourShort
-import io.github.yamilmedina.naturalkron.elementprovider.hour.Base24Hour
-import io.github.yamilmedina.naturalkron.elementprovider.hour.Midnight
-import io.github.yamilmedina.naturalkron.elementprovider.hour.Noon
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryDay
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryDayNumberStep
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryHour
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryMinute
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryMonth
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryMonthStep
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryWeek
-import io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryYear
-import java.util.*
-import java.util.regex.Pattern
-
-class NaturalKronExpressionParser {
-    private val mappings: MutableMap<String, KronExpression> by lazy {
-        mutableMapOf<String, KronExpression>().also {
-            it["yearly"] = KronExpression("0", "0", "0", "1", "1", "*")
-            it["annually"] = KronExpression("0", "0", "0", "1", "1", "*")
-            it["monthly"] = KronExpression("0", "0", "0", "1", "*", "*")
-            it["weekly"] = KronExpression("0", "0", "0", "*", "*", "0")
-            it["midnight"] = KronExpression("0", "0", "0", "*", "*", "*")
-            it["daily"] = KronExpression("0", "0", "0", "*", "*", "*")
-            it["hourly"] = KronExpression("0", "0", "*", "*", "*", "*")
-            it["midday"] = KronExpression("0", "0", "12", "*", "*", "*")
-        }
-    }
-    private val elementProviders: List<ExpressionElementProvider> = listOf(
-        EveryYear(),
-        EveryMonth(),
-        EveryMonthStep(),
-        EveryWeek(),
-        io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryDayNumber(),
-        EveryDayNumberStep(),
-        EveryDay(),
-        EveryHour(),
-        EveryMinute(),
-        DayNumber(),
-        Noon(),
-        Midnight(),
-        Base12Hour(),
-        Base12HourShort(),
-        Base24Hour(),
-    )
-
-    /**
-     * Parses a natural language expression into a KronExpression
-     *
-     * @param input the natural language expression
-     * @return the KronExpression
-     * @throws KronParserException if the input cannot be parsed
-     */
-    fun parse(input: String): KronExpression {
-        val rawExpression = input.lowercase(Locale.getDefault())
-        if (mappings[rawExpression] != null) {
-            // return a 'cached' value of common expressions
-            return mappings[rawExpression]!!
-        }
-
-        val kronExpression = KronExpression()
-        var isSecondElementLocked = false
-        var isMinuteElementLocked = false
-        var isHourElementLocked = false
-        var isDayNumberElementLocked = false
-        var isMonthElementLocked = false
-        var isDayOfWeekElementLocked = false
-
-        elementProviders.forEach { elementProvider ->
-            if (elementProvider.matches(input)) {
-                if (shouldUpdateSecond(elementProvider, isSecondElementLocked)) {
-                    kronExpression.setSecond(elementProvider.secondElement)
-                }
-
-                if (shouldUpdateMinute(elementProvider, isMinuteElementLocked)) {
-                    kronExpression.setMinute(elementProvider.minuteElement)
-                }
-
-                if (shouldUpdateHour(elementProvider, isHourElementLocked)) {
-                    kronExpression.setHour(elementProvider.hourElement)
-                }
-
-                if (shouldUpdateDayNumber(elementProvider, isDayNumberElementLocked)) {
-                    kronExpression.setDayNumber(elementProvider.dayNumberElement)
-                }
-
-                if (shouldUpdateMonth(elementProvider, isMonthElementLocked)) {
-                    kronExpression.setMonth(elementProvider.monthElement)
-                }
-
-                if (shouldUpdateDayOfWeek(elementProvider, isDayOfWeekElementLocked)) {
-                    kronExpression.setDayOfWeek(elementProvider.dayOfWeekElement)
-                }
-
-                if (elementProvider.isSecondElementLocked) {
-                    isSecondElementLocked = true
-                }
-
-                if (elementProvider.isMinuteElementLocked) {
-                    isMinuteElementLocked = true
-                }
-
-                if (elementProvider.isHourElementLocked) {
-                    isHourElementLocked = true
-                }
-
-                if (elementProvider.isDayNumberElementLocked) {
-                    isDayNumberElementLocked = true
-                }
-
-                if (elementProvider.isMonthElementLocked) {
-                    isMonthElementLocked = true
-                }
-
-                if (elementProvider.isDayOfWeekElementLocked) {
-                    isDayOfWeekElementLocked = true
-                }
-            }
-        }
-
-        val isValidKronExpression = pattern.matcher(kronExpression.toString())
-        if (kronExpression.hasNothing() || !isValidKronExpression.matches()) {
-            throw KronParserException(String.format("Unable to parse \"%s\"", input))
-        }
-
-        return kronExpression
-    }
-
-    internal companion object {
-        private const val VALID_PATTERN: String =
-            "^(((?:[1-9]?\\d|\\*)\\s*(?:(?:[\\/-][1-9]?\\d)|(?:,[1-9]?\\d)+)?\\s*){6})$"
-        private val pattern: Pattern = Pattern.compile(VALID_PATTERN)
-
-        private fun shouldUpdateSecond(subParser: ExpressionElementProvider, isSecondElementLocked: Boolean): Boolean {
-            return subParser.canProvideSecond() && !isSecondElementLocked
-        }
-
-        private fun shouldUpdateMinute(subParser: ExpressionElementProvider, isMinuteElementLocked: Boolean): Boolean {
-            return subParser.canProvideMinute() && !isMinuteElementLocked
-        }
-
-        private fun shouldUpdateHour(subParser: ExpressionElementProvider, isHourElementLocked: Boolean): Boolean {
-            return subParser.canProvideHour() && !isHourElementLocked
-        }
-
-        private fun shouldUpdateDayNumber(
-            subParser: ExpressionElementProvider,
-            isDayNumberElementLocked: Boolean
-        ): Boolean {
-            return subParser.canProvideDayNumber() && !isDayNumberElementLocked
-        }
-
-        private fun shouldUpdateMonth(subParser: ExpressionElementProvider, isMonthElementLocked: Boolean): Boolean {
-            return subParser.canProvideMonth() && !isMonthElementLocked
-        }
-
-        private fun shouldUpdateDayOfWeek(
-            subParser: ExpressionElementProvider,
-            isDayOfWeekElementLocked: Boolean
-        ): Boolean {
-            return subParser.canProvideDayOfWeek() && !isDayOfWeekElementLocked
-        }
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/DayNumber.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/DayNumber.kt
deleted file mode 100644
index bda299b..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/DayNumber.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal class DayNumber : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return true
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override fun canProvideHour(): Boolean {
-        return true
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override fun canProvideDayNumber(): Boolean {
-        return true
-    }
-
-    override val dayNumberElement: String?
-        get() = if (segments.size >= 1) segments[1] else null
-
-    override val isDayNumberElementLocked: Boolean
-        get() = true
-
-    override fun canProvideMonth(): Boolean {
-        return segments.size > 0
-    }
-
-    override val monthElement: String
-        get() = "*"
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return true
-    }
-
-    override val dayOfWeekElement: String
-        get() = "*"
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN = "([0-9]?[0-9])(st|nd|rd|th)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base12Hour.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base12Hour.kt
deleted file mode 100644
index 74cffa3..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base12Hour.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.hour
-
-import java.util.*
-import java.util.regex.Pattern
-
-internal open class Base12Hour : Base24Hour() {
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                if (m.group(i) != null) {
-                    segments.add(m.group(i))
-                }
-            }
-        }
-        return segments.size > 0
-    }
-
-    override val hourElement: String?
-        get() {
-            if (segments.size > 3) {
-                if (segments[3].lowercase(Locale.getDefault()) == "pm" && segments[1].toInt() < 12) {
-                    return (segments[1].toInt() + 12).toString()
-                } else if (segments[3].lowercase(Locale.getDefault()) == "am" && segments[1].toInt() == 12) {
-                    return "0"
-                }
-            }
-            return if (segments.size > 1) segments[1] else null
-        }
-
-    companion object {
-        private const val PATTERN = "(1[012]|[1-9]):([0-5][0-9])?(?i)\\s?(am|pm)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base12HourShort.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base12HourShort.kt
deleted file mode 100644
index 0643e46..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base12HourShort.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.hour
-
-import java.util.*
-import java.util.regex.Pattern
-
-internal class Base12HourShort : Base12Hour() {
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                if (m.group(i) != null) {
-                    segments.add(m.group(i))
-                }
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return segments.size > 1
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override val hourElement: String?
-        get() {
-            if (segments.size > 2) {
-                if (segments[2].lowercase(Locale.getDefault()) == "pm" && segments[1].toInt() < 12) {
-                    return (segments[1].toInt() + 12).toString()
-                } else if (segments[2].lowercase(Locale.getDefault()) == "am" && segments[1].toInt() == 12) {
-                    return "0"
-                }
-            }
-            return if (segments.size > 1) segments[1] else null
-        }
-
-    companion object {
-        private const val PATTERN = "(1[012]|[1-9])\\s?(?i)\\s?(am|pm)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base24Hour.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base24Hour.kt
deleted file mode 100644
index 8af6118..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Base24Hour.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.hour
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal open class Base24Hour : ExpressionElementProvider {
-
-    @JvmField
-    protected var segments: MutableList<String> = mutableListOf()
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                if (m.group(i) != null) {
-                    segments.add(m.group(i))
-                }
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return segments.size > 2
-    }
-
-    override val minuteElement: String?
-        get() = if (segments.size > 2) segments[2].toInt().toString() else null
-
-    override fun canProvideHour(): Boolean {
-        return segments.size > 1
-    }
-
-    override val hourElement: String?
-        get() = if (segments.size > 1) segments[1].toInt().toString() else null
-
-    override fun canProvideDayNumber(): Boolean {
-        return false
-    }
-
-    override val dayNumberElement: String?
-        get() = null
-
-    override fun canProvideMonth(): Boolean {
-        return false
-    }
-
-    override val monthElement: String?
-        get() = null
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return false
-    }
-
-    override val dayOfWeekElement: String?
-        get() = null
-
-    override val isMinuteElementLocked: Boolean
-        get() = canProvideMinute()
-
-    override val isHourElementLocked: Boolean
-        get() = canProvideHour()
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN = "(2[0-3]|[01]?[0-9]):([0-5]?[0-9])"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Midnight.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Midnight.kt
deleted file mode 100644
index 18d8149..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Midnight.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.hour
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.*
-
-
-internal open class Midnight : ExpressionElementProvider {
-    protected var match: Boolean = false
-
-    override fun matches(value: String): Boolean {
-        match = value != null && value.lowercase(Locale.getDefault()).indexOf("midnight") >= 0
-        return match
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return match
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override fun canProvideHour(): Boolean {
-        return match
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override fun canProvideDayNumber(): Boolean {
-        return false
-    }
-
-    override val dayNumberElement: String?
-        get() = null
-
-    override fun canProvideMonth(): Boolean {
-        return false
-    }
-
-    override val monthElement: String?
-        get() = null
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return false
-    }
-
-    override val dayOfWeekElement: String?
-        get() = null
-
-    override val isMinuteElementLocked: Boolean
-        get() = canProvideMinute()
-
-    override val isHourElementLocked: Boolean
-        get() = canProvideHour()
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Noon.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Noon.kt
deleted file mode 100644
index 9d1af51..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/hour/Noon.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.hour
-
-import java.util.*
-
-internal class Noon : Midnight() {
-    override fun matches(value: String): Boolean {
-        match = value != null &&
-                (value.lowercase(Locale.getDefault()).indexOf("noon") >= 0 || value.lowercase(Locale.getDefault())
-                    .indexOf("midday") >= 0)
-        return match
-    }
-
-    override val hourElement: String
-        get() = "12"
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDay.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDay.kt
deleted file mode 100644
index adad6f2..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDay.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.*
-import java.util.regex.Pattern
-
-internal class EveryDay : ExpressionElementProvider {
-
-    private val segments: MutableList<String> = mutableListOf()
-    private val dayMap: MutableMap<String, String> by lazy {
-        mutableMapOf(
-            "sunday" to "0",
-            "monday" to "1",
-            "tuesday" to "2",
-            "wednesday" to "3",
-            "thursday" to "4",
-            "friday" to "5",
-            "saturday" to "6"
-        )
-    }
-
-    override fun matches(value: String): Boolean {
-        val m = dayOfWeekPattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return true
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override fun canProvideHour(): Boolean {
-        return true
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override fun canProvideDayNumber(): Boolean {
-        return true
-    }
-
-    override val dayNumberElement: String
-        get() = "*"
-
-    override fun canProvideMonth(): Boolean {
-        return true
-    }
-
-    override val monthElement: String
-        get() = "*"
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return segments.size > 0
-    }
-
-    override val dayOfWeekElement: String?
-        get() {
-            if ((segments.size > 0 && segments[0] == "daily") || (segments.size > 3 && segments[3] == "day")) {
-                return "*"
-            }
-            return if (segments.size > 3 && dayMap[segments[3].lowercase(Locale.getDefault())] != null) dayMap[segments[3].lowercase(
-                Locale.getDefault()
-            )]
-            else null
-        }
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = true
-
-    companion object {
-        private const val DAY_OF_WEEK_PATTERN =
-            "(daily|(every|each|on)\\s(day|monday|tuesday|wednesday|thursday|friday|saturday|sunday)(?s))"
-        private val dayOfWeekPattern: Pattern = Pattern.compile(DAY_OF_WEEK_PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDayNumber.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDayNumber.kt
deleted file mode 100644
index ccc4b14..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDayNumber.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.*
-import java.util.regex.Pattern
-
-internal class EveryDayNumber : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-    private val monthMap: MutableMap<String, String> by lazy {
-        mutableMapOf(
-            "january" to "1",
-            "february" to "2",
-            "march" to "3",
-            "april" to "4",
-            "may" to "5",
-            "june" to "6",
-            "july" to "7",
-            "august" to "8",
-            "september" to "9",
-            "october" to "10",
-            "november" to "11",
-            "december" to "12"
-        )
-    }
-
-    override fun matches(value: String): Boolean {
-        val m = io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryDayNumber.Companion.pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return true
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override fun canProvideHour(): Boolean {
-        return true
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override fun canProvideDayNumber(): Boolean {
-        return true
-    }
-
-    override val dayNumberElement: String?
-        get() = if (segments.size >= 3) segments[2] else null
-
-    override fun canProvideMonth(): Boolean {
-        return segments.size >= 5
-    }
-
-    override val monthElement: String?
-        get() = if (segments.size >= 5 && monthMap[segments[4].lowercase(Locale.getDefault())] != null
-        ) monthMap[segments[4].lowercase(Locale.getDefault())]
-        else null
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return true
-    }
-
-    override val dayOfWeekElement: String
-        get() = "*"
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = canProvideMonth()
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN =
-            "(every|each)\\s([0-9]?[0-9])(st|nd|rd|th)\\sof\\s(month|january|february|march|april|may|june|july|august|september|october|november|december)"
-        private val pattern: Pattern = Pattern.compile(io.github.yamilmedina.naturalkron.elementprovider.recurring.EveryDayNumber.Companion.PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDayNumberStep.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDayNumberStep.kt
deleted file mode 100644
index 6ab673b..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryDayNumberStep.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal class EveryDayNumberStep : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return true
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override fun canProvideHour(): Boolean {
-        return true
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override fun canProvideDayNumber(): Boolean {
-        return segments.size == 3
-    }
-
-    override val dayNumberElement: String
-        get() = if (segments.size > 2) String.format("*/%s", segments[2]) else "*"
-
-    override fun canProvideMonth(): Boolean {
-        return true
-    }
-
-    override val monthElement: String
-        get() = "*"
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return true
-    }
-
-    override val dayOfWeekElement: String
-        get() = "*"
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override val isDayNumberElementLocked: Boolean
-        get() = true
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN = "(every\\s?(3[0-1]|2[0-9]|1[1-9]|[1-9])?\\s?days)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryHour.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryHour.kt
deleted file mode 100644
index d27bc37..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryHour.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal class EveryHour : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                if (m.group(i) != null) {
-                    segments.add(m.group(i))
-                }
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return false
-    }
-
-    override val minuteElement: String?
-        get() = null
-
-    override fun canProvideHour(): Boolean {
-        return segments.size > 0
-    }
-
-    override val hourElement: String?
-        get() {
-            if (canProvideHour()) {
-                return if (segments.size > 3) String.format("*/%s", segments[3]) else "*"
-            }
-            return null
-        }
-
-    override fun canProvideDayNumber(): Boolean {
-        return false
-    }
-
-    override val dayNumberElement: String?
-        get() = null
-
-    override fun canProvideMonth(): Boolean {
-        return false
-    }
-
-    override val monthElement: String?
-        get() = null
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return false
-    }
-
-    override val dayOfWeekElement: String?
-        get() = null
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override val isHourElementLocked: Boolean
-        get() = true
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN = "(hourly|(every|each) ?([0-9]+)?\\shour)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMinute.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMinute.kt
deleted file mode 100644
index 92f6c2e..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMinute.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal class EveryMinute : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                if (m.group(i) != null) {
-                    segments.add(m.group(i))
-                }
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return segments.size > 0
-    }
-
-    override val minuteElement: String?
-        get() {
-            if (canProvideMinute()) {
-                return if (segments.size > 3) String.format("*/%s", segments[3]) else "*"
-            }
-            return null
-        }
-
-    override fun canProvideHour(): Boolean {
-        return canProvideMinute()
-    }
-
-    override val hourElement: String
-        get() = "*"
-
-    override fun canProvideDayNumber(): Boolean {
-        return false
-    }
-
-    override val dayNumberElement: String?
-        get() = null
-
-    override fun canProvideMonth(): Boolean {
-        return false
-    }
-
-    override val monthElement: String?
-        get() = null
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return false
-    }
-
-    override val dayOfWeekElement: String?
-        get() = null
-
-    override val isMinuteElementLocked: Boolean
-        get() = true
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN = "((every|each) ?([0-9]+)?\\sminute)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMonth.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMonth.kt
deleted file mode 100644
index 1b0f5d7..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMonth.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.*
-import java.util.regex.Pattern
-
-internal class EveryMonth : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-    private val monthMap: MutableMap<String, String?> by lazy {
-        mutableMapOf(
-            "january" to "1",
-            "february" to "2",
-            "march" to "3",
-            "april" to "4",
-            "may" to "5",
-            "june" to "6",
-            "july" to "7",
-            "august" to "8",
-            "september" to "9",
-            "october" to "10",
-            "november" to "11",
-            "december" to "12"
-        )
-    }
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return true
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override fun canProvideHour(): Boolean {
-        return true
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override fun canProvideDayNumber(): Boolean {
-        return true
-    }
-
-    override val dayNumberElement: String
-        get() = "1"
-
-    override fun canProvideMonth(): Boolean {
-        return true
-    }
-
-    override val monthElement: String?
-        get() {
-            if ((segments.size > 0 && segments[0] == "monthly")
-                || (segments.size > 3 && segments[3] == "month")
-            ) {
-                return "*"
-            }
-            return if (segments.size > 3 && monthMap[segments[3].lowercase(Locale.getDefault())] != null
-            ) monthMap[segments[3].lowercase(Locale.getDefault())]
-            else null
-        }
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return true
-    }
-
-    override val dayOfWeekElement: String
-        get() = "*"
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN =
-            "(monthly|(every|each)\\s(month|january|february|march|april|may|june|july|august|september|october|november|december))"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMonthStep.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMonthStep.kt
deleted file mode 100644
index 5a7df18..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryMonthStep.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal class EveryMonthStep : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return true
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override fun canProvideHour(): Boolean {
-        return true
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override fun canProvideDayNumber(): Boolean {
-        return true
-    }
-
-    override val dayNumberElement: String
-        get() = "0"
-
-    override fun canProvideMonth(): Boolean {
-        return segments.size == 3
-    }
-
-    override val monthElement: String
-        get() = if (segments.size > 2) String.format("*/%s", segments[2]) else "*"
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return true
-    }
-
-    override val dayOfWeekElement: String
-        get() = "*"
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override val isDayNumberElementLocked: Boolean
-        get() = true
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN = "(every\\s?(1[1-2]|[1-9])?\\s?months)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryWeek.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryWeek.kt
deleted file mode 100644
index f9960ac..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryWeek.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal class EveryWeek : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute(): Boolean {
-        return true
-    }
-
-    override val minuteElement: String
-        get() = "0"
-
-    override fun canProvideHour(): Boolean {
-        return true
-    }
-
-    override val hourElement: String
-        get() = "0"
-
-    override fun canProvideDayNumber(): Boolean {
-        return true
-    }
-
-    override val dayNumberElement: String
-        get() = "*"
-
-    override fun canProvideMonth(): Boolean {
-        return true
-    }
-
-    override val monthElement: String
-        get() = "*"
-
-    override fun canProvideDayOfWeek(): Boolean {
-        return segments.size > 0
-    }
-
-    override val dayOfWeekElement: String
-        get() = "0"
-
-    override val isMinuteElementLocked: Boolean
-        get() = false
-
-    override val isHourElementLocked: Boolean
-        get() = false
-
-    override val isDayNumberElementLocked: Boolean
-        get() = false
-
-    override val isMonthElementLocked: Boolean
-        get() = false
-
-    override val isDayOfWeekElementLocked: Boolean
-        get() = false
-
-    companion object {
-        private const val PATTERN = "(weekly|(every|each)\\sweek)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryYear.kt b/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryYear.kt
deleted file mode 100644
index dd4355f..0000000
--- a/src/main/kotlin/io/github/yamilmedina/naturalkron/elementprovider/recurring/EveryYear.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package  io.github.yamilmedina.naturalkron.elementprovider.recurring
-
-import  io.github.yamilmedina.naturalkron.ExpressionElementProvider
-import java.util.regex.Pattern
-
-internal class EveryYear : ExpressionElementProvider {
-    private val segments: MutableList<String> = mutableListOf()
-    override fun matches(value: String): Boolean {
-        val m = pattern.matcher(value)
-        while (m.find()) {
-            for (i in 0..m.groupCount()) {
-                segments.add(m.group(i))
-            }
-        }
-        return segments.size > 0
-    }
-
-    override fun canProvideMinute() = true
-
-    override val minuteElement = "0"
-
-    override fun canProvideHour() = true
-
-    override val hourElement = "0"
-
-    override fun canProvideDayNumber() = true
-
-    override val dayNumberElement = "1"
-
-    override fun canProvideMonth() = true
-
-    override val monthElement = "1"
-
-    override fun canProvideDayOfWeek() = true
-
-    override val dayOfWeekElement = "*"
-
-    override val isMinuteElementLocked = false
-
-    override val isHourElementLocked = false
-
-    override val isDayNumberElementLocked = false
-
-    override val isMonthElementLocked = false
-
-    override val isDayOfWeekElementLocked = false
-
-    companion object {
-        private const val PATTERN = "(yearly|annually|(every|each) ?([0-9]+)?\\s?year)"
-        private val pattern: Pattern = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE)
-    }
-}
diff --git a/src/test/kotlin/io/github/yamilmedina/kron/NaturalKronParserTest.kt b/src/test/kotlin/io/github/yamilmedina/kron/NaturalKronParserTest.kt
new file mode 100644
index 0000000..aaedff5
--- /dev/null
+++ b/src/test/kotlin/io/github/yamilmedina/kron/NaturalKronParserTest.kt
@@ -0,0 +1,71 @@
+package io.github.yamilmedina.kron
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.EnumSource
+
+class NaturalKronParserTest {
+
+    @ParameterizedTest
+    @EnumSource(TestParams::class)
+    fun testParser(params: TestParams) {
+        val actual = io.github.yamilmedina.kron.NaturalKronParser().parse(params.input)
+        assertEquals(params.expected, actual.toString(), "Failed for input: <${params.input}>")
+    }
+
+    companion object {
+
+        enum class TestParams(val input: String, val expected: String) {
+            CASE_1("@yearly", "0 0 1 1 *"),
+            CASE_2("@annually", "0 0 1 1 *"),
+            CASE_3("@monthly", "0 0 1 * *"),
+            CASE_4("@weekly", "0 0 * * 0"),
+            CASE_5("@daily", "0 0 * * *"),
+            CASE_6("@midnight", "0 0 * * *"),
+            CASE_7("@hourly", "0 * * * *"),
+            CASE_8("each day", "0 0 * * *"),
+            CASE_9("every day", "0 0 * * *"),
+            CASE_10("daily", "0 0 * * *"),
+            CASE_11("every day at 3 AM", "0 3 * * *"),
+            CASE_12("5am", "0 5 * * *"),
+            CASE_13("daily at 5am", "0 5 * * *"),
+            CASE_14("every friday at 5am", "0 5 * * 5"),
+            CASE_15("daily at 17:30", "30 17 * * *"),
+            CASE_16("every week", "0 0 * * 0"),
+            CASE_17("weekly", "0 0 * * 0"),
+            CASE_18("every minute", "* * * * *"),
+            CASE_19("every 5 minutes", "*/5 * * * *"),
+            CASE_20("every 30 minutes", "*/30 * * * *"),
+            CASE_21("every month", "0 0 1 * *"),
+            CASE_22("monthly", "0 0 1 * *"),
+            CASE_23("every Monday", "0 0 * * 1"),
+            CASE_24("every Wednesday", "0 0 * * 3"),
+            CASE_25("every Friday", "0 0 * * 5"),
+            CASE_26("every hour", "0 * * * *"),
+            CASE_27("every 6 hours", "0 */6 * * *"),
+            CASE_28("hourly", "0 * * * *"),
+            CASE_29("every year", "0 0 1 1 *"),
+            CASE_30("yearly", "0 0 1 1 *"),
+            CASE_31("annually", "0 0 1 1 *"),
+            CASE_32("every day at 9am", "0 9 * * *"),
+            CASE_33("every day at 5pm", "0 17 * * *"),
+            CASE_34("every day at 5:45pm", "45 17 * * *"),
+            CASE_35("every day at 17:00", "0 17 * * *"),
+            CASE_36("every day at 17:25", "25 17 * * *"),
+            CASE_37("5:15am every Tuesday", "15 5 * * 2"),
+            CASE_38("7pm every Thursday", "0 19 * * 4"),
+            CASE_39("every May", "0 0 1 5 *"),
+            CASE_40("every december", "0 0 1 12 *"),
+            CASE_41("midnight", "0 0 * * *"),
+            CASE_42("midnight on tuesdays", "0 0 * * 2"),
+            CASE_43("every 5 minutes on Tuesdays", "*/5 * * * 2"),
+            CASE_44("noon", "0 12 * * *"),
+            CASE_45("every 25th", "0 0 25 * *"),
+            CASE_46("every 3rd of January", "0 0 3 1 *"),
+            CASE_47("11pm every 3rd of december", "0 23 3 12 *"),
+            CASE_48("every day at 17:01", "1 17 * * *"),
+            CASE_49("every day at 5:01pm", "1 17 * * *"),
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/kotlin/io/github/yamilmedina/naturalkron/NaturalKronExpressionParserTest.kt b/src/test/kotlin/io/github/yamilmedina/naturalkron/NaturalKronExpressionParserTest.kt
deleted file mode 100644
index a6d215f..0000000
--- a/src/test/kotlin/io/github/yamilmedina/naturalkron/NaturalKronExpressionParserTest.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package io.github.yamilmedina.naturalkron
-
-import java.time.DayOfWeek
-import org.junit.jupiter.api.assertDoesNotThrow
-import org.junit.jupiter.api.assertThrows
-import kotlin.test.Test
-import kotlin.test.assertEquals
-
-class NaturalKronExpressionParserTest {
-
-    @Test
-    fun `given every monday is parsed, then returns every monday expression`() {
-        val expression = "every monday"
-        val parsed = NaturalKronExpressionParser().parse(expression)
-
-        assertEquals(expression, parsed.toNaturalLanguage()?.lowercase())
-        assertEquals(DayOfWeek.MONDAY.value.toString(), parsed.dayOfWeek)
-    }
-
-    @Test
-    fun `given every friday at 1015am is parsed, then returns every monday expression`() {
-        val expression = "every friday at 10:15am"
-        val parsed = NaturalKronExpressionParser().parse(expression)
-
-        val expectedKronExpressionEveryFridayAt1015am = "0 15 10 * * 5"
-        assertEquals(DayOfWeek.FRIDAY.value.toString(), parsed.dayOfWeek)
-        assertEquals(expectedKronExpressionEveryFridayAt1015am, parsed.toString())
-    }
-
-    @Test
-    fun `given every day at 9am is parsed, then returns every day expression with date`() {
-        val expression = "every day at 9am"
-        val parsed = NaturalKronExpressionParser().parse(expression)
-
-        val expectedKronExpressionEveryFridayAt9am = "0 0 9 * * *"
-        assertEquals("*", parsed.dayOfWeek)
-        assertEquals(expectedKronExpressionEveryFridayAt9am, parsed.toString())
-    }
-
-    @Test
-    fun `given an invalid expression, then raise KronException`() {
-        val invalidExpression = "invalid expression"
-        assertThrows<KronParserException> {
-            NaturalKronExpressionParser().parse(invalidExpression)
-        }
-    }
-
-    @Test
-    fun `given a partial invalid expression, then parse valid part`() {
-        val invalidExpression = "invalid at 1st of the month"
-        assertDoesNotThrow {
-            val parsed = NaturalKronExpressionParser().parse(invalidExpression)
-            assertEquals("0 0 0 1 * *", parsed.toString())
-        }
-    }
-
-}
\ No newline at end of file