Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีแปลง ISO 8601 String เป็น Date/Time Object ใน Android ด้วย Kotlin

ตัวอย่างนี้สาธิตวิธี แปลง String รูปแบบ ISO 8601 (เช่น "2020-07-05T09:27:37Z") เป็น Date Object ใน Android โดยใช้ SimpleDateFormat และ java.time (API 26+)

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → New Project → Empty Activity

ขั้นตอนที่ 2: Layout (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="24dp"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:text="ISO 8601 Parser Demo"
        android:textAlignment="center"
        android:textColor="@android:color/holo_green_dark"
        android:textSize="24sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tvResult"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textAlignment="center"
        android:textColor="@android:color/holo_red_dark"
        android:textSize="18sp"
        android:textStyle="bold"
        android:lineSpacingExtra="8dp" />

</RelativeLayout>

ขั้นตอนที่ 3: โค้ดหลัก (MainActivity.kt)

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = "ISO 8601 Parser"

        val tvResult = findViewById(R.id.tvResult)

        val isoString = "2020-07-05T09:27:37Z"

        // 🔑 วิธีที่ 1: SimpleDateFormat (Legacy, ทุก API)
        val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()).apply {
            timeZone = TimeZone.getTimeZone("UTC") // สำคัญ: Z = UTC
        }

        val date: Date = try {
            sdf.parse(isoString)!!
        } catch (e: java.text.ParseException) {
            Date() // Fallback
        }

        // แสดงผล
        val outputFormat = SimpleDateFormat("dd MMM yyyy, HH:mm:ss z", Locale.getDefault())
        tvResult.text = "Input: $isoString\n\nParsed Date:\n${outputFormat.format(date)}"
    }
}

🚀 วิธีทันสมัย: java.time (API 26+ / Android 8.0+)

แนะนำให้ใช้ java.time package (ต้อง enable Core Library Desugaring)

// build.gradle (Module)
android {
    defaultConfig {
        multiDexEnabled true
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    coreLibraryDesugaringEnabled true
}
dependencies {
    coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
}
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

// Parse ISO 8601 (รองรับหลายรูปแบบอัตโนมัติ)
val instant = Instant.parse("2020-07-05T09:27:37Z")
val zonedDateTime = instant.atZone(ZoneId.systemDefault())

// Format
val formatter = DateTimeFormatter.ofPattern("dd MMM yyyy, HH:mm:ss z", Locale.getDefault())
val formatted = zonedDateTime.format(formatter)

// หรือใช้ ZonedDateTime.parse โดยตรง
val zdt = ZonedDateTime.parse("2020-07-05T09:27:37+07:00[Asia/Bangkok]")

📋 รูปแบบ ISO 8601 ที่พบบ่อย

Format ตัวอย่าง Pattern (SimpleDateFormat)
UTC (Zulu) 2020-07-05T09:27:37Z yyyy-MM-dd'T'HH:mm:ss'Z'
With Offset 2020-07-05T09:27:37+07:00 yyyy-MM-dd'T'HH:mm:ssXXX
With Millis 2020-07-05T09:27:37.123Z yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
Date Only 2020-07-05 yyyy-MM-dd

🛠️ ฟังก์ชันช่วย Parse แบบทนทาน (Robust Parser)

fun parseIso8601(isoString: String): Date? {
    val patterns = arrayOf(
        "yyyy-MM-dd'T'HH:mm:ss'Z'",
        "yyyy-MM-dd'T'HH:mm:ssXXX",
        "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
        "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
        "yyyy-MM-dd"
    )

    for (pattern in patterns) {
        try {
            val sdf = SimpleDateFormat(pattern, Locale.US).apply {
                timeZone = TimeZone.getTimeZone("UTC")
                isLenient = false
            }
            return sdf.parse(isoString)
        } catch (e: java.text.ParseException) {
            // Try next pattern
        }
    }
    return null
}

💡 Best Practices

  • ✅ ใช้ java.time (API 26+) เป็นหลัก - Immutable, Thread-safe, รองรับ Timezone ดี
  • ✅ ใช้ ThreeTenABP สำหรับ API < 26 (Legacy)
  • ✅ ตั้ง TimeZone = UTC เมื่อ Parse String ที่มี Z
  • ✅ ใช้ Locale.US สำหรับ Parse (Thai Locale อาจ Parse ไม่ได้)
  • ❌ หลีกเลี่ยง SimpleDateFormat ใน Production (Not Thread-safe, Lenient)

ขั้นตอนที่ 4: AndroidManifest.xml

Manifest มาตรฐาน

ทดสอบ

  1. รันแอป → แสดงวันที่แปลงแล้ว: "05 Jul 2020, 09:27:37 UTC" (หรือ Timezone ของเครื่อง)
  2. ลองเปลี่ยน isoString ในโค้ดเป็นรูปแบบอื่นๆ
  3. ทดสอบกับ java.time หาก minSdkVersion >= 26