Natural language date/time parser for Kotlin/JVM. Zero dependencies.
val result = QuickAddParser.parse("Coffee with Sarah tomorrow at 3pm at Starbucks")
result.title // "Coffee with Sarah"
result.startDate // 2026-04-17
result.startTime // 15:00
result.location // "Starbucks"
result.emoji // "☕"
result.confidence // HIGH
repositories {
maven {
url = uri("https://maven.pkg.github.com/KashCal/parsely")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
implementation("org.onekash.parsely:parsely:0.1.0")
}
repositories {
maven {
url = uri("https://maven.pkg.github.com/KashCal/parsely")
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
implementation 'org.onekash.parsely:parsely:0.1.0'
}
Note: GitHub Packages requires authentication. Add gpr.user and gpr.key (a personal access token with read:packages scope) to your ~/.gradle/gradle.properties.
import org.onekash.parsely.QuickAddParser
// Dates and times
QuickAddParser.parse("Dentist Jan 15 at 2pm")
// title="Dentist", startDate=Jan 15, startTime=14:00
// Time ranges
QuickAddParser.parse("Team standup 9-10am")
// startTime=09:00, endTime=10:00
// Durations
QuickAddParser.parse("Workshop tomorrow for 2 hours")
// endTime = startTime + 2h
// Recurrence
QuickAddParser.parse("Yoga every Monday")
// rrule="FREQ=WEEKLY;BYDAY=MO"
// Locations (last "at" phrase after time)
QuickAddParser.parse("Dinner Friday at 7pm at The Italian Place")
// location="The Italian Place"
// Relative offsets
QuickAddParser.parse("Follow up in 3 days")
// startDate = today + 3
// Structured dates
QuickAddParser.parse("Report due 2026-01-15")
// startDate=2026-01-15
Parsing resolves relative dates against LocalDateTime.now(). For testing, pass a fixed reference:
val ref = LocalDateTime.of(2026, 1, 15, 9, 0)
val result = QuickAddParser.parse("Meeting next Friday at 2pm", reference = ref)
// startDate=2026-01-23, startTime=14:00
| Field |
Type |
Description |
title |
String |
Extracted event title (remaining text after parsing) |
startDate |
LocalDate |
Parsed date, or today if none found |
startTime |
LocalTime? |
Parsed time, or null for all-day |
endTime |
LocalTime? |
End time from range or duration |
location |
String? |
Location from trailing "at" phrase |
rrule |
String? |
RFC 5545 RRULE string |
emoji |
String? |
Matched emoji based on title keywords |
isAllDay |
Boolean |
True when no time was parsed |
confidence |
ParseConfidence |
HIGH (date+time), MEDIUM (date or time), LOW (title only) |
| Pattern |
Examples |
| Relative |
today, tomorrow, yesterday, day after tomorrow |
| Weekday |
Monday, next Friday, last Tuesday |
| Month + day |
January 15, Jan 15, 15th of March |
| Structured |
1/15, 2026-01-15, 15.01.2026, 1/15/26 |
| Offset |
in 3 days, 2 weeks ago, in 6 months |
| Pattern |
Examples |
| With meridiem |
3pm, 3:30pm, 3:30 PM, 3 pm |
| 24-hour |
15:00, 9:30 |
| Keywords |
noon, midnight |
| Ranges |
9-10am, 11am-1pm, 2pm to 4pm |
| Duration |
for 2 hours, for 30 minutes |
| Pattern |
Examples |
| Frequency |
daily, weekly, monthly, yearly, biweekly |
| Interval |
every 2 weeks, every 3 months |
| Weekday |
every Monday, every Friday |
| Pattern |
Examples |
| Location |
... at Conference Room A |
| Number words |
one, fifteen, twenty-five |
- JVM 11+
- Kotlin 1.9+ (or Java 11+)
Apache License 2.0. See LICENSE.