Kotlin: Convert string to date
Video: Kotlin: Convert string to date by Taught by Celeste AI - AI Coding Coach
Kotlin: Convert a String to a Date
SimpleDateFormatfor legacy code;LocalDate.parsefor everything new. Both work, but the modern API is type-safe, immutable, and thread-safe — pick the second when starting fresh.
A common task: turn "2024-06-15" into a date object you can compare, format, and arithmetic on. Copilot suggests the legacy approach first (because it has the most training data); the modern path is a cleaner fit.
The Copilot prompt
fun main() {
val dateString = "2024-06-15"
// Parse the string into a Date object using the format "yyyy-MM-dd"
Copilot generates:
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
fun main() {
val dateString = "2024-06-15"
val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
try {
val date: Date = format.parse(dateString)!!
println("Parsed date: $date")
} catch (e: Exception) {
println("Failed to parse date: ${e.message}")
}
}
Walkthrough
Three pieces.
1. Format string. "yyyy-MM-dd" describes the input layout: 4-digit year, dash, 2-digit month, dash, 2-digit day. The case matters — MM is month, mm is minute. Mix them up and you parse silently wrong.
2. Parse call. format.parse(dateString) returns a java.util.Date or null (if the locale doesn't reach a successful parse). The !! says "I'm confident this isn't null" — fine here for hardcoded valid input, dangerous for user input.
3. Try/catch. SimpleDateFormat.parse throws ParseException on malformed input. Always wrap in try/catch when the input could be bad.
Why this is the legacy path
SimpleDateFormat and java.util.Date are pre-Java-8. They have several footguns:
- Mutable.
Dateobjects can be modified —date.setTime(...). Sharing them across threads is unsafe. - Not thread-safe.
SimpleDateFormatitself is not thread-safe. Two threads using the sameSimpleDateFormatinstance produce wrong results or crash. - Magic numbers in calendar APIs.
Calendar.MONTHis 0-indexed (January = 0, December = 11). Off-by-one bugs. - No type for "just a date" or "just a time."
Dateis a timestamp; you can't say "I want June 15, no time component."
For new code, prefer java.time (Java 8+).
The modern way: java.time
import java.time.LocalDate
fun main() {
val dateString = "2024-06-15"
val date = LocalDate.parse(dateString) // ISO format by default
println("Parsed date: $date")
}
LocalDate.parse accepts ISO 8601 (yyyy-MM-dd) by default. No format string needed for the common case.
For non-ISO formats, supply a DateTimeFormatter:
import java.time.LocalDate
import java.time.format.DateTimeFormatter
val formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")
val date = LocalDate.parse("15/06/2024", formatter)
LocalDate is immutable, thread-safe, and self-describing (it's a date, not a timestamp). Same for LocalTime (just time), LocalDateTime (date + time, no zone), ZonedDateTime (date + time + zone), Instant (epoch).
Steering Copilot toward java.time
Copilot leans on SimpleDateFormat because that's what most legacy training data uses. To get the modern version, mention it explicitly:
// Parse the string into a LocalDate using java.time
or
// Parse "2024-06-15" into a LocalDate
Naming LocalDate in the comment usually triggers the right import and code path.
Error handling
The modern equivalent of "parse and handle errors":
fun parseDate(s: String): LocalDate? {
return try {
LocalDate.parse(s)
} catch (e: DateTimeParseException) {
null
}
}
val date = parseDate("2024-06-15")
val bad = parseDate("not-a-date")
println(date) // 2024-06-15
println(bad) // null
Returning null for invalid input is the Kotlin way. The caller decides what to do.
Date arithmetic
val today = LocalDate.now()
val birthday = LocalDate.of(1990, 5, 15)
val age = java.time.Period.between(birthday, today).years
val tomorrow = today.plusDays(1)
val nextMonth = today.plusMonths(1)
val yearFromNow = today.plusYears(1)
val daysSinceBirthday = java.time.temporal.ChronoUnit.DAYS.between(birthday, today)
LocalDate supports natural arithmetic — add days/months/years, calculate periods between two dates. Same on LocalTime, LocalDateTime, etc.
Formatting back to string
val date = LocalDate.of(2024, 6, 15)
println(date.toString()) // "2024-06-15" (ISO default)
println(date.format(DateTimeFormatter.ofPattern("MMM dd, yyyy"))) // "Jun 15, 2024"
println(date.format(DateTimeFormatter.ofPattern("EEEE"))) // "Saturday"
toString() produces ISO 8601 by default. Custom formats via DateTimeFormatter.ofPattern.
Common mistakes
Mixing mm (minutes) and MM (months). SimpleDateFormat("yyyy-mm-dd") is silently wrong — minutes will fill the month slot.
Not handling ParseException. User input is always potentially malformed.
Using SimpleDateFormat from multiple threads. Use ThreadLocal<SimpleDateFormat> or — better — switch to DateTimeFormatter, which is thread-safe.
Force-unwrapping format.parse(null). parse returns null for unparseable input on some locales; !! crashes.
Locale confusion. "Jan 15, 2024" parses with one locale, fails with another. Always specify Locale.ENGLISH (or Locale.ROOT) for machine-readable formats.
Common Copilot habits
When asked to "parse a date string":
- Default suggestion:
SimpleDateFormat. - Mentioning
LocalDatein the comment:java.time.LocalDate.parse. - Mentioning ISO 8601:
LocalDate.parse(no format string). - Mentioning thread safety or immutability:
DateTimeFormatter.
Naming the API in the comment is the lever. Without explicit mention, you get whatever Copilot considers the "most common" code, which often reflects training data from before Java 8.
What's next
Episode 4: Create a random date in Kotlin. Generate a random LocalDate between two bounds — useful for test data and generating plausible-looking dummy records.
Recap
SimpleDateFormat is the legacy parser — works, but mutable, not thread-safe, format-string typo-prone. LocalDate.parse(s) is the modern way — immutable, thread-safe, ISO 8601 default. For non-ISO formats, supply a DateTimeFormatter. Handle parse failures with try/catch returning null. Steer Copilot to the modern API by mentioning LocalDate or DateTimeFormatter in the comment.
Next episode: random date generation.