将ISO8601字符串转Epoch毫秒遇解析错误,求兼容API26的方案
Hey there! Let's tackle your problem head-on—you're hitting two key issues here: a parsing error caused by missing timezone information, and an API level 26 restriction on Instant.toEpochMilli(). Here's how to fix both, with solid options for different use cases:
Why the parsing error happens
Your input string 2019-02-15T00:00:00 doesn't include a timezone identifier (like Z for UTC or +08:00 for GMT+8). The Instant class expects explicit timezone data, so it chokes at index 19 (the end of the string) where it can't find that critical info.
Solution 1: Use ThreeTenABP (Recommended)
This is the official Android-compatible implementation of the JSR 310 date/time APIs (the same ones used in API 26+), and it works back to API 16. It's far more reliable than legacy classes like SimpleDateFormat.
Step 1: Add the dependency
Add this to your app-level build.gradle (or build.gradle.kts):
implementation 'com.jakewharton.threetenabp:threetenabp:1.4.6'
Step 2: Initialize the library
In your Application class:
class MyApplication : Application() { override fun onCreate() { super.onCreate() AndroidThreeTen.init(this) } }
Don't forget to declare this Application class in your AndroidManifest.xml if you haven't already.
Step 3: Parse and convert to milliseconds
import org.threeten.bp.LocalDateTime import org.threeten.bp.ZoneOffset import org.threeten.bp.ZonedDateTime val dateStr = "2019-02-15T00:00:00" // Parse the string into a LocalDateTime (since it has no timezone) val localDateTime = LocalDateTime.parse(dateStr) // Attach a timezone—use ZoneOffset.UTC for universal time, or ZoneId.systemDefault() for local timezone val zonedDateTime = localDateTime.atZone(ZoneOffset.UTC) // Convert to milliseconds since epoch val millisecondsSinceUnixEpoch = zonedDateTime.toInstant().toEpochMilli() println("EPOCH $millisecondsSinceUnixEpoch")
Solution 2: Use SimpleDateFormat (No Third-Party Library)
If you don't want to add a dependency, you can use the legacy SimpleDateFormat—just be aware it's not thread-safe, so wrap it in a ThreadLocal if you're using it in multi-threaded contexts like coroutines or background tasks.
import java.text.ParseException import java.text.SimpleDateFormat import java.util.* val dateStr = "2019-02-15T00:00:00" // Define the format matching your input string val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US) // Set the timezone to ensure consistent parsing—use UTC unless your business logic requires local time sdf.timeZone = TimeZone.getTimeZone("UTC") try { val date = sdf.parse(dateStr) val millisecondsSinceUnixEpoch = date?.time ?: 0 // date.time gives epoch milliseconds println("EPOCH $millisecondsSinceUnixEpoch") } catch (e: ParseException) { // Handle parsing failure e.printStackTrace() }
Final Notes
- Prefer ThreeTenABP for most cases: it avoids the pitfalls of
SimpleDateFormat(like thread unsafety and weird edge cases with date parsing) and aligns with modern date/time best practices. - Always be explicit about timezones: whether you use UTC or local time, make sure it's intentional—ambiguous timezones are a common source of bugs.
内容的提问来源于stack exchange,提问作者Jodi Shane

